<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>Wesley Moore</title>
    <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2</link>
    <description>Posts by Wesley Moore</description>
    <generator>Zola</generator>
    <language>en</language>
    <managingEditor>wes@wezm.net (Wesley Moore)</managingEditor>
    <webMaster>wes@wezm.net (Wesley Moore)</webMaster>
    <atom:link href="https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;rss.xml" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Mon, 26 Jan 2026 11:36:15 +1000</lastBuildDate>
    
    <item>
      <title>Exploring Linux on a LoongArch Mini PC</title>
      <pubDate>Mon, 26 Jan 2026 11:36:15 +1000</pubDate>
      <atom:published>2026-01-26T11:36:15+10:00</atom:published>
      <atom:updated>2026-01-27T09:25:58+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2026&#x2F;loongarch-mini-pc-m700s&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2026&#x2F;loongarch-mini-pc-m700s&#x2F;</guid>
      <description>



&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;&amp;#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;m700s-box.0a57be1b3c878901.jpg&quot; alt=&quot;Photo of the MOREFINE mini PC with its box in the background. The box has the text &amp;#x27;MINI PC&amp;#x27; in large letters outlined in gold, which is reflecting in the top of the PC.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;MOREFINE M700S Mini PC&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Ever the fan of an underdog, I recently acquired a new mini-PC with a
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20260112212150&#x2F;https:&#x2F;&#x2F;www.loongson.cn&#x2F;EN&#x2F;product&#x2F;show?id=11&quot;&gt;Loongson 3A6000 CPU&lt;&#x2F;a&gt;. This CPU uses the LoongArch64 instruction set
architecture (ISA). &lt;code&gt;loongarch64&lt;&#x2F;code&gt; is a 64-bit RISC ISA inspired by MIPS and
RISC-V introduced by Loongson Technology in 2021.
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Loongson&quot;&gt;From Wikipedia&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A Loongson developer described it as “…a new RISC ISA, which is a bit like
MIPS or RISC-V. LoongArch includes a reduced 32-bit version (LA32R), a
standard 32-bit version (LA32S) and a 64-bit version (LA64)”.&lt;sup&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lwn.net&#x2F;Articles&#x2F;861951&#x2F;&quot;&gt;31&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;
The stated rationale was to make Loongson and China not dependent on foreign
technology or authorisation to develop their processor capability, whilst not
infringing on any technology patents.&lt;sup&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.cnx-software.com&#x2F;2021&#x2F;04&#x2F;17&#x2F;loongson-loongarch-cpu-instruction-set-architecture&#x2F;&quot;&gt;32&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;LA64 has 32 64-bit general purpose registers (&lt;code&gt;$r0&lt;&#x2F;code&gt;–&lt;code&gt;$r31&lt;&#x2F;code&gt;). Like RISC-V &lt;code&gt;$r0&lt;&#x2F;code&gt;
is hard-wired to zero. There’s also 32 64-bit floating point registers
(&lt;code&gt;$f0&lt;&#x2F;code&gt;–&lt;code&gt;$f31&lt;&#x2F;code&gt;). The 3A6000 supports two vector extensions (SIMD):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;LSX (Loongson SIMD eXtension) with 128-bit vectors. (&lt;code&gt;$v0&lt;&#x2F;code&gt;–&lt;code&gt;$v31&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;LASX (Loongson Advanced SIMD eXtension) with 256-bit vectors. (&lt;code&gt;$x0&lt;&#x2F;code&gt;–&lt;code&gt;$x31&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Both have 32 registers of their respective size, although they overlap with other
registers. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.kernel.org&#x2F;arch&#x2F;loongarch&#x2F;introduction.html&quot;&gt;kernel.org: Introduction to LoongArch&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;for example, on a core implementing LSX and LASX, the lower 128 bits of &lt;code&gt;$x0&lt;&#x2F;code&gt;
is shared with &lt;code&gt;$v0,&lt;&#x2F;code&gt; and the lower 64 bits of &lt;code&gt;$v0&lt;&#x2F;code&gt; is shared with &lt;code&gt;$f0;&lt;&#x2F;code&gt;
same with all other VRs.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;LoongArch is interesting to me because:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;It’s a different architecture to the vast majority of systems in use today, which
use x86_64 and ARM CPUs.&lt;&#x2F;li&gt;
&lt;li&gt;Performance is better than most RISC-V CPUs currently available.&lt;&#x2F;li&gt;
&lt;li&gt;It’s somewhat Linux first.&lt;&#x2F;li&gt;
&lt;li&gt;It’s supported by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;&quot;&gt;Chimera Linux&lt;&#x2F;a&gt;. As a Chimera package maintainer I thought it would
be handy to have hardware to test on.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Regarding №3: Since neither Windows, nor macOS support the architecture that leaves
Linux-based operating systems as a great option. Therefore Loongson has an
interest in that working well. They have contributed to Linux, musl, and also
did their own initial ports of Debian and Alpine Linux. This kind of makes it
an architecture with Linux-first support.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hardware&quot;&gt;Hardware&lt;&#x2F;h3&gt;
&lt;p&gt;The specific hardware I bought is a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;s.click.aliexpress.com&#x2F;e&#x2F;_c2J96QUj&quot;&gt;MOREFINE M700S mini-PC from
AliExpress&lt;&#x2F;a&gt; (affiliate link). It’s about the size of the original Mac
mini and constructed from aluminium: 15×14.5×5 cm.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;m700s-front.JPG&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;m700s-front.307e8da524133eef.jpg&quot; alt=&quot;Photo of the top and front of the M700S. It&amp;#x27;s a small black computer with a Type-C and two Type-A USB ports on the front along with a power button.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;M700S Front&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;m700s-back.JPG&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;m700s-back.f7311c25eee20f60.jpg&quot; alt=&quot;Back of the M700S showing a variety of ports: DC in, four USB-A, two HDMI, two Ethernet, one 3.5mm audio jack.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;M700S Back&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h4 id=&quot;specifications&quot;&gt;Specifications&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CPU:&lt;&#x2F;strong&gt; Loongson-3A6000 4-core&#x2F;8-thread 64-bit @ 2.5Ghz&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;RAM:&lt;&#x2F;strong&gt; 16Gb DDR4 SO-DIMM, 1 of 2 slots populated&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;GPU:&lt;&#x2F;strong&gt; Loongson LG100&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Chipset:&lt;&#x2F;strong&gt; 7A2000&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Wi-FI:&lt;&#x2F;strong&gt; RTL8821CE 802.11ac PCIe Wireless Network Adapter&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It has a plethora of ports:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Front:
&lt;ul&gt;
&lt;li&gt;1 × USB 3.0 Type-C with PD&lt;&#x2F;li&gt;
&lt;li&gt;2 × USB 3.0 Type-A&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Back:
&lt;ul&gt;
&lt;li&gt;4 × USB 2.0 Type-A&lt;&#x2F;li&gt;
&lt;li&gt;2 × HDMI, supporting up to 4K 30Hz each&lt;&#x2F;li&gt;
&lt;li&gt;2 × Gigabit Ethernet ports&lt;&#x2F;li&gt;
&lt;li&gt;1 × 3.5mm audio jack&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;what-s-inside&quot;&gt;What’s Inside&lt;&#x2F;h4&gt;
&lt;p&gt;Opening the bottom of the case requires removing the four screws in the bottom,
one under each of the rubber feet. In the bottom section is the M.2 slot and
large blower fan.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;m700s-bottom-grill.JPG&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;m700s-bottom-grill.5b2d1ce4c18b5efe.jpg&quot; alt=&quot;Photo of the underside of the M700S with the feet and screws removed. There&amp;#x27;s a four-by-four grid of slots cut into it with the slots alternating between vertical and horizontal. There is a mesh behind the slots.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Underside with feet &amp;amp; screws removed.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;m700s-bottom.JPG&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;m700s-bottom.f54b4b70c84c7b34.jpg&quot; alt=&quot;Inside the bottom of the M700S showing a large blower fan and M.2 NVMe drive.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;M700S with bottom grill removed.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The blower fan runs constantly at a fairly high speed, making it much noisier
than any other computer I own. I contacted MOREFINE about it, and
they confirmed that it was expected:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Does not affect normal operations&lt;br&gt;
There is currently no other way to adjust its noise; this is a normal state&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;figure&gt;
  &lt;audio controls src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;fan-noise.mp3&quot;&gt;&lt;&#x2F;audio&gt;
  &lt;figcaption&gt;Listen to the fan.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;

&lt;p&gt;Opening the top of the case requires removing the two screws on the back panel
above the ports. Then using something thin in one of the screw holes lever the
top up a bit so you can get under it and flip it up. Be careful as it has Wi-Fi and
Bluetooth antennas attached to it.&lt;&#x2F;p&gt;
&lt;p&gt;In the top you get access to the SO-DIMM RAM slots. There’s also space for a
3.5“ SATA SSD or HD. The M700S came with mounting hardware for this and a small SATA
cable that plugs into the ports at the top right.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;m700s-top.JPG&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;m700s-top.8300a4b945230af2.jpg&quot; alt=&quot;M700S with top lid flipped up and being held in place with my hand. Inside is a circuit board filling the space. On the right is a double-decker SO-DIMM RAM slot with one slot populated.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Under the top lid. Note one spare RAM slot, M.2 slot for WiFI&amp;#x2F;Bluetooth, and microphone (bottom right).&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;installing-chimera-linux&quot;&gt;Installing Chimera Linux&lt;&#x2F;h3&gt;
&lt;p&gt;Out of the box it comes with Loongnix installed, an apt-based loongarch
distribution with KDE 5 desktop. It indicates it was built in 2024, but uses
quite dated components. The project seems defunct as the website and package
servers were inaccessible, although perhaps it’s only accessible within China.
No matter, as the goal was always to run Chimera Linux on it. The password to
the Loongnix installation was not readily obvious to me, but a recent post on
the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;duckdb.org&#x2F;2026&#x2F;01&#x2F;06&#x2F;duckdb-on-loongarch-morefine&quot;&gt;DuckDB blog about the same machine&lt;&#x2F;a&gt; had the necessary details.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;loongnix.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;loongnix.b69c2e1ed265267b.png&quot; alt=&quot;Screenshot of KDE System Information showing Loongnix GNU&amp;#x2F;Linux 20 with KDE 5, Qt 5, and Linux kernel 4.19.0.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;KDE System Information in Loongnix.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The BIOS (UEFI) is in Chinese by default. When the machine boots press F2 or down arrow
to enter the UEFI.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;uefi.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;uefi.32d523aaa89d36bd.png&quot; alt=&quot;Screen capture of UEFI in Chinese. There is a dialog in the middle of the screen with the English option highlighted.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;UEFI in Chinese.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The initial option selected when the UEFI starts is the language
selector. Press Enter, then select English. If you save and exit at this point the
UEFI and boot messages will remain in English.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;uefi-english.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;uefi-english.6fc903589a6302b2.png&quot; alt=&quot;Screen capture of UEFI in English. There&amp;#x27;s a menu with the following options: Main, Set Date and Time, Security, Device Manager, Boot Manager, Boot Maintenance Manager, Save &amp;amp; Exit, Continue.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;UEFI in English.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;With that out of the way I booted off a Chimera ISO on a USB stick and followed
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;docs&#x2F;installation&quot;&gt;the instructions&lt;&#x2F;a&gt; for a normal install. There’s nothing out
of the ordinary required for the &lt;code&gt;loongarch64&lt;&#x2F;code&gt; install. The steps are identical
to an x86_64 install, right down to using &lt;code&gt;systemd-boot&lt;&#x2F;code&gt; as the bootloader.
This makes for a refreshing change from Snapdragon X machines, which still
don’t have complete or widespread Linux distribution support.&lt;&#x2F;p&gt;





&lt;figure class=&quot;text-center&quot;&gt;
  &lt;video controls preload=&quot;auto&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;M700S.mp4&quot; poster=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;M700S.mp4.png&quot;  style=&quot;max-height: 450px&quot; aria-label=&quot;Video capture of M700S boot to Chimera login prompt. It starts with a Loongson logo, briefly shows a menu, then starts systemd-boot, and finally starts booting Chimera Linux, finishing at a login prompt.&quot;&gt;&lt;&#x2F;video&gt;
  &lt;figcaption&gt;Video capture of M700S booting to Chimera login prompt.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;With the base installation complete I proceeded to install &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gnome.org&#x2F;&quot;&gt;GNOME&lt;&#x2F;a&gt;. This is
where I ran into my first issue. I could log in with GDM and get to the
desktop, even open a terminal or Firefox but within a few seconds I’d be kicked
back to the GDM login screen. I also tried &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wayfire.org&#x2F;&quot;&gt;Wayfire&lt;&#x2F;a&gt;, and an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.xfce.org&#x2F;&quot;&gt;Xfce&lt;&#x2F;a&gt; Wayland session
with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;labwc.github.io&#x2F;&quot;&gt;Labwc&lt;&#x2F;a&gt;, but all of them yielded EGL-related errors and failed to start. For
example this is the Wayfire output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;II 24-01-26 11:52:43.795 - [backend&#x2F;drm&#x2F;backend.c:202] Initializing DRM backend for &#x2F;dev&#x2F;dri&#x2F;card0 (loongson)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;II 24-01-26 11:52:43.795 - [backend&#x2F;drm&#x2F;drm.c:255] Found 2 DRM CRTCs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;II 24-01-26 11:52:43.796 - [backend&#x2F;drm&#x2F;drm.c:213] Found 4 DRM planes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;II 24-01-26 11:52:43.796 - [render&#x2F;egl.c:206] Supported EGL client extensions: EGL_EXT_client_extensions EGL_EXT_device_base EGL_EXT_device_enumeration EGL_EXT_device_query EGL_EXT_platform_base EGL_KHR_client_get_all_proc_addresses EGL_KHR_debug EGL_EXT_platform_device EGL_EXT_explicit_device EGL_EXT_platform_wayland EGL_KHR_platform_wayland EGL_EXT_platform_x11 EGL_KHR_platform_x11 EGL_EXT_platform_xcb EGL_MESA_platform_gbm EGL_KHR_platform_gbm EGL_MESA_platform_surfaceless&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;EE 24-01-26 11:52:43.802 - [EGL] command: eglInitialize, error: EGL_NOT_INITIALIZED (0x3001), message: &amp;quot;DRI2: failed to create screen&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;EE 24-01-26 11:52:43.807 - [EGL] command: eglInitialize, error: EGL_NOT_INITIALIZED (0x3001), message: &amp;quot;DRI2: failed to create screen&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;EE 24-01-26 11:52:43.809 - [EGL] command: eglInitialize, error: EGL_NOT_INITIALIZED (0x3001), message: &amp;quot;DRI2: failed to load driver&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;EE 24-01-26 11:52:43.809 - [EGL] command: eglInitialize, error: EGL_NOT_INITIALIZED (0x3001), message: &amp;quot;eglInitialize&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;EE 24-01-26 11:52:43.809 - [render&#x2F;egl.c:269] Failed to initialize EGL&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;EE 24-01-26 11:52:43.809 - [render&#x2F;egl.c:572] Failed to initialize EGL context&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;EE 24-01-26 11:52:43.809 - [render&#x2F;gles2&#x2F;renderer.c:804] Could not initialize EGL&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Undeterred, I installed X.Org and started an Xfce X11 session, and all was well.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;xfce.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;xfce.7431ffb308e38770.png&quot; alt=&quot;Screenshot of Xfce 4 desktop with Firefox, Terminal, and sticky notes application open.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Xfce 4&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;m700s-desk.JPG&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;m700s-desk.0a2c9cfd14d3d767.jpg&quot; alt=&quot;MOREFINE on wooden desktop with portable LCD on top. In front is a mechanical keyboard styled after the keyboard on the original Macintosh and a red and white Raspberry Pi mouse.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Miniature computing.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;performance-efficiency&quot;&gt;Performance &amp;amp; Efficiency&lt;&#x2F;h3&gt;
&lt;p&gt;The Loongson-3A6000 is not particularly fast or efficient. At idle it consumes
about 27W and under load it goes up to 65W.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;m700s-power.JPG&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;m700s-power.c2b99909566bf960.jpg&quot; alt=&quot;Photo of a watt-meter in front of the M700S showing 27.7W of power.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Idle power usage.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;As a crude benchmark it scores 4.45 on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;browserbench.org&#x2F;Speedometer3.1&#x2F;&quot;&gt;Speedometer 3.1 browser benchmark&lt;&#x2F;a&gt;
in Firefox.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;speedometer.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;loongarch-mini-pc-m700s&amp;#x2F;speedometer.png&quot; alt=&quot;Screenshot of Speedometer result. It&amp;#x27;s styled like an old car speedometer and has a large number, 4.45 with ± 0.078 under it.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;M700S Speedometer 3.1 benchmark result.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;For comparison the Intel N100 based mini-PC connected to my TV consumes ~7W
when idle and scores 12.7 on Speedometer 3.1. My AMD Ryzen 9950X3D scores 36.7 (and
chews through way more power).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt; All Speedometer tests were done in modern Firefox, with no extensions.
It does appear that as of last year Firefox has JIT support for loongarch64.&lt;&#x2F;p&gt;
&lt;p&gt;Another test I performed was building the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;yeslogic&#x2F;allsorts&quot;&gt;allsorts Rust crate&lt;&#x2F;a&gt; (v0.16.1). On
the LoongArch machine it takes almost 44 seconds. On my Ryzen 9950X3D with
&lt;code&gt;--jobs 4&lt;&#x2F;code&gt; (to make it slightly more comparable) it completes the build in 22
seconds.&lt;&#x2F;p&gt;
&lt;p&gt;So, overall it’s not a particularly efficient machine, and while the performance
is nothing special it does seem readily usable. Browsing JS heavy web applications like
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mattermost&#x2F;mattermost&quot;&gt;Mattermost&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.decentralised.social&#x2F;@wezm&quot;&gt;Mastodon&lt;&#x2F;a&gt; runs fine. Subjectively it feels faster than all the
Raspberry Pi systems I’ve used (up to a Pi 400).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;compatibility&quot;&gt;Compatibility&lt;&#x2F;h3&gt;
&lt;p&gt;One of the reasons I got the machine was for testing software and attempting
to address incompatibilities.&lt;&#x2F;p&gt;
&lt;p&gt;A rudimentary search (&lt;code&gt;rg -g template.py -B 1 broken | rg -A 1 loongarch64&lt;&#x2F;code&gt;) through
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chimera-linux&#x2F;cports&quot;&gt;cports&lt;&#x2F;a&gt;, the Chimera Linux ports collection, revealed this list of packages
marked broken on &lt;code&gt;loongarch64&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;cargo-watch&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“old nix crate, can’t update”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;gocryptfs&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“vendor&#x2F;github.com&#x2F;aperturerobotics&#x2F;jacobsa-crypto&#x2F;cmac&#x2F;hash.go:97:3: undefined: xorBlock”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;comrak&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“linux-raw-sys does not support, can’t bump (semver)”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;git-branchless&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“outdated nix crate, can’t update”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;halloy&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“ring 0.16.20 fails to build”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;kanata&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“outdated nix crate, can’t update”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;lazygit&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“vendor&#x2F;github.com&#x2F;creack&#x2F;pty&#x2F;pty_linux.go:39:8: undefined: _C_uint”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;rclone&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“saferith@v0.33.0&#x2F;arith_decl.go:…: missing function body”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;spotify-player&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“rustix&#x2F;libc interaction garbage strikes again”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;systeroid&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“outdated nix crate, can’t update”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;swww&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“cannot find value &lt;code&gt;MADV_SOFT_OFFLINE&lt;&#x2F;code&gt; in module &lt;code&gt;c&lt;&#x2F;code&gt;”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;tectonic&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“outdated nix crate, can’t update”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;tiny&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“outdated nix crate, can’t update”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user&#x2F;typstyle&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“sigbus in tests”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;main&#x2F;containerd&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“cgo runtime stuff”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;main&#x2F;docker-cli&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“PIC linking issues”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;main&#x2F;grub&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“causes a machine exception at runtime”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;main&#x2F;helvum&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“old nix crate, can’t update”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;main&#x2F;openblas&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;“riscv64&#x2F;loongarch64 dynamic_arch is currently broken”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As you can see the list is pretty small. Most of the software packaged
in cports is compatible.&lt;&#x2F;p&gt;
&lt;p&gt;Many of the broken ports are Rust projects using old versions of the &lt;code&gt;nix&lt;&#x2F;code&gt; or
&lt;code&gt;rustix&lt;&#x2F;code&gt; crates. So far I have looked into &lt;code&gt;systeroid&lt;&#x2F;code&gt;, &lt;code&gt;spotify-player&lt;&#x2F;code&gt;, &lt;code&gt;halloy&lt;&#x2F;code&gt;,
and &lt;code&gt;tiny&lt;&#x2F;code&gt;. For the first two the problematic dependency is deep in the tree via
&lt;code&gt;protobuf-parse&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rustix v0.38.44&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└── which v4.4.2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    └── protobuf-parse v3.7.2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        └── protobuf-codegen v3.7.2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            [build-dependencies]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            └── librespot-protocol v0.8.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                ├── librespot-connect v0.8.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                │   └── spotify_player v0.21.3 (&#x2F;home&#x2F;wmoore&#x2F;src&#x2F;github.com&#x2F;aome510&#x2F;spotify-player&#x2F;spotify_player)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                ├── librespot-core v0.8.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                │   ├── librespot-audio v0.8.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                │   │   └── librespot-playback v0.8.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                │   │       ├── librespot-connect v0.8.0 (*)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                │   │       └── spotify_player v0.21.3 (&#x2F;home&#x2F;wmoore&#x2F;src&#x2F;github.com&#x2F;aome510&#x2F;spotify-player&#x2F;spotify_player)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It seems that the pure Rust 3.x series of &lt;code&gt;protobuf-parse&lt;&#x2F;code&gt; is not maintained.
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;protocolbuffers&#x2F;protobuf&#x2F;tree&#x2F;3a3560bb87058b31ac5f094fcd4dbbf6f90dddaf&#x2F;rust&#x2F;release_crates&#x2F;protobuf#v4-ownership-and-implementation-change&quot;&gt;The new 4.x series managed by Google&lt;&#x2F;a&gt; has a totally
different API with C dependencies:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;V4 of this crate is officially supported by the Protobuf team at Google.
Prior major versions were developed by as a community project by stepancheg
who generously donated the crate name to Google.&lt;&#x2F;p&gt;
&lt;p&gt;V4 is a completely new implementation with a different API, as well as a
fundamentally different approach than prior versions of this crate. It
focuses on delivering a high-quality Rust API which is backed by either a
pure C implementation (upb) or the Protobuf C++ implementation. This choice
was made for performance, feature parity, development velocity, and security
reasons. More discussion about the rationale and design philosophy can be
found at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;protobuf.dev&#x2F;reference&#x2F;rust&#x2F;&quot;&gt;https:&#x2F;&#x2F;protobuf.dev&#x2F;reference&#x2F;rust&#x2F;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It is not planned for the V3 pure Rust lineage to be actively developed going
forward. While it is not expected to receive significant further development,
as a stable and high quality pure Rust implementation, many open source
projects may reasonably continue to stay on the V3 API.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;So, &lt;code&gt;spotify-player&lt;&#x2F;code&gt; and &lt;code&gt;systeroid&lt;&#x2F;code&gt; are difficult to fix. The ideal fix would
be a new release of the 3.x series of the protobuf crates. They would bump
their &lt;code&gt;rustix&lt;&#x2F;code&gt; dependency by way of updating &lt;code&gt;which&lt;&#x2F;code&gt;. However, since that
lineage is unmaintained it’s unlikely. Given I use neither tool personally I
gave up on them, and moved on to &lt;code&gt;tiny&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;tiny&lt;&#x2F;code&gt; is a command line IRC client that I do use. It turned out to be easier
to fix: use a newer version of the &lt;code&gt;nix&lt;&#x2F;code&gt; crate. I have made that change and
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;osa1&#x2F;tiny&#x2F;pull&#x2F;459&quot;&gt;opened a PR upstream&lt;&#x2F;a&gt;, which has been merged.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, there was a new release of &lt;code&gt;halloy&lt;&#x2F;code&gt;, a GUI IRC client. The new version
uses an updated version of &lt;code&gt;ring&lt;&#x2F;code&gt;, which fixed the build issues. I’ve &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chimera-linux&#x2F;cports&#x2F;pull&#x2F;5123&quot;&gt;opened a
PR&lt;&#x2F;a&gt; to bump the package to that version. Over time I plan to look
into some of the other broken projects as well.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;So there we have it. A small foray into modern computing on a new and
interesting RISC architecture. Back in the day there were all sorts of ISAs:
alpha, mips, arm, x86, m68k, powerpc, sparc to name a few. Most of these have
died out or are expensive to acquire. That’s why it’s interesting to me to see
a new affordable one spring up in recent times, and get adopted in the Linux
ecosystem relatively quickly. Happy computing.&lt;&#x2F;p&gt;
&lt;h3&gt;Comments&lt;&#x2F;h3&gt;

&lt;ul&gt;
  
  &lt;li&gt;&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;mastodon.decentralised.social&amp;#x2F;@wezm&amp;#x2F;115961431304905606&quot;&gt;Fediverse&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  
  
  &lt;li&gt;&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;bsky.app&amp;#x2F;profile&amp;#x2F;wezm.net&amp;#x2F;post&amp;#x2F;3mddddziqos2a&quot;&gt;Bluesky&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  
  
  &lt;li&gt;&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;lobste.rs&amp;#x2F;s&amp;#x2F;ve0986&amp;#x2F;exploring_linux_on_loongarch_mini_pc&quot;&gt;Lobsters&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  
  
  &lt;li&gt;&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;news.ycombinator.com&amp;#x2F;item?id=46762098&quot;&gt;Hacker News&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  
  
&lt;&#x2F;ul&gt;

</description>
    </item>
    
    <item>
      <title>Tech Stack 2026</title>
      <pubDate>Fri, 09 Jan 2026 08:36:37 +1000</pubDate>
      <atom:published>2026-01-09T08:36:37+10:00</atom:published>
      <atom:updated>2026-02-03T20:53:28+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2026&#x2F;tech-stack&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2026&#x2F;tech-stack&#x2F;</guid>
      <description>&lt;p&gt;A summary of my personal tech stack as we start 2026. I previously did one of these
in &lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;tech-stack&#x2F;&quot;&gt;2024&lt;&#x2F;a&gt;. I was prompted to write this one
by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dbushell.com&#x2F;2026&#x2F;01&#x2F;08&#x2F;app-defaults&#x2F;&quot;&gt;David Bushell&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rknight.me&#x2F;blog&#x2F;app-defaults-2026&#x2F;&quot;&gt;Robb Knight&lt;&#x2F;a&gt;’s App Defaults 2026 posts.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2026&amp;#x2F;tech-stack&amp;#x2F;desktop.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;desktop.16e9338d82a8b4e8.jpg&quot; alt=&quot;A photo of my standing desk. There&amp;#x27;s a large display showing a Chimera Linux GNOME desktop, on the right is Macintosh LC 475. In front of the monitor is: a PS4 controller, TI voyage 200 graphing calculator, tenkeyless mechanical keyboard, mouse, and Boox Go 103 e-Note.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;My desk in late 2025.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h3 id=&quot;software&quot;&gt;Software&lt;&#x2F;h3&gt;
&lt;p&gt;My dotfiles are public so if you’re curious about the configuration of some
of the tools mentioned below check out &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;dotfiles&quot;&gt;github.com&#x2F;wezm&#x2F;dotfiles&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Operating System:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;archlinux.org&#x2F;&quot;&gt;Arch Linux&lt;&#x2F;a&gt; on my desktop, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;&quot;&gt;Chimera Linux&lt;&#x2F;a&gt; on my laptop. Each can also
dual boot the other OS as well. I like rolling release distros for personal computing: you never
have to do big upgrades, and everything is always up to date.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Desktop Environment:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.system76.com&#x2F;tags&#x2F;COSMIC%20DE&quot;&gt;COSMIC&lt;&#x2F;a&gt; on my desktop, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gnome.org&#x2F;&quot;&gt;GNOME&lt;&#x2F;a&gt; on my laptop. I like that COSMIC
has window tiling built in. Eventually I’d like to get the laptop on COSMIC too.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Text Editor:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;zed.dev&#x2F;&quot;&gt;Zed&lt;&#x2F;a&gt; for most coding, backed up with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;neovim.io&#x2F;&quot;&gt;Neovim&lt;&#x2F;a&gt; for ancillary editing.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Merge Tool:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.scootersoftware.com&#x2F;&quot;&gt;Beyond Compare&lt;&#x2F;a&gt;. If you don’t have a proper tool that can do three-way merges
to resolve git merge conflicts, often automatically, you’re missing out.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Web Browser:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;getfirefox.com&#x2F;&quot;&gt;Firefox&lt;&#x2F;a&gt;. People complain a lot about Mozilla, and some of it
is justified, but it’s still a better choice than the alternatives.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;RSS:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;feedbin.com&#x2F;&quot;&gt;Feedbin&lt;&#x2F;a&gt;. I’m a heavy user of RSS, (a quick check says I’m subscribed to 3739 feeds)
and have been a paid Feedbin subscriber since the demise of Google Reader, it’s great.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Email:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.fastmail.com&#x2F;&quot;&gt;Fastmail&lt;&#x2F;a&gt;. I’ve been with them since 2012 and it continues to be 100% worth the money.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Calendar:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.fastmail.com&#x2F;&quot;&gt;Fastmail&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Social Media:&lt;&#x2F;strong&gt; Personal &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;joinmastodon.org&#x2F;&quot;&gt;Mastodon&lt;&#x2F;a&gt; instance accessed via the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;phanpy.social&#x2F;&quot;&gt;Phanpy&lt;&#x2F;a&gt; client.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Terminal Emulator:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alacritty.org&#x2F;&quot;&gt;Alacritty&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Shell:&lt;&#x2F;strong&gt;  &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.zsh.org&#x2F;&quot;&gt;Z Shell&lt;&#x2F;a&gt; as has been the case for at least 18 year now.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Text Expander:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;espanso.org&#x2F;&quot;&gt;Espanso&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;CLI Tools:&lt;&#x2F;strong&gt;  I use a bunch of CLI tools, aside from standard POSIX&#x2F;UNIX tools I use these a lot:
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;bat&quot;&gt;bat&lt;&#x2F;a&gt; — &lt;code&gt;cat&lt;&#x2F;code&gt; with syntax highlighting&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;fd&quot;&gt;fd&lt;&#x2F;a&gt; — file finder&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;junegunn&#x2F;fzf&quot;&gt;fzf&lt;&#x2F;a&gt; — fuzzy finder (integrated with zsh and nvim)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lsd-rs&#x2F;lsd&quot;&gt;lsd&lt;&#x2F;a&gt; — &lt;code&gt;ls&lt;&#x2F;code&gt; but better&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mergiraf.org&#x2F;&quot;&gt;mergiraf&lt;&#x2F;a&gt; — syntax aware merge conflict resolution tool&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;ripgrep&quot;&gt;ripgrep&lt;&#x2F;a&gt; — clever regex search&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonas&#x2F;tig&quot;&gt;tig&lt;&#x2F;a&gt; — git TUI&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Morganamilo&#x2F;paru&quot;&gt;paru&lt;&#x2F;a&gt; — &lt;code&gt;pacman&lt;&#x2F;code&gt; wrapper&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Notes:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;obsidian.md&#x2F;&quot;&gt;Obsidian&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;File System&lt;&#x2F;strong&gt;: ZFS, nothing else can be trusted with my data.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Password Manager:&lt;&#x2F;strong&gt;  &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;1password.com&#x2F;&quot;&gt;1Password&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Photo Library:&lt;&#x2F;strong&gt;  &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.gnome.org&#x2F;Apps&#x2F;Shotwell&quot;&gt;Shotwell&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Image Editor:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gimp.org&#x2F;&quot;&gt;GIMP&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Audio Editor:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.audacityteam.org&#x2F;&quot;&gt;Audacity&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;“AI” Coding Tools&lt;&#x2F;strong&gt;: None really. I sometimes have the default Zeta completion
in Zed but that’s about all.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;hardware&quot;&gt;Hardware&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;desktop-computer&quot;&gt;Desktop Computer&lt;&#x2F;h4&gt;
&lt;p&gt;My computer is a desktop machine that I assembled myself in 2023, it has had a few
upgrades since I last documented it—how great are upgradeable computers!&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CPU:&lt;&#x2F;strong&gt; AMD Ryzen 9 9950X3D 16-Core&#x2F;32-Thread Processor&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;RAM:&lt;&#x2F;strong&gt; 64 Gb DDR5 6000 MT&#x2F;s&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Storage:&lt;&#x2F;strong&gt;
&lt;ul&gt;
&lt;li&gt;Root disk: Crucial T700 1TB PCIe Gen5 NVMe M.2 SSD, ext4&lt;&#x2F;li&gt;
&lt;li&gt;&#x2F;home: 2x WD_BLACK 2TB SN850X PCIe Gen4 NVMe, ZFS mirror&lt;&#x2F;li&gt;
&lt;li&gt;Extra storage: 3x Samsung 1Tb Evo 870 SSDs, ZFS RAIDZ-1&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;GPU:&lt;&#x2F;strong&gt; AMD Radeon RX 9060 XT with 16Gb VRAM&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Display:&lt;&#x2F;strong&gt; ASUS PA32QCV 6016x3384 6K display at 2x scaling&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Web Cam:&lt;&#x2F;strong&gt; Razer Kiyo&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Microphone:&lt;&#x2F;strong&gt; Audio Technica ATR2100X&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Mouse:&lt;&#x2F;strong&gt; Logi MX Vertical mouse + BenQ ZOWIE FK2-C mouse&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Keyboard:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20250905143853&#x2F;https:&#x2F;&#x2F;www.keebmonkey.com&#x2F;en-au&#x2F;products&#x2F;wk870&quot;&gt;WK870 mechanical keyboard&lt;&#x2F;a&gt; with Gateron G Pro 2.0 brown switches&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
    </item>
    
    <item>
      <title>Doing My Day Job on Chimera Linux</title>
      <pubDate>Sun, 29 Jun 2025 12:05:41 +1000</pubDate>
      <atom:published>2025-06-29T12:05:41+10:00</atom:published>
      <atom:updated>2025-06-30T13:45:50+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2025&#x2F;daily-driving-chimera-for-work&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2025&#x2F;daily-driving-chimera-for-work&#x2F;</guid>
      <description>



&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;daily-driving-chimera-for-work&amp;#x2F;cane-fields.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;cane-fields.56667a93ef582a31.jpg&quot; alt=&quot;Landscape scene with trimmed, green grass in the foreground, sugar cane fields in the middle, and foggy bushland mountains in the background topped with a clear blue sky.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;View of sugar cane fields from where I was working.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;aside class=&quot;float-right&quot;&gt;
  &lt;div class=&quot;emoji text-center&quot;&gt;💡&lt;&#x2F;div&gt;
  &lt;strong&gt;What Is Chimera Linux?&lt;&#x2F;strong&gt;

  &lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;&quot;&gt;Chimera Linux&lt;&#x2F;a&gt; is a unique from-scratch
Linux distribution created in 2021 by &lt;a href=&quot;https:&#x2F;&#x2F;q66.moe&#x2F;&quot;&gt;q66&lt;&#x2F;a&gt; that
combines the &lt;a href=&quot;https:&#x2F;&#x2F;www.kernel.org&#x2F;&quot;&gt;Linux kernel&lt;&#x2F;a&gt;, &lt;a
href=&quot;https:&#x2F;&#x2F;musl.libc.org&#x2F;&quot;&gt;musl libc&lt;&#x2F;a&gt;, &lt;a
href=&quot;https:&#x2F;&#x2F;www.freebsd.org&#x2F;&quot;&gt;FreeBSD&lt;&#x2F;a&gt; userland, &lt;a
href=&quot;https:&#x2F;&#x2F;wiki.alpinelinux.org&#x2F;wiki&#x2F;Alpine_Package_Keeper&quot;&gt;apk package
manager&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;davmac.org&#x2F;projects&#x2F;dinit&#x2F;&quot;&gt;dinit binary init
system&lt;&#x2F;a&gt;. The whole system is built with the &lt;a href=&quot;https:&#x2F;&#x2F;llvm.org&#x2F;&quot;&gt;LLVM&lt;&#x2F;a&gt; toolchain.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;Since &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.decentralised.social&#x2F;@wezm&#x2F;110545610926304063&quot;&gt;I started running the first alpha&lt;&#x2F;a&gt; release of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;&quot;&gt;Chimera Linux&lt;&#x2F;a&gt; in
2023, my goal has been to eventually migrate to Chimera as my primary operating
system. This includes personal tinkering as well as for my job as a programmer.
A recent trip to Central Queensland afforded an opportunity to test the waters
of daily driving Chimera Linux for work. The trip spanned two weeks and involved
working remotely (as usual) during the week, and sightseeing on the weekends.&lt;&#x2F;p&gt;
&lt;p&gt;This post details some of the barriers that I encountered and how I worked
around them. While the post is focussed on Chimera Linux, the details probably
apply to most distributions using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;musl.libc.org&#x2F;&quot;&gt;musl libc&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;This is not the first time I’ve tried using a musl based distribution while on
a working holiday. I did the same with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bitcannon.net&#x2F;post&#x2F;huawei-matebook-x-pro-void-linux&#x2F;&quot;&gt;Void Linux (musl) in 2019&lt;&#x2F;a&gt;.
So, I’m not going into this 100% blind.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;preparations&quot;&gt;Preparations&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;hardware&quot;&gt;Hardware&lt;&#x2F;h4&gt;
&lt;p&gt;The last time I did working travel like this I used Windows + WSL on my
&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;yoga-7x-snapdragon-developer-review&#x2F;&quot;&gt;Lenovo Yoga Slim 7x ARM laptop&lt;&#x2F;a&gt;.
This worked pretty well, but since then Windows has become unstable on that machine.
Every few days it crashes and reboots with the error:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The system has rebooted without cleanly shutting down first. This error is
caused because the system stopped responding and the hardware watchdog
triggered a system reset.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Usually I don’t see the reboot. I just open it and it has restarted—
everything I had open is gone. Apparently &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@Darep&#x2F;114661470696786319&quot;&gt;I’m not the only one
experiencing the issue&lt;&#x2F;a&gt;. I’ve tried various things to fix it, including a
clean reinstall, but the issue remains.&lt;&#x2F;p&gt;
&lt;p&gt;After the reinstall failed to fix Windows &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.decentralised.social&#x2F;@wezm&#x2F;114506161082726738&quot;&gt;I finally got around to installing
Chimera Linux on it&lt;&#x2F;a&gt;. However, Linux support for Snapdragon X based laptops is
still a work in progress. The main issues are: the webcam doesn’t work, and the
battery drains pretty quickly while suspended. It is more stable than Windows though.&lt;&#x2F;p&gt;
&lt;p&gt;While usable, this felt a little too bleeding edge for a machine I’d need to
work from, so I picked up a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.lenovo.com&#x2F;au&#x2F;en&#x2F;p&#x2F;laptops&#x2F;yoga&#x2F;yoga-2-in-1-series&#x2F;lenovo-yoga-7-2-in-1-gen-10-14-inch-amd&#x2F;83jrcto1wwau1&quot;&gt;Lenovo Yoga 7 2-in-1 (14&quot;, Gen 10)&lt;&#x2F;a&gt; laptop
with a decent &lt;abbr title=&quot;End of Financial Year&quot;&gt;EoFY&lt;&#x2F;abbr&gt; discount. It’s
got a AMD Ryzen AI 7 350 CPU with 8 cores (4×Zen 5, 4×Zen 5c), 32 Gb RAM, and
1Tb NVMe. I was also curious to see what things were looking like on the x86
side of the fence (my last x86_64 laptop was from 2022).&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;daily-driving-chimera-for-work&amp;#x2F;Lenovo Yoga 7 2-in-1 (14&amp;quot;, Gen 10).jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;Lenovo Yoga 7 2-in-1 (14&amp;quot;, Gen 10).3af9c7b0851c306a.jpg&quot; alt=&quot;Lenovo Yoga 7 2-in-1 sitting open with two windows in GNOME open&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Lenovo Yoga 7 2-in-1 (14&amp;quot; Gen 10)&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h4 id=&quot;software&quot;&gt;Software&lt;&#x2F;h4&gt;
&lt;p&gt;Installation of Chimera on the new laptop was straightforward:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Disable Secure Boot&lt;&#x2F;li&gt;
&lt;li&gt;Boot from live USB flash drive&lt;&#x2F;li&gt;
&lt;li&gt;Follow &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;docs&#x2F;installation&quot;&gt;installation instructions&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;daily-driving-chimera-for-work&amp;#x2F;fastfetch-chimera-yoga7.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;fastfetch-chimera-yoga7.a41f9cd4178fa0d2.png&quot; alt=&quot;Screenshot of a terminal window with the output of fastfetch.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;fastfetch output on the Yoga 7 2-in-1.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The product I work on, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.princexml.com&#x2F;&quot;&gt;Prince&lt;&#x2F;a&gt;, is implemented in a mixture of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mercurylang.org&#x2F;&quot;&gt;Mercury&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt;.
I created &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;cports&#x2F;blob&#x2F;4123e5be51d28dc084642bbd917d009b779d4441&#x2F;user&#x2F;mercury&#x2F;template.py&quot;&gt;an apk package for the specific version of the Mercury&lt;&#x2F;a&gt; compiler we use.
The main issue I encountered here was Chimera’s &lt;code&gt;fortify-headers&lt;&#x2F;code&gt; did not play nice
with the Mercury build, so I added a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;cports&#x2F;blob&#x2F;4123e5be51d28dc084642bbd917d009b779d4441&#x2F;user&#x2F;mercury&#x2F;patches&#x2F;no-fortify.patch&quot;&gt;patch to disable it&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We also use a pinned version of Rust for building Prince, but &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rustup.rs&#x2F;&quot;&gt;rustup&lt;&#x2F;a&gt; toolchains
don’t work on Chimera—it’s only possible to run the packaged
version of Rust. Building Prince with this version of Rust was fine.&lt;&#x2F;p&gt;
&lt;p&gt;With Prince built, the next hurdle to jump over was passing the test suite. This
initially failed due to different output produced by &lt;code&gt;zlib-ng&lt;&#x2F;code&gt; used by Chimera,
compared to &lt;code&gt;zlib&lt;&#x2F;code&gt; used by other distros. This was easily fixed by telling our
build system to build &lt;code&gt;zlib&lt;&#x2F;code&gt; when on Chimera Linux (like we already do for some
other systems).&lt;&#x2F;p&gt;
&lt;p&gt;Now most of the test suite passed, but there were still some failing tests. I
tracked this down to a difference in the regular expression syntax of GNU &lt;code&gt;diff&lt;&#x2F;code&gt;
and BSD &lt;code&gt;diff&lt;&#x2F;code&gt;. We use the following &lt;code&gt;diff&lt;&#x2F;code&gt; invocation to compare PDFs in our test
suite:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;diff_pdf&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; () {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    ( [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ! -s&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; ] &amp;amp;&amp;amp; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ! -s&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; ] )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ||&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    $DIFF&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; -u -a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      --show-function-line=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^[0-9][0-9]* 0 obj&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;	\&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^&#x2F;FontName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^&#x2F;BaseFont&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^&#x2F;MediaBox&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^&#x2F;BleedBox&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^&#x2F;TrimBox&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^&amp;lt;pdf:Producer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^&amp;lt;xmp:CreateDate&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^&amp;lt;xmp:MetadataDate&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^&amp;lt;xmp:ModifyDate&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^&amp;lt;xmpMM:DocumentID&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^\(&amp;lt;&amp;lt;\)*&#x2F;ModDate&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^\(&amp;lt;&amp;lt;\)*&#x2F;CreationDate&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^\(&amp;lt;&amp;lt;\)*&#x2F;Producer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^[0-9]* 00000 n $&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^[1-9][0-9]*$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;^&#x2F;ID \[&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notably, grouping in GNU &lt;code&gt;diff&lt;&#x2F;code&gt; uses escaped parenthesis &lt;code&gt;\(&lt;&#x2F;code&gt;, &lt;code&gt;\)&lt;&#x2F;code&gt;, which is
what our test harness uses. Whereas BSD &lt;code&gt;diff&lt;&#x2F;code&gt; uses the more conventional
unescaped version &lt;code&gt;(&lt;&#x2F;code&gt;, &lt;code&gt;)&lt;&#x2F;code&gt;. If I edited the &lt;code&gt;diff_pdf&lt;&#x2F;code&gt; function to remove the
escaped parenthesis, then the test suite passed. I didn’t want to carry this change
specifically for my environment though. For various reasons I also didn’t come
up with a satisfying way to automatically switch syntax, so I &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chimera-linux&#x2F;cports&#x2F;pull&#x2F;4309&quot;&gt;packaged GNU
diffutils&lt;&#x2F;a&gt;. Our test runner is already set up to prefer using a
&lt;code&gt;gdiff&lt;&#x2F;code&gt; binary when present, so with my &lt;code&gt;diffutils&lt;&#x2F;code&gt; package installed the test
suite passed without modification.&lt;&#x2F;p&gt;
&lt;p&gt;The last piece of the puzzle was a text editor. For work I use a mix of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;zed.dev&#x2F;&quot;&gt;Zed&lt;&#x2F;a&gt;
and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jetbrains.com&#x2F;rust&#x2F;&quot;&gt;Rust Rover&lt;&#x2F;a&gt; (although now that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;zed.dev&#x2F;blog&#x2F;debugger&quot;&gt;Zed has debugger support&lt;&#x2F;a&gt; Rust
Rover’s days may be numbered). I have previously got Rust Rover running on
Chimera, but when I tried again it failed to start with an &lt;code&gt;IOException&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I turned my attention to Zed. Zed used to be packaged for Chimera, but it
started depending on the &lt;code&gt;livekit&lt;&#x2F;code&gt; crate, which downloads a pre-compiled
&lt;code&gt;libwebrtc.so&lt;&#x2F;code&gt; in &lt;code&gt;build.rs&lt;&#x2F;code&gt;. Unsurprisingly this pre-built library does not work
on Chimera, and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zed-industries&#x2F;zed&#x2F;issues&#x2F;22374&quot;&gt;Zed project are unwilling to make LiveKit
optional&lt;&#x2F;a&gt;. The Zed project in general is extremely &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zed-industries&#x2F;zed&#x2F;issues&#x2F;12589&quot;&gt;free-and-easy with
downloading pre-compiled binaries&lt;&#x2F;a&gt; (that don’t run on Chimera),
and despite this being raised, acknowledged, and work started in October 2024
it remains unresolved as of 29 Jun 2025.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately Zed is open-source, and Chimera community member &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;panekj&#x2F;zed&#x2F;tree&#x2F;pj&#x2F;release&#x2F;0.194.0&quot;&gt;pj has a
fork&lt;&#x2F;a&gt; that strips it down and makes it buildable on Chimera. He also
resurrected the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;panekj&#x2F;cports&#x2F;blob&#x2F;821d3ebe00e9d83c33c0b364bb09eeebcbf8e444&#x2F;user&#x2F;zed&#x2F;template.py&quot;&gt;cports package&lt;&#x2F;a&gt;. I built this and my editor
situation was solved. I could also fall back to Neovim if needed too.&lt;&#x2F;p&gt;
&lt;p&gt;Four other programs that are important to my workflow are &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;1password.com&#x2F;&quot;&gt;1Password&lt;&#x2F;a&gt;,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;obsidian.md&#x2F;&quot;&gt;Obsidian&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;signal.org&#x2F;&quot;&gt;Signal&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.scootersoftware.com&#x2F;&quot;&gt;Beyond Compare&lt;&#x2F;a&gt;. 1Password, Obsidian, and Signal
were readily installed via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;flatpak.org&#x2F;&quot;&gt;Flatpak&lt;&#x2F;a&gt; packages on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;flathub.org&#x2F;&quot;&gt;Flathub&lt;&#x2F;a&gt;. For Beyond Compare
I set up a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;distrobox.it&#x2F;&quot;&gt;Distrobox&lt;&#x2F;a&gt; Arch Linux container. This worked great, whenever I
needed to call upon Beyond Compare’s merge conflict resolution abilities I ran:
&lt;code&gt;distrobox enter arch -- git mergetool&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;After confirming that the webcam worked in Signal (we do meetings in Signal) I
was good to travel!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-did-it-go&quot;&gt;How did it go?&lt;&#x2F;h3&gt;
&lt;p&gt;Totally fine. My preparations paid off and I didn’t encounter any blockers to
getting my work done. I did run into one tool that I use occasionally
that was not available for Chimera: the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;archlinux.org&#x2F;packages&#x2F;extra&#x2F;x86_64&#x2F;mupdf-tools&#x2F;&quot;&gt;MuPDF tools&lt;&#x2F;a&gt;. I briefly
considered packaging them, but after seeing &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.archlinux.org&#x2F;archlinux&#x2F;packaging&#x2F;packages&#x2F;mupdf&#x2F;-&#x2F;blob&#x2F;15c222524843735a69387cfa9c9b97a4b1f914d7&#x2F;PKGBUILD&quot;&gt;the Arch PKGBUILD&lt;&#x2F;a&gt; I
promptly aborted that plan, and installed them in the Arch Distrobox container
with &lt;code&gt;paru -S mupdf-tools&lt;&#x2F;code&gt; and carried on.&lt;&#x2F;p&gt;
&lt;p&gt;As a Rust developer (i.e. someone that primarily develops in Rust) one of the
main annoyances on Chimera is missing support for &lt;code&gt;rustup&lt;&#x2F;code&gt;. Being able to
install and switch to different Rust versions is nice at times, but the main
issue is being unable to install targets for cross-compiling. This is needed when
targeting microcontrollers, or say a Raspberry Pi Zero. I also can’t do &lt;code&gt;rustup doc --std&lt;&#x2F;code&gt; to open an offline copy of the standard library documentation (it
seems the &lt;code&gt;rust-doc&lt;&#x2F;code&gt; package in Chimera only contains licences). I think the
solution to this will have to be Distrobox for now.&lt;&#x2F;p&gt;
&lt;p&gt;Having said that, &lt;code&gt;rustup&lt;&#x2F;code&gt; is fully statically linked and &lt;em&gt;does&lt;&#x2F;em&gt; run on
Chimera. The issue is that the toolchains that it installs do not work. There
is a community project publishing &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;musl.rs&#x2F;&quot;&gt;builds of Rust nightly at
&lt;code&gt;musl.rs&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; that are compatible with Chimera. This can come in handy
in the odd occasions where a nightly toolchain is needed.&lt;&#x2F;p&gt;
&lt;p&gt;To make it easier to switch between the system rust and nightly rust I installed rustup with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;RUSTUP_INIT_SKIP_PATH_CHECK=1 sh rustup-init --default-toolchain none&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then linked the local toolchains:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rustup toolchain link system &#x2F;usr&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rustup default system&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rustup toolchain link musl-nightly ~&#x2F;.local&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This makes it easy to use the nightly version only when needed. For
example, I wanted to format the code in a doc-test, which is not stable yet. I
could do this with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rustup musl-nightly cargo fmt -- --unstable-features --config format_code_in_doc_comments=true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;small&gt;(for some reason the usual &lt;code&gt;cargo +musl-nightly fmt ...&lt;&#x2F;code&gt; invocation did not work)&lt;&#x2F;small&gt;&lt;&#x2F;p&gt;
&lt;p&gt;On Distrobox, I reached for an Arch Linux container as my fallback distro, but
Arch only officially supports x86_64. This poses a challenge if I am to
replicate this setup on the ARM-based Yoga Slim 7x. For this reason I think
I’ll switch to using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;voidlinux.org&#x2F;&quot;&gt;Void Linux&lt;&#x2F;a&gt; as my Distrobox fallback in the future. (In
case it isn’t obvious I have no desire to use Debian&#x2F;Ubuntu if I can avoid it).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;thoughts-on-the-laptop&quot;&gt;Thoughts on the laptop&lt;&#x2F;h3&gt;
&lt;p&gt;As an aside, how did a current generation AMD laptop compare to the year old
ARM-based Yoga Slim 7x? Overall fine, although I still strongly prefer the 7x.
The 7x runs cooler, and is thinner and lighter, which makes it more pleasant to
move and hold. The AMD machine seemed to run hotter, and fire up the fan more.
In particular, the fan seemed to run whenever it was charging and being used at
the same time. Battery life seemed decent. I was able to do about 7 hours of
programming work on Prince, before it needed charging.&lt;&#x2F;p&gt;
&lt;p&gt;As in my &lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;yoga-7x-snapdragon-developer-review&#x2F;&quot;&gt;original review of the Yoga Slim 7x&lt;&#x2F;a&gt;
I used a clean build of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gleam.run&#x2F;&quot;&gt;Gleam&lt;&#x2F;a&gt; compiler to do some crude benchmarking:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;gleam-lang&#x2F;gleam&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cd gleam&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git checkout v1.11.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cargo fetch --locked&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cargo clean &amp;amp;&amp;amp; time cargo build --release --locked&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Yoga Slim 7x specifications for reference:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;daily-driving-chimera-for-work&amp;#x2F;fastfetch-chimera-yoga7x.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;fastfetch-chimera-yoga7x.06fb1546eb56419e.png&quot; alt=&quot;Screenshot of a terminal window with the output of fastfetch.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;fastfetch output on the Yoga 7x&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I ran the Gleam build a couple of times on both laptops while plugged into power
using Rust 1.87.0. These were the results:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Device&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;CPU&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Topology&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Arch&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Time&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Yoga Slim 7x&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Snapdragon X Elite&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;12c&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;ARM&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;0:49&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Yoga 7 2-in-1&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;AMD Ryzen AI 7 350&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;8c&#x2F;16t&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;x86_64&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1:21&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;So, the 7x with its 4 additional cores is a good bit faster.&lt;&#x2F;p&gt;
&lt;p&gt;The 2-in-1 does have USB-A ports, which is nice. I didn’t make use of the
2-in-1 functionality, or the included stylus. It doesn’t seem like GNOME
detects the different lid configurations, and the on-screen keyboard didn’t
appear when the screen was flipped around (although this could be a setting I
missed).&lt;&#x2F;p&gt;
&lt;p&gt;In the end the 7x is cooler, faster, lighter, and thinner. I plan to sell the
2-in-1.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;The experiment was a success. This has given me more confidence to pursue
setting up my dev environment on my primary desktop computer, but there’s still
some things to address. Currently that machine runs the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;system76.com&#x2F;cosmic&quot;&gt;COSMIC Desktop&lt;&#x2F;a&gt;, which
is not packaged for Chimera, and doesn’t look like it would be a lot of fun to
implement.&lt;&#x2F;p&gt;
&lt;p&gt;The switch to COSMIC was prompted by X11 having unfixable tearing on my Intel
B580 GPU. Prior to that I ran &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;awesomewm.org&#x2F;&quot;&gt;AwesomeWM&lt;&#x2F;a&gt; for many years, and would like to
explore &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pinnacle-comp&#x2F;pinnacle&quot;&gt;Pinnacle&lt;&#x2F;a&gt; (an Awesome-like Wayland compositor) as an alternative. It
is simpler to package than COSMIC, and I’ve already &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.decentralised.social&#x2F;@wezm&#x2F;114712597249998867&quot;&gt;partially completed
it&lt;&#x2F;a&gt;. I shall continue to check things off the TODO list and
will get there eventually.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Trying to Get Chimera Linux Running on Pentium Class Hardware</title>
      <pubDate>Sun, 04 May 2025 08:09:28 +1000</pubDate>
      <atom:published>2025-05-04T08:09:28+10:00</atom:published>
      <atom:updated>2025-05-04T19:51:02+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2025&#x2F;chimera-i586&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2025&#x2F;chimera-i586&#x2F;</guid>
      <description>&lt;p&gt;Since declaring in &lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2025&#x2F;website-fit-for-1999&#x2F;&quot;&gt;my last post&lt;&#x2F;a&gt;
that “it was time to return to slightly less frivolous projects for a bit” I
instead spent the last week-and-change attempting to make &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;&quot;&gt;Chimera Linux&lt;&#x2F;a&gt;
run on Pentium class 32-bit x86 hardware.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;chimera-i586&amp;#x2F;chimera-i586-qemu.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;chimera-i586&amp;#x2F;chimera-i586-qemu.png&quot; width=&quot;594&quot; alt=&quot;Screenshot of a terminal with the output of fastfetch. It indicates the OS is Chimera Linux i586. It&amp;#x27;s using 216 MiB of 256MiB of RAM (85%).&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;I was moderately successful.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;This was sparked by a friend linking to the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pcengines.ch&#x2F;eol.htm&quot;&gt;EoL page on the PC Engines website&lt;&#x2F;a&gt;
quoting the fact that they were exiting the market:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Despite having used considerable quantities of AMD processors and Intel NICs,
we don’t get adequate design support for new projects. In addition, the x86
silicon currently offered is not very appealing for our niche of passively
cooled boards. After about 20 years of WRAP, ALIX and APU, it is time for me
to move on to different things.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This reminded me that I had two ALIX boards in neat little aluminium cases
sitting in the cupboard:
an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.pcengines.ch&#x2F;alix2d13.htm&quot;&gt;alix2d13&lt;&#x2F;a&gt; purchased in 2011, and an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.pcengines.ch&#x2F;alix3d2.htm&quot;&gt;alix3d2&lt;&#x2F;a&gt; purchased in 2012. My
immediate thought was the alix3d2 would be perfect for hosting &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;home.wezm.net&#x2F;~wmoore&#x2F;&quot;&gt;my retro
website&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;chimera-i586&amp;#x2F;alix3d2.jpg&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;chimera-i586&amp;#x2F;alix3d2.jpg&quot; alt=&quot;Photo of alix3d2 in aluminum case. It&amp;#x27;s longer than it is wide, kinda like a modem. It has power, Ethernet, and DB-9 serial cables connected.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;alix3d2&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;They are powered by an AMD Geode LX800 CPU clocked at 500Mhz with 256Mb of RAM.
The Geode is &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;strohmayers.com&#x2F;linux&#x2F;news&#x2F;GeodeLX_as_686.pdf&quot;&gt;mostly an i686&lt;&#x2F;a&gt; class 32-bit x86 CPU. Instead
of installing an OS that I know works on them like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;openwrt.org&#x2F;&quot;&gt;OpenWRT&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;netbsd.org&#x2F;&quot;&gt;NetBSD&lt;&#x2F;a&gt; I have
spent the last week and a bit bringing up Chimera Linux on i586 (Pentium).&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to use Chimera Linux because:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;I was already hosting my retro site with it.&lt;&#x2F;li&gt;
&lt;li&gt;I had already packaged the Rust binary that serves part of the site.
Chimera makes cross-compiling that package super easy, as well as service
monitoring with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;davmac.org&#x2F;projects&#x2F;dinit&#x2F;&quot;&gt;Dinit&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;I thought it would be fun to use a modern distro on Pentium class hardware.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The process of bringing up a new platform on Chimera Linux was interesting, but
tested me at times. Especially early on when I was getting segfaults in &lt;code&gt;apk&lt;&#x2F;code&gt;,
thus preventing anything from working.
This post aims to document the steps I took (which may not be optimal) in
case it happens to be useful.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;As I was working this out as I went, I made a few missteps along the way and
had to repeat the process a few times (I compiled LLVM &lt;em&gt;many&lt;&#x2F;em&gt; times). I’ll
mostly leave those missteps out, and describe the process that ended up
working.&lt;&#x2F;p&gt;
&lt;p&gt;All of this work was completed on my Arch Linux system. Since the Chimera
tooling does everything in a sandbox, this works fine. These are the steps
I took:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Add a new x86 build profile.&lt;&#x2F;li&gt;
&lt;li&gt;Cross-compile a toolchain via the &lt;code&gt;base-cbuild&lt;&#x2F;code&gt; package.
&lt;ol&gt;
&lt;li&gt;Update various packages to accept the x86 architecture.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Re-build the toolchain in a native sandbox.&lt;&#x2F;li&gt;
&lt;li&gt;Attempt to build &lt;code&gt;base-full&lt;&#x2F;code&gt;, addressing issues along the way.&lt;&#x2F;li&gt;
&lt;li&gt;Bootstrap Rust and Cargo.&lt;&#x2F;li&gt;
&lt;li&gt;Build &lt;code&gt;fastfetch&lt;&#x2F;code&gt;, addressing issues along the way.&lt;&#x2F;li&gt;
&lt;li&gt;Build &lt;code&gt;limine&lt;&#x2F;code&gt; and &lt;code&gt;grub&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Define and build a Linux kernel.&lt;&#x2F;li&gt;
&lt;li&gt;Update &lt;code&gt;chimera-live&lt;&#x2F;code&gt; to produce an ISO.&lt;&#x2F;li&gt;
&lt;li&gt;Use the ISO to boot and install into virtual machine.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Note: These steps are partially from memory, so may not be perfectly reproducible.
All of the code changes are captured in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;cports&#x2F;tree&#x2F;i586&quot;&gt;my i586 cports branch&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;add-new-build-profile-and-cross-compile-toolchain&quot;&gt;Add New Build Profile and Cross-Compile Toolchain&lt;&#x2F;h3&gt;
&lt;p&gt;Everything starts with &lt;code&gt;etc&#x2F;build_profiles&#x2F;x86.ini&lt;&#x2F;code&gt;, which defines what we’re
building for:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;profile&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;endian&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    =&lt;&#x2F;span&gt;&lt;span&gt; little&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;wordsize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  =&lt;&#x2F;span&gt;&lt;span&gt; 32&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;triplet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;   =&lt;&#x2F;span&gt;&lt;span&gt; i586-chimera-linux-musl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;repos&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;     =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;goarch&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    =&lt;&#x2F;span&gt;&lt;span&gt; 386&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;flags&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;# clang defaults to pentium4 on i*86- triples.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;# see https:&#x2F;&#x2F;github.com&#x2F;llvm&#x2F;llvm-project&#x2F;issues&#x2F;61347&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;CFLAGS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    =&lt;&#x2F;span&gt;&lt;span&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;march&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;pentium&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;CXXFLAGS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  =&lt;&#x2F;span&gt;&lt;span&gt; ${CFLAGS}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;FFLAGS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    =&lt;&#x2F;span&gt;&lt;span&gt; ${CFLAGS}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With that in place I tried cross-compiling &lt;code&gt;base-cbuild&lt;&#x2F;code&gt;:
&lt;code&gt;.&#x2F;cbuild -a x86 main&#x2F;base-cbuild&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This revealed various packages that are restricted to specific architectures.
Updating them to accept &lt;code&gt;x86&lt;&#x2F;code&gt; and regenerating sub-package links
(&lt;code&gt;.&#x2F;cbuild relink-subpkgs&lt;&#x2F;code&gt;) gets us a cross-build toolchain.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;starting-afresh&quot;&gt;Starting Afresh&lt;&#x2F;h3&gt;
&lt;p&gt;With &lt;code&gt;base-cbuild&lt;&#x2F;code&gt; built it was now possible to bootstrap an x86 build root:&lt;br&gt;
&lt;code&gt;.&#x2F;cbuild -A x86 bootstrap&lt;&#x2F;code&gt;. I initially wasted a bunch of time with an Alpine
x86 virtual machine here, before discovering the neat &lt;code&gt;-A&lt;&#x2F;code&gt; argument to
&lt;code&gt;.&#x2F;cbuild&lt;&#x2F;code&gt;. The docs describe &lt;code&gt;-A&lt;&#x2F;code&gt; as follows:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;-A ARCH&lt;&#x2F;code&gt;, &lt;code&gt;--host-arch ARCH&lt;&#x2F;code&gt; Override the host architecture. The given host
arch must be runnable on the current kernel. This is typically useful for
e.g. 32-bit builds on 64-bit architectures, or for emulated targets.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I then found out that the cross-built &lt;code&gt;apk&lt;&#x2F;code&gt; would segfault,
seemingly due to a recursive call in &lt;code&gt;libatomic&lt;&#x2F;code&gt; causing stack overflow:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;thousands more omitted&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;⋮&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174557: 0xf7f925e8 apk.static`__atomic_load + 312&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174558: 0xf7f925e8 apk.static`__atomic_load + 312&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174559: 0xf7f925e8 apk.static`__atomic_load + 312&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174560: 0xf7f925e8 apk.static`__atomic_load + 312&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174561: 0xf7f925e8 apk.static`__atomic_load + 312&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174562: 0xf7db48e4 apk.static`CRYPTO_atomic_load + 68&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174563: 0xf7d7eb44 apk.static`OPENSSL_init_crypto + 148&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174564: 0xf7d44852 apk.static`ossl_err_get_state_int + 50&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174565: 0xf7d45b8a apk.static`ERR_new + 26&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174566: 0xf7d492f9 apk.static`___lldb_unnamed_symbol12090 + 329&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174567: 0xf7d49874 apk.static`EVP_DigestInit_ex + 36&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174568: 0xf7bec625 apk.static`apk_digest_ctx_init at crypto_openssl.c:101:6&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174569: 0xf7bebe30 apk.static`apk_ctx_init at context.c:33:2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174570: 0xf7bd1703 apk.static`main at apk.c:587:2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174571: 0xf7f96cbf apk.static&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174572: 0xf7f96c7d apk.static`__libc_start_main + 61&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174573: 0xf7bd148d apk.static`__dls2 at rcrt1.c:13:2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174574: 0xf7bd1400 apk.static`_start_c at dlstart.c:162:2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;frame #174575: 0xf7bd101b apk.static`_start + 27&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After quite a lot of challenging debugging and staring at assembly in
the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;godbolt.org&#x2F;&quot;&gt;Compiler Explorer&lt;&#x2F;a&gt; I eventually concluded that
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chimera-linux&#x2F;libatomic-chimera&#x2F;blob&#x2F;2681747f227c6fee6a50a33c72b09b397d2ecefd&#x2F;atomic.c#L130&quot;&gt;&lt;code&gt;__atomic_load&lt;&#x2F;code&gt; ended up calling itself through &lt;code&gt;__atomic_load_n&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.
This was extra tricky for me to debug because there were compiler builtins
in play, so I also had to poke through LLVM source as well.&lt;&#x2F;p&gt;
&lt;p&gt;Eventually I started gathering the information necessary to open a bug on LLVM,
since &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chimera-linux&#x2F;libatomic-chimera&quot;&gt;libatomic-chimera&lt;&#x2F;a&gt; is derived from &lt;code&gt;atomic.c&lt;&#x2F;code&gt; in LLVM’s &lt;code&gt;compiler-rt&lt;&#x2F;code&gt;. I
wrote a small reproducer based on the current LLVM version of &lt;code&gt;atomic.c&lt;&#x2F;code&gt;, only to
discover that it worked fine. In the end I was able to work out that
&lt;code&gt;libatomic-chimera&lt;&#x2F;code&gt; was triggering a code path in &lt;code&gt;__atomic_load_n&lt;&#x2F;code&gt; for
misaligned atomics, even though it was only making that call after checking for
compatibility with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;__atomic_always_lock_free&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;size&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ||&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;__atomic_always_lock_free&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;size&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;uintptr_t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;p &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt; size&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I didn’t chase this into LLVM to find the root cause. Instead I &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;cports&#x2F;commit&#x2F;387f3d4eedefda207f21f77078bf02260528cf81&quot;&gt;updated
&lt;code&gt;libatomic-chimera&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; based on the newer version of
&lt;code&gt;atomic.c&lt;&#x2F;code&gt; in LLVM, and this resolved the problem. It was
possible to re-build &lt;code&gt;base-cbuild&lt;&#x2F;code&gt; in the x86 build root.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;attempt-to-build-base-full&quot;&gt;Attempt to Build base-full&lt;&#x2F;h3&gt;
&lt;p&gt;Next was to build &lt;code&gt;base-full&lt;&#x2F;code&gt;, which provides the core packages for a Chimera system.
This was a somewhat iterative process of run &lt;code&gt;.&#x2F;cbuild -A x86 pkg main&#x2F;base-full&lt;&#x2F;code&gt; until
a package failed to build, and then work out how to fix it. The fixes were usually one of:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Fix the package to make it build (unrelated to x86), like setting&lt;br&gt;
&lt;code&gt;-DCMAKE_POLICY_VERSION_MINIMUM=3.5&lt;&#x2F;code&gt; to make it build under CMake 4.0.&lt;&#x2F;li&gt;
&lt;li&gt;Patch the package so that it builds on x86. Patches from Alpine and Debian
were often helpful here.&lt;&#x2F;li&gt;
&lt;li&gt;Tweak the package template to skip tests that fail on x86.&lt;&#x2F;li&gt;
&lt;li&gt;Tweak the package template to skip the &lt;code&gt;check&lt;&#x2F;code&gt; phase entirely on x86.&lt;&#x2F;li&gt;
&lt;li&gt;Patch the package template to build without dependencies that were currently
broken (mostly doxygen).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This was a fairly straightforward iterative process. I was pretty free and easy
about skipping failing tests if it was just one or two, since my initial goal
was to get stuff compiling.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;rust&quot;&gt;Rust&lt;&#x2F;h4&gt;
&lt;p&gt;Eventually I ran into a failure that I was anticipating: Rust. It gets pulled
in by &lt;code&gt;pipewire&lt;&#x2F;code&gt; via a curious chain of dependencies ultimately leading to
&lt;code&gt;mesa&lt;&#x2F;code&gt; and &lt;code&gt;rust&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; pipewire&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  alsa-lib&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   alsa-ucm-conf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  avahi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   gtk+3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    adwaita-icon-theme&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     hicolor-icon-theme&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    at-spi2-core&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     libsm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      libice&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     libxtst&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      libxi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       libxfixes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    colord&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     lcms2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     libgudev&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      vala&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       graphviz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        gdk-pixbuf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;         shared-mime-info&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        libgd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;         libavif&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          dav1d&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          libaom&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          libwebp&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;           freeglut&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            glu&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;             mesa&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              cbindgen&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;               cargo-auditable&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                cargo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                 cargo-bootstrap&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                 libgit2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  http-parser&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                 rust&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  rust-bootstrap&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  wasi-libc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once I worked out the process this was straightforward to deal with:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Add patches so that Rust knows about &lt;code&gt;i586-chimera-linux-musl&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Generate a Rust bootstrap compiler via cross-compilation:&lt;br&gt;
&lt;code&gt;.&#x2F;cbuild -a x86 invoke-custom bootstrap main&#x2F;rust&lt;&#x2F;code&gt;. This also
requires building a new host Rust compiler so that it knows about
the new triple too.&lt;&#x2F;li&gt;
&lt;li&gt;Upload the boostrap binaries and update the &lt;code&gt;rust-bootstrap&lt;&#x2F;code&gt; package
so that it has an entry for &lt;code&gt;x86&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Repeat for cargo: &lt;code&gt;.&#x2F;cbuild -a x86 invoke-custom bootstrap main&#x2F;cargo&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;With that in place it’s possible to return to trying to build &lt;code&gt;base-full&lt;&#x2F;code&gt;.
The &lt;code&gt;rust&lt;&#x2F;code&gt; package is able to be built from the &lt;code&gt;rust-bootstrap&lt;&#x2F;code&gt; package.
I actually took this a step further and rebuild the bootstrap binaries
using the non-cross-compiled &lt;code&gt;rust&lt;&#x2F;code&gt; and republished them.&lt;&#x2F;p&gt;
&lt;p&gt;Eventually &lt;code&gt;base-full&lt;&#x2F;code&gt; succeeded, which was quite satisfying.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;build-fastfetch&quot;&gt;Build fastfetch&lt;&#x2F;h3&gt;
&lt;p&gt;Now because I wanted to be able to show pretty screenshots of my handiwork
I also built &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;fastfetch-cli&#x2F;fastfetch&quot;&gt;fastfetch&lt;&#x2F;a&gt;. It happens to have a quite deep dependency tree,
which was more of the same iteration to get everything building.&lt;&#x2F;p&gt;
&lt;details&gt;
&lt;summary&gt;Full build graph for fastfetch&lt;&#x2F;summary&gt;
&lt;pre&gt;
fastfetch
 chafa
  automake
   autoconf
    base-files
    gm4
     texinfo
      ncurses
       pkgconf
      perl
       bzip2
       zlib-ng-compat
  docbook-xsl-nons
   docbook-xml
    xmlcatmgr
     libtool
      help2man
       perl-locale-gettext
  freetype
   brotli
    cmake
     curl
      c-ares
      ca-certificates
       debianutils
       openssl3
        linux-headers
      libidn2
       gettext
        libarchive
         acl
          attr
         lz4
         musl-bsd-headers
         xz
         zstd
          meson
           ninja
            python
             autoconf-archive
             bluez-headers
             libedit
             libexpat
             libffi8
             sqlite
           python-build
            python-flit_core
            python-installer
            python-packaging
             python-pyparsing
            python-pyproject_hooks
           python-setuptools
            python-wheel
        libunistring
        libxml2
         icu
       gtk-doc-tools
        itstool
        libxslt
         libgcrypt
          libgpg-error
           slibtool
        python-lxml
         python-cython
         python-html5lib
          python-six
          python-webencodings
           python-pytest
            python-iniconfig
             python-hatch_vcs
              python-hatchling
               python-editables
               python-pathspec
               python-pluggy
                python-setuptools_scm
                 python-typing_extensions
               python-trove-classifiers
                python-calver
            python-sphinx
             python-alabaster
             python-babel
             python-docutils
              python-pygments
             python-imagesize
             python-jinja2
              python-markupsafe
             python-requests
              python-charset-normalizer
              python-idna
              python-urllib3
             python-roman-numerals-py
             python-snowballstemmer
             python-sphinxcontrib-applehelp
             python-sphinxcontrib-devhelp
             python-sphinxcontrib-htmlhelp
             python-sphinxcontrib-jsmath
             python-sphinxcontrib-qthelp
             python-sphinxcontrib-serializinghtml
      libpsl
      libssh2
       bash
        bison
        readline
      mandoc
       less
      nghttp2
       cppunit
       jansson
       libev
       libevent
      nghttp3
     libuv
     rhash
   freetype-bootstrap
   harfbuzz
    cairo
     fontconfig
      gperf
      util-linux
       bash-completion
       file
       flex
        byacc
       libcap-ng
       linux-pam
        docbook-xsl
        linux-pam-base
       shadow
        base-shells
         chimerautils
          libxo
          sd-tools
           libcap
     glib-bootstrap
      dbus
       libx11
        libxcb
         libxau
          xorg-util-macros
          xorgproto
         libxdmcp
         xcbproto
        xtrans
       xmlto
        ugetopt
      elfutils
       argp-standalone
       json-c
       libmicrohttpd
        gnutls
         gmp
         libtasn1
         nettle
         p11-kit
         tpm2-tss
          cmocka
          libftdi1
           libconfuse
           libusb
            udev
             kmod
              scdoc
         trousers
         unbound
          dns-root-data
          hiredis
          libsodium
          protobuf-c
           boost
           protobuf
            abseil-cpp
             gtest
       musl-obstack
      pcre2
     libpng
     libxext
     libxrender
     lzo
     pixman
    glib
     gobject-introspection
      python-mako
      python-markdown
       python-pyyaml
        libyaml
     sysprof-capture
    graphite2
  libavif
   dav1d
    nasm
     asciidoc
   gdk-pixbuf
    libtiff
     jbigkit
      check
     libjpeg-turbo
    shared-mime-info
   libaom
   libwebp
    freeglut
     glu
      mesa
       cbindgen
        cargo-auditable
         cargo
          cargo-bootstrap
          libgit2
           heimdal
            e2fsprogs
             fuse
            openldap
             libsasl
            perl-json
             perl-test-pod
           http-parser
          rust
           llvm
            fortify-headers
            libatomic-chimera
            llvm-bootstrap
            musl
           rust-bootstrap
           wasi-libc
         cargo-auditable-bootstrap
       glslang
        spirv-tools
         spirv-headers
       libclc
        spirv-llvm-translator
       libdrm
        libpciaccess
       libva-bootstrap
       libxdamage
        libxfixes
       libxrandr
       libxshmfence
       libxv
       libxxf86vm
       lm-sensors
       lua5.4
       python-ply
       python-pycparser
       rust-bindgen
       vulkan-loader
        libxkbcommon
         wayland
         wayland-protocols
         xkeyboard-config
          gawk
          xkbcomp
           libxkbfile
        vulkan-headers
     libxi
    giflib
  librsvg
   cargo-c
   pango
    fribidi
    libthai
     libdatrie
    libxft
   vala
    graphviz
     fonts-liberation
      fontforge-cli
       libspiro
       libuninameslist
       woff2
      mkfontscale
       libfontenc
      python-fonttools
       python-brotli
       python-pytest-xdist
        python-execnet
        python-filelock
        python-pexpect
         python-ptyprocess
         zsh
        python-psutil
     libgd
      libheif
       libde265
       x265
        numactl
 dconf
  gtk+3
   adwaita-icon-theme
    hicolor-icon-theme
   at-spi2-core
    libsm
     libice
    libxtst
   colord
    dinit-dbus
     libdinitctl
    lcms2
    libgudev
    libgusb
     json-glib
    polkit
     duktape
     elogind
      libseccomp
       gsed
      tangle
      turnstile
       dinit-chimera
        dinit
        snooze
        tzdb
    sane-backends
     avahi-bootstrap
      libdaemon
     libgphoto2
      libexif
     v4l-utils
   cups
    libpaper
    xdg-utils
     lynx
     xset
      libxfontcache
      libxmu
       libxt
      libxxf86misc
   iso-codes
   libcloudproviders
   libepoxy
   libxcomposite
   libxcursor
   libxinerama
   tinysparql
    libsoup
     glib-networking
      gsettings-desktop-schemas
       chimera-artwork
       fonts-adwaita-ttf
      libproxy
    python-gobject
     python-cairo
 ddcutil
 imagemagick
  djvulibre
  fftw
  ghostscript
   ijs
   jasper
   jbig2dec
   openjpeg
  libjxl
   gflags
   highway
   openexr
    imath
    libdeflate
  libraw
 libpulse
  libsamplerate
   libsndfile
    flac
     libogg
    lame
    libvorbis
    opus
  orc
  perl-xml-parser
 ocl-icd
  opencl-headers
  ruby
 xfconf
  libxfce4util
   xfce4-dev-tools
  xwayland-run
   weston
    libdisplay-info
     hwdata
    libinput
     libevdev
     libwacom
      python-libevdev
      python-pyudev
     mtdev
    libseat
    libva
    pipewire
     alsa-lib
      alsa-ucm-conf
     avahi
      python-dbus
      xmltoman
     bluez
      libical
     fdk-aac
     gst-plugins-base
      cdparanoia
      graphene
      gstreamer
      libtheora
     ldacbt
     libcamera
     libcanberra
      tdb
     libfreeaptx
     liblc3
     libmysofa
      cunit
     lilv
      lv2
      serd
      sord
       zix
      sratom
     rtkit
     sbc
     webrtc-audio-processing
   xauth
   xwayland
    font-util
     bdftopcf
     font-alias
    libei
     python-attrs
     python-dbusmock
      libnotify
      modemmanager
       libmbim
       libqmi
        libqrtr-glib
       ppp
        libpcap
         libnl
      networkmanager
       iproute2
        iptables
         libmnl
         libnetfilter_conntrack
          libnfnetlink
         libnftnl
       libndp
       mobile-broadband-provider-info
       newt
        popt
        slang
       nss
        nspr
       resolvconf
        openresolv
       wpa_supplicant
        pcsc-lite
     python-structlog
      python-freezegun
       python-dateutil
      python-pretend
      python-pytest-asyncio
      python-simplejson
    libtirpc
    libxcvt
    libxfont2
    xserver-xorg-protocol
 yyjson
&lt;&#x2F;pre&gt;
&lt;&#x2F;details&gt;
&lt;h3 id=&quot;bootloaders&quot;&gt;Bootloaders&lt;&#x2F;h3&gt;
&lt;p&gt;Back to necessary things. The Chimera ISOs use the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;limine-bootloader.org&#x2F;&quot;&gt;Limine bootloader&lt;&#x2F;a&gt; on most
platforms, although it’s not currently fully supported for installations, so GRUB is
also needed (systemd-boot is another option but does not support x86). Limine does not
officially support i585 class CPUs, but it seemed to work:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;For 32-bit x86 systems, support is only ensured starting with those with Pentium Pro (i686) class CPUs.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Limine and GRUB built fine once x86 was added to their &lt;code&gt;arch&lt;&#x2F;code&gt; list in the template.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;linux-kernel&quot;&gt;Linux kernel&lt;&#x2F;h3&gt;
&lt;p&gt;The final piece of the puzzle was a kernel. It’s a little unclear to me how
distros go about defining the config for a new platform. I first tried starting
with a &lt;code&gt;defconfig&lt;&#x2F;code&gt; kernel, but that didn’t boot the system in a VM. Next I
tried taking the x86_64 kernel config and tweaking it to turn it into an i586 kernel.
This resulted in a pretty large kernel, but one that actually booted in a VM.&lt;&#x2F;p&gt;
&lt;p&gt;The process for tweaking the kernel config was to initially populate
&lt;code&gt;main&#x2F;linux-lts&#x2F;files&#x2F;config-x86.generic&lt;&#x2F;code&gt;, then run &lt;code&gt;.&#x2F;cbuild -A x86 invoke-custom generate-configs main&#x2F;linux-lts&lt;&#x2F;code&gt;. This pauses on each
architecture allowing you to enter the chroot and mess with the config. Running
kernel make commands is done with the assistance of the &lt;code&gt;chimera-buildkernel&lt;&#x2F;code&gt;
command.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.&#x2F;cbuild chroot main&#x2F;linux-lts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;chimera-buildkernel config&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The latter command runs &lt;code&gt;make menuconfig&lt;&#x2F;code&gt;, you make and save your changes,
exit, and then hit enter in the other window running &lt;code&gt;generate-configs&lt;&#x2F;code&gt; and it
updates &lt;code&gt;main&#x2F;linux-lts&#x2F;files&#x2F;config-x86.generic&lt;&#x2F;code&gt;. It’s then possible to build
the kernel the usual way: &lt;code&gt;.&#x2F;cbuild -A x86 pkg main&#x2F;linux-lts&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;building-an-iso&quot;&gt;Building an ISO&lt;&#x2F;h3&gt;
&lt;p&gt;After making some &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;chimera-live&#x2F;commit&#x2F;e7647326931cb1fc065772084b994574398a3c9c&quot;&gt;small tweaks&lt;&#x2F;a&gt; to the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chimera-linux&#x2F;chimera-live&quot;&gt;chimera-live&lt;&#x2F;a&gt; repo
to recognise x86 it was possible to build an ISO. This process requires the
&lt;code&gt;main&#x2F;xorriso&lt;&#x2F;code&gt; and &lt;code&gt;base-live&lt;&#x2F;code&gt; packages to be built first.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doas .&#x2F;mklive-image.sh -b base -k linux-lts -p fastfetch -- -a x86 -r ..&#x2F;cports-i586&#x2F;packages&#x2F;main -k ..&#x2F;cports-i586&#x2F;etc&#x2F;keys&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After a short time this will spit out an ISO that you can try booting in a VM or real hardware.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;extra-challenges&quot;&gt;Extra Challenges&lt;&#x2F;h3&gt;
&lt;p&gt;During my time working on this the freedesktop.org project was in the process
of migrating their infrastructure and at various times tarballs from
freedesktop.org, gitlab.freedesktop.org, mesa.freedesktop.org, and x.org were
down, which caused the &lt;code&gt;fetch&lt;&#x2F;code&gt; phase that pulls source code to fail for affected
packages. Additionally it seems that mirror sites for these projects were all
either down or last updated late 2023. I was able to work around these by:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Switching to GitLab archives from published tarballs when the former was up but not the latter.&lt;&#x2F;li&gt;
&lt;li&gt;Pull archives from the Internet Archive.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;will-it-run&quot;&gt;Will it Run?&lt;&#x2F;h3&gt;
&lt;p&gt;With ISOs built, I was able to boot and install Chimera into a virtual machine
with the CPU set to &lt;code&gt;pentium&lt;&#x2F;code&gt;, as shown at the top of the post. Next it was
time to try running it on the ALIX. The ALIX boards I have use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pcengines.ch&#x2F;tinybios.htm&quot;&gt;tinyBIOS&lt;&#x2F;a&gt; and
don’t support booting from USB. They also don’t have any graphical output, only
supplying a serial port. The primary storage is a 2Gb Compact Flash card. These
constraints provided some challenges.&lt;&#x2F;p&gt;
&lt;p&gt;First I created a virtual machine and passed-through the CF card connected via
an IDE to USB cable:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&amp;lt;!-- libvirt config --&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;disk&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;block&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; device&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;disk&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;driver&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;qemu&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;raw&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;source&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; dev&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;dev&#x2F;sdd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;target&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; dev&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;vdb&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; bus&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;virtio&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;boot&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; order&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;disk&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I then booted from the ISO and did a normal installation onto the CF card.&lt;&#x2F;p&gt;
&lt;p&gt;After that I’d shutdown the VM, remove the CF card, swap it into the ALIX and
see if it would boot. Initially I was not getting any output, not even from
GRUB. That was fixed through tweaking the GRUB and kernel command line to
output on the serial port (&lt;code&gt;ttyS0&lt;&#x2F;code&gt;) and use 115200 baud rate to match tinyBIOS.
At this point I could get the machine to boot GRUB and try to start Chimera,
but it never got further than that.&lt;&#x2F;p&gt;
&lt;p&gt;I tried a bunch of different avenues, including coming up with a new kernel
package tailored specifically for the ALIX hardware. This also didn’t work.
Lastly I confirmed that OpenWRT boots on the machine and then copied their
kernel config over and tweaked it a little (to enable some things like
compressed initramfs that Chimera uses). That still failed to start. After
the GRUB menu and choosing an entry this is all that is seen:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;chimera-i586&amp;#x2F;chimera-i585-alix-boot.svg&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;chimera-i586&amp;#x2F;chimera-i585-alix-boot.svg&quot; width=&quot;600&quot; alt=&quot;asciinema capture of the output on the serial console when trying to boot Chimera on the ALIX. It starts off with the tinyBIOS memory check, then loading GRUB, the GRUB boot menu, loading Linux, loading initial ramdisk, decompressing Linux, and finally booting the kernel.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Serial output captured from the ALIX when booting.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Given more-or-less the same config is able to boot on OpenWRT, there must
be something particular about the Chimera build that’s causing it to fail.&lt;&#x2F;p&gt;
&lt;p&gt;This is where I have decided to pause this project. If I pick it up again I
think I’d need to delve into kernel debugging to try to work out what’s
happening on the machine when it stops.&lt;&#x2F;p&gt;
&lt;p&gt;Finally the code from this adventure is in my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;cports&#x2F;tree&#x2F;i586&quot;&gt;i586 cports branch&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Building a Website Fit for 1999</title>
      <pubDate>Mon, 21 Apr 2025 08:29:24 +1000</pubDate>
      <atom:published>2025-04-21T08:29:24+10:00</atom:published>
      <atom:updated>2025-05-30T17:54:08+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2025&#x2F;website-fit-for-1999&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2025&#x2F;website-fit-for-1999&#x2F;</guid>
      <description>&lt;p&gt;Over the last week I’ve had a &lt;em&gt;lot&lt;&#x2F;em&gt; of fun building a little &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;home.wezm.net&#x2F;~wmoore&#x2F;&quot;&gt;retro-themed
website&lt;&#x2F;a&gt; that I’m hosting at home. Inspired by &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;retro.rubenerd.com&#x2F;&quot;&gt;Ruben’s Retro Corner&lt;&#x2F;a&gt;
I’ve been meaning to do this for a while, and actually started on it in June
last year. More recently Joel Humphries shared on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;thesizzle.com.au&#x2F;&quot;&gt;The Sizzle&lt;&#x2F;a&gt; forum that he’d
built &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;joelhumphries.com.au&#x2F;&quot;&gt;a little site&lt;&#x2F;a&gt; that he was hosting at home on a Raspberry Pi. This
reignited my interest in getting my own site up again. For the fun of it I
decided to implement it in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.w3.org&#x2F;TR&#x2F;html401&#x2F;cover.html&quot;&gt;HTML4&lt;&#x2F;a&gt; and serve it over plain HTTP so that it
would work on old computers.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;website-fit-for-1999&amp;#x2F;nonsense-website-mac-os-8.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;website-fit-for-1999&amp;#x2F;nonsense-website-mac-os-8.png&quot; alt=&quot;Screenshot of a Mac OS 8.1 desktop with a IE 4 window open showing Wes&amp;#x27; Nonsense Website&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;The site in IE 4.01 on Mac OS 8.1&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h3 id=&quot;website-on-a-microcontroller&quot;&gt;Website on a Microcontroller?&lt;&#x2F;h3&gt;
&lt;p&gt;Sticking with the retro theme I initially thought it might be fun to host the
site on an resource constrained device. So, on the first evening I put together
a simple page hosted on a RISC-V ESP32-C3 microcontroller using bare-metal Rust (with
the exception of some C libraries used for Wi-Fi support). This was fine, but
felt a little &lt;em&gt;too&lt;&#x2F;em&gt; constrained, and there was some &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ivmarkov&#x2F;edge-net&#x2F;issues&#x2F;62&quot;&gt;non-ideal behaviour&lt;&#x2F;a&gt;
of the http crate I used. Nonetheless I published &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;esp32-website&quot;&gt;the code&lt;&#x2F;a&gt; to
my git forge.&lt;&#x2F;p&gt;




  

&lt;figure class=&quot;text-center float-left&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;website-fit-for-1999&amp;#x2F;esp32-c3.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;esp32-c3.d31ccafeb50ad937.jpg&quot; width=&quot;350&quot; alt=&quot;Photo of a ESP32-C3 dev board sitting in red foam with a USB cable attached. A white LED on it is illuminated&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;The ESP32 Rust powered web sever&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;




  

&lt;figure class=&quot;text-center float-right&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;website-fit-for-1999&amp;#x2F;esp32-website.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;esp32-website.dac54bd1f02977b0.png&quot; width=&quot;350&quot; alt=&quot;Screenshot of the web page hosted by the ESP32. Is includes basic information about the microcontroller and the internal temperature of the device.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;The ESP32 web page&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;the-best-server-is-the-server-you-already-have&quot;&gt;The Best Server Is the Server You Already Have&lt;&#x2F;h3&gt;
&lt;p&gt;The next version I threw together went back to basics. Digging up my website
building knowledge from university I constructed a two page site using HTML4,
complete with 88×31 buttons. Akin to my university website I hosted it on a ~wmoore path:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;home.wezm.net&#x2F;~wmoore&#x2F;&quot;&gt;http:&#x2F;&#x2F;home.wezm.net&#x2F;~wmoore&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This one was hosted by the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;qotom.net&#x2F;product&#x2F;29.html&quot;&gt;mini fanless Qotom computer&lt;&#x2F;a&gt; running
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;&quot;&gt;Chimera Linux&lt;&#x2F;a&gt; already sitting on my desk. I wanted it to be &lt;em&gt;slightly&lt;&#x2F;em&gt; more
interesting than just static HTML so I included &lt;code&gt;free&lt;&#x2F;code&gt; and &lt;code&gt;uptime&lt;&#x2F;code&gt; information
about the server on the home page:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update 30 May 2025:&lt;&#x2F;strong&gt; I migrated the hosting of the site to another fanless computer
I had in a drawer today: a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.lattepanda.com&#x2F;lattepanda-v1&quot;&gt;LattePanda V1 single-board computer&lt;&#x2F;a&gt;. See
&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;home.wezm.net&#x2F;~wmoore&#x2F;cgi-bin&#x2F;about.cgi&quot;&gt;the about page&lt;&#x2F;a&gt; for more info.&lt;&#x2F;p&gt;


  



&lt;figure class=&quot;text-center figure-border&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;website-fit-for-1999&amp;#x2F;untitled-website.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;website-fit-for-1999&amp;#x2F;untitled-website.png&quot; width=&quot;655&quot; alt=&quot;Screenshot of Untitled Website showing information about the server and a brief FAQ&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Server stats and 88×31 buttons!&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I used the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mitsuhiko&#x2F;minijinja&quot;&gt;MiniJinja&lt;&#x2F;a&gt; CLI, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;01mf02&#x2F;jaq&quot;&gt;jaq&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;make&#x2F;manual&#x2F;make.html&quot;&gt;make&lt;&#x2F;a&gt; to regenerate the static HTML from templates,
funnelling the dynamic content into a JSON file.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;make&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;# minijinja-cli creates files with 600 perms, due to using a tempfile&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;# library to generate output. Fixed in main, but not released yet.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;public&#x2F;about.html&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; about.j2 data.json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	minijinja-cli &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;$^&lt;&#x2F;span&gt;&lt;span&gt; -o &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;$@&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	chmod 644 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;$@&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;public&#x2F;index.html&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; index.j2 data.json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	minijinja-cli &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;$^&lt;&#x2F;span&gt;&lt;span&gt; -o &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;$@&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	chmod 644 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;$@&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;data.json&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; memory.json uptime.json generated.json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	cat &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;$^&lt;&#x2F;span&gt;&lt;span&gt; | jaq -s add &amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;$@&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;memory.json&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	free -h | jaq -Rs &amp;#39;{memory: .}&amp;#39; &amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;$@&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;uptime.json&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	uptime | jaq -Rs &amp;#39;{uptime: .}&amp;#39; &amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;$@&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;generated.json&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	date | jaq -Rs &amp;#39;{generated: .}&amp;#39; &amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;$@&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To get the site online I made use of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tailscale.com&#x2F;&quot;&gt;Tailscale&lt;&#x2F;a&gt;. This allowed me to avoid
dealing with port forwarding on my Wi-Fi router, Dynamic DNS, and things like
that. The server hosting the page you are reading now (a VPS with
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.vultr.com&#x2F;?ref=7903263&quot;&gt;Vultr&lt;&#x2F;a&gt;&lt;sup&gt;(affiliate link)&lt;&#x2F;sup&gt;) uses Nginx to reverse proxy to the
Tailscale IP of the Qotom server. Nginx on the Qotom serves the website. I set
up a cron job to run &lt;code&gt;make&lt;&#x2F;code&gt; and regenerate the dynamic content every 5 minutes.&lt;&#x2F;p&gt;
&lt;div style=&quot;text-align: center&quot;&gt;
&lt;pre style=&quot;color: #444; display: inline-block; text-align: left; font-size: 9pt; line-height: 1; margin: 0; padding: 0; overflow: initial&quot;&gt;
            Internet

                │
                │
                │
     ┌─────────────────────┐
     │                     │
     │     Vultr Server    │
     │                     │
     │   ┌─────────────┐   │
     │   │    nginx    │   │
     │   └─────────────┘   │
     │        ┌──┐         │
     └────────│──│─────────┘
              │  │
              │  │ Tailscale
              │  │
┌───────────────────────────────┐
│             │  │              │
│    ┌────────│──│─────────┐    │
│    │        └──┘         │    │
│    │   ┌─────────────┐   │    │
│    │   │    nginx    │   │    │
│    │   └─────────────┘   │    │
│    │                     │    │
│    │       Qotom         │    │
│    │                     │    │
│    └─────────────────────┘    │
│                               │
│        Home Network           │
│        behind Router          │
│                               │
└───────────────────────────────┘
&lt;&#x2F;pre&gt;
&lt;&#x2F;div&gt;
&lt;h3 id=&quot;what-if-i-used-cgi-instead&quot;&gt;What if I Used CGI Instead?&lt;&#x2F;h3&gt;
&lt;p&gt;This was fine, but I didn’t like the idea that regenerating the site every five
minutes was causing unnecessary writes to the disk. It’s just a cheap 32Gb
mSATA drive, and is the second one this machine has used over its lifetime.
For more retro nostalgia I thought it would be neat to generate the dynamic
content with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Common_Gateway_Interface&quot;&gt;Common Gateway Interface&lt;&#x2F;a&gt; (CGI) scripts, served from &lt;code&gt;cgi‑bin&lt;&#x2F;code&gt; of
course.&lt;&#x2F;p&gt;
&lt;p&gt;I tinkered with this but in the end decided not to use actual CGI. Firstly,
Nginx doesn’t support it, only FastCGI. Secondly it complicated development
as it required a dev server that supported CGI and static file hosting. The
best&#x2F;closest option I found was &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.lighttpd.net&#x2F;&quot;&gt;Lighttpd&lt;&#x2F;a&gt;, but this all seemed harder than
it needed to be.&lt;&#x2F;p&gt;
&lt;p&gt;I fell back on the more modern approach of having web applications talk HTTP
directly behind a proxy. I put together a small &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; server with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tokio-rs&#x2F;axum&quot;&gt;Axum&lt;&#x2F;a&gt;.
Sticking the with theme though, the dynamic pages are served on paths like
&lt;code&gt;cgi-bin&#x2F;about.cgi&lt;&#x2F;code&gt;. In development the Rust binary serves the static files
and dynamic pages. In a release build it only serves dynamic content and
Nginx does the static files.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-joy-of-html4&quot;&gt;The Joy of HTML4&lt;&#x2F;h3&gt;
&lt;p&gt;Here’s where the fun really began. I thoroughly enjoyed filling out the
pages on what was now known as “Wes’ Nonsense Website”—nonsense in the
&lt;em&gt;extravagant foolishness or frivolity&lt;&#x2F;em&gt; sense. It was genuinely fun to
write &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;home.wezm.net&#x2F;~wmoore&#x2F;pineapples.html&quot;&gt;about growing pineapples&lt;&#x2F;a&gt;, include pixelated GIF icons, and the
odd animated GIF.&lt;&#x2F;p&gt;
&lt;p&gt;Coding in HTML4 Transitional is somewhat freeing. CSS is great, but it’s large
and sprawling. Throwing that away and sticking to only what you can achieve
with a comparatively small set of HTML tags and their attributes dramatically
reduces the scope of things. Also feels a bit cheeky to mix content and style
with wild abandon.&lt;&#x2F;p&gt;
&lt;p&gt;It wouldn’t be the classic web without feeding your pages through the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;validator.w3.org&#x2F;&quot;&gt;W3C Validator&lt;&#x2F;a&gt;. Amazingly the validator still
accepts and validates HTML4. The checks were useful for finding messed up
closing tags and other mistakes.&lt;&#x2F;p&gt;
&lt;p&gt;Now part of the reason to use HTML4 and serve the site over plain HTTP
is so that it has a chance of working on old computers. As I was building
the site I continually tested it in Internet Explorer 4.01 and Netscape
Navigator 3.01 running in Mac OS 8.1 in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cebix&#x2F;macemu&quot;&gt;Basilisk II 68k emulator&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;website-fit-for-1999&amp;#x2F;netscape-and-ie.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;website-fit-for-1999&amp;#x2F;netscape-and-ie.png&quot; alt=&quot;Screenshot of a Mac OS 8.1 desktop with a IE 4 and Netscape 3 windows showing Wes&amp;#x27; Nonsense Website&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Testing in IE4 and Netscape 3&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;These posed some unique challenges, mostly around text encoding. The content is
authored and served as UTF-8, but this was only supported by version 4 onwards
of IE and Netscape. To try to avoid issues, I initially stuck to the ASCII
subset of UTF-8 and used HTML entities for all characters outside this. Despite
my intentions I discovered that there were a number of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.w3.org&#x2F;TR&#x2F;html401&#x2F;sgml&#x2F;entities.html#h-24.4&quot;&gt;newly added
entities&lt;&#x2F;a&gt; that were unsupported by the browsers I was testing on.
These included &lt;code&gt;&amp;amp;rsquo;&lt;&#x2F;code&gt; for ’, and &lt;code&gt;&amp;amp;sup1;&lt;&#x2F;code&gt; for ¹. Annoyingly, the
default encoding for HTML in the HTML4 era was ISO&#x2F;IEC 8859-1 (aka Latin 1),
which &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ISO&#x2F;IEC_8859-1#Quotation_marks&quot;&gt;doesn’t encode curly quotes&lt;&#x2F;a&gt; at all, unlike Mac Roman, which had
them in 1984.&lt;&#x2F;p&gt;
&lt;p&gt;The unknown entities were rendered as text like “Wesrsquo Nonsense”, which was
not ideal. Since the version 4 browsers supported UTF-8 I switched to including
the desired characters directly. For ’ this worked as desired in Netscape
4 and IE 4 (on Mac OS 8 at least). Neither of them handled ¹ properly though.
IE shows a normal 1, and Netscape a ?, For browsers that don’t understand
UTF-8, like Netscape 3 most of the content is fine as it is just ASCII, but the
UTF-8 encoded chars come out using a Latin-1 rendition, such as “Wesâ*™
Nonsense” for “Wes’ Nonsense”. For a silly website, that’s good enough.&lt;&#x2F;p&gt;
&lt;p&gt;Along with the static content I included some dynamic content. The &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;home.wezm.net&#x2F;~wmoore&#x2F;&quot;&gt;home page&lt;&#x2F;a&gt;
shows live energy information about the Qotom server. This is pulled from an
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.athom.tech&#x2F;blank-1&#x2F;au-plug&quot;&gt;Athom smart plug&lt;&#x2F;a&gt; running &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tasmota.github.io&#x2F;docs&#x2F;&quot;&gt;Tasmota&lt;&#x2F;a&gt; that the server is plugged into.
The Rust server refreshes this information every 10s. The &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;home.wezm.net&#x2F;~wmoore&#x2F;cgi-bin&#x2F;about.cgi&quot;&gt;about page&lt;&#x2F;a&gt; shows
uptime and memory information, and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;home.wezm.net&#x2F;~wmoore&#x2F;cgi-bin&#x2F;sunshinecoast.cgi&quot;&gt;the Sunshine Coast page&lt;&#x2F;a&gt; includes
climate and season information for the current month.&lt;&#x2F;p&gt;
&lt;p&gt;The primary drawback to sticking to HTML4 tags is that it somewhat implies
table based layouts, which are not responsive. Therefore the site isn’t great
on smartphone sized screens. However, because it’s only 640 pixels across it
does render respectably on a smartphone in landscape orientation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;deployment&quot;&gt;Deployment&lt;&#x2F;h3&gt;
&lt;p&gt;Deployment is comprised of two parts:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The Rust binary&lt;&#x2F;li&gt;
&lt;li&gt;The rest of the content&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The content is placed onto the server via a simple &lt;code&gt;git pull&lt;&#x2F;code&gt;. The Rust binary
is installed as an &lt;code&gt;apk&lt;&#x2F;code&gt; package. I am able to build this package on my Arch
Linux desktop and copy it over to the server to install. This is possible
because the Chimera Linux package tooling does all building in a sandbox and
doesn’t require the host to be running Chimera. This is the package template:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pkgname&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;nonsense&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pkgver&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;0.1.2_git20250420&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pkgrel&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;_gitrev&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;27dc77c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;build_style&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;hostmakedepends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;cargo-auditable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;makedepends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;rust-std&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pkgdesc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Nonsense web application&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;license&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;custom:none&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;home.wezm.net&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;source&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;home.wezm.net&#x2F;archive&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;_gitrev&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.tar.gz&amp;gt;nonsense-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;pkgver&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.tar.gz&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sha256&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;7bdbadb1e1f4b1fceadb7a8f3ccce3ec0b0a5b2d3b9e776e2d9dc13682d906c4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; post_install&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;install_service&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;files_path&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;nonsense&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Included in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;cports&#x2F;blob&#x2F;wezm&#x2F;user&#x2F;nonsense&#x2F;template.py&quot;&gt;the package&lt;&#x2F;a&gt; is a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;davmac.org&#x2F;projects&#x2F;dinit&#x2F;&quot;&gt;Dinit&lt;&#x2F;a&gt; service file that runs the binary
and provides process monitoring to restart it if it should exit for some
reason.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;type = process&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;command = &#x2F;usr&#x2F;bin&#x2F;nonsense&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;working-dir = &#x2F;home&#x2F;wmoore&#x2F;home.wezm.net&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;run-as = wmoore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;env-file = nonsense.env&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;logfile = &#x2F;var&#x2F;log&#x2F;nonsense.log&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;depends-on = local.target&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;smooth-recovery = true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For basic content updates I can do &lt;code&gt;git pull&lt;&#x2F;code&gt;, and then run &lt;code&gt;make&lt;&#x2F;code&gt; if the
changes are to static files or &lt;code&gt;doas dinitctl restart nonsense&lt;&#x2F;code&gt; if the dynamic
stuff has changed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-s-next&quot;&gt;What’s Next&lt;&#x2F;h3&gt;
&lt;p&gt;I still want to add more content on the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;home.wezm.net&#x2F;~wmoore&#x2F;computers.html&quot;&gt;Computer&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;home.wezm.net&#x2F;~wmoore&#x2F;calculators.html&quot;&gt;Calculator&lt;&#x2F;a&gt; pages, but
decided it was time to return to slightly less frivolous projects for a bit.
If you haven’t checked out the site itself, you can do so at:
&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;home.wezm.net&#x2F;~wmoore&#x2F;&quot;&gt;http:&#x2F;&#x2F;home.wezm.net&#x2F;~wmoore&#x2F;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, all &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;home.wezm.net&quot;&gt;the code is published on my git forge&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Making the Web More Readable With Stylus</title>
      <pubDate>Mon, 17 Feb 2025 10:21:22 +1000</pubDate>
      <atom:published>2025-02-17T10:21:22+10:00</atom:published>
      <atom:updated>2025-02-17T13:14:49+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2025&#x2F;stylus&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2025&#x2F;stylus&#x2F;</guid>
      <description>&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;openstyles&#x2F;stylus&quot;&gt;Stylus&lt;&#x2F;a&gt; is an open-source browser extension for managing and applying “user
styles”—custom snippets of CSS—to websites. It allows you to tweak sites you
visit to tailor them to your preferences. In this post I list the ways I use
Stylus to make my browsing experience nicer.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Stylus has the ability to share and use shared-styles, but I just use styles
I write myself. Styles can by applied to all sites, specific sub-domains,
domains, or domains + paths. I note the matching rule of each style at the
top of each entry.&lt;&#x2F;p&gt;
&lt;p&gt;You will probably notice that are large number of my rules are just tweaks
to fonts. What can I say, I have opinions about fonts and I use Stylus to
express them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;sign-in-with-google&quot;&gt;Sign in with Google&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; Everything&lt;&#x2F;p&gt;
&lt;p&gt;Hide the stupid Sign in with Google popup that appears on sites using a Log in
with Google button, such as StackOverflow. I &lt;em&gt;never&lt;&#x2F;em&gt; want to Sign in with
Google on a third-party site.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;credential_picker_container&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;has&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;iframe&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;*=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;accounts.google.com&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;]) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    display&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; none;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;arstechnica-com&quot;&gt;arstechnica.com&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;arstechnica.com&quot;&gt;arstechnica.com&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Remove auto-playing videos in the middle of articles.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;ars-interlude-container&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    display&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; none;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;d-shoot-net&quot;&gt;d-shoot.net&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;d-shoot.net&quot;&gt;d-shoot.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Set a font and &lt;code&gt;max-width&lt;&#x2F;code&gt; more suited to reading.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    max-width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 640&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;px;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    font-family&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; sans-serif;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    margin&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; auto;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;danluu-com&quot;&gt;danluu.com&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;danluu.com&quot;&gt;danluu.com&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Set a &lt;code&gt;max-width&lt;&#x2F;code&gt; more suited to reading.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    max-width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 600&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;px;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    margin&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; auto;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;feedbin-com&quot;&gt;feedbin.com&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;feedbin.com&quot;&gt;feedbin.com&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Put my single glyph WMAppleLogo font first in the stack so that when people use
the Apple logo in text it appears correctly on non-Apple devices; use Muli as
the article font.&lt;&#x2F;p&gt;
&lt;p&gt;On Apple devices you can type an Apple logo, which uses the Private Use Area
code point U+F8FF. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.decentralised.social&#x2F;@wezm&#x2F;109827184573904673&quot;&gt;I created a font with an Apple logo mapped to this code
point so that posts that use it appear as intended&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;font-default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;content-styles&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    font-family&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; WMAppleLogo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Muli&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; sans-serif;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;github-com&quot;&gt;github.com&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;&quot;&gt;github.com&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Use the default &lt;code&gt;sans-serif&lt;&#x2F;code&gt; font for body text on GitHub. I have my system
font set to Cantarell, which unlike many I actually like for system controls
like menus, buttons, and titles. I don’t however want to see prose written in
it. So I override GitHub’s use of &lt;code&gt;system-ui&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;markdown-body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    font-family&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; sans-serif;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt; &#x2F;* Use sans-serif font instead of system UI font *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;gkeenan-co&quot;&gt;gkeenan.co&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gkeenan.co&quot;&gt;gkeenan.co&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This blog uses a typewriter font for body text which I don’t like visually,
nor find particularly easy to read. Replace it with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;weiweihuanghuang&#x2F;Work-Sans&quot;&gt;Work Sans&lt;&#x2F;a&gt;, which still
fits the vibe of the site.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    font-family&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Work Sans&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; sans-serif;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;goughlui-com&quot;&gt;goughlui.com&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;goughlui.com&quot;&gt;goughlui.com&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Uses a Times New Roman font by default, which is a bit ugly on my system.
Replace with a nicer serif font.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;textarea&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;page-title&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; span&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;pingback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; font-family&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;TeX Gyre Pagella&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; serif&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;lwn-net&quot;&gt;lwn.net&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lwn.net&quot;&gt;lwn.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;LWN allows you to choose from a selection of fonts, but I still chose to
override it with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;adobe-fonts&#x2F;source-sans&quot;&gt;Source Sans 3&lt;&#x2F;a&gt; variable font. I also adjust the headings
to be less bold and also constrain the article text width.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    font-family&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; SourceSans3VF&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; sans-serif;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;h1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    font-weight&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 600&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;DIV&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;PageHeadline&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;DIV&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;ArticleText&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    max-width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 800&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;px;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;news-ycombinator-com&quot;&gt;news.ycombinator​.com&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&quot;&gt;news.ycombinator.com&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The post text in Hacker News self-posts like “Ask HN” posts is shown in barely
legible light grey. Fix this.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;* Make HN post text readable *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;toptext&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    color&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: #&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;333&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;toptext&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    color&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: #&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;1973c2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;https-okmij-org-ftp-papers-dreamospaper-html&quot;&gt;https:&#x2F;&#x2F;okmij.org​&#x2F;ftp&#x2F;papers&#x2F;​DreamOSPaper.html&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs starting with:
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;okmij.org&#x2F;ftp&#x2F;papers&#x2F;DreamOSPaper.html&quot;&gt;https:&#x2F;&#x2F;okmij.org&#x2F;ftp&#x2F;papers&#x2F;​DreamOSPaper.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Readable &lt;code&gt;max-width&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    max-width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 640&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;px;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    margin&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;em auto;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;phanpy-social&quot;&gt;phanpy.social&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;phanpy.social&quot;&gt;phanpy.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Custom font.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    font-family&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Figtree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; sans-serif;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;thingspool-net&quot;&gt;thingspool.net&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;thingspool.net&quot;&gt;thingspool.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Readable &lt;code&gt;max-width&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    max-width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 700&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;px;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;wikipedia-org&quot;&gt;wikipedia.org&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wikipedia.org&quot;&gt;wikipedia.org&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Better font on desktop.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    font-family&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Lato&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; sans-serif;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt; &#x2F;* Lato is used by m.wikipedia *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;www-bleepingcomputer-com&quot;&gt;www.​bleepingcomputer​.com&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.bleepingcomputer.com&quot;&gt;www.bleepingcomputer.com&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Nicer font.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;bc_main_content&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;bc_main_content&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; li&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    font-family&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Figtree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; sans-serif;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    font-weight&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; normal;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;yeslogic-element-io&quot;&gt;yeslogic.element.io&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Applies to:&lt;&#x2F;strong&gt; URLs on the domain: yeslogic.element.io&lt;&#x2F;p&gt;
&lt;p&gt;This is a hosted Matrix instance using the Element client.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Use the system emoji font (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;joypixels.com&#x2F;&quot;&gt;JoyPixels&lt;&#x2F;a&gt;) instead of the heinous Noto Color Emoji.&lt;&#x2F;li&gt;
&lt;li&gt;Use the system monospace font (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fsd.it&#x2F;shop&#x2F;fonts&#x2F;pragmatapro&#x2F;&quot;&gt;PragmataPro&lt;&#x2F;a&gt;) instead of a custom one.&lt;&#x2F;li&gt;
&lt;li&gt;Reduce line height of monospace text to match other places monospace text shown on my system.&lt;&#x2F;li&gt;
&lt;li&gt;Stop the aptly named “Spaces” bar from wasting space.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    font-family&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Inter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; sans-serif;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    &#x2F;* Make it use system emoji font *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;mx_EventTile_content&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;markdown-body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; code&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;mx_EventTile_content&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;markdown-body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; pre&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    font-family&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; monospace !important;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;mx_EventTile_content&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;markdown-body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; pre&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    line-height&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1.3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;aria-label&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Spaces&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    display&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; none;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</description>
    </item>
    
    <item>
      <title>Replicating My Alacritty Appearance in Ghostty</title>
      <pubDate>Fri, 10 Jan 2025 08:54:40 +1000</pubDate>
      <atom:published>2025-01-10T08:54:40+10:00</atom:published>
      <atom:updated>2025-01-15T09:51:03+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2025&#x2F;ghostty-config&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2025&#x2F;ghostty-config&#x2F;</guid>
      <description>&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ghostty.org&#x2F;&quot;&gt;Ghostty&lt;&#x2F;a&gt; by Mitchell Hashimoto is the new hotness in the terminal emulator
world. It recently came out of private beta launching publicly as 1.0. It’s
similar to other GPU accelerated terminal emulators like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alacritty.org&#x2F;&quot;&gt;Alacritty&lt;&#x2F;a&gt; and
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sw.kovidgoyal.net&#x2F;kitty&#x2F;&quot;&gt;Kitty&lt;&#x2F;a&gt;, but differs in that it uses the native toolkit on macOS and Linux
(GTK). For nerds it’s also interesting because it’s implemented in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ziglang.org&#x2F;&quot;&gt;Zig&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Initially I dismissed Ghostty as not offering me anything over my current
terminal emulator, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alacritty.org&#x2F;&quot;&gt;Alacritty&lt;&#x2F;a&gt;. Largely because of my use of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;awesomewm.org&#x2F;&quot;&gt;Awesome window
manager&lt;&#x2F;a&gt;. Using a tiling window manager means I have no need for tabs in my
terminal emulator, and I have Awesome configured to show no window decorations
on most windows. I thought this meant that Ghostty using a native UI offered me
very little. However, after some recent discussions I noted that Ghostty did
support two longstanding missing features in Alacritty:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Text rendering with ligatures&lt;&#x2F;li&gt;
&lt;li&gt;Bitmap image support, such as sixel&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This prompted me to take another look at Ghostty. I set about tweaking the
settings to remove all the UI chrome and get the theme to match my Alacritty
config. This is the result:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;ghostty-config&amp;#x2F;ghostty-fastfetch.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;ghostty-config&amp;#x2F;ghostty-fastfetch.png&quot; width=&quot;700&quot; alt=&quot;Screenshot of the output of fastfetch in Ghostty.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;fastfetch output in Ghostty.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Compared to Alacritty:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;ghostty-config&amp;#x2F;alacritty-fastfetch.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;ghostty-config&amp;#x2F;alacritty-fastfetch.png&quot; width=&quot;700&quot; alt=&quot;Screenshot of the output of fastfetch in Ghostty. It&amp;#x27;s slightly narrower than the Ghostty output.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;fastfetch output in Alacritty.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;For some reason Ghostty is rendering PragmataPro slightly wider than Alacritty
despite them both being set to the same font size.&lt;&#x2F;p&gt;
&lt;p&gt;Finally here’s a sample document in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;swsnr&#x2F;mdcat&quot;&gt;mdcat&lt;&#x2F;a&gt; showing image and ligature support:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;ghostty-config&amp;#x2F;ghostty-mdcat.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2025&amp;#x2F;ghostty-config&amp;#x2F;ghostty-mdcat.png&quot; width=&quot;700&quot; alt=&quot;Screenshot of the output of mdcat rendering a showcase Markdown file that includes formatting, images, and ligatures.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;mdcat rendering a sample Markdown document.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;With the visuals out of the way, now I just need to spend some time with
Ghostty to see how it compares in practice. My configuration for both terminal
emulators can be found in my dotfiles repo:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;dotfiles&#x2F;blob&#x2F;master&#x2F;config&#x2F;alacritty&#x2F;alacritty.yml&quot;&gt;Alacritty&lt;&#x2F;a&gt; (I need to migrate this one to TOML)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;dotfiles&#x2F;blob&#x2F;master&#x2F;config&#x2F;ghostty&#x2F;config&quot;&gt;Ghostty&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Update 15 Jan 2025:&lt;&#x2F;strong&gt; I have suspended my use of Ghostty for now, as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ghostty-org&#x2F;ghostty&#x2F;issues&#x2F;4504#issuecomment-2589154353&quot;&gt;the
text rendering is not right&lt;&#x2F;a&gt;. I’ve been staring at PragmataPro
for more than a decade, so the difference from how I expect it to look keeps
distracting me. If that issue is fixed I will likely revisit Ghostty as I did
enjoy some of the features.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Building a Tiny CDN With pyinfra and Chimera Linux</title>
      <pubDate>Mon, 09 Dec 2024 10:02:49 +1000</pubDate>
      <atom:published>2024-12-09T10:02:49+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;tiny-cdn&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;tiny-cdn&#x2F;</guid>
      <description>&lt;p&gt;In my quest to make &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;linkedlist.org&#x2F;&quot;&gt;linkedlist.org&lt;&#x2F;a&gt;—my link blog—faster, I set
up multiple deployments around the world. I used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pyinfra.com&#x2F;&quot;&gt;pyinfra&lt;&#x2F;a&gt; to automate the
process and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;&quot;&gt;Chimera Linux&lt;&#x2F;a&gt; as the host operating system. Join me on this
adventure in over-engineering to see how I dropped the average response time
across nine global locations from 807ms to 189ms without spending a fortune.&lt;&#x2F;p&gt;
&lt;figure class=&quot;text-center&quot;&gt;
&lt;svg
   width=&quot;93.756485mm&quot;
   height=&quot;168.49266mm&quot;
   style=&quot;width: 250px; height: auto;&quot;
   viewBox=&quot;0 0 93.756485 168.49266&quot;
   version=&quot;1.1&quot;
   id=&quot;svg1&quot;
   aria-labelledby=&quot;title desc&quot;
   role=&quot;img&quot;
   xml:space=&quot;preserve&quot;
   xmlns:xlink=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;1999&#x2F;xlink&quot;
   xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot;
   xmlns:svg=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot;&gt;
   &lt;title&gt;Network diagram of Linked List infrastructure&lt;&#x2F;title&gt;
   &lt;desc&gt;A network diagram showing a user at the top. An arrow from the user points downward to a node labelled Gcore GeoDNS. Three dashed arrows point down from the Gcore node to three servers labelled: AU, FR, and NY. Below the servers at the bottom of the diagram is another smaller server titled Qotom. It has arrows pointing up to each of the other servers with a label over the arrows, &quot;Certs&quot;.&lt;&#x2F;desc&gt;
   &lt;defs
     id=&quot;defs1&quot;&gt;&lt;marker
       style=&quot;overflow:visible&quot;
       id=&quot;marker67&quot;
       refX=&quot;0&quot;
       refY=&quot;0&quot;
       orient=&quot;auto-start-reverse&quot;
       markerWidth=&quot;1&quot;
       markerHeight=&quot;1&quot;
       viewBox=&quot;0 0 1 1&quot;
       preserveAspectRatio=&quot;xMidYMid&quot;&gt;&lt;path
         transform=&quot;scale(0.5)&quot;
         style=&quot;fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt&quot;
         d=&quot;M 5.77,0 -2.88,5 V -5 Z&quot;
         id=&quot;path67&quot; &#x2F;&gt;&lt;&#x2F;marker&gt;&lt;marker
       style=&quot;overflow:visible&quot;
       id=&quot;marker62&quot;
       refX=&quot;0&quot;
       refY=&quot;0&quot;
       orient=&quot;auto-start-reverse&quot;
       markerWidth=&quot;1&quot;
       markerHeight=&quot;1&quot;
       viewBox=&quot;0 0 1 1&quot;
       preserveAspectRatio=&quot;xMidYMid&quot;&gt;&lt;path
         transform=&quot;scale(0.5)&quot;
         style=&quot;fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt&quot;
         d=&quot;M 5.77,0 -2.88,5 V -5 Z&quot;
         id=&quot;path62&quot; &#x2F;&gt;&lt;&#x2F;marker&gt;&lt;marker
       style=&quot;overflow:visible&quot;
       id=&quot;Triangle&quot;
       refX=&quot;0&quot;
       refY=&quot;0&quot;
       orient=&quot;auto-start-reverse&quot;
       markerWidth=&quot;1&quot;
       markerHeight=&quot;1&quot;
       viewBox=&quot;0 0 1 1&quot;
       preserveAspectRatio=&quot;xMidYMid&quot;&gt;&lt;path
         transform=&quot;scale(0.5)&quot;
         style=&quot;fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt&quot;
         d=&quot;M 5.77,0 -2.88,5 V -5 Z&quot;
         id=&quot;path135&quot; &#x2F;&gt;&lt;&#x2F;marker&gt;&lt;linearGradient
       xlink:href=&quot;#gradient0-3&quot;
       id=&quot;linearGradient31&quot;
       gradientUnits=&quot;userSpaceOnUse&quot;
       x1=&quot;27.65&quot;
       y1=&quot;-1.9400001&quot;
       x2=&quot;51.700001&quot;
       y2=&quot;19.120001&quot; &#x2F;&gt;&lt;linearGradient
       xlink:href=&quot;#gradient1-6&quot;
       id=&quot;linearGradient32&quot;
       gradientUnits=&quot;userSpaceOnUse&quot;
       x1=&quot;48.049999&quot;
       y1=&quot;-27.809999&quot;
       x2=&quot;77.269997&quot;
       y2=&quot;-16.549999&quot; &#x2F;&gt;&lt;linearGradient
       xlink:href=&quot;#gradient2-61&quot;
       id=&quot;linearGradient33&quot;
       gradientUnits=&quot;userSpaceOnUse&quot;
       x1=&quot;31.91&quot;
       y1=&quot;46.25&quot;
       x2=&quot;4.4099998&quot;
       y2=&quot;34.93&quot; &#x2F;&gt;&lt;linearGradient
       xlink:href=&quot;#gradient3-65&quot;
       id=&quot;linearGradient34&quot;
       gradientUnits=&quot;userSpaceOnUse&quot;
       x1=&quot;54.040001&quot;
       y1=&quot;-6.4099998&quot;
       x2=&quot;60.5&quot;
       y2=&quot;1.62&quot; &#x2F;&gt;&lt;linearGradient
       xlink:href=&quot;#gradient0-3&quot;
       id=&quot;linearGradient47&quot;
       gradientUnits=&quot;userSpaceOnUse&quot;
       x1=&quot;27.65&quot;
       y1=&quot;-1.9400001&quot;
       x2=&quot;51.700001&quot;
       y2=&quot;19.120001&quot; &#x2F;&gt;&lt;linearGradient
       xlink:href=&quot;#gradient1-6&quot;
       id=&quot;linearGradient48&quot;
       gradientUnits=&quot;userSpaceOnUse&quot;
       x1=&quot;48.049999&quot;
       y1=&quot;-27.809999&quot;
       x2=&quot;77.269997&quot;
       y2=&quot;-16.549999&quot; &#x2F;&gt;&lt;linearGradient
       xlink:href=&quot;#gradient2-61&quot;
       id=&quot;linearGradient49&quot;
       gradientUnits=&quot;userSpaceOnUse&quot;
       x1=&quot;31.91&quot;
       y1=&quot;46.25&quot;
       x2=&quot;4.4099998&quot;
       y2=&quot;34.93&quot; &#x2F;&gt;&lt;linearGradient
       xlink:href=&quot;#gradient3-65&quot;
       id=&quot;linearGradient50&quot;
       gradientUnits=&quot;userSpaceOnUse&quot;
       x1=&quot;54.040001&quot;
       y1=&quot;-6.4099998&quot;
       x2=&quot;60.5&quot;
       y2=&quot;1.62&quot; &#x2F;&gt;&lt;linearGradient
       xlink:href=&quot;#gradient0-3&quot;
       id=&quot;linearGradient51&quot;
       gradientUnits=&quot;userSpaceOnUse&quot;
       x1=&quot;27.65&quot;
       y1=&quot;-1.9400001&quot;
       x2=&quot;51.700001&quot;
       y2=&quot;19.120001&quot; &#x2F;&gt;&lt;linearGradient
       xlink:href=&quot;#gradient1-6&quot;
       id=&quot;linearGradient52&quot;
       gradientUnits=&quot;userSpaceOnUse&quot;
       x1=&quot;48.049999&quot;
       y1=&quot;-27.809999&quot;
       x2=&quot;77.269997&quot;
       y2=&quot;-16.549999&quot; &#x2F;&gt;&lt;linearGradient
       xlink:href=&quot;#gradient2-61&quot;
       id=&quot;linearGradient53&quot;
       gradientUnits=&quot;userSpaceOnUse&quot;
       x1=&quot;31.91&quot;
       y1=&quot;46.25&quot;
       x2=&quot;4.4099998&quot;
       y2=&quot;34.93&quot; &#x2F;&gt;&lt;linearGradient
       xlink:href=&quot;#gradient3-65&quot;
       id=&quot;linearGradient54&quot;
       gradientUnits=&quot;userSpaceOnUse&quot;
       x1=&quot;54.040001&quot;
       y1=&quot;-6.4099998&quot;
       x2=&quot;60.5&quot;
       y2=&quot;1.62&quot; &#x2F;&gt;&lt;&#x2F;defs&gt;&lt;g
     id=&quot;layer1&quot;
     transform=&quot;translate(-63.291283,-13.793306)&quot;&gt;&lt;g
       style=&quot;color-interpolation:linearRGB&quot;
       id=&quot;g1&quot;
       transform=&quot;matrix(0.26458333,0,0,0.26458333,97.868591,13.793306)&quot;&gt;&lt;g
         id=&quot;g14&quot;&gt;&lt;path
           style=&quot;fill:#010101;fill-opacity:0.4235&quot;
           d=&quot;m 28,64 c 0,0 5,0 10,0 5,0 10.2,-3.04 14.75,-2.88 10.37,0.38 14.5,-4.75 8.12,-6.75 C 57.04,53.17 52,53.25 49.12,51.25 45.45,48.69 38,48 38,48 v 7 z&quot;
           id=&quot;path1&quot; &#x2F;&gt;&lt;path
           style=&quot;fill:none;stroke:#000000;stroke-width:4&quot;
           d=&quot;m 12,18 v 17 l 4,4 v 17 l 4,2 h 2 l 1,1.5 5,2.5 8,-8 V 43 l 4,-4 V 21 L 21,12 Z&quot;
           id=&quot;path2&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient0&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;73.739998&quot;
           y1=&quot;-57.07&quot;
           x2=&quot;116.61&quot;
           y2=&quot;-8.0500002&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#ffdb97&quot;
             id=&quot;stop2&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#fcaf29&quot;
             id=&quot;stop3&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:url(#gradient0)&quot;
           d=&quot;m 12,18 v 17 l 4,4 v 17 l 4,2 h 2 l 1,1.5 5,2.5 V 45 h 4 V 27 Z&quot;
           id=&quot;path3&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient1&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;-43.970001&quot;
           y1=&quot;-33.98&quot;
           x2=&quot;-24.83&quot;
           y2=&quot;-51.950001&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#fff7ea&quot;
             id=&quot;stop4&quot; &#x2F;&gt;&lt;stop
             offset=&quot;0.9962&quot;
             stop-color=&quot;#fdd17b&quot;
             id=&quot;stop5&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:url(#gradient1)&quot;
           d=&quot;M 12,18 32,27 40,21 26,20 31.99,16.99 21,12 Z&quot;
           id=&quot;path5&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient2&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;54.23&quot;
           y1=&quot;-52.610001&quot;
           x2=&quot;75.839996&quot;
           y2=&quot;-45.970001&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#c85805&quot;
             id=&quot;stop6&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#f06306&quot;
             id=&quot;stop7&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:url(#gradient2)&quot;
           d=&quot;m 32,45 h -4 v 17 l 8,-8 V 43 l 4,-4 V 21 l -8,6 z&quot;
           id=&quot;path7&quot; &#x2F;&gt;&lt;path
           style=&quot;fill:#a32904&quot;
           d=&quot;m 28,45 v 6 l 8,-8 -4,2 z&quot;
           id=&quot;path8&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient3&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;28.92&quot;
           y1=&quot;-64&quot;
           x2=&quot;39.07&quot;
           y2=&quot;-64&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#c85804&quot;
             id=&quot;stop8&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#dc952f&quot;
             id=&quot;stop9&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:url(#gradient3)&quot;
           d=&quot;m 26,20 14,1 -8,-4 z&quot;
           id=&quot;path9&quot; &#x2F;&gt;&lt;path
           style=&quot;fill:none;stroke:#000000;stroke-width:4&quot;
           d=&quot;m 26,2 c -4,0 -8,4 -8,8 0,4 4,8 8,8 4,0 8,-4 8,-8 0,-4 -4,-8 -8,-8 z&quot;
           id=&quot;path10&quot; &#x2F;&gt;&lt;radialGradient
           id=&quot;gradient4&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           cx=&quot;0&quot;
           cy=&quot;0&quot;
           r=&quot;64&quot;
           gradientTransform=&quot;matrix(0.2361,0,0,0.2321,22.625,6.375)&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#f2f2f2&quot;
             id=&quot;stop10&quot; &#x2F;&gt;&lt;stop
             offset=&quot;0.6742&quot;
             stop-color=&quot;#7d7a7a&quot;
             id=&quot;stop11&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#bca184&quot;
             id=&quot;stop12&quot; &#x2F;&gt;&lt;&#x2F;radialGradient&gt;&lt;path
           style=&quot;fill:url(#gradient4)&quot;
           d=&quot;m 26,2 c -4,0 -8,4 -8,8 0,4 4,8 8,8 4,0 8,-4 8,-8 0,-4 -4,-8 -8,-8 z&quot;
           id=&quot;path12&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient5&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;54.23&quot;
           y1=&quot;-52.610001&quot;
           x2=&quot;75.839996&quot;
           y2=&quot;-45.970001&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#c85805&quot;
             id=&quot;stop13&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#f06306&quot;
             id=&quot;stop14&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:url(#gradient5)&quot;
           d=&quot;m 20,58 h 2 V 44 l -2,-1 z&quot;
           id=&quot;path14&quot; &#x2F;&gt;&lt;&#x2F;g&gt;&lt;&#x2F;g&gt;&lt;g
       style=&quot;color-interpolation:linearRGB&quot;
       id=&quot;g2&quot;
       transform=&quot;matrix(0.34933984,0,0,0.34933984,93.821125,57.788386)&quot;&gt;&lt;g
         id=&quot;g18&quot;&gt;&lt;path
           style=&quot;fill:#010101;fill-opacity:0.4313&quot;
           d=&quot;m 44,64 h 5 L 64,48 60,46 Z M 19,20 c -7.19,0 -13,2.23 -13,5 0,2.75 5.81,5 13,5 7.17,0 13,-2.25 13,-5 0,-2.77 -5.83,-5 -13,-5 z&quot;
           id=&quot;path1-3&quot; &#x2F;&gt;&lt;path
           style=&quot;fill:none;stroke:#010101;stroke-width:4;stroke-opacity:0.4313&quot;
           d=&quot;M 32,42 C 32,42 18,42 18,38 18,30 34,28 34,22 34,18 23,18 23,18&quot;
           transform=&quot;matrix(1,0,0,0.9285,2,3.1428)&quot;
           id=&quot;path2-6&quot; &#x2F;&gt;&lt;path
           style=&quot;fill:none;stroke:#000000;stroke-width:4&quot;
           d=&quot;M 32,42 C 32,42 18,42 18,38 18,30 34,28 34,22 34,18 23,18 23,18 m 26,4 9,3 V 48 L 44,62 34,56 V 32 Z M 14,2 C 7.37,2 2,7.37 2,14 2,20.61 7.37,26 14,26 20.61,26 26,20.61 26,14 26,7.37 20.61,2 14,2 Z&quot;
           id=&quot;path3-7&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient0-5&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;33.689999&quot;
           y1=&quot;1.88&quot;
           x2=&quot;51.599998&quot;
           y2=&quot;3.6700001&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#d7d7d7&quot;
             id=&quot;stop3-3&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#b8b8b8&quot;
             id=&quot;stop4-5&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:url(#gradient0-5)&quot;
           d=&quot;m 44,36 v 26 l -2.13,-1.38 v -10 L 36,48 36.02,57.21 34,56 V 32 Z&quot;
           id=&quot;path4&quot; &#x2F;&gt;&lt;path
           style=&quot;fill:#949494&quot;
           d=&quot;M 37.48,48.66 36,48 l 0.02,9.21 1.29,0.75 z&quot;
           id=&quot;path5-6&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient1-2&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;33.689999&quot;
           y1=&quot;1.88&quot;
           x2=&quot;51.599998&quot;
           y2=&quot;3.6700001&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#d2d2d2&quot;
             id=&quot;stop5-9&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#858585&quot;
             id=&quot;stop6-1&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:url(#gradient1-2)&quot;
           d=&quot;m 40.39,49.96 -2.91,-1.3 -0.17,9.3 3.06,1.79 z&quot;
           id=&quot;path6&quot; &#x2F;&gt;&lt;path
           style=&quot;fill:#c4c4c4&quot;
           d=&quot;m 41.87,60.62 v -10 l -1.48,-0.66 -0.02,9.79 z&quot;
           id=&quot;path7-2&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient2-7&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;24&quot;
           y1=&quot;7.9899998&quot;
           x2=&quot;64&quot;
           y2=&quot;7.9899998&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#f4f4f4&quot;
             id=&quot;stop7-0&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#dbdbdb&quot;
             id=&quot;stop8-9&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:url(#gradient2-7)&quot;
           d=&quot;M 49,22 58,25 44,36 34,32 Z&quot;
           id=&quot;path8-3&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient3-6&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;55.689999&quot;
           y1=&quot;-10.23&quot;
           x2=&quot;74.970001&quot;
           y2=&quot;-5.4099998&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#5c5c5c&quot;
             id=&quot;stop9-0&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#838383&quot;
             id=&quot;stop10-6&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:url(#gradient3-6)&quot;
           d=&quot;M 44,36 58,25 V 48 L 44,62 Z&quot;
           id=&quot;path10-2&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient4-6&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;33.689999&quot;
           y1=&quot;1.88&quot;
           x2=&quot;51.599998&quot;
           y2=&quot;3.6700001&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#d2d2d2&quot;
             id=&quot;stop11-1&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#858585&quot;
             id=&quot;stop12-8&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:none;stroke:url(#gradient4-6);stroke-width:1&quot;
           d=&quot;m 40,38 v 2 l 2,0.5 v -2 z&quot;
           id=&quot;path12-7&quot; &#x2F;&gt;&lt;path
           style=&quot;fill:#a9ff00&quot;
           d=&quot;m 40,38 v 2 l 2,0.5 v -2 z&quot;
           id=&quot;path13&quot; &#x2F;&gt;&lt;radialGradient
           id=&quot;gradient5-9&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           cx=&quot;0&quot;
           cy=&quot;0&quot;
           r=&quot;64&quot;
           gradientTransform=&quot;matrix(0.25,0,0,0.25,10,10)&quot;&gt;&lt;stop
             offset=&quot;0.044&quot;
             stop-color=&quot;#9eedff&quot;
             id=&quot;stop13-2&quot; &#x2F;&gt;&lt;stop
             offset=&quot;0.1457&quot;
             stop-color=&quot;#67ceff&quot;
             id=&quot;stop14-0&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#0473b3&quot;
             id=&quot;stop15&quot; &#x2F;&gt;&lt;&#x2F;radialGradient&gt;&lt;path
           style=&quot;fill:url(#gradient5-9)&quot;
           d=&quot;M 14,2 C 10.46,2 7.27,3.52 5.07,5.96 5.07,5.96 8,6 8,8 8,10 4,14 4,14 v 2 c 0,0 2,0 4,2 2,2 0,4 0,4 l 2.27,3.41 C 11.44,25.78 12.69,26 14,26 c 2.99,0 5.72,-1.1 7.83,-2.93 L 20,22 22,18 c 0,0 -6,-2 -6,-4 0,-2 4,-6 4,-6 L 18,6 15.87,8 H 14 L 16,6 14,4 17.39,2.48 C 16.31,2.16 15.17,2 14,2 Z&quot;
           id=&quot;path15&quot; &#x2F;&gt;&lt;radialGradient
           id=&quot;gradient6&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           cx=&quot;0&quot;
           cy=&quot;0&quot;
           r=&quot;64&quot;
           gradientTransform=&quot;matrix(0.25,0,0,0.25,10,10)&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#b9ff97&quot;
             id=&quot;stop16&quot; &#x2F;&gt;&lt;stop
             offset=&quot;0.7457&quot;
             stop-color=&quot;#05d65e&quot;
             id=&quot;stop17&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#049943&quot;
             id=&quot;stop18&quot; &#x2F;&gt;&lt;&#x2F;radialGradient&gt;&lt;path
           style=&quot;fill:url(#gradient6)&quot;
           d=&quot;M 5.07,5.96 C 3.16,8.08 2,10.9 2,14 c 0,5.32 3.47,9.84 8.27,11.41 L 8,22 C 8,22 10,20 8,18 6,16 4,16 4,16 V 14 C 4,14 8,10 8,8 8,6 5.07,5.96 5.07,5.96 Z M 14,8 h 2.37 L 18,6 20,8 c 0,0 -4,4 -4,6 0,2 6,4 6,4 l -2,4 1.83,1.07 C 24.38,20.88 26,17.62 26,14 26,8.54 22.36,3.95 17.39,2.48 L 14,4 16,6 Z&quot;
           id=&quot;path18&quot; &#x2F;&gt;&lt;&#x2F;g&gt;&lt;&#x2F;g&gt;&lt;g
       style=&quot;color-interpolation:linearRGB&quot;
       id=&quot;g3&quot;
       transform=&quot;matrix(0.26458333,0,0,0.26458333,96.533333,159.16141)&quot;&gt;&lt;g
         id=&quot;g11&quot;&gt;&lt;path
           style=&quot;fill:#010101;fill-opacity:0.5019&quot;
           d=&quot;m 36,61 h 5 l 23,-31 -6,-3 1,4 z&quot;
           id=&quot;path1-2&quot; &#x2F;&gt;&lt;path
           style=&quot;fill:none;stroke:#010000;stroke-width:4&quot;
           d=&quot;M 2,28 V 41 L 36,59 58,30 V 17 L 25,5 Z&quot;
           id=&quot;path2-3&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient0-7&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;13.84&quot;
           y1=&quot;-38.139999&quot;
           x2=&quot;58.34&quot;
           y2=&quot;-27.700001&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#ffffff&quot;
             id=&quot;stop2-5&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#d3d3d3&quot;
             id=&quot;stop3-9&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:url(#gradient0-7)&quot;
           d=&quot;M 2,28 36,43 58,17 25,5 Z&quot;
           id=&quot;path3-2&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient1-28&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;44.720001&quot;
           y1=&quot;-7.9000001&quot;
           x2=&quot;86.099998&quot;
           y2=&quot;10.51&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#474747&quot;
             id=&quot;stop4-9&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#a5a0a0&quot;
             id=&quot;stop5-7&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:url(#gradient1-28)&quot;
           d=&quot;M 36,43 V 59 L 58,30 V 17 Z&quot;
           id=&quot;path5-3&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient2-6&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;22.969999&quot;
           y1=&quot;92.589996&quot;
           x2=&quot;-14.6&quot;
           y2=&quot;67.290001&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#7d7d7d&quot;
             id=&quot;stop6-12&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#d4d4d4&quot;
             id=&quot;stop7-9&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:url(#gradient2-6)&quot;
           d=&quot;M 2,28 V 41 L 36,59 V 43 Z&quot;
           id=&quot;path7-3&quot; &#x2F;&gt;&lt;linearGradient
           id=&quot;gradient3-1&quot;
           gradientUnits=&quot;userSpaceOnUse&quot;
           x1=&quot;32.049999&quot;
           y1=&quot;9.6800003&quot;
           x2=&quot;43.48&quot;
           y2=&quot;24.049999&quot;&gt;&lt;stop
             offset=&quot;0&quot;
             stop-color=&quot;#596756&quot;
             id=&quot;stop8-94&quot; &#x2F;&gt;&lt;stop
             offset=&quot;1&quot;
             stop-color=&quot;#ebb2b2&quot;
             id=&quot;stop9-7&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
           style=&quot;fill:none;stroke:url(#gradient3-1);stroke-width:5;stroke-linecap:round&quot;
           d=&quot;m 7,35 5.75,2.5&quot;
           id=&quot;path9-8&quot; &#x2F;&gt;&lt;path
           style=&quot;fill:none;stroke:#ff0000;stroke-width:2&quot;
           d=&quot;m 6,35 3,1&quot;
           transform=&quot;translate(4,1.5)&quot;
           id=&quot;path10-4&quot; &#x2F;&gt;&lt;path
           style=&quot;fill:none;stroke:#a7ff00;stroke-width:2&quot;
           d=&quot;m 6,35 3,1&quot;
           transform=&quot;translate(0,-0.25)&quot;
           id=&quot;path11&quot; &#x2F;&gt;&lt;&#x2F;g&gt;&lt;&#x2F;g&gt;&lt;g
       id=&quot;g54&quot;
       transform=&quot;translate(6.6579919,2.8395622)&quot;&gt;&lt;g
         style=&quot;color-interpolation:linearRGB&quot;
         id=&quot;g4&quot;
         transform=&quot;matrix(0.26458333,0,0,0.26458333,55.574958,105.35719)&quot;&gt;&lt;g
           id=&quot;g13&quot;&gt;&lt;path
             style=&quot;fill:#000000;fill-opacity:0.3294&quot;
             d=&quot;m 6,52 20,-14 18,-1 15,4 c 2,1 0,3 0,3 L 43,61 c -1,1 -3.04,1 -3.04,1 H 28 Z&quot;
             id=&quot;path1-5&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:none;stroke:#000000;stroke-width:4&quot;
             d=&quot;M 23,3 7,15 c 0,0 -1,0 -1,2 0,2 0,28 0,28 0,2 1,3 1,3 l 19,10 c 0,0 1,1 3,-1 L 46,39 c 0,0 0,-25 0,-27 0,-2 -1,-2 -1,-2 L 27.1,2.8 C 27.1,2.8 25,1 23,3 Z&quot;
             id=&quot;path2-0&quot; &#x2F;&gt;&lt;linearGradient
             id=&quot;gradient0-3&quot;
             gradientUnits=&quot;userSpaceOnUse&quot;
             x1=&quot;27.65&quot;
             y1=&quot;-1.9400001&quot;
             x2=&quot;51.700001&quot;
             y2=&quot;19.120001&quot;&gt;&lt;stop
               offset=&quot;0&quot;
               stop-color=&quot;#cecece&quot;
               id=&quot;stop2-6&quot; &#x2F;&gt;&lt;stop
               offset=&quot;1&quot;
               stop-color=&quot;#acacac&quot;
               id=&quot;stop3-1&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
             style=&quot;fill:url(#linearGradient51)&quot;
             d=&quot;m 6,17 c 0,1 0,26 0,28 0,2 1.05,2.52 1.05,2.52 l 19.93,9.97 C 26.98,57.49 28,57 28,56 28,55 28,27 28,25 28,23 26.97,22.57 26.97,22.57 L 7,15 c 0,0 -1,0 -1,2 z&quot;
             id=&quot;path3-0&quot; &#x2F;&gt;&lt;linearGradient
             id=&quot;gradient1-6&quot;
             gradientUnits=&quot;userSpaceOnUse&quot;
             x1=&quot;48.049999&quot;
             y1=&quot;-27.809999&quot;
             x2=&quot;77.269997&quot;
             y2=&quot;-16.549999&quot;&gt;&lt;stop
               offset=&quot;0&quot;
               stop-color=&quot;#858585&quot;
               id=&quot;stop4-3&quot; &#x2F;&gt;&lt;stop
               offset=&quot;1&quot;
               stop-color=&quot;#bababa&quot;
               id=&quot;stop5-2&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
             style=&quot;fill:url(#linearGradient52)&quot;
             d=&quot;M 28,25 V 57 L 46,39 V 11 Z&quot;
             id=&quot;path5-0&quot; &#x2F;&gt;&lt;linearGradient
             id=&quot;gradient2-61&quot;
             gradientUnits=&quot;userSpaceOnUse&quot;
             x1=&quot;31.91&quot;
             y1=&quot;46.25&quot;
             x2=&quot;4.4099998&quot;
             y2=&quot;34.93&quot;&gt;&lt;stop
               offset=&quot;0&quot;
               stop-color=&quot;#c3c3c3&quot;
               id=&quot;stop6-5&quot; &#x2F;&gt;&lt;stop
               offset=&quot;1&quot;
               stop-color=&quot;#fffcf9&quot;
               id=&quot;stop7-5&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
             style=&quot;fill:url(#linearGradient53)&quot;
             d=&quot;M 25,2 7,15 27,23 45,10 Z&quot;
             id=&quot;path7-4&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#000000&quot;
             d=&quot;m 18,20 v 2 c 0,0 4,1 4,4 0,1 0,29 0,29 l 5,2 V 26 c 0,-2 -1,-2 -1,-2 z&quot;
             id=&quot;path8-7&quot; &#x2F;&gt;&lt;linearGradient
             id=&quot;gradient3-65&quot;
             gradientUnits=&quot;userSpaceOnUse&quot;
             x1=&quot;54.040001&quot;
             y1=&quot;-6.4099998&quot;
             x2=&quot;60.5&quot;
             y2=&quot;1.62&quot;&gt;&lt;stop
               offset=&quot;0&quot;
               stop-color=&quot;#e8e8e8&quot;
               id=&quot;stop8-6&quot; &#x2F;&gt;&lt;stop
               offset=&quot;0.9973&quot;
               stop-color=&quot;#a5a5a5&quot;
               id=&quot;stop9-9&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
             style=&quot;fill:url(#linearGradient54)&quot;
             d=&quot;m 28,25 c 0,0 0,-2 -1,-2 2,-1 18,-13 18,-13 0,0 1,0 1,2 -3,2 -18,13 -18,13 z&quot;
             id=&quot;path9-3&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#000000&quot;
             d=&quot;m 44,42 c 0,0 2,1 2,2 0,1 -2,3 -3,3 -1,0 -2,-2 -2,-2&quot;
             id=&quot;path10-7&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#000000&quot;
             d=&quot;m 44,42 c 0,0 2,1 2,2 0,1 -2,3 -3,3 -1,0 -2,-2 -2,-2&quot;
             transform=&quot;translate(-10,11)&quot;
             id=&quot;path11-4&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#a9ff00&quot;
             d=&quot;m 44,42 c 0,0 2,1 2,2 0,1 -2,3 -3,3 -1,0 -2,-2 -2,-2&quot;
             transform=&quot;matrix(-0.2449,-0.2777,0.1656,-0.4108,27.9469,59.752)&quot;
             id=&quot;path12-5&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#a9ff00&quot;
             d=&quot;m 44,42 c 0,0 2,1 2,2 0,1 -2,3 -3,3 -1,0 -2,-2 -2,-2&quot;
             transform=&quot;matrix(-0.2449,-0.2777,0.1656,-0.4108,27.9469,64.752)&quot;
             id=&quot;path13-2&quot; &#x2F;&gt;&lt;&#x2F;g&gt;&lt;&#x2F;g&gt;&lt;g
         style=&quot;color-interpolation:linearRGB&quot;
         id=&quot;g31&quot;
         transform=&quot;matrix(0.26458333,0,0,0.26458333,126.32179,105.35719)&quot;&gt;&lt;g
           id=&quot;g30&quot;&gt;&lt;path
             style=&quot;fill:#000000;fill-opacity:0.3294&quot;
             d=&quot;m 6,52 20,-14 18,-1 15,4 c 2,1 0,3 0,3 L 43,61 c -1,1 -3.04,1 -3.04,1 H 28 Z&quot;
             id=&quot;path16&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:none;stroke:#000000;stroke-width:4&quot;
             d=&quot;M 23,3 7,15 c 0,0 -1,0 -1,2 0,2 0,28 0,28 0,2 1,3 1,3 l 19,10 c 0,0 1,1 3,-1 L 46,39 c 0,0 0,-25 0,-27 0,-2 -1,-2 -1,-2 L 27.1,2.8 C 27.1,2.8 25,1 23,3 Z&quot;
             id=&quot;path17&quot; &#x2F;&gt;&lt;linearGradient
             id=&quot;linearGradient20&quot;
             gradientUnits=&quot;userSpaceOnUse&quot;
             x1=&quot;27.65&quot;
             y1=&quot;-1.9400001&quot;
             x2=&quot;51.700001&quot;
             y2=&quot;19.120001&quot;&gt;&lt;stop
               offset=&quot;0&quot;
               stop-color=&quot;#cecece&quot;
               id=&quot;stop19&quot; &#x2F;&gt;&lt;stop
               offset=&quot;1&quot;
               stop-color=&quot;#acacac&quot;
               id=&quot;stop20&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
             style=&quot;fill:url(#linearGradient31)&quot;
             d=&quot;m 6,17 c 0,1 0,26 0,28 0,2 1.05,2.52 1.05,2.52 l 19.93,9.97 C 26.98,57.49 28,57 28,56 28,55 28,27 28,25 28,23 26.97,22.57 26.97,22.57 L 7,15 c 0,0 -1,0 -1,2 z&quot;
             id=&quot;path20&quot; &#x2F;&gt;&lt;linearGradient
             id=&quot;linearGradient22&quot;
             gradientUnits=&quot;userSpaceOnUse&quot;
             x1=&quot;48.049999&quot;
             y1=&quot;-27.809999&quot;
             x2=&quot;77.269997&quot;
             y2=&quot;-16.549999&quot;&gt;&lt;stop
               offset=&quot;0&quot;
               stop-color=&quot;#858585&quot;
               id=&quot;stop21&quot; &#x2F;&gt;&lt;stop
               offset=&quot;1&quot;
               stop-color=&quot;#bababa&quot;
               id=&quot;stop22&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
             style=&quot;fill:url(#linearGradient32)&quot;
             d=&quot;M 28,25 V 57 L 46,39 V 11 Z&quot;
             id=&quot;path22&quot; &#x2F;&gt;&lt;linearGradient
             id=&quot;linearGradient24&quot;
             gradientUnits=&quot;userSpaceOnUse&quot;
             x1=&quot;31.91&quot;
             y1=&quot;46.25&quot;
             x2=&quot;4.4099998&quot;
             y2=&quot;34.93&quot;&gt;&lt;stop
               offset=&quot;0&quot;
               stop-color=&quot;#c3c3c3&quot;
               id=&quot;stop23&quot; &#x2F;&gt;&lt;stop
               offset=&quot;1&quot;
               stop-color=&quot;#fffcf9&quot;
               id=&quot;stop24&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
             style=&quot;fill:url(#linearGradient33)&quot;
             d=&quot;M 25,2 7,15 27,23 45,10 Z&quot;
             id=&quot;path24&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#000000&quot;
             d=&quot;m 18,20 v 2 c 0,0 4,1 4,4 0,1 0,29 0,29 l 5,2 V 26 c 0,-2 -1,-2 -1,-2 z&quot;
             id=&quot;path25&quot; &#x2F;&gt;&lt;linearGradient
             id=&quot;linearGradient26&quot;
             gradientUnits=&quot;userSpaceOnUse&quot;
             x1=&quot;54.040001&quot;
             y1=&quot;-6.4099998&quot;
             x2=&quot;60.5&quot;
             y2=&quot;1.62&quot;&gt;&lt;stop
               offset=&quot;0&quot;
               stop-color=&quot;#e8e8e8&quot;
               id=&quot;stop25&quot; &#x2F;&gt;&lt;stop
               offset=&quot;0.9973&quot;
               stop-color=&quot;#a5a5a5&quot;
               id=&quot;stop26&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
             style=&quot;fill:url(#linearGradient34)&quot;
             d=&quot;m 28,25 c 0,0 0,-2 -1,-2 2,-1 18,-13 18,-13 0,0 1,0 1,2 -3,2 -18,13 -18,13 z&quot;
             id=&quot;path26&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#000000&quot;
             d=&quot;m 44,42 c 0,0 2,1 2,2 0,1 -2,3 -3,3 -1,0 -2,-2 -2,-2&quot;
             id=&quot;path27&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#000000&quot;
             d=&quot;m 44,42 c 0,0 2,1 2,2 0,1 -2,3 -3,3 -1,0 -2,-2 -2,-2&quot;
             transform=&quot;translate(-10,11)&quot;
             id=&quot;path28&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#a9ff00&quot;
             d=&quot;m 44,42 c 0,0 2,1 2,2 0,1 -2,3 -3,3 -1,0 -2,-2 -2,-2&quot;
             transform=&quot;matrix(-0.2449,-0.2777,0.1656,-0.4108,27.9469,59.752)&quot;
             id=&quot;path29&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#a9ff00&quot;
             d=&quot;m 44,42 c 0,0 2,1 2,2 0,1 -2,3 -3,3 -1,0 -2,-2 -2,-2&quot;
             transform=&quot;matrix(-0.2449,-0.2777,0.1656,-0.4108,27.9469,64.752)&quot;
             id=&quot;path30&quot; &#x2F;&gt;&lt;&#x2F;g&gt;&lt;&#x2F;g&gt;&lt;g
         style=&quot;color-interpolation:linearRGB&quot;
         id=&quot;g47&quot;
         transform=&quot;matrix(0.26458333,0,0,0.26458333,90.948374,104.41229)&quot;&gt;&lt;g
           id=&quot;g46&quot;&gt;&lt;path
             style=&quot;fill:#000000;fill-opacity:0.3294&quot;
             d=&quot;m 6,52 20,-14 18,-1 15,4 c 2,1 0,3 0,3 L 43,61 c -1,1 -3.04,1 -3.04,1 H 28 Z&quot;
             id=&quot;path34&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:none;stroke:#000000;stroke-width:4&quot;
             d=&quot;M 23,3 7,15 c 0,0 -1,0 -1,2 0,2 0,28 0,28 0,2 1,3 1,3 l 19,10 c 0,0 1,1 3,-1 L 46,39 c 0,0 0,-25 0,-27 0,-2 -1,-2 -1,-2 L 27.1,2.8 C 27.1,2.8 25,1 23,3 Z&quot;
             id=&quot;path35&quot; &#x2F;&gt;&lt;linearGradient
             id=&quot;linearGradient36&quot;
             gradientUnits=&quot;userSpaceOnUse&quot;
             x1=&quot;27.65&quot;
             y1=&quot;-1.9400001&quot;
             x2=&quot;51.700001&quot;
             y2=&quot;19.120001&quot;&gt;&lt;stop
               offset=&quot;0&quot;
               stop-color=&quot;#cecece&quot;
               id=&quot;stop35&quot; &#x2F;&gt;&lt;stop
               offset=&quot;1&quot;
               stop-color=&quot;#acacac&quot;
               id=&quot;stop36&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
             style=&quot;fill:url(#linearGradient47)&quot;
             d=&quot;m 6,17 c 0,1 0,26 0,28 0,2 1.05,2.52 1.05,2.52 l 19.93,9.97 C 26.98,57.49 28,57 28,56 28,55 28,27 28,25 28,23 26.97,22.57 26.97,22.57 L 7,15 c 0,0 -1,0 -1,2 z&quot;
             id=&quot;path36&quot; &#x2F;&gt;&lt;linearGradient
             id=&quot;linearGradient38&quot;
             gradientUnits=&quot;userSpaceOnUse&quot;
             x1=&quot;48.049999&quot;
             y1=&quot;-27.809999&quot;
             x2=&quot;77.269997&quot;
             y2=&quot;-16.549999&quot;&gt;&lt;stop
               offset=&quot;0&quot;
               stop-color=&quot;#858585&quot;
               id=&quot;stop37&quot; &#x2F;&gt;&lt;stop
               offset=&quot;1&quot;
               stop-color=&quot;#bababa&quot;
               id=&quot;stop38&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
             style=&quot;fill:url(#linearGradient48)&quot;
             d=&quot;M 28,25 V 57 L 46,39 V 11 Z&quot;
             id=&quot;path38&quot; &#x2F;&gt;&lt;linearGradient
             id=&quot;linearGradient40&quot;
             gradientUnits=&quot;userSpaceOnUse&quot;
             x1=&quot;31.91&quot;
             y1=&quot;46.25&quot;
             x2=&quot;4.4099998&quot;
             y2=&quot;34.93&quot;&gt;&lt;stop
               offset=&quot;0&quot;
               stop-color=&quot;#c3c3c3&quot;
               id=&quot;stop39&quot; &#x2F;&gt;&lt;stop
               offset=&quot;1&quot;
               stop-color=&quot;#fffcf9&quot;
               id=&quot;stop40&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
             style=&quot;fill:url(#linearGradient49)&quot;
             d=&quot;M 25,2 7,15 27,23 45,10 Z&quot;
             id=&quot;path40&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#000000&quot;
             d=&quot;m 18,20 v 2 c 0,0 4,1 4,4 0,1 0,29 0,29 l 5,2 V 26 c 0,-2 -1,-2 -1,-2 z&quot;
             id=&quot;path41&quot; &#x2F;&gt;&lt;linearGradient
             id=&quot;linearGradient42&quot;
             gradientUnits=&quot;userSpaceOnUse&quot;
             x1=&quot;54.040001&quot;
             y1=&quot;-6.4099998&quot;
             x2=&quot;60.5&quot;
             y2=&quot;1.62&quot;&gt;&lt;stop
               offset=&quot;0&quot;
               stop-color=&quot;#e8e8e8&quot;
               id=&quot;stop41&quot; &#x2F;&gt;&lt;stop
               offset=&quot;0.9973&quot;
               stop-color=&quot;#a5a5a5&quot;
               id=&quot;stop42&quot; &#x2F;&gt;&lt;&#x2F;linearGradient&gt;&lt;path
             style=&quot;fill:url(#linearGradient50)&quot;
             d=&quot;m 28,25 c 0,0 0,-2 -1,-2 2,-1 18,-13 18,-13 0,0 1,0 1,2 -3,2 -18,13 -18,13 z&quot;
             id=&quot;path42&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#000000&quot;
             d=&quot;m 44,42 c 0,0 2,1 2,2 0,1 -2,3 -3,3 -1,0 -2,-2 -2,-2&quot;
             id=&quot;path43&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#000000&quot;
             d=&quot;m 44,42 c 0,0 2,1 2,2 0,1 -2,3 -3,3 -1,0 -2,-2 -2,-2&quot;
             transform=&quot;translate(-10,11)&quot;
             id=&quot;path44&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#a9ff00&quot;
             d=&quot;m 44,42 c 0,0 2,1 2,2 0,1 -2,3 -3,3 -1,0 -2,-2 -2,-2&quot;
             transform=&quot;matrix(-0.2449,-0.2777,0.1656,-0.4108,27.9469,59.752)&quot;
             id=&quot;path45&quot; &#x2F;&gt;&lt;path
             style=&quot;fill:#a9ff00&quot;
             d=&quot;m 44,42 c 0,0 2,1 2,2 0,1 -2,3 -3,3 -1,0 -2,-2 -2,-2&quot;
             transform=&quot;matrix(-0.2449,-0.2777,0.1656,-0.4108,27.9469,64.752)&quot;
             id=&quot;path46&quot; &#x2F;&gt;&lt;&#x2F;g&gt;&lt;&#x2F;g&gt;&lt;&#x2F;g&gt;&lt;path
       style=&quot;font-weight:600;font-size:5.29167px;font-family:&#x27;Work Sans&#x27;;-inkscape-font-specification:&#x27;Work Sans Semi-Bold&#x27;&quot;
       d=&quot;m 67.490102,130.96066 h -0.756709 l -0.291042,-0.85196 h -1.338792 l -0.291042,0.85196 h -0.746125 l 1.275292,-3.4925 h 0.873126 z m -2.222501,-1.39171 h 1.010709 l -0.508001,-1.524 z m 5.52429,-2.10079 v 2.11667 q 0,0.71437 -0.381,1.07421 -0.381,0.35454 -1.11125,0.35454 -0.730251,0 -1.111251,-0.35454 -0.381,-0.35984 -0.381,-1.07421 v -2.11667 h 0.73025 v 2.08492 q 0,0.43921 0.185209,0.65087 0.1905,0.21167 0.576792,0.21167 0.386291,0 0.5715,-0.21167 0.185208,-0.21166 0.185208,-0.65087 v -2.08492 z&quot;
       id=&quot;text54&quot;
       aria-label=&quot;AU&quot; &#x2F;&gt;&lt;path
       style=&quot;font-weight:600;font-size:5.29167px;font-family:&#x27;Work Sans&#x27;;-inkscape-font-specification:&#x27;Work Sans Semi-Bold&#x27;&quot;
       d=&quot;m 104.48671,128.08199 h -1.88383 v 0.88901 h 1.48695 v 0.58737 h -1.48695 v 1.42875 h -0.73025 v -3.4925 h 2.61408 z m 2.16429,-0.58737 q 0.59267,0 0.93133,0.28575 0.33867,0.28575 0.33867,0.76729 0,0.51329 -0.33867,0.78846 -0.33866,0.26988 -0.92604,0.26988 l -0.0741,0.0423 h -0.75142 v 1.33879 h -0.71967 v -3.4925 z m -0.0847,1.59808 q 0.30692,0 0.45509,-0.1217 0.15346,-0.127 0.15346,-0.3863 0,-0.25929 -0.15346,-0.381 -0.14817,-0.127 -0.45509,-0.127 h -0.73554 v 1.016 z m 0.42863,0.15346 1.13242,1.74096 h -0.82021 l -0.93663,-1.52929 z&quot;
       id=&quot;text55&quot;
       aria-label=&quot;FR&quot; &#x2F;&gt;&lt;path
       style=&quot;font-weight:600;font-size:5.29167px;font-family:&#x27;Work Sans&#x27;;-inkscape-font-specification:&#x27;Work Sans Semi-Bold&#x27;&quot;
       d=&quot;m 138.23485,127.49462 v 3.4925 h -0.83608 l -1.22238,-2.05317 -0.30163,-0.5715 h -0.005 l 0.0212,0.60325 v 2.02142 h -0.65617 v -3.4925 h 0.83079 l 1.22238,2.04788 0.30163,0.57679 h 0.0106 l -0.0212,-0.60325 v -2.02142 z m 3.71475,0 -1.23825,2.18546 v 1.30704 h -0.73025 v -1.30704 l -1.23825,-2.18546 h 0.77788 l 0.56091,1.04775 0.26459,0.54504 0.26987,-0.54504 0.56092,-1.04775 z&quot;
       id=&quot;text56&quot;
       aria-label=&quot;NY&quot; &#x2F;&gt;&lt;path
       style=&quot;font-weight:600;font-size:5.29167px;font-family:&#x27;Work Sans&#x27;;-inkscape-font-specification:&#x27;Work Sans Semi-Bold&#x27;&quot;
       d=&quot;m 99.119839,182.28596 q -0.3175,0 -0.576792,-0.11112 -0.254,-0.10583 -0.402167,-0.33867 -0.148167,-0.23283 -0.148167,-0.59266 0,-0.0847 0.01588,-0.17992 0.02117,-0.0952 0.0635,-0.18521 l 0.47625,-0.0741 q -0.03704,0.0952 -0.05821,0.17462 -0.01587,0.0741 -0.01587,0.13759 0,0.24341 0.08996,0.37041 0.08996,0.127 0.238125,0.17463 0.148166,0.0476 0.328083,0.0476 0.216959,0 0.370417,-0.0529 0.15875,-0.0476 0.264584,-0.13759 l 0.132291,0.54504 q -0.132291,0.10584 -0.354542,0.16405 -0.216958,0.0582 -0.423333,0.0582 z m -0.867834,-4.60375 q 0.518584,0 0.894292,0.21696 0.375709,0.21696 0.576792,0.61913 0.206375,0.40216 0.206375,0.96308 0,0.56092 -0.206375,0.96308 -0.201083,0.40217 -0.576792,0.61913 -0.375708,0.21696 -0.894292,0.21696 -0.518584,0 -0.899584,-0.21696 -0.375708,-0.21696 -0.582084,-0.61913 -0.201083,-0.40216 -0.201083,-0.96308 0,-0.56092 0.201083,-0.96308 0.206376,-0.40217 0.582084,-0.61913 0.381,-0.21696 0.899584,-0.21696 z m 0,0.59796 q -0.296334,0 -0.502709,0.13758 -0.206375,0.13759 -0.3175,0.40746 -0.105833,0.26459 -0.105833,0.65617 0,0.38629 0.105833,0.65617 0.111125,0.26987 0.3175,0.40746 0.206375,0.13758 0.502709,0.13758 0.291042,0 0.497417,-0.13758 0.206375,-0.13759 0.312208,-0.40746 0.111126,-0.26988 0.111126,-0.65617 0,-0.39158 -0.111126,-0.65617 -0.105833,-0.26987 -0.312208,-0.40746 -0.206375,-0.13758 -0.497417,-0.13758 z m 3.545415,0.24871 q 0.40217,0 0.70379,0.16404 0.30692,0.16404 0.47625,0.47096 0.17463,0.30692 0.17463,0.74083 0,0.42863 -0.17463,0.74084 -0.16933,0.30691 -0.47625,0.47096 -0.30162,0.16404 -0.70379,0.16404 -0.39688,0 -0.70379,-0.16404 -0.30692,-0.16405 -0.48154,-0.47096 -0.16934,-0.31221 -0.16934,-0.74084 0,-0.43391 0.16934,-0.74083 0.17462,-0.30692 0.48154,-0.47096 0.30691,-0.16404 0.70379,-0.16404 z m 0,0.53975 q -0.20108,0 -0.34396,0.0952 -0.13758,0.09 -0.21167,0.27517 -0.0741,0.1852 -0.0741,0.46566 0,0.28046 0.0741,0.46567 0.0741,0.18521 0.21167,0.28046 0.14288,0.09 0.34396,0.09 0.19579,0 0.33337,-0.09 0.14288,-0.0952 0.21696,-0.28046 0.0741,-0.18521 0.0741,-0.46567 0,-0.28046 -0.0741,-0.46566 -0.0741,-0.18521 -0.21696,-0.27517 -0.13758,-0.0952 -0.33337,-0.0952 z m 2.87031,-1.29646 v 2.59292 q 0,0.1905 0.0952,0.28046 0.10054,0.0847 0.26987,0.0847 0.13229,0 0.23284,-0.037 0.10054,-0.0423 0.17991,-0.11112 l 0.11642,0.48154 q -0.127,0.10054 -0.3175,0.15875 -0.1905,0.0582 -0.40217,0.0582 -0.26458,0 -0.47096,-0.0794 -0.20108,-0.0794 -0.3175,-0.254 -0.11112,-0.17462 -0.11112,-0.46566 v -2.50826 z m 0.84666,0.80963 v 0.53975 h -2.05846 v -0.53975 z m 1.69863,-0.0529 q 0.40217,0 0.70379,0.16404 0.30692,0.16404 0.47625,0.47096 0.17463,0.30692 0.17463,0.74083 0,0.42863 -0.17463,0.74084 -0.16933,0.30691 -0.47625,0.47096 -0.30162,0.16404 -0.70379,0.16404 -0.39687,0 -0.70379,-0.16404 -0.30692,-0.16405 -0.48154,-0.47096 -0.16933,-0.31221 -0.16933,-0.74084 0,-0.43391 0.16933,-0.74083 0.17462,-0.30692 0.48154,-0.47096 0.30692,-0.16404 0.70379,-0.16404 z m 0,0.53975 q -0.20108,0 -0.34396,0.0952 -0.13758,0.09 -0.21166,0.27517 -0.0741,0.1852 -0.0741,0.46566 0,0.28046 0.0741,0.46567 0.0741,0.18521 0.21166,0.28046 0.14288,0.09 0.34396,0.09 0.19579,0 0.33338,-0.09 0.14287,-0.0952 0.21696,-0.28046 0.0741,-0.18521 0.0741,-0.46567 0,-0.28046 -0.0741,-0.46566 -0.0741,-0.18521 -0.21696,-0.27517 -0.13759,-0.0952 -0.33338,-0.0952 z m 1.96322,2.159 v -2.64583 h 0.65087 l 0.0265,0.47625 q 0.13229,-0.26459 0.35983,-0.39688 0.23284,-0.13229 0.5133,-0.13229 0.28575,0 0.51329,0.13229 0.23283,0.13229 0.34925,0.39158 0.0847,-0.17462 0.22754,-0.29104 0.14288,-0.11641 0.3175,-0.17462 0.17463,-0.0582 0.35454,-0.0582 0.25929,0 0.47096,0.10583 0.21696,0.10584 0.34396,0.3175 0.127,0.21167 0.127,0.53975 v 1.73567 h -0.72496 v -1.59279 q 0,-0.29104 -0.127,-0.41275 -0.127,-0.12171 -0.32808,-0.12171 -0.15875,0 -0.29634,0.0794 -0.13229,0.0794 -0.21166,0.23813 -0.0741,0.15346 -0.0741,0.38629 v 1.42346 h -0.72496 v -1.59279 q 0,-0.29104 -0.13229,-0.41275 -0.127,-0.12171 -0.32808,-0.12171 -0.13758,0 -0.27517,0.0741 -0.13229,0.0741 -0.22225,0.23284 -0.0847,0.15875 -0.0847,0.41804 v 1.40229 z&quot;
       id=&quot;text58&quot;
       aria-label=&quot;Qotom&quot; &#x2F;&gt;&lt;path
       style=&quot;font-weight:600;font-size:5.29167px;font-family:&#x27;Work Sans&#x27;;-inkscape-font-specification:&#x27;Work Sans Semi-Bold&#x27;&quot;
       d=&quot;m 121.1676,72.850387 q -0.48154,0 -0.84667,-0.211667 -0.35983,-0.211667 -0.56092,-0.613834 -0.20108,-0.402167 -0.20108,-0.968375 0,-0.560917 0.21167,-0.963084 0.21166,-0.407459 0.59796,-0.624417 0.39158,-0.216959 0.90487,-0.216959 0.56621,0 0.91546,0.211667 0.34925,0.206375 0.55563,0.645584 l -0.67734,0.291042 q -0.0847,-0.280459 -0.29104,-0.412751 -0.20108,-0.137583 -0.49742,-0.137583 -0.29633,0 -0.51329,0.142875 -0.21696,0.137583 -0.33337,0.407459 -0.11642,0.264583 -0.11642,0.650875 0,0.396875 0.11113,0.672042 0.11112,0.269875 0.32808,0.407459 0.22225,0.132291 0.54504,0.132291 0.17463,0 0.32279,-0.04233 0.15346,-0.04233 0.26459,-0.127 0.11112,-0.08467 0.17462,-0.216958 0.0635,-0.132292 0.0635,-0.306917 v -0.07937 h -0.91546 v -0.534459 h 1.54517 v 1.841501 h -0.51329 l -0.0529,-0.762 0.127,0.08996 q -0.11642,0.34925 -0.41275,0.53975 -0.29104,0.185209 -0.73554,0.185209 z m 3.51896,-2.751669 q 0.35454,0 0.59796,0.105834 0.2487,0.100542 0.39687,0.280458 0.15346,0.174625 0.21696,0.391584 l -0.67733,0.243417 q -0.0529,-0.238125 -0.17992,-0.359834 -0.127,-0.121708 -0.34396,-0.121708 -0.20108,0 -0.34396,0.09525 -0.14287,0.08996 -0.21696,0.280458 -0.0741,0.185209 -0.0741,0.465667 0,0.280459 0.0741,0.465667 0.0794,0.185209 0.22225,0.275167 0.14817,0.08996 0.34396,0.08996 0.15875,0 0.26988,-0.05292 0.11112,-0.05821 0.17991,-0.164042 0.0741,-0.105833 0.10055,-0.254 l 0.65616,0.211667 q -0.0582,0.238125 -0.21696,0.418042 -0.15875,0.179916 -0.40745,0.280458 -0.24871,0.100542 -0.5768,0.100542 -0.40745,0 -0.71966,-0.164042 -0.31221,-0.164042 -0.48155,-0.470959 -0.16933,-0.306916 -0.16933,-0.740833 0,-0.433917 0.16933,-0.740834 0.16934,-0.306917 0.47626,-0.470959 0.30691,-0.164042 0.70379,-0.164042 z m 2.98979,0 q 0.40216,0 0.70379,0.164042 0.30692,0.164042 0.47625,0.470959 0.17462,0.306917 0.17462,0.740834 0,0.428625 -0.17462,0.740833 -0.16933,0.306917 -0.47625,0.470959 -0.30163,0.164042 -0.70379,0.164042 -0.39688,0 -0.7038,-0.164042 -0.30691,-0.164042 -0.48154,-0.470959 -0.16933,-0.312208 -0.16933,-0.740833 0,-0.433917 0.16933,-0.740834 0.17463,-0.306917 0.48154,-0.470959 0.30692,-0.164042 0.7038,-0.164042 z m 0,0.539751 q -0.20109,0 -0.34396,0.09525 -0.13759,0.08996 -0.21167,0.275167 -0.0741,0.185208 -0.0741,0.465667 0,0.280458 0.0741,0.465667 0.0741,0.185208 0.21167,0.280458 0.14287,0.08996 0.34396,0.08996 0.19579,0 0.33337,-0.08996 0.14288,-0.09525 0.21696,-0.280458 0.0741,-0.185209 0.0741,-0.465667 0,-0.280459 -0.0741,-0.465667 -0.0741,-0.185209 -0.21696,-0.275167 -0.13758,-0.09525 -0.33337,-0.09525 z m 1.96321,2.159001 v -2.645835 h 0.62971 l 0.0476,0.518584 q 0.10584,-0.275167 0.30692,-0.423334 0.20638,-0.148167 0.51329,-0.148167 0.09,0 0.16404,0.01587 0.0741,0.01587 0.12171,0.04233 l -0.0847,0.608542 q -0.0529,-0.02117 -0.1323,-0.03175 -0.0741,-0.01058 -0.19579,-0.01058 -0.15875,0 -0.30691,0.07937 -0.14817,0.07408 -0.24342,0.232833 -0.0952,0.153458 -0.0952,0.391584 v 1.370542 z m 3.40826,0.05292 q -0.41804,0 -0.73025,-0.164042 -0.30691,-0.164042 -0.47625,-0.470959 -0.16933,-0.306916 -0.16933,-0.740833 0,-0.433917 0.16933,-0.740834 0.16934,-0.306917 0.47096,-0.470959 0.30692,-0.164042 0.6985,-0.164042 0.40217,0 0.68263,0.164042 0.28046,0.15875 0.42862,0.439209 0.14817,0.275167 0.14817,0.629709 0,0.105833 -0.005,0.201083 -0.005,0.09525 -0.0159,0.169333 h -2.11137 v -0.502708 h 1.79387 l -0.34925,0.127 q 0,-0.333375 -0.15346,-0.513292 -0.14816,-0.179917 -0.42333,-0.179917 -0.20108,0 -0.34925,0.09525 -0.14288,0.09525 -0.21696,0.28575 -0.0741,0.185209 -0.0741,0.470959 0,0.280458 0.0794,0.465667 0.0847,0.179917 0.23284,0.269875 0.15345,0.08996 0.36512,0.08996 0.23284,0 0.37571,-0.08996 0.14288,-0.08996 0.22225,-0.248708 l 0.57679,0.22225 q -0.0847,0.206375 -0.25929,0.354542 -0.16933,0.148166 -0.40746,0.227541 -0.23283,0.07408 -0.50271,0.07408 z m 4.96359,0 q -0.48154,0 -0.84667,-0.211667 -0.35983,-0.211667 -0.56091,-0.613834 -0.20109,-0.402167 -0.20109,-0.968375 0,-0.560917 0.21167,-0.963084 0.21167,-0.407459 0.59796,-0.624417 0.39158,-0.216959 0.90488,-0.216959 0.5662,0 0.91545,0.211667 0.34925,0.206375 0.55563,0.645584 l -0.67733,0.291042 q -0.0847,-0.280459 -0.29105,-0.412751 -0.20108,-0.137583 -0.49741,-0.137583 -0.29634,0 -0.51329,0.142875 -0.21696,0.137583 -0.33338,0.407459 -0.11642,0.264583 -0.11642,0.650875 0,0.396875 0.11113,0.672042 0.11112,0.269875 0.32808,0.407459 0.22225,0.132291 0.54504,0.132291 0.17463,0 0.3228,-0.04233 0.15345,-0.04233 0.26458,-0.127 0.11112,-0.08467 0.17462,-0.216958 0.0635,-0.132292 0.0635,-0.306917 v -0.07937 h -0.91545 V 70.95598 h 1.54516 v 1.841501 h -0.51329 l -0.0529,-0.762 0.127,0.08996 q -0.11641,0.34925 -0.41275,0.53975 -0.29104,0.185209 -0.73554,0.185209 z m 3.54542,0 q -0.41804,0 -0.73025,-0.164042 -0.30692,-0.164042 -0.47625,-0.470959 -0.16934,-0.306916 -0.16934,-0.740833 0,-0.433917 0.16934,-0.740834 0.16933,-0.306917 0.47096,-0.470959 0.30691,-0.164042 0.6985,-0.164042 0.40216,0 0.68262,0.164042 0.28046,0.15875 0.42863,0.439209 0.14816,0.275167 0.14816,0.629709 0,0.105833 -0.005,0.201083 -0.005,0.09525 -0.0159,0.169333 h -2.11138 v -0.502708 h 1.79388 l -0.34925,0.127 q 0,-0.333375 -0.15346,-0.513292 -0.14817,-0.179917 -0.42334,-0.179917 -0.20108,0 -0.34925,0.09525 -0.14287,0.09525 -0.21695,0.28575 -0.0741,0.185209 -0.0741,0.470959 0,0.280458 0.0794,0.465667 0.0847,0.179917 0.23283,0.269875 0.15346,0.08996 0.36513,0.08996 0.23283,0 0.3757,-0.08996 0.14288,-0.08996 0.22225,-0.248708 l 0.5768,0.22225 q -0.0847,0.206375 -0.25929,0.354542 -0.16934,0.148166 -0.40746,0.227541 -0.23284,0.07408 -0.50271,0.07408 z m 3.04271,-2.751669 q 0.40217,0 0.70379,0.164042 0.30692,0.164042 0.47625,0.470959 0.17463,0.306917 0.17463,0.740834 0,0.428625 -0.17463,0.740833 -0.16933,0.306917 -0.47625,0.470959 -0.30162,0.164042 -0.70379,0.164042 -0.39688,0 -0.70379,-0.164042 -0.30692,-0.164042 -0.48154,-0.470959 -0.16934,-0.312208 -0.16934,-0.740833 0,-0.433917 0.16934,-0.740834 0.17462,-0.306917 0.48154,-0.470959 0.30691,-0.164042 0.70379,-0.164042 z m 0,0.539751 q -0.20108,0 -0.34396,0.09525 -0.13758,0.08996 -0.21167,0.275167 -0.0741,0.185208 -0.0741,0.465667 0,0.280458 0.0741,0.465667 0.0741,0.185208 0.21167,0.280458 0.14288,0.08996 0.34396,0.08996 0.19579,0 0.33338,-0.08996 0.14287,-0.09525 0.21695,-0.280458 0.0741,-0.185209 0.0741,-0.465667 0,-0.280459 -0.0741,-0.465667 -0.0741,-0.185209 -0.21695,-0.275167 -0.13759,-0.09525 -0.33338,-0.09525 z m 3.35714,-1.333501 q 0.82021,0 1.27,0.455084 0.44979,0.449792 0.44979,1.291167 0,0.836084 -0.44979,1.291167 -0.44979,0.455084 -1.27,0.455084 h -1.34937 v -3.492502 z m -0.037,2.910418 q 0.49742,0 0.74612,-0.296333 0.25401,-0.301625 0.25401,-0.867834 0,-0.566209 -0.25401,-0.862542 -0.2487,-0.301625 -0.74612,-0.301625 h -0.58208 v 2.328334 z m 5.49275,-2.910418 v 3.492502 h -0.83608 l -1.22238,-2.053168 -0.30162,-0.5715 h -0.005 l 0.0212,0.60325 v 2.021418 h -0.65616 v -3.492502 h 0.83079 l 1.22237,2.047876 0.30163,0.576792 h 0.0106 l -0.0212,-0.60325 v -2.021418 z m 2.18546,-0.05292 q 0.47625,0 0.83609,0.164042 0.35983,0.164042 0.61383,0.486834 l -0.40746,0.470958 q -0.21696,-0.275167 -0.47625,-0.402167 -0.254,-0.132291 -0.59266,-0.132291 -0.22226,0 -0.37042,0.05821 -0.14288,0.05821 -0.21167,0.153458 -0.0635,0.09525 -0.0635,0.211667 0,0.142875 0.11113,0.243417 0.11112,0.09525 0.381,0.15875 l 0.65087,0.148167 q 0.51859,0.116417 0.74084,0.34925 0.22754,0.227542 0.22754,0.592667 0,0.343959 -0.17992,0.592667 -0.17992,0.243417 -0.50271,0.375709 -0.32279,0.127 -0.74083,0.127 -0.36513,0 -0.67204,-0.08467 -0.30163,-0.08996 -0.53975,-0.243417 -0.23813,-0.153458 -0.39688,-0.354542 l 0.41275,-0.492125 q 0.12171,0.169334 0.30163,0.301625 0.17991,0.132292 0.40745,0.211667 0.23284,0.07408 0.48684,0.07408 0.21696,0 0.36512,-0.04763 0.15346,-0.05292 0.22755,-0.142875 0.0794,-0.09525 0.0794,-0.227541 0,-0.137584 -0.0953,-0.232834 -0.09,-0.100542 -0.33867,-0.153458 l -0.70908,-0.15875 q -0.30692,-0.07408 -0.52917,-0.190501 -0.21696,-0.121708 -0.33337,-0.306916 -0.11113,-0.190501 -0.11113,-0.449792 0,-0.312209 0.16934,-0.560917 0.16933,-0.248709 0.48683,-0.391584 0.32279,-0.148167 0.77258,-0.148167 z&quot;
       id=&quot;text59&quot;
       aria-label=&quot;Gcore GeoDNS&quot; &#x2F;&gt;&lt;path
       style=&quot;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.914483;stroke-opacity:1;marker-end:url(#Triangle)&quot;
       d=&quot;m 105,35.169677 v 17.46231&quot;
       id=&quot;path59&quot; &#x2F;&gt;&lt;g
       id=&quot;g72&quot;
       transform=&quot;translate(5.9813979,-79.950116)&quot;&gt;&lt;path
         style=&quot;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.914483;stroke-linecap:round;stroke-opacity:1;marker-start:url(#marker62)&quot;
         d=&quot;m 99.016609,216.82519 v 20.07485&quot;
         id=&quot;path61&quot; &#x2F;&gt;&lt;path
         style=&quot;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-opacity:1;marker-end:url(#marker67)&quot;
         d=&quot;M 98.971309,236.85147 71.211814,216.04016&quot;
         id=&quot;path66&quot; &#x2F;&gt;&lt;path
         style=&quot;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-opacity:1;marker-end:url(#marker67)&quot;
         d=&quot;M 99.065889,236.85837 126.82539,216.04706&quot;
         id=&quot;path68&quot; &#x2F;&gt;&lt;&#x2F;g&gt;&lt;g
       id=&quot;g71&quot;
       transform=&quot;translate(2.39699)&quot;&gt;&lt;path
         style=&quot;fill:none;fill-opacity:1;stroke:#3a3a3a;stroke-width:0.914483;stroke-linecap:round;stroke-dasharray:2.74345, 1.82897;stroke-dashoffset:0;stroke-opacity:1;marker-start:url(#marker62)&quot;
         d=&quot;M 102.6076,102.96746 V 82.846783&quot;
         id=&quot;path69&quot; &#x2F;&gt;&lt;path
         style=&quot;fill:none;fill-opacity:1;stroke:#3a3a3a;stroke-width:1;stroke-linecap:round;stroke-dasharray:3, 2;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker67)&quot;
         d=&quot;m 102.56488,82.89535 -27.7595,20.81131&quot;
         id=&quot;path70&quot; &#x2F;&gt;&lt;path
         style=&quot;fill:none;fill-opacity:1;stroke:#3a3a3a;stroke-width:1;stroke-linecap:round;stroke-dasharray:3, 2;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker67)&quot;
         d=&quot;m 102.64114,82.89535 27.7595,20.81131&quot;
         id=&quot;path71&quot; &#x2F;&gt;&lt;&#x2F;g&gt;&lt;rect
       style=&quot;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:bevel;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1&quot;
       id=&quot;rect72&quot;
       width=&quot;14.248107&quot;
       height=&quot;4.8766723&quot;
       x=&quot;97.148735&quot;
       y=&quot;145.25316&quot; &#x2F;&gt;&lt;path
       style=&quot;font-weight:500;font-size:4.23333px;font-family:&#x27;Work Sans&#x27;;-inkscape-font-specification:&#x27;Work Sans Medium&#x27;;stroke-linecap:round;stroke-linejoin:bevel&quot;
       d=&quot;m 102.20201,148.16407 q -0.0847,0.254 -0.254,0.4318 -0.16933,0.17357 -0.39793,0.26247 -0.2286,0.0889 -0.50377,0.0889 -0.40217,0 -0.6985,-0.17357 -0.29633,-0.17356 -0.457199,-0.4953 -0.160866,-0.32173 -0.160866,-0.77046 0,-0.44873 0.160866,-0.77047 0.160869,-0.32173 0.457199,-0.4953 0.29633,-0.17356 0.69427,-0.17356 0.27516,0 0.4953,0.0762 0.22436,0.0762 0.381,0.2286 0.15663,0.1524 0.2413,0.381 l -0.42757,0.1905 q -0.0847,-0.24977 -0.24553,-0.36407 -0.16087,-0.11853 -0.4191,-0.11853 -0.25824,0 -0.45297,0.12276 -0.19473,0.12277 -0.3048,0.3556 -0.10583,0.23284 -0.10583,0.56727 0,0.3302 0.1016,0.56726 0.1016,0.23284 0.2921,0.3556 0.1905,0.12277 0.4572,0.12277 0.25823,0 0.4445,-0.13123 0.1905,-0.13547 0.27093,-0.40217 z m 1.4478,0.78317 q -0.3175,0 -0.5588,-0.13123 -0.2413,-0.13124 -0.37677,-0.37677 -0.13123,-0.24977 -0.13123,-0.59267 0,-0.34289 0.13123,-0.58843 0.13547,-0.24976 0.37254,-0.381 0.23706,-0.13123 0.53763,-0.13123 0.30903,0 0.52493,0.127 0.2159,0.127 0.3302,0.34713 0.1143,0.22014 0.1143,0.49953 0,0.0762 -0.004,0.14394 -0.004,0.0677 -0.0127,0.11853 h -1.7018 v -0.34713 h 1.50283 l -0.22437,0.0677 q 0,-0.28786 -0.14393,-0.44026 -0.14393,-0.15664 -0.3937,-0.15664 -0.18203,0 -0.3175,0.0847 -0.13546,0.0847 -0.20743,0.254 -0.072,0.1651 -0.072,0.4064 0,0.23706 0.0762,0.40216 0.0762,0.1651 0.2159,0.24977 0.1397,0.0847 0.3302,0.0847 0.21167,0 0.3429,-0.0804 0.13123,-0.0804 0.20743,-0.22436 l 0.35984,0.16933 q -0.0762,0.1524 -0.20744,0.2667 -0.127,0.11007 -0.3048,0.16933 -0.1778,0.0593 -0.38946,0.0593 z m 1.51553,-0.0423 v -2.11667 h 0.38523 l 0.0423,0.381 q 0.0847,-0.20743 0.24977,-0.31326 0.16933,-0.11007 0.41487,-0.11007 0.0635,0 0.127,0.0127 0.0635,0.008 0.10583,0.0296 l -0.0635,0.39794 q -0.0466,-0.0169 -0.10583,-0.0254 -0.055,-0.0127 -0.1524,-0.0127 -0.13124,0 -0.25824,0.072 -0.127,0.0677 -0.21166,0.20744 -0.0804,0.1397 -0.0804,0.35559 v 1.12184 z m 2.26652,-2.73897 v 2.10397 q 0,0.15663 0.0804,0.2286 0.0847,0.072 0.22437,0.072 0.1143,0 0.19896,-0.0339 0.0847,-0.0381 0.15663,-0.1016 l 0.1016,0.3302 q -0.0974,0.0847 -0.2413,0.13546 -0.13969,0.0466 -0.31326,0.0466 -0.18203,0 -0.33443,-0.0593 -0.1524,-0.0593 -0.23707,-0.1905 -0.0847,-0.13546 -0.0889,-0.35136 v -2.05317 z m 0.72813,0.6223 v 0.3556 h -1.5875 v -0.3556 z m 1.25306,2.159 q -0.3175,0 -0.57996,-0.12277 -0.26247,-0.12276 -0.41487,-0.34713 l 0.31327,-0.27093 q 0.0931,0.17356 0.2667,0.2794 0.1778,0.1016 0.42333,0.1016 0.19473,0 0.30903,-0.0677 0.1143,-0.0677 0.1143,-0.1905 0,-0.0804 -0.055,-0.1397 -0.0508,-0.0635 -0.19897,-0.0974 l -0.4445,-0.0931 q -0.33866,-0.0677 -0.4826,-0.2159 -0.14393,-0.1524 -0.14393,-0.38523 0,-0.17356 0.1016,-0.32173 0.1016,-0.14817 0.29633,-0.23707 0.19897,-0.0931 0.47414,-0.0931 0.3175,0 0.54186,0.1143 0.22437,0.11007 0.3429,0.3175 l -0.31326,0.26247 q -0.0804,-0.16934 -0.23707,-0.24977 -0.15663,-0.0847 -0.32597,-0.0847 -0.13546,0 -0.23283,0.0339 -0.0974,0.0339 -0.14817,0.0931 -0.0508,0.0593 -0.0508,0.1397 0,0.0847 0.0635,0.14817 0.0635,0.0635 0.2286,0.0974 l 0.4826,0.1016 q 0.3048,0.0593 0.42757,0.2032 0.127,0.1397 0.127,0.3429 0,0.2032 -0.10583,0.35984 -0.10584,0.1524 -0.3048,0.23706 -0.19897,0.0847 -0.47414,0.0847 z&quot;
       id=&quot;text72&quot;
       aria-label=&quot;Certs&quot; &#x2F;&gt;&lt;&#x2F;g&gt;&lt;&#x2F;svg&gt;
&lt;figcaption&gt;
&lt;p&gt;A diagram of what we’re building.&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;For some back story on Linked Listed see: &lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;linked-list&#x2F;&quot;&gt;Building and Launching My
New Link Blog, linkedlist.org (Twice)&lt;&#x2F;a&gt;. While I
should have been focussing on writing content for the site, I instead continued
optimising what is currently a very low traffic website. Since the last post, I
added caching so that the application only renders a page once. After that, the
cached render result is reused for subsequent responses. These cached responses
are typically generated in about a third of a millisecond (~323µs on average),
which I was pretty happy with.&lt;&#x2F;p&gt;
&lt;p&gt;The problem was, for my convenience the server hosting Linked List was
located in Australia, where I live. Unfortunately most other people do not
live in Australia, and we’re a long way from everywhere. This meant that
visitors would often encounter a lot of latency, just due to the
distances covered. Is this &lt;em&gt;really&lt;&#x2F;em&gt; a problem for a lightweight website with
low traffic? Not really, but it bothered me, so I set about looking into
options to improve the situation.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;tiny-cdn&amp;#x2F;response-times-before.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;response-times-before.f1d2564d1d2a6735.png&quot; alt=&quot;Screenshot of updown.io monitoring. It has response times for nine locations around the world. The average total time is 807ms.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Average response timing from my monitoring at updown.io before the changes.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I briefly explored options like Cloudflare and Fastly. These would have been
a cheap and sensible choice, but I didn’t really feel like giving them (especially
Cloudflare) even more of the Internet’s traffic, no matter how miniscule. Plus,
this is a personal project I do for fun, and just sticking a hosted cache in
front of it is no fun.&lt;&#x2F;p&gt;
&lt;p&gt;My stats in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.goatcounter.com&#x2F;&quot;&gt;GoatCounter&lt;&#x2F;a&gt; showed the top 10 visitor locations were:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Australia&lt;&#x2F;li&gt;
&lt;li&gt;United States&lt;&#x2F;li&gt;
&lt;li&gt;Germany&lt;&#x2F;li&gt;
&lt;li&gt;United Kingdom&lt;&#x2F;li&gt;
&lt;li&gt;Netherlands&lt;&#x2F;li&gt;
&lt;li&gt;France&lt;&#x2F;li&gt;
&lt;li&gt;Ireland&lt;&#x2F;li&gt;
&lt;li&gt;Japan&lt;&#x2F;li&gt;
&lt;li&gt;Canada&lt;&#x2F;li&gt;
&lt;li&gt;Poland&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This suggested I’d get a decent improvement in latency for the most number of
visitors by adding presences in Europe and the US. It’s probably worth noting
at this point that due to the nature of Linked List there is no central
database, so it’s a relatively simple to improve performance by deploying an
instance in each location to be sped up.&lt;&#x2F;p&gt;
&lt;p&gt;Linked List is implemented in Rust and has very meagre system requirements. I
did some research into bargain-basement VPS’s on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lowendbox.com&#x2F;&quot;&gt;LowEndBox&lt;&#x2F;a&gt;. I found that
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;my.racknerd.com&#x2F;aff.php?aff=12841&quot;&gt;RackNerd&lt;&#x2F;a&gt;&lt;sup&gt;(affiliate link)&lt;&#x2F;sup&gt; were offering &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Kernel-based_Virtual_Machine&quot;&gt;KVM&lt;&#x2F;a&gt; based VMs with 1Gb of
RAM&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-2-1&quot;&gt;&lt;a href=&quot;#fn-2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, custom ISO support, and US and European data centres for about US$12
per &lt;strong&gt;year&lt;&#x2F;strong&gt;—probably still more than Cloudflare or Fastly, but still cheap.&lt;&#x2F;p&gt;
&lt;p&gt;I created servers in the New York US datacentre, as well as France. These
servers were so cheap that I wanted to make sure the network and underlying
hardware was not over provisioned before committing to them. They were running
on older hardware&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-3-1&quot;&gt;&lt;a href=&quot;#fn-3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, but after some basic testing seemed fine.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;provisioning-configuration&quot;&gt;Provisioning &amp;amp; Configuration&lt;&#x2F;h3&gt;
&lt;aside class=&quot;float-right&quot;&gt;
  &lt;div class=&quot;emoji text-center&quot;&gt;💡&lt;&#x2F;div&gt;
  &lt;strong&gt;Want the code?&lt;&#x2F;strong&gt;

  &lt;p&gt;I’ve &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;chimera-pyinfra&quot;&gt;published the pyinfra install scripts to my git forge&lt;&#x2F;a&gt;
if you’re curious.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;To provision the servers I used some some &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pyinfra.com&#x2F;&quot;&gt;pyinfra&lt;&#x2F;a&gt; code I’d written previously
to automate the installation of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;&quot;&gt;Chimera Linux&lt;&#x2F;a&gt; in virtual machines. I picked
Chimera Linux because I like it and it’s easy to do minimal installs with just
the things I want. There were some other benefits described later on too.&lt;&#x2F;p&gt;
&lt;p&gt;The basic steps for provisioning were:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Boot from the ISO and login as &lt;code&gt;root&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Bootstrap ssh access to the live environment with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jordansissel&#x2F;xdotool&quot;&gt;xdotool&lt;&#x2F;a&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;xdotool windowfocus --sync $(xdotool selectwindow) type &#x27;dinitctl start sshd &amp;amp;&amp;amp; mkdir ~&#x2F;.ssh &amp;amp;&amp;amp; echo &quot;ssh-rsa &amp;lt;key&amp;gt; wmoore-key&quot; &amp;gt; ~&#x2F;.ssh&#x2F;authorized_keys &amp;amp;&amp;amp; ip addr &amp;amp;&amp;amp; fdisk -l&#x27;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;This works with local VMs in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;virt-manager.org&#x2F;&quot;&gt;virt-manager&lt;&#x2F;a&gt; as well as most web-based VNC
consoles provided by VPC hosts, including RackNerd.&lt;&#x2F;li&gt;
&lt;li&gt;It uses &lt;code&gt;xdotool&lt;&#x2F;code&gt; to type into the selected window. The typed text enables
sshd and adds my public key to the SSH authorized_keys, then prints some
info about the network and disks that will be needed later.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Run the pyinfra installation deployment against the server:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pyinfra -vvv --user root IP install-bios.py&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;After that, the server is rebooted and boots off its disk. I wrote subsequent
pyinfra code to install and configure the server for hosting Linked List. This
includes things like installing packages, creating users, nginx configuration,
and ensuring services are started.&lt;&#x2F;p&gt;
&lt;p&gt;To deploy the Linked List application I defined a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chimera-linux&#x2F;cports&quot;&gt;cports&lt;&#x2F;a&gt; template to
build Linked List as a system (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.alpinelinux.org&#x2F;alpine&#x2F;apk-tools&quot;&gt;apk&lt;&#x2F;a&gt;) package:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pkgname&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;linkedlist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pkgver&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;2.0.12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pkgrel&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;_gitrev&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;5e1aed8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;_token&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;get_data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;forge_token&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;build_style&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;make_build_args&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;--bin&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;linkedlistd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;make_install_args&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;make_build_args&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;hostmakedepends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;cargo-auditable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;pkgconf&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;makedepends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;oniguruma-devel&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;rust-std&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pkgdesc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Linked List web application&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;maintainer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Wesley Moore &amp;lt;wes@wezm.net&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;license&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;custom:none&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;linkedlist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;source&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;forge.wezm.net&#x2F;api&#x2F;v1&#x2F;repos&#x2F;wezm&#x2F;linkedlist&#x2F;archive&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;_gitrev&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.tar.gz?access_token=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;_token&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;gt;linkedlist-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;pkgver&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.tar.gz&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sha256&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;907978d15960b46f96fb1e5afaf3ff8dff888a00711dbe9c887e106314d85d70&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; post_install&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;install_sysusers&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;files_path&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;sysusers.conf&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;install_tmpfiles&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;files_path&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;tmpfiles.conf&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;install_service&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;files_path&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;linkedlist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As shown in the &lt;code&gt;post_install&lt;&#x2F;code&gt; hook, I was also able to make use of cports
support for &lt;code&gt;systemd-tmpfiles&lt;&#x2F;code&gt; and &lt;code&gt;systemd-sysusers&lt;&#x2F;code&gt;&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-4-1&quot;&gt;&lt;a href=&quot;#fn-4&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; to create the
&lt;code&gt;linkedlist&lt;&#x2F;code&gt; user and data directory that will hold the content of the site.
There’s also a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;davmac.org&#x2F;projects&#x2F;dinit&#x2F;&quot;&gt;Dinit&lt;&#x2F;a&gt; service definition to run and manage the server:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# linkedlist service&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;type = process&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;command = &#x2F;usr&#x2F;bin&#x2F;linkedlistd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;run-as = _linkedlist&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;env-file = linkedlist.env&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;logfile = &#x2F;var&#x2F;log&#x2F;linkedlist.log&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;depends-on = local.target&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;smooth-recovery = true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;env-file&lt;&#x2F;code&gt; allows the configuration to be changed without needing to rebuild the package.&lt;&#x2F;p&gt;
&lt;p&gt;The servers are configured with an additional &lt;code&gt;apk&lt;&#x2F;code&gt; repository that points at
my locally built packages. A pyinfa deployment takes care of building the
packages and syncing the repo to the servers.&lt;&#x2F;p&gt;
&lt;aside class=&quot;float-right&quot;&gt;
  &lt;div class=&quot;emoji text-center&quot;&gt;💡&lt;&#x2F;div&gt;
  &lt;strong&gt;I thought cross-compiling Rust was easy?&lt;&#x2F;strong&gt;

  &lt;p&gt;Cross-compiling Rust binaries is easy when just Rust is involved, but it gets
harder when there are system library dependencies. For example,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kkos&#x2F;oniguruma&quot;&gt;oniguruma&lt;&#x2F;a&gt;, which
Linked List uses transitively. Using &lt;code&gt;cports&lt;&#x2F;code&gt; makes managing this easy.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;Using Chimera Linux and building the binary through &lt;code&gt;cports&lt;&#x2F;code&gt; has some other
neat benefits:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The cports build tool, &lt;code&gt;cbuild&lt;&#x2F;code&gt;, does not require a Chimera Linux host.
I am able to re-build the package on my desktop that is still running Arch Linux.
All that’s required is a recent version of &lt;code&gt;apk-tools&lt;&#x2F;code&gt;, which is in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aur.archlinux.org&#x2F;&quot;&gt;AUR&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;yoga-7x-snapdragon-developer-review&#x2F;&quot;&gt;My laptop&lt;&#x2F;a&gt; is
aarch64 based, but the servers are not. Cross-compiling is trivial with
&lt;code&gt;cbuild&lt;&#x2F;code&gt; though, just add &lt;code&gt;-a x86_64&lt;&#x2F;code&gt; to the &lt;code&gt;.&#x2F;cbuild -a x86_64 pkg user&#x2F;linkedlist&lt;&#x2F;code&gt; command, and it doesn’t matter what host architecture I’m
on. This allows me to build and deploy an updated package from the
aarch64 WSL2 Chimera install on my laptop as well as my x86_64 desktop.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;tls-certificates&quot;&gt;TLS Certificates&lt;&#x2F;h3&gt;
&lt;p&gt;At this point I had the application running on the servers. Next I needed to
handle certificates for &lt;code&gt;https&lt;&#x2F;code&gt;. I usually use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;go-acme.github.io&#x2F;lego&#x2F;&quot;&gt;Lego&lt;&#x2F;a&gt; to manage certificates
from Let’s Encrypt. This posed a challenge though, as each server needed to get
a copy of the same certificates.&lt;&#x2F;p&gt;
&lt;p&gt;I explored various options here. It’s a common problem with a bunch of hosted
and self-hosted solutions. All seemed too complicated for my need of syncing
two files between three servers.&lt;&#x2F;p&gt;
&lt;p&gt;One option would be to designate one of the servers the primary, and have it sync
the certificates to the others. However, I wasn’t keen on one of the internet
facing app servers having passwordless (key based) access to the others in
order to push updated certificates to them.&lt;&#x2F;p&gt;
&lt;p&gt;In the end I revived a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;qotom.net&#x2F;product&#x2F;29.html&quot;&gt;fanless Qotom Mini
PC&lt;&#x2F;a&gt; I had at home. I again used my pyinfra
Chimera install scripts to set it up, followed by more pyinfra code to
configure it. This machine is responsible for managing the certificates with Lego. It
pushes out updated files when they’re renewed via a renew-hook script. The
script is managed by pyinfra and templated so that it syncs to each server in
my pyinfra inventory within the &lt;code&gt;linkedlist_servers&lt;&#x2F;code&gt; group:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#!&#x2F;bin&#x2F;sh -x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# push updated files to each server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# restart nginx on each server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;if [ &amp;quot;$LEGO_CERT_DOMAIN&amp;quot; = &amp;quot;linkedlist.org&amp;quot; ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  {%- for server in linkedlist_servers %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    scp &amp;quot;$LEGO_CERT_PATH&amp;quot; &amp;quot;$LEGO_CERT_KEY_PATH&amp;quot; {{ server }}:&#x2F;etc&#x2F;ssl&#x2F;lego&#x2F;certificates&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ssh {{ server }} doas dinitctl restart nginx&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  {%- endfor %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The compromise here was that I had to allow the cert server &lt;code&gt;ssh&lt;&#x2F;code&gt; access to
each of the app servers, but each of the app servers has no access to the
others. I created a dedicated user for this purpose. A &lt;code&gt;doas&lt;&#x2F;code&gt; rule allows this
user to restart &lt;code&gt;nginx&lt;&#x2F;code&gt; to pick up the updated certificates:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;permit nopass lego as root cmd dinitctl args restart nginx&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;geodns&quot;&gt;GeoDNS&lt;&#x2F;h3&gt;
&lt;p&gt;The final piece of the puzzle was how to determine which server to send a
visitor to in order to minimise their latency. One option would be to use an
edge compute service like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;deno.com&#x2F;deploy&quot;&gt;Deno Deploy&lt;&#x2F;a&gt;, and proxy the request to the desired
host. However, that adds another request, with its own latency—a bit over 20ms
in my testing. I wanted to avoid the extra hop, so I looked into GeoDNS. With
GeoDNS the source IP of DNS requests is geo-located in order to resolve to an
server IP that should minimise latency. Geo-location from IP addresses is
imperfect but good enough for this project.&lt;&#x2F;p&gt;
&lt;p&gt;I settled on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gcore.com&#x2F;dns&quot;&gt;Gcore’s Managed DNS service&lt;&#x2F;a&gt; as it had the necessary Geo
features, a generous free tier, and a reasonable paid tier if that was ever
reached. The UI is perhaps not super intuitive but I eventually got it set up
as desired:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;tiny-cdn&amp;#x2F;gcore-dns-config.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;tiny-cdn&amp;#x2F;gcore-dns-config.png&quot; width=&quot;709&quot; alt=&quot;Screenshot of the Gcore DNS configuration. There&amp;#x27;s three records. The first is assigned to North America, the second Europe, Africa, and South America, the last one is the default fallback record.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Gcore DNS configuration for linkedlist.org.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;ul&gt;
&lt;li&gt;North America is served by the US server.&lt;&#x2F;li&gt;
&lt;li&gt;Europe, Africa, and South America are served by the French server.&lt;&#x2F;li&gt;
&lt;li&gt;Everything else is served by the Australian server.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The default is the AU server because it’s an existing server I already had that
has more RAM and CPU than the others. Some of these mappings were informed by
results from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pingbear.com&#x2F;&quot;&gt;PingBear&lt;&#x2F;a&gt; (down at the time of writing). I was also able to
utilise the health checking feature in Gcore to avoid resolving DNS requests to
servers that are down for some reason.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;taking-it-live&quot;&gt;Taking It Live&lt;&#x2F;h3&gt;
&lt;p&gt;With the DNS sorted, I finally switched the NS records for &lt;code&gt;linkedlist.org&lt;&#x2F;code&gt; over
to Gcore and started seeing each of the servers handle traffic 🎉.&lt;&#x2F;p&gt;
&lt;p&gt;One final detail: how is new content deployed? The content is independent of
the application, which monitors the content directory for changes, reloading as
necessary. Therefore deploying new content is done with a simple &lt;code&gt;rsync&lt;&#x2F;code&gt;. All
three servers are synced in parallel, with a bit of help from &lt;code&gt;xargs&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;printf&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;au\nny\nfr\n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; xargs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; -P&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; -I {host}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    rsync -azhP --delete content&#x2F; {host}.linkedlist.org:&#x2F;var&#x2F;lib&#x2F;linkedlist&#x2F;content&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Deployment is actually initiated via a Makefile, so I don’t have to remember
all that: &lt;code&gt;make deploy&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So how did I go, was this whole exercise in over-engineering worth it? Yep, it
looks like it was. This is the end result in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;updown.io&#x2F;&quot;&gt;updown.io&lt;&#x2F;a&gt;, which shows a decent
improvement over the stats at the beginning of the post:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;tiny-cdn&amp;#x2F;response-times-after.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;response-times-after.912e7e0cb5e7d8d9.png&quot; alt=&quot;Screenshot of updown.io monitoring. It has response times for nine locations around the world. The average total time is 189ms.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Average response timing from my monitoring after the changes&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Most of the monitored locations are showing lower average response times. In
particular, the times in Europe are much improved. It also scores 100 for
performance in a Lighthouse test in Chromium:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;tiny-cdn&amp;#x2F;linked-list-lighthouse.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;tiny-cdn&amp;#x2F;linked-list-lighthouse.png&quot; width=&quot;741&quot; alt=&quot;Screenshot of a Lighthouse report for linkedlist.org in Chromium. It shows 100 for performance, 82 for accessibility, 100 for best practices, adn 92 for SEO.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;linkedlist.org Lighthouse report.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;While these results are good, I still only have a presence in three places
around the world. Notably absent are servers in Asia and Africa. Should I start
to see regular visitors from these or other countries the pyinfa config should
make it straightforward to add servers as needed.&lt;&#x2F;p&gt;
&lt;p&gt;For now though, I need to get back to writing on Linked List. If you haven’t
checked it out already please do. It’s also easy to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;linkedlist.org&#x2F;follow&quot;&gt;follow via RSS, Mastodon,
and Bluesky&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;references&quot;&gt;References&lt;&#x2F;h3&gt;
&lt;p&gt;This series of three blog posts by Stefano Marinelli served as a good reference for
what I was trying to achieve. Stefano uses Varnish to add a layer of caching, which
I didn’t do since the application manages caching itself.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;it-notes.dragas.net&#x2F;2024&#x2F;08&#x2F;26&#x2F;building-a-self-hosted-cdn-for-bsd-cafe-media&#x2F;&quot;&gt;Building a Self-Hosted CDN for BSD Cafe Media&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;it-notes.dragas.net&#x2F;2024&#x2F;08&#x2F;29&#x2F;make-your-own-cdn-openbsd&#x2F;&quot;&gt;Make Your Own CDN With OpenBSD Base and Just 2 Packages&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;it-notes.dragas.net&#x2F;2024&#x2F;09&#x2F;03&#x2F;make-your-own-cdn-netbsd&#x2F;&quot;&gt;Make Your Own CDN With NetBSD&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;The icons in this diagram are from the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.haiku-os.org&#x2F;&quot;&gt;Haiku project&lt;&#x2F;a&gt; used under
the terms of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;haiku&#x2F;haiku&#x2F;blob&#x2F;8ada0c0e7c645e90d9e8d2addb10e7ffbd2bdf56&#x2F;License.md&quot;&gt;their BSD license&lt;&#x2F;a&gt;. Haiku is a really cool
operating system, you should check it out. &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;This gave plenty of headroom, as each server is currently only using about 240MiB of memory. &lt;a href=&quot;#fr-2-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;&lt;code&gt;&#x2F;proc&#x2F;cpuinfo&lt;&#x2F;code&gt; reports: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz &lt;a href=&quot;#fr-3-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-4&quot;&gt;
&lt;p&gt;I covered &lt;code&gt;systemd-sysusers&lt;&#x2F;code&gt; in &lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;systemd-sysusers-and-chimera-linux&#x2F;&quot;&gt;a previous post&lt;&#x2F;a&gt; &lt;a href=&quot;#fr-4-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</description>
    </item>
    
    <item>
      <title>Ubuntu Linux on Snapdragon X Laptop (Lenovo Yoga Slim 7x)</title>
      <pubDate>Sun, 01 Dec 2024 11:17:03 +1000</pubDate>
      <atom:published>2024-12-01T11:17:03+10:00</atom:published>
      <atom:updated>2025-04-09T19:47:25+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;linux-on-yoga-7x-snapdragon&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;linux-on-yoga-7x-snapdragon&#x2F;</guid>
      <description>



&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;linux-on-yoga-7x-snapdragon&amp;#x2F;fastfetch.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;fastfetch.ddefdf34e74d28c4.png&quot; alt=&quot;Screenshot of fastfetch output in a terminal window. The details indicate that it&amp;#x27;s running Ubuntu oracular 24.10 on aarch64.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Ubuntu running on Yoga Slim 7x&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Over the course of the last few months some fine folks in the Linux community
have been plugging away implementing support for Qualcomm Snapdragon X based
ARM laptops. Recently Canonical published &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;discourse.ubuntu.com&#x2F;t&#x2F;ubuntu-24-10-concept-snapdragon-x-elite&#x2F;48800&#x2F;1&quot;&gt;Ubuntu 24.10
Concept&lt;&#x2F;a&gt; for testing on these laptops, which I
installed and tested on my Lenovo Yoga Slim 7x.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h3 id=&quot;installation&quot;&gt;Installation&lt;&#x2F;h3&gt;
&lt;aside class=&quot;float-right&quot;&gt;
  &lt;div class=&quot;emoji text-center&quot;&gt;💡&lt;&#x2F;div&gt;
  &lt;strong&gt;Want a full review?&lt;&#x2F;strong&gt;

  &lt;p&gt;See: &lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;yoga-7x-snapdragon-developer-review&#x2F;&quot;&gt;A Developer’s Review of a Snapdragon X Laptop (Lenovo Yoga Slim 7x)&lt;&#x2F;a&gt;
for my detailed review of this laptop.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;Installation alongside Windows was almost as straightforward as a normal Ubuntu
install. It booted to a graphical live environment with working Wi-Fi.&lt;&#x2F;p&gt;
&lt;p&gt;The main issue was that the installer application in the live environment was only
rendering the bottom quarter of its window when display scaling was set to 200%
(which is the expected value for the display and was automatically applied at
boot). Changing it to 100% made everything tiny, but allowed me to complete the
install.&lt;&#x2F;p&gt;
&lt;p&gt;As per the notes in the forum post I installed and ran &lt;code&gt;qcom-firmware-extract&lt;&#x2F;code&gt;
after installation to fetch and install firmware blobs from the Windows
partition, and then rebooted. This appeared to fix battery level reporting and
possibly hardware video acceleration.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;linux-on-yoga-7x-snapdragon&amp;#x2F;qcom-firmware-extract.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;qcom-firmware-extract.3cc5d2634c6d8e88.png&quot; alt=&quot;Screenshot of a terminal showing the output from running qcom-firmware-extract. It says &amp;#x27;extracting firmware&amp;#x27; followed by a list of files, and ends with &amp;#x27;Building package qcom-x1e-firmware-extracted_20241201_arm64&amp;#x27;.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Running qcom-firmware-extract&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;usage&quot;&gt;Usage&lt;&#x2F;h3&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;linux-on-yoga-7x-snapdragon&amp;#x2F;system-details.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;system-details.c5dc9ed5e45903f8.png&quot; alt=&quot;Screenshot of System Details in GNOME settings indicating that GNOME is running on a Lenovo Yoga Slim 7 14Q8X9 with 32Gb RAM, 1Tb disk, GNOME 47 on Wayland. The Processor section is blank.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;System Details in GNOME Settings&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I have not spent a huge amount of time with this installation as Ubuntu isn’t my
distro of choice, so I don’t want to invest much time setting it up. I did
try most common functionality to get an idea of what works though.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;wi-fi&quot;&gt;Wi-Fi&lt;&#x2F;h4&gt;
&lt;p&gt;I was able to connect to my 5Ghz AP without issue. A &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.speedtest.net&#x2F;&quot;&gt;speed test&lt;&#x2F;a&gt; showed very
similar results between Windows and Linux (although Linux was a bit faster):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Linux: 137 Mbps down, 47 Mbps up&lt;&#x2F;li&gt;
&lt;li&gt;Windows: 131 Mbps down, 45 Mbps up&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;bluetooth&quot;&gt;Bluetooth&lt;&#x2F;h4&gt;
&lt;p&gt;I was able to pair and use some Sennheiser Bluetooth headphones.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;graphics&quot;&gt;Graphics&lt;&#x2F;h4&gt;
&lt;p&gt;The display works fine. I was able to change it to run at the full 90Hz in the
GNOME settings. The Wayland based GNOME desktop appears to be using GPU
acceleration and runs smoothly.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;linux-on-yoga-7x-snapdragon&amp;#x2F;glxgears.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;glxgears.376f5062485f061e.png&quot; alt=&quot;Screenshot showing glxgears running and reporting 89fps in the terminal.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;glxgears running at 90Hz&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;linux-on-yoga-7x-snapdragon&#x2F;glxinfo.txt&quot;&gt;glxinfo&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;linux-on-yoga-7x-snapdragon&#x2F;vulkaninfo.txt&quot;&gt;vulkaninfo&lt;&#x2F;a&gt; both suggest hardware
graphics acceleration is available. However, Firefox reports &lt;code&gt;llvmpipe&lt;&#x2F;code&gt; in
&lt;code&gt;about:support&lt;&#x2F;code&gt; and drops frames playing 4K 60Hz video on YouTube, same with
Chromium. Despite &lt;code&gt;llvmpipe&lt;&#x2F;code&gt; Firefox runs smoothly.&lt;&#x2F;p&gt;
&lt;p&gt;It appears that hardware video decoding via is not supported yet (&lt;code&gt;vainfo&lt;&#x2F;code&gt;
doesn’t find a suitable device).&lt;&#x2F;p&gt;
&lt;h4 id=&quot;audio&quot;&gt;Audio&lt;&#x2F;h4&gt;
&lt;p&gt;The built-in speakers didn’t work, with the only output device being a dummy device.
However, pairing Bluetooth headphones worked as expected. I didn’t test microphones.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;input-devices&quot;&gt;Input Devices&lt;&#x2F;h4&gt;
&lt;p&gt;The keyboard and trackpad work, including tap-to-click out of the box on the
trackpad. The brightness and volume controls work on the keyboard.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;power-management&quot;&gt;Power Management&lt;&#x2F;h4&gt;
&lt;p&gt;As mentioned above, installing and running &lt;code&gt;qcom-firmware-extract&lt;&#x2F;code&gt; copies
firmware from the Windows installation and fixed battery level reporting. I
can’t comment on battery life yet, but it did seem to run a bit warmer than
Windows—although not as hot as
&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;yoga-7x-snapdragon-developer-review&#x2F;#non-windows-operating-systems&quot;&gt;my early testing of OpenBSD&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;other&quot;&gt;Other&lt;&#x2F;h4&gt;
&lt;p&gt;I didn’t try the USB ports. Suspending &lt;em&gt;appeared&lt;&#x2F;em&gt; to work: the screen turned
off, however the LED on the power switch did not start pulsing like it does in
Windows. More testing required here.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;Linux support for these laptops is coming along quite nicely, and if you’re
willing to put up with a few rough edges Ubuntu is quite usable. No doubt
things will continue to improve.&lt;&#x2F;p&gt;
&lt;p&gt;There’s been work happening in other distros too. Relevant to my interests,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;JamiKettunen&#x2F;cports&#x2F;tree&#x2F;x1e&quot;&gt;Jami Kettunen has been working on X1E support in Chimera Linux&lt;&#x2F;a&gt;
and has that running on a HP OmniBook X. It’s on my todo list to try out Jami’s
work on my Yoga 7x.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;linux-on-yoga-7x-snapdragon&amp;#x2F;chimera-kde-plasma-x1e.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;chimera-kde-plasma-x1e.8ac3f4811c35a6d2.png&quot; alt=&quot;About this system in KDE Plasma showing Chimera Linux running on Snapdragon X hardware.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;About this system in KDE Plasma showing Chimera Linux running on Snapdragon X hardware.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
</description>
    </item>
    
    <item>
      <title>Generating a Static Website From a Pleroma Archive</title>
      <pubDate>Mon, 25 Nov 2024 20:21:44 +1000</pubDate>
      <atom:published>2024-11-25T20:21:44+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;pleroma-archive&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;pleroma-archive&#x2F;</guid>
      <description>&lt;p&gt;Almost two years ago, in January 2023 I migrated my Fediverse presence from a
self-hosted &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pleroma.social&#x2F;&quot;&gt;Pleroma&lt;&#x2F;a&gt; instance to a single user Mastodon instance hosted by
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;masto.host&#x2F;&quot;&gt;masto.host&lt;&#x2F;a&gt;. Since then I’ve wanted to retire the Pleroma instance, but I
didn’t want to just take it offline. I wanted to preserve my posts and links to
them. That became a priority over the weekend so I built a tool,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;pleroma-archive&quot;&gt;pleroma-archive&lt;&#x2F;a&gt; to do it.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;A few months after the switch to Mastodon I tried pulling my posts via RSS, but
hit a runtime error. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.pleroma.social&#x2F;pleroma&#x2F;pleroma&#x2F;-&#x2F;issues&#x2F;3149&quot;&gt;I reported it to the project&lt;&#x2F;a&gt; but nothing
came of it.&lt;&#x2F;p&gt;
&lt;p&gt;I ignored it for another 18 months until this weekend. I tried to upgrade my
PostgreSQL server from version 12 to 16, and migrate it to a new host. I chose
the dump and load method of doing this, but when restoring the Pleroma database
it appeared to get stuck building one of the indexes. I estimated that it was
going to take something like 30 hours to complete. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.pleroma.social&#x2F;pleroma&#x2F;pleroma&#x2F;-&#x2F;issues&#x2F;3031&quot;&gt;I’m not the first one to
hit this problem either&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Retiring Pleroma had now become a priority. I discovered that there was now an
account backup option in the import&#x2F;export section of the settings. I
downloaded my archive and set about building a tool that could generate a
website from it.&lt;&#x2F;p&gt;
&lt;p&gt;As usual I built the tool in Rust, my scripting language of choice. It’s
imaginatively called &lt;code&gt;pleroma-archive&lt;&#x2F;code&gt;. It generates an index page of all posts
as well as a page for each individual post. The public URLs that Pleroma uses
are not part of the archive, so for each post the tool does a &lt;code&gt;HEAD&lt;&#x2F;code&gt; request
with the post id to determine the public URL of the post. The results of this
are cached so it only needs to do it once for each post.&lt;&#x2F;p&gt;
&lt;p&gt;With a little bit of help from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nginx.org&#x2F;en&#x2F;docs&#x2F;http&#x2F;ngx_http_core_module.html#try_files&quot;&gt;Nginx try_files&lt;&#x2F;a&gt; a page like
&lt;code&gt;notice&#x2F;ARQGKLTJNiP8Lu2gT2.html&lt;&#x2F;code&gt; can be served at &lt;code&gt;&#x2F;notice&#x2F;ARQGKLTJNiP8Lu2gT2&lt;&#x2F;code&gt;,
matching the URL it had when served by Pleroma:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nginx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span&gt; &#x2F; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    try_files $&lt;&#x2F;span&gt;&lt;span&gt;uri&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;uri&#x2F;index.html &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;uri.html &lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;=404&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The end result is at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;decentralised.social&#x2F;&quot;&gt;https:&#x2F;&#x2F;decentralised.social&#x2F;&lt;&#x2F;a&gt; and I have now retired
my Pleroma instance.&lt;&#x2F;p&gt;
&lt;p&gt;Source code and instructions for using &lt;code&gt;pleroma-archive&lt;&#x2F;code&gt; is available at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;pleroma-archive&quot;&gt;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;pleroma-archive&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Building and Launching My New Link Blog, linkedlist.org (Twice)</title>
      <pubDate>Thu, 31 Oct 2024 13:56:30 +1000</pubDate>
      <atom:published>2024-10-31T13:56:30+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;linked-list&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;linked-list&#x2F;</guid>
      <description>&lt;p&gt;I’ve started a new tech focused link blog over at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;linkedlist.org&quot;&gt;linkedlist.org&lt;&#x2F;a&gt;. “Not another tech blog”, I hear you
groan, and rightly so. However my intention is &lt;strong&gt;not&lt;&#x2F;strong&gt; to cover topics that are
already well reported upon like Apple, Google, Microsoft, the latest drama at
OpenAI, and other stuff like that. Instead, I plan to focus more open-source,
programming, hardware, software, Linux, Rust, retro computing etc. There’s some
more details in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;linkedlist.org&#x2F;2024&#x2F;09&#x2F;14&#x2F;welcome-to-linkedlist&quot;&gt;the welcome post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In this post I’m going to cover the process I took to the build the site
(twice) and some of the considerations that went into it—for a site with only a
handful of pages there was a surprising amount of them.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;When building Linked List my reference was &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;daringfireball.net&#x2F;&quot;&gt;Daring Fireball&lt;&#x2F;a&gt; by John Gruber,
which I’ve enjoyed reading for close to two decades now (aside from some of the
recent takes on the EU DMA). The site is simple on the surface but it’s clear
that John has put a bunch of thought into a number of different aspects of the
site.&lt;&#x2F;p&gt;
&lt;p&gt;The high-level things I wanted were:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The distinction of a link post (to an external site) versus a first party post&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;An RSS feed that has special accommodations for link posts where the item
links to the external site.&lt;&#x2F;li&gt;
&lt;li&gt;Automated publishing of new posts to Mastodon.&lt;&#x2F;li&gt;
&lt;li&gt;A clean design.&lt;&#x2F;li&gt;
&lt;li&gt;Responsive (mobile friendly).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;the-first-build&quot;&gt;The First Build&lt;&#x2F;h3&gt;
&lt;p&gt;I initially toyed with the idea of using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;micro.blog&#x2F;&quot;&gt;micro.blog&lt;&#x2F;a&gt; to host the site as it
can host blogs, micro or not, and has a lot of built-in support for
cross-posting to services like Mastodon. Ultimately I decided it wasn’t quite
the right fit for what I was aiming for.&lt;&#x2F;p&gt;
&lt;p&gt;Concluding I’d need to build it myself I reached for my go-to
static-site-compiler: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt;. As part of the micro.blog experiment I took a
liking to their &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microdotblog&#x2F;theme-alpine&quot;&gt;Alpine theme&lt;&#x2F;a&gt;, which as it was open-source I used as the base
style for the Zola site.&lt;&#x2F;p&gt;
&lt;p&gt;My first hurdle was that I wanted a particular URL hierarchy:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;linkedlist.org&#x2F;YYYY&#x2F;MM&#x2F;DD&#x2F;post-slug&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This proved a challenge to achieve with Zola. There was an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola&#x2F;issues&#x2F;2275&quot;&gt;open issue&lt;&#x2F;a&gt;
about this sort of structure, which led me
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;scouten&#x2F;ericscouten.travel&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;scouten&#x2F;ericscouten.travel&lt;&#x2F;a&gt;. I was able to replicate the
approach used there to achieve what I wanted.&lt;&#x2F;p&gt;
&lt;p&gt;Creating a new post was a bit involved though as it required creating each of
the intermediate directories if they didn’t exist, as well as an &lt;code&gt;_index.md&lt;&#x2F;code&gt;
file with particular content in each newly created directory. I automated this
with a Ruby script, which was also able to pre-populate the front matter for a
new link post.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cross-posting-to-mastodon&quot;&gt;Cross-posting to Mastodon&lt;&#x2F;h3&gt;
&lt;p&gt;For cross-posting I wanted to use the site’s feed as the source and have the
tool post newly published items. There are dozens of these RSS to Mastodon
tools on GitHub. I evaluated 16 of them against a handful of requirements:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;No runtime&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-2-1&quot;&gt;&lt;a href=&quot;#fn-2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This ruled out the vast majority, as many were written in Python or
JavaScript&#x2F;TypeScript. I don’t want to have to deal with operating tools using
these languages as I find handling their dependencies and breakage due to
upgrades annoying.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Conditional Requests&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It feels like table stakes that a tool that is polling a HTTP resource will
make conditional requests using headers like &lt;code&gt;If-Modified-Since&lt;&#x2F;code&gt; or
&lt;code&gt;If-None-Match&lt;&#x2F;code&gt; so that it will only be fetched and processed if modified. The
vast majority of the tools I evaluated just fetched the feed every time they
polled it though.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Robust Against Duplicate Posts&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I want to reduce the possibility of something causing a flood of posts or
duplicates. Some of the tools I evaluated did alright here, but many did not.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;None felt like they covered all these, so I took &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;read-rust&#x2F;tree&#x2F;master&#x2F;rust&#x2F;src&quot;&gt;the code I had written for
the Read Rust tooter&lt;&#x2F;a&gt; and reworked it to use a feed as the data source
instead of a database. I spent a fair bit of time making it support
multiple feeds and feed formats, as well multiple posting targets. I plan to
open-source it in the future.&lt;&#x2F;p&gt;
&lt;p&gt;I protect against duplicate posts and post floods by:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Marking all existing items of a feed as seen before on first run.&lt;&#x2F;li&gt;
&lt;li&gt;Tracking the guid of each published item, and only publishing new ones.&lt;&#x2F;li&gt;
&lt;li&gt;Tracking the content of each Mastodon post published so that if an item slips
through the other guards it will hopefully be stopped at this point.&lt;&#x2F;li&gt;
&lt;li&gt;Use idempotency keys when publishing to Mastodon so that if a post fails
on the client but is actually successfully processed by the server,
it will be rejected on a subsequent attempt to post it.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Before the scope creep in the cross-posting tool occurred I added a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jsonfeed.org&#x2F;&quot;&gt;JSON Feed&lt;&#x2F;a&gt;
to Linked List as these was a bit easier to consume than the Atom feed. Only
problem was &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola&#x2F;issues&#x2F;311&quot;&gt;Zola didn’t support JSON Feed&lt;&#x2F;a&gt;. I solved this with
a &lt;code&gt;jaq&lt;&#x2F;code&gt; script described in &lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;json-feed-zola&#x2F;&quot;&gt;my previous post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;With the cross-posting tool mostly feature complete I deployed it to a
Raspberry Pi Zero W, running on my desk. Every 15 mins &lt;code&gt;cron&lt;&#x2F;code&gt; fires it up to
check for, and post new items in the feed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;polish&quot;&gt;Polish&lt;&#x2F;h3&gt;
&lt;p&gt;There was a long tail of smaller things that I implemented before the initial
launch, such as:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Dark mode&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ogp.me&#x2F;&quot;&gt;OpenGraph&lt;&#x2F;a&gt; metadata&lt;&#x2F;li&gt;
&lt;li&gt;Mastodon account and verification metadata&lt;&#x2F;li&gt;
&lt;li&gt;Archive pages such as &lt;code&gt;&#x2F;2024&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;2024&#x2F;10&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;The myriad of favicon images
(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;realfavicongenerator.net&#x2F;&quot;&gt;realfavicongenerator.net&lt;&#x2F;a&gt; helped a lot
here)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;launch-rewrite&quot;&gt;Launch &amp;amp; Rewrite&lt;&#x2F;h3&gt;
&lt;p&gt;Finally, on 11 October I soft-launched the site with a post on Mastodon. Over
the next couple of weeks I published posts to the site, eventually realising
I’d never added pagination to the home page. I went to add it that weekend and
discovered that it was going to be very difficult owing to how I’d achieved
the URL hierarchy with Zola.&lt;&#x2F;p&gt;
&lt;p&gt;Zola had got the site up, but I took this (as well as some of the earlier
friction) as a sign it was time for a custom approach. Over the next few
evenings and some weekend time I rewrote it in Rust using Axum as the HTTP
server layer.&lt;&#x2F;p&gt;
&lt;p&gt;Since things were more under my control now I took the opportunity to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Drop trailing slashes from URLs.&lt;&#x2F;li&gt;
&lt;li&gt;Set long-lasting &lt;code&gt;Cache-Control&lt;&#x2F;code&gt; values for static files and include them
with a cache busting hash.&lt;&#x2F;li&gt;
&lt;li&gt;Bundle all static files like CSS and fonts into the binary in release builds.&lt;&#x2F;li&gt;
&lt;li&gt;Add pagination to the home page.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I also had to do a bit of extra work to support things you get for free in a
Zola&#x2F;static site:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Etag&lt;&#x2F;code&gt; headers&lt;&#x2F;li&gt;
&lt;li&gt;Conditional request support&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;sitemap.xml&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;On 30 October I deployed the new version of the site. It renders the same
Markdown files as the Zola site, so publishing new posts is still just rsyncing
the files.&lt;&#x2F;p&gt;
&lt;p&gt;The Markdown content is loaded from disk at start up and rendered out of RAM.
It doesn’t currently cache the rendered result, but most responses are generated
in 1ms or less anyway. A filesystem watcher is used to notice when the files
are changed and automatically reload them.&lt;&#x2F;p&gt;
&lt;p&gt;With the rewrite out of the way I now have more time for more regular posting
to the site, I hope you’ll &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;linkedlist.org&#x2F;follow&quot;&gt;follow along&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;Gruber actually calls the collection of link posts on Daring Fireball
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;daringfireball.net&#x2F;linked&#x2F;&quot;&gt;the Linked List&lt;&#x2F;a&gt;, although I was only tangentially aware of
this when I embarked on this project. My main motivation was that I liked
the cross-over with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Linked_list&quot;&gt;the data structure&lt;&#x2F;a&gt; and that I already
had the &lt;code&gt;linkedlist.org&lt;&#x2F;code&gt; domain, originally registering it in 2011. &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;I.e. a native binary that can be run without having to install an interpreter
or similar first. &lt;a href=&quot;#fr-2-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</description>
    </item>
    
    <item>
      <title>Generate a JSON Feed for a Zola Website</title>
      <pubDate>Sat, 28 Sep 2024 20:50:30 +1000</pubDate>
      <atom:published>2024-09-28T20:50:30+10:00</atom:published>
      <atom:updated>2024-09-30T09:08:57+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;json-feed-zola&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;json-feed-zola&#x2F;</guid>
      <description>&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jsonfeed.org&#x2F;&quot;&gt;JSON Feed&lt;&#x2F;a&gt; is a specification for representing an RSS-style feed in JSON. I
wanted to add one as an alternative alongside the Atom feed on a new website
I’m building. The website is built with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt;, which unfortunately &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola&#x2F;issues&#x2F;311&quot;&gt;doesn’t
support the format&lt;&#x2F;a&gt;, so this is how I went about adding one.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;My first idea was to add a Tera template &lt;code&gt;json.feed&lt;&#x2F;code&gt; and try to generate JSON
in the template. This was foiled on multiple fronts:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Zola only loads files matching &lt;code&gt;*.{*ml,md}&lt;&#x2F;code&gt; and &lt;code&gt;robots.txt&lt;&#x2F;code&gt; as templates,
so it didn’t render my &lt;code&gt;json.feed&lt;&#x2F;code&gt; template.&lt;&#x2F;li&gt;
&lt;li&gt;Tera has a &lt;code&gt;json_encode&lt;&#x2F;code&gt; filter but &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;issues&#x2F;898&quot;&gt;does not support object literals&lt;&#x2F;a&gt;,
which makes building well formed JSON tricky.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;My next thought was to write a tool to convert the Atom feed to JSON. Rust is
my scripting language of choice these days (only a tiny bit joking) but I
didn’t feel like adding a Rust project and build step for this. I then wondered
if it could be done with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jqlang.github.io&#x2F;jq&#x2F;&quot;&gt;jq&lt;&#x2F;a&gt;, or in my case &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;01mf02&#x2F;jaq&quot;&gt;jaq&lt;&#x2F;a&gt;—a &lt;code&gt;jq&lt;&#x2F;code&gt; implementation in
Rust. Short answer it can. Here’s what I came up with:&lt;&#x2F;p&gt;
&lt;p&gt;I created &lt;code&gt;templates&#x2F;dump.xml&lt;&#x2F;code&gt; with these contents:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{{ __tera_context }}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This dumps the whole Tera context as JSON when rendered into &lt;code&gt;public&#x2F;dump.xml&lt;&#x2F;code&gt;.
It’s &lt;code&gt;.xml&lt;&#x2F;code&gt; so that Zola will render it as a template. I added it to
&lt;code&gt;feed_filenames&lt;&#x2F;code&gt; in the Zola &lt;code&gt;config.toml&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;feed_filenames&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;atom.xml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;dump.xml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt; # HACK: This just dumps the Tera context as JSON&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then I wrote this &lt;code&gt;jaq&lt;&#x2F;code&gt; filter (&lt;code&gt;json-feed.jaq&lt;&#x2F;code&gt;) to generate a JSON Feed
&lt;code&gt;feed.json&lt;&#x2F;code&gt; from the rendered version of the template.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# vim: ft=jq&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# Generate a JSON feed from context of a Zola feed template&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;version&amp;quot;: &amp;quot;https:&#x2F;&#x2F;jsonfeed.org&#x2F;version&#x2F;1.1&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;title&amp;quot;: .config.title,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;home_page_url&amp;quot;: .config.base_url,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;feed_url&amp;quot;: .feed_url | sub(&amp;quot;dump\\.xml$&amp;quot;; &amp;quot;feed.json&amp;quot;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;authors&amp;quot;: [ { &amp;quot;name&amp;quot;: .config.author } ],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;language&amp;quot;: &amp;quot;en-AU&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;items&amp;quot;: .pages | map({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;quot;id&amp;quot;: .permalink,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;quot;url&amp;quot;: .permalink,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;quot;title&amp;quot;: .title,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;quot;content_html&amp;quot;: .content,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;quot;date_published&amp;quot;: .date,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;quot;tags&amp;quot;: .taxonomies.tags,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    } + if .updated then {&amp;quot;date_modified&amp;quot;: .updated} else {} end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      + if .extra.link then {&amp;quot;external_url&amp;quot;: .extra.link} else {} end)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It turned out quite nice. The main challenge with the &lt;code&gt;jaq&lt;&#x2F;code&gt; filter was working
out how to conditionally include a key only if the value was non-null. I ended
up with the trailing &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jqlang.github.io&#x2F;jq&#x2F;manual&#x2F;#if-then-else-end&quot;&gt;if-then-else-end&lt;&#x2F;a&gt; expressions. If there’s a better way
I’d be keen to hear about it.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, to coordinate all this I added a &lt;code&gt;Makefile&lt;&#x2F;code&gt; that deletes &lt;code&gt;dump.xml&lt;&#x2F;code&gt;
afterwards:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;make&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;help&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;	@&lt;&#x2F;span&gt;&lt;span&gt;echo &amp;quot;Available tasks:&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;	@&lt;&#x2F;span&gt;&lt;span&gt;echo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;	@&lt;&#x2F;span&gt;&lt;span&gt;echo &amp;quot;- build&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;	@&lt;&#x2F;span&gt;&lt;span&gt;echo &amp;quot;- deploy&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;build&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	zola build&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	jaq --from-file json-feed.jaq public&#x2F;dump.xml &amp;gt; public&#x2F;feed.json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	rm public&#x2F;dump.xml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;deploy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;	@&lt;&#x2F;span&gt;&lt;span&gt;echo todo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;.PHONY&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; build depoly&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</description>
    </item>
    
    <item>
      <title>Australian Chimera Linux Mirror</title>
      <pubDate>Sun, 25 Aug 2024 09:17:27 +1000</pubDate>
      <atom:published>2024-08-25T09:17:27+10:00</atom:published>
      <atom:updated>2026-02-16T14:10:52+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;australian-chimera-linux-mirror&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;australian-chimera-linux-mirror&#x2F;</guid>
      <description>&lt;p&gt;I have set up a mirror of
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;repo.chimera-linux.org&#x2F;&quot;&gt;repo.chimeralinux.org&lt;&#x2F;a&gt; on a server in
Australia (Brisbane). It’s been running well for a couple of weeks now. The
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;au.mirror.7bit.org&#x2F;&quot;&gt;root of the mirror&lt;&#x2F;a&gt; shows an index of what is hosted and when it was
last synced. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;au.mirror.7bit.org&#x2F;chimera&#x2F;&quot;&gt;&#x2F;chimera&lt;&#x2F;a&gt; is where the Chimera data lives.&lt;&#x2F;p&gt;
&lt;p&gt;It mirrors the packages as well as ISO and rootfs downloads. Using the mirror
greatly speeds up package downloads, which in-turn makes things like &lt;code&gt;apk upgrade&lt;&#x2F;code&gt; a lot faster. Some rudimentary testing suggests this this server may
also provide a speed improvement for folks in parts of Asia too.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h3 id=&quot;how-to-use-the-mirror&quot;&gt;How to Use the Mirror&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;packages&quot;&gt;Packages&lt;&#x2F;h4&gt;
&lt;p&gt;Refer to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;docs&#x2F;apk&#x2F;mirrors&quot;&gt;the documentation on the Chimera Linux website&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;downloads&quot;&gt;Downloads&lt;&#x2F;h4&gt;
&lt;p&gt;For ISOs and rootfs images visit: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;au.mirror.7bit.org&#x2F;chimera&#x2F;live&#x2F;latest&#x2F;&quot;&gt;https:&#x2F;&#x2F;au.mirror.7bit.org&#x2F;chimera&#x2F;live&#x2F;latest&#x2F;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;building-and-running-the-mirror&quot;&gt;Building and Running the Mirror&lt;&#x2F;h3&gt;
&lt;p&gt;I set up a new server with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.binarylane.com.au&#x2F;&quot;&gt;BinaryLane&lt;&#x2F;a&gt; in Brisbane and installed Chimera Linux
on it—yes the mirror is hosted with Chimera. Specifically I installed the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkgs.chimera-linux.org&#x2F;package&#x2F;current&#x2F;main&#x2F;x86_64&#x2F;base-minimal&quot;&gt;base-minimal&lt;&#x2F;a&gt; package set to keep the package count low. On top of that I
installed &lt;code&gt;nginx&lt;&#x2F;code&gt; to serve the data and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;go-acme.github.io&#x2F;lego&#x2F;&quot;&gt;Lego&lt;&#x2F;a&gt; (which I added to cports) to
manage TLS certificates from Let’s Encrypt.&lt;&#x2F;p&gt;
&lt;p&gt;To limit access and avoid the use of &lt;code&gt;root&lt;&#x2F;code&gt; there are dedicated regular users
for syncing, &lt;code&gt;lego&lt;&#x2F;code&gt;, &lt;code&gt;nginx&lt;&#x2F;code&gt;, and &lt;code&gt;www-data&lt;&#x2F;code&gt;. To allow &lt;code&gt;lego&lt;&#x2F;code&gt; to restart
&lt;code&gt;nginx&lt;&#x2F;code&gt; when certificates are updated I use a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Duncaen&#x2F;OpenDoas&quot;&gt;doas&lt;&#x2F;a&gt; rule.&lt;&#x2F;p&gt;
&lt;p&gt;Synchronisation is triggered hourly by a cron job. I use the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;RsyncProject&#x2F;rsync&#x2F;blob&#x2F;9615a2492bbf96bc145e738ebff55bbb91e0bbee&#x2F;support&#x2F;atomic-rsync&quot;&gt;atomic-rsync&lt;&#x2F;a&gt;
script to make updates atomic. The script manages a pair of directories
&lt;code&gt;chimera-1&lt;&#x2F;code&gt; and &lt;code&gt;chimera-2&lt;&#x2F;code&gt;. The active one is symlinked to &lt;code&gt;chimera&lt;&#x2F;code&gt;. The
script syncs into the inactive directory, creating hard links for unchanged
data, then flips the symlink at the end.&lt;&#x2F;p&gt;
&lt;p&gt;After syncing, the current date is written to a file, which is used by a small
snippet of JavaScript on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;au.mirror.7bit.org&#x2F;&quot;&gt;the index page&lt;&#x2F;a&gt; to show when the data was
last synced.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Announcing Feedlynx</title>
      <pubDate>Mon, 29 Jul 2024 09:43:57 +1000</pubDate>
      <atom:published>2024-07-29T09:43:57+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;announcing-feedlynx&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;announcing-feedlynx&#x2F;</guid>
      <description>&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;images&amp;#x2F;feedlynx.svg&quot; width=&quot;75&quot; alt=&quot;Feedlynx logo: a caricature of a Lynx with a stem in its mouth. At the end of the stem is the orange RSS logo.&quot; class=&quot;image-left&quot; &#x2F;&gt;
&lt;p&gt;My latest project, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;feedlynx&quot;&gt;Feedlynx&lt;&#x2F;a&gt;, is a self-hosted tool that allows you to
collect links in an RSS feed&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. You subscribe to the feed in your RSS reader of
choice and read or watch later at your leisure. Plus it has an adorable mascot!&lt;&#x2F;p&gt;
&lt;p&gt;Feedlynx runs on most mainstream operating systems including Linux, macOS, BSD,
and Windows and has no runtime dependencies. Check out &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;feedlynx&#x2F;releases&#x2F;latest&quot;&gt;the latest release&lt;&#x2F;a&gt; to
download pre-compiled binaries for some common platforms.&lt;&#x2F;p&gt;
&lt;p&gt;After a few weeks using Feedlynx myself I think it’s ready for others to check out.
Read on for more information about my motivations behind building Feedlynx.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h3 id=&quot;motivation&quot;&gt;Motivation&lt;&#x2F;h3&gt;
&lt;p&gt;Since &lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;youtube-subscriptions-opml&#x2F;&quot;&gt;moving all my YouTube subscriptions to my RSS
reader&lt;&#x2F;a&gt; there was one thing I
missed from using YouTube directly: Watch Later. For videos that I’d encounter
on social media, or shared in chats I missed having a way to quickly stash
them for later. This is what motivated me to build Feedlynx.&lt;&#x2F;p&gt;
&lt;p&gt;You might wonder, though, why I didn’t use one of the existing bookmarking
or read-later services. Well, the main reason was that I wanted the links to
show up in the same place that I was already watching videos: in my RSS reader.
Of course, I also really like building little self-hosted tools to solve my own
problems.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;usage&quot;&gt;Usage&lt;&#x2F;h3&gt;
&lt;p&gt;The Feedlynx server is implemented in Rust (as is tradition) and provides HTTP
endpoints to accept new links and serve the RSS feed.&lt;&#x2F;p&gt;
&lt;p&gt;On a real computer new links are added via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;feedlynx-ext&quot;&gt;the Firefox browser extension&lt;&#x2F;a&gt; I
wrote. Click the icon and a moment later a notification is shown indicating the
link was added.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;announcing-feedlynx&amp;#x2F;notification.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;announcing-feedlynx&amp;#x2F;notification.png&quot; width=&quot;303&quot; alt=&quot;TODO&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Notification from Firefox extension.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;On my phone, I’ve set up a workflow using the Shortcuts app that lets me add
links directly from the share sheet.. The Shortcut can be installed on iOS,
iPadOS, and macOS. It’s linked in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;feedlynx&quot;&gt;the README&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;When a new link is submitted to the server it fetches the page to try to
extract &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ogp.me&#x2F;&quot;&gt;OpenGraph metadata&lt;&#x2F;a&gt; to help fill out the item in the RSS
feed. The title of the tab is also submitted by the browser extension as a fall
back.&lt;&#x2F;p&gt;
&lt;p&gt;The fall back is particularly necessary for YouTube since it seems they often
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;iv-org&#x2F;invidious&#x2F;issues&#x2F;4734&quot;&gt;block simple requests for the HTML of the video page&lt;&#x2F;a&gt;. Instead of the
video page they return a &lt;code&gt;200 Ok&lt;&#x2F;code&gt; response with a “prove you’re not a robot
page” and a generic title and description.&lt;&#x2F;p&gt;
&lt;p&gt;Adding new links via &lt;code&gt;cURL&lt;&#x2F;code&gt; is also quite simple, should you want to do so from
a script:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;curl -d &amp;#39;url=https:&#x2F;&#x2F;example.com&#x2F;&amp;#39; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     -d &amp;#39;token=ExampleExampleExampleExample1234&amp;#39; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     http:&#x2F;&#x2F;localhost:8001&#x2F;add&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;implementation-notes&quot;&gt;Implementation Notes&lt;&#x2F;h3&gt;
&lt;p&gt;Some notes on the implementation:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I &lt;em&gt;tried&lt;&#x2F;em&gt; to keep the number of dependencies low and favoured dependencies
that had few dependencies of their own.&lt;&#x2F;li&gt;
&lt;li&gt;I vendored code from a few crates since I only needed a small piece
of their functionality. These are acknowledged in the README and in
the code.&lt;&#x2F;li&gt;
&lt;li&gt;Since this is only intended to serve sporadic requests for one person it
doesn’t use async Rust. Regular synchronous code is more than enough and
avoids the need to pull in a whole async runtime.&lt;&#x2F;li&gt;
&lt;li&gt;The RSS feed is the data store, no need for a DB or anything like that.
The file is guarded by a lock.&lt;&#x2F;li&gt;
&lt;li&gt;Adding a feed and fetching the feed requires two different tokens that
are read from the environment when the server starts.&lt;&#x2F;li&gt;
&lt;li&gt;The browser extension requests the bare minimum permissions necessary to get
the job done. It is not able to see the content of pages.&lt;&#x2F;li&gt;
&lt;li&gt;YouTube links are detected and an &lt;code&gt;iframe&lt;&#x2F;code&gt; embed is included in the RSS
item to allow watching in your RSS reader.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;future-work-ideas&quot;&gt;Future Work&#x2F;Ideas&lt;&#x2F;h3&gt;
&lt;p&gt;My primary use-case was stashing videos to watch later but as I was building
Feedlynx it made sense to make it work for any link. It seems like an logical
extension to have Feedlynx manage multiple feeds so that you can send videos
to one feed and other links to one or more other feeds.&lt;&#x2F;p&gt;
&lt;p&gt;I only use Firefox so that’s the only browser extension that exists so far. It
should be straightforward to port the extension to other browsers
(contributions welcome). I haven’t submitted the extension to
addons.mozilla.org yet. I’ll do that soon.&lt;&#x2F;p&gt;
&lt;p&gt;Since Feedlynx manages an RSS feed on disk it could be useful to have a mode
where the server is not run, instead relying on an existing HTTP server like
&lt;code&gt;nginx&lt;&#x2F;code&gt; to serve the feed. A command like &lt;code&gt;feedlynx add some-url&lt;&#x2F;code&gt; could be
used to add new entries.&lt;&#x2F;p&gt;
&lt;p&gt;I’ve been considering offering low-cost paid hosting for tools like Feedlynx
and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rsspls.7bit.org&#x2F;&quot;&gt;RSS Please&lt;&#x2F;a&gt; for folks that don’t want to deal with their own server. If
you’d be interested in that let me know.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;links&quot;&gt;Links&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;feedlynx&quot;&gt;Source code&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;feedlynx-ext&quot;&gt;Browser extension code&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;I refer to the feed as an RSS feed throughout but it’s actually an Atom feed. &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</description>
    </item>
    
    <item>
      <title>A Developer&#x27;s Review of a Snapdragon X Laptop (Lenovo Yoga Slim 7x)</title>
      <pubDate>Tue, 16 Jul 2024 07:19:09 +1000</pubDate>
      <atom:published>2024-07-16T07:19:09+10:00</atom:published>
      <atom:updated>2024-12-03T20:16:27+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;yoga-7x-snapdragon-developer-review&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;yoga-7x-snapdragon-developer-review&#x2F;</guid>
      <description>



&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;yoga-7x-on-desk.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;yoga-7x-on-desk.48a90bdce291545c.jpg&quot; alt=&quot;Photo of the Yoga 7x laptop open on a desk showing the Glass House Mountains on the desktop. To the right of the laptop is a coffee mug and a pair of glasses.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Lenovo Yoga Slim 7x Snapdragon&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;For the last two weeks I’ve been testing out my new laptop, a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;archive.is&#x2F;kgfke&quot;&gt;Lenovo Yoga Slim
7x (14&quot;, Gen 9) Snapdragon&lt;&#x2F;a&gt;. This laptop is interesting because
it’s one of the initial batch based on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.anandtech.com&#x2F;show&#x2F;21445&#x2F;qualcomm-snapdragon-x-architecture-deep-dive&quot;&gt;Qualcomm’s Snapdragon X Elite Arm
CPUs&lt;&#x2F;a&gt;. In this post I aim to provide a detailed review of the device
and the experience of using it from the perspective of a software developer.
This post was written on the Yoga 7x.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;!-- toc --&gt;
&lt;h3 id=&quot;introduction-purchasing&quot;&gt;Introduction &amp;amp; Purchasing&lt;&#x2F;h3&gt;
&lt;p&gt;All prices are quoted in Australian dollars. You can select a
different currency, which will use exchange rates at the time I purchased the
laptop.&lt;&#x2F;p&gt;


&lt;label&gt;Currency:
&lt;select id=&quot;forex&quot;&gt;
    
    
    &lt;option value=&quot;AUD&quot; data-price=&quot;1&quot;&gt;AUD&lt;&#x2F;option&gt;
    
    
    &lt;option value=&quot;BRL&quot; data-price=&quot;3.443683&quot;&gt;BRL&lt;&#x2F;option&gt;
    
    
    &lt;option value=&quot;CAD&quot; data-price=&quot;0.906966&quot;&gt;CAD&lt;&#x2F;option&gt;
    
    
    &lt;option value=&quot;CHF&quot; data-price=&quot;0.604562&quot;&gt;CHF&lt;&#x2F;option&gt;
    
    
    &lt;option value=&quot;EUR&quot; data-price=&quot;0.612166&quot;&gt;EUR&lt;&#x2F;option&gt;
    
    
    &lt;option value=&quot;GBP&quot; data-price=&quot;0.520619&quot;&gt;GBP&lt;&#x2F;option&gt;
    
    
    &lt;option value=&quot;HKD&quot; data-price=&quot;5.171143&quot;&gt;HKD&lt;&#x2F;option&gt;
    
    
    &lt;option value=&quot;INR&quot; data-price=&quot;55.171611&quot;&gt;INR&lt;&#x2F;option&gt;
    
    
    &lt;option value=&quot;JPY&quot; data-price=&quot;104.325495&quot;&gt;JPY&lt;&#x2F;option&gt;
    
    
    &lt;option value=&quot;KRW&quot; data-price=&quot;906.279836&quot;&gt;KRW&lt;&#x2F;option&gt;
    
    
    &lt;option value=&quot;PLN&quot; data-price=&quot;2.617042&quot;&gt;PLN&lt;&#x2F;option&gt;
    
    
    &lt;option value=&quot;RUB&quot; data-price=&quot;59.675759&quot;&gt;RUB&lt;&#x2F;option&gt;
    
    
    &lt;option value=&quot;SEK&quot; data-price=&quot;7.058684&quot;&gt;SEK&lt;&#x2F;option&gt;
    
    
    &lt;option value=&quot;USD&quot; data-price=&quot;0.66182&quot;&gt;USD&lt;&#x2F;option&gt;
    
&lt;&#x2F;select&gt;
&lt;&#x2F;label&gt;
&lt;p&gt;I purchased the Yoga 7x for &lt;span class=&quot;money&quot; data-amount=&quot;2466.98&quot;&gt;AU$2466.98&lt;&#x2F;span&gt;
 direct from Lenovo.
There are two models offered, one with 16 Gb RAM&#x2F;512 Gb storage the other with
32 Gb&#x2F;1 Tb. Strangely, if you option the first model up to the same specs as
the other one the price is cheaper, so I did that. Additionally, I was able to
get a &lt;span class=&quot;money&quot; data-amount=&quot;200&quot;&gt;AU$200&lt;&#x2F;span&gt;
 discount and carry case for &lt;span class=&quot;money&quot; data-amount=&quot;1&quot;&gt;AU$1&lt;&#x2F;span&gt;

by asking in the web chat if they had any “special offers” for this machine.&lt;&#x2F;p&gt;
&lt;p&gt;The Yoga 7x is to replace a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;support.hp.com&#x2F;lv-en&#x2F;document&#x2F;c08303941&quot;&gt;HP Aero&lt;&#x2F;a&gt; that I bought for &lt;span class=&quot;money&quot; data-amount=&quot;1079&quot;&gt;AU$1079&lt;&#x2F;span&gt;
 two years ago. My use-case for a laptop is mostly for tinkering in the
evening and for work when travelling. Normally for work I use a desktop
computer. The main tasks I perform on my laptop are programming, web browsing,
YouTube, and other technical pursuits.&lt;&#x2F;p&gt;
&lt;p&gt;I normally use Linux for all my computing. I bought this laptop knowing that it
would not yet run Linux (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@ProjectFarm&quot;&gt;Project Farm&lt;&#x2F;a&gt; voice: &lt;em&gt;we’re going to test that!&lt;&#x2F;em&gt;) and
that I’d have to use Windows for a period of time. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.qualcomm.com&#x2F;developer&#x2F;blog&#x2F;2024&#x2F;05&#x2F;upstreaming-linux-kernel-support-for-the-snapdragon-x-elite&quot;&gt;Qualcomm has been
upstreaming support to the Linux kernel&lt;&#x2F;a&gt;, but it’s still
ongoing. It is my intention to use Linux as the primary OS on this machine as
soon as it’s viable.&lt;&#x2F;p&gt;
&lt;p&gt;I have not used Windows for development since around 2007, and I’ll admit I’ve
never really been a fan. In this review I’ve &lt;em&gt;tried&lt;&#x2F;em&gt; to stick to details
specific to the experience of using this machine and omit the many complaints I
have with Windows.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hardware-specifications&quot;&gt;Hardware &amp;amp; Specifications&lt;&#x2F;h3&gt;
&lt;p&gt;Let’s get the specifications and inevitable comparisons to MacBooks out of the
way first:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;fastfetch.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;fastfetch.53685335f12c3c37.jpg&quot; alt=&quot;Screenshot of a Windows Terminal window with the output of fastfetch.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;fastfetch output on the Yoga 7x.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CPU&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Qualcomm Snapdragon X Elite X1E-78-100 Arm CPU, 12 cores, max Turbo up to 3.4GHz, all core turbo 3.4Ghz, with fan&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;RAM&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;32Gb LPDDR5X-8448&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Storage&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;1TB SSD M.2 2242 PCIe 4.0x4 NVMe&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;GPU&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Qualcomm Adreno X integrated GPU&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Display&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;14.5&quot; 3K (2944×1840) OLED 1000nits (Peak) &#x2F; 500nits (Typical), 100% DCI-P3 with touchscreen&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Camera&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;1080p (2.0MP) + IR, with E-shutter, fixed focus&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Speakers&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;4 stereo speakers, 2 x 2W (woofers), 2 x 2W (tweeters), Dolby Atmos&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Microphone&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Quad-mic array&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Ports&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;3 x USB-C® (USB4® 40Gbps), with USB PD 3.1 and DisplayPort™ 1.4&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Wi-Fi&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Wi-Fi 7, 802.11be 2x2 Wi-Fi + Bluetooth 5.4&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Battery&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;70Wh&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Power Adapter&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;65W USB-C&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Case Material&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Aluminium&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Weight&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;1.28kg&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Qualcomm hyped up these CPUs for months before their release with frequent
comparisons to Apple’s MacBook Air. While it seems unlikely that they would woo
Mac users into switching, the point is that there are now laptops in the PC
market that can compete with Apple’s M-series laptops.&lt;&#x2F;p&gt;
&lt;p&gt;The Yoga 7x sits somewhere in between the MacBook Air and the MacBook Pro
spec-wise, while managing to be significantly cheaper than both. I configured a
15-inch MacBook Air&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; and 14-inch MacBook Pro to similar specifications as my
7x and came up with the following:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;MacBook Air 15-inch&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Apple M3 chip with 8-core CPU with four performance cores and four efficiency cores, fanless&lt;&#x2F;li&gt;
&lt;li&gt;24GB unified memory&lt;&#x2F;li&gt;
&lt;li&gt;1TB SSD storage&lt;&#x2F;li&gt;
&lt;li&gt;15.3-inch Liquid Retina display (2880×1864), 500 nits brightness&lt;&#x2F;li&gt;
&lt;li&gt;1080p FaceTime HD camera&lt;&#x2F;li&gt;
&lt;li&gt;Two Thunderbolt&#x2F;USB 4 ports&lt;&#x2F;li&gt;
&lt;li&gt;70W USB-C Power Adapter&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;span class=&quot;money&quot; data-amount=&quot;3399&quot;&gt;AU$3399&lt;&#x2F;span&gt;
&lt;p&gt;&lt;strong&gt;MacBook Pro 14-inch&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Apple M3 Pro chip with 12-core CPU with six performance cores and six efficiency cores, with fan&lt;&#x2F;li&gt;
&lt;li&gt;36GB unified memory&lt;&#x2F;li&gt;
&lt;li&gt;1TB SSD storage&lt;&#x2F;li&gt;
&lt;li&gt;14-inch Liquid Retina XDR display² with notch, 3024x1964
&lt;ul&gt;
&lt;li&gt;XDR brightness: 1,000 nits sustained full-screen, 1,600 nits peak&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.apple.com&#x2F;au&#x2F;macbook-pro&#x2F;specs&#x2F;#footnote-2&quot;&gt;2&lt;&#x2F;a&gt; (HDR content only)&lt;&#x2F;li&gt;
&lt;li&gt;SDR brightness: 600 nits&lt;&#x2F;li&gt;
&lt;li&gt;ProMotion technology for adaptive refresh rates up to 120Hz&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;96W USB-C Power Adapter&lt;&#x2F;li&gt;
&lt;li&gt;Three Thunderbolt 4 ports, HDMI port, SDXC card slot, headphone jack, MagSafe 3 port&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;span class=&quot;money&quot; data-amount=&quot;4799&quot;&gt;AU$4799&lt;&#x2F;span&gt;
&lt;p&gt;The Air comes out &lt;span class=&quot;money&quot; data-amount=&quot;932&quot;&gt;AU$932&lt;&#x2F;span&gt;
 more than the Yoga 7x, and the Pro is
whopping &lt;span class=&quot;money&quot; data-amount=&quot;2332&quot;&gt;AU$2332&lt;&#x2F;span&gt;
 more (almost enough to buy a second Yoga 7x),
but it is admittedly more full-featured.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;usage&quot;&gt;Usage&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;case-construction&quot;&gt;Case Construction&lt;&#x2F;h4&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;yoga-7x-top.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;yoga-7x-top.3306848905b93a4f.jpg&quot; alt=&quot;Photo of the top of the laptop showing the Lenovo detail in the middle.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Top of laptop, back to the reader.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The laptop is dressed in a lovely blue-grey colour that Lenovo call Cosmic
Blue. The surface&#x2F;colour of the case is a bit of fingerprint magnet but cleans
up easily with a micro-fibre cloth. The build quality and construction is
top-notch. The case is made of aluminium and is rigid and nicely finished. The
edges are rounded (not sharp) and pleasant to touch&#x2F;be in contact with.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;display&quot;&gt;Display&lt;&#x2F;h4&gt;
&lt;p&gt;The display is amazing! It’s bright, with vivid colours and true blacks owing
to the OLED technology. The bezels around the display are narrow and the entire
display is usable as there is no notch. It came set to 60Hz by default but
supports 90Hz as well. After changing it to 90Hz I didn’t initially notice much
difference. However, after changing back to 60Hz I can see that scrolling in
Firefox is a lot smoother with the 90Hz refresh rate. Windows 11 supports
dynamic refresh rate but the 7x does not appear to be compatible.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;yoga-7x-camera-bump.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;yoga-7x-camera-bump.cad2448dacece303.jpg&quot; alt=&quot;Photo of the front edge of the laptop showing the camera bump.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Camera bump.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Instead of a notch there’s a slightly taller region at the top of the screen
where the camera, and other sensors are. There is also a camera bump behind this
part of the display. I saw at least one video deriding this, but I think it’s
an excellent compromise. The bump on the top edge of the display is a nice
affordance for your fingers when opening the laptop. It also means there’s no
need for a cutout on the bottom half of the laptop like on MacBooks.&lt;&#x2F;p&gt;
&lt;p&gt;The movement of the display hinge is firm and smooth. It can be opened with one
hand without the bottom lifting. When closing the lid there is a positive feel
when the two halves meet, as though they are attracted to each other
magnetically. There is a very thin plastic surround on the top half to prevent
the display contacting the bottom half. I’ve not seen any keyboard prints on
the display.&lt;&#x2F;p&gt;
&lt;p&gt;The laptop lid is rigid and does not exhibit wobble even when using it on your
lap. The rigidity allows the touchscreen to work well too, although I’ve not
really worked out how a touchscreen fits into my workflow. I tried the
Precision Pen 2 that came with my Lenovo tablet on the screen but it didn’t
work. It seems they sell &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.lenovo.com&#x2F;au&#x2F;en&#x2F;p&#x2F;accessories-and-software&#x2F;stylus-pens-and-supplies&#x2F;pens&#x2F;4x81h95637?orgRef=https%253A%252F%252Fduckduckgo.com%252F&quot;&gt;a version specifically for laptops&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;keyboard&quot;&gt;Keyboard&lt;&#x2F;h4&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;yoga-7x-keyboard-and-stickers.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;yoga-7x-keyboard-and-stickers.f654459eb056dd14.jpg&quot; alt=&quot;Photo of the keyboard and trackpad. On the right hand palm rest there is a Snapdragon X sticker and a tall sticker from Lenovo.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Keyboard and trackpad. Yes, free of the tyranny of Intel Inside stickers what did Lenovo do? More stickers! The tall one peeled off ok, but you could see where it had been. A quick wipe with an alcohol swab sorted it out. I left the Snapdragon one on for now. I don&amp;#x27;t know what&amp;#x27;s wrong with me.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The keyboard is pleasant to type on. It has an unsurprising layout without any
weird quirks. It has a firm feel and slightly more travel than my old laptop,
which did take a moment to adjust to. It has full height left and right arrow
keys, which I find bad for positioning without looking. I would have preferred
an inverted-T layout like that on a MacBook. I also miss the dedicated Page
Up&#x2F;Down Home&#x2F;End keys from the HP Aero.&lt;&#x2F;p&gt;
&lt;p&gt;There is an Fn key next to the outermost Ctrl key on the left for accessing
function keys, as well as home, end, page up, and page down on the arrow keys.
The UEFI has a “Fool Proof Fn Ctrl” feature enabled by default that will “Treat
Fn as Ctrl when combined with non function key for some frequently-used
shortcut key”.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;trackpad&quot;&gt;Trackpad&lt;&#x2F;h4&gt;
&lt;p&gt;The trackpad is quite large—in my opinion larger than it needs to be. The
surface is pleasant to use and motion is accurate. Multitouch gestures work as
expected. It is top-pivoting with a physical button underneath like pretty much
all PC laptops. I would have preferred a haptic action like Apple’s, but I’m
used to tap-to-click at this point. I have had the odd unintentional action
from my palm or stray finger so perhaps the automatic rejection could be
better.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;battery-fan&quot;&gt;Battery &amp;amp; Fan&lt;&#x2F;h4&gt;
&lt;p&gt;The experience of using this laptop is superior to any other PC laptop I’ve
owned. It runs cool and quiet and has an amazing battery life. Battery life is
hard to quantify without time-consuming tests but Lenovo claims the following
and I think they’re probably in the right ballpark:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;MobileMark® 25@250nits: up to 16.4 hours&lt;&#x2F;li&gt;
&lt;li&gt;Local video (1080p) playback@150nits: up to 23.8 hours&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I think I easily get double the runtime out of the 7x compared to the Aero.
Depending on what you’re doing I think it would be perfectly feasible to go a
typical work day without the need to plug in.&lt;&#x2F;p&gt;
&lt;p&gt;As I write this, the battery is down to 51% and it’s reporting 7h 25m left.&lt;&#x2F;p&gt;
&lt;p&gt;While almost any activity beyond the basic would spur the fan on my HP Aero
into action the fan on the Yoga 7x pretty much only comes on when you’re doing
something multicore intensive like compiling software. From what I’ve gathered
it does turn on more frequently than a MacBook Pro, which I’ve heard need
pretty strong motivation to get the fan going. I’d rate the fan pitch when it
is running at higher speeds in the middle of the road—it’s not bad, but it’s
not a neutral white noise type sound either.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;audio-visual&quot;&gt;Audio&#x2F;Visual&lt;&#x2F;h4&gt;
&lt;p&gt;The camera does not seem to be anything remarkable. In indoor conditions
without any direct lighting of the subject it produces an image that shows
heavy noise reduction but is fairly bright.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;camera.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;camera.f1a35d8bceba196f.jpg&quot; alt=&quot;Photo of your author taken with the built-in webcam.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Photo of the author taken with the built-in webcam.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I have not used the built-in microphones. The speakers are extremely crisp and
clear in mid and high ranges. They are capable of going very loud without
distortion. As with most laptops the low-end bass is limited but better than
some I’ve heard. I can feel very little vibration in the case even with the
volume cranked.&lt;&#x2F;p&gt;
&lt;p&gt;There is no 3.5mm headphone jack. I paired some Bluetooth Sennheiser headphones
but ran into an issue with audio being out of sync with the video when watching
YouTube. These headphones work fine with iOS, Android, and Linux, so I’m not
sure what going on with them in Windows. Some searching online suggests this is
not an uncommon issue with Windows, but I suspect they’re a noisy minority—at
least I’m hoping that’s the case.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;ports-power&quot;&gt;Ports &amp;amp; Power&lt;&#x2F;h4&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;yoga-7x-left-edge.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;yoga-7x-left-edge.c5304c4f42fec901.jpg&quot; alt=&quot;Photo of the left edge of the laptop showing the two USB-C ports.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;The left-side ports.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;On the left hand side are two of the three USB-C ports. Between them is an LED
that glows orange when charging and white when full. On the right hand side is
the other USB-C port as well as a power button and camera privacy switch. The
power button is a thin phone-style unit on the edge of the body. It has a small
white LED the glows when the laptop is on and pulses slowly while it is
sleeping.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;yoga-7x-right-edge.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;yoga-7x-right-edge.c678be748c893f3f.jpg&quot; alt=&quot;Photo of the left edge of the laptop showing the two USB-C ports.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;The right-side ports.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Pressing the power button will sleep&#x2F;wake the laptop. You must dwell with the
button pressed for just a moment to have it work, which helps prevent
accidental presses. Although, it is natural to brace the right side where the
button is when plugging in something to the left, which can result in the power
button being pressed if you aren’t careful.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;yoga-7x-camera-switch.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;yoga-7x-camera-switch.e8abbb617b1738b3.jpg&quot; alt=&quot;Photo of the laptop upside-down showing the camera privacy switch.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;The camera privacy switch. This sticker was easy to remove with the little red pull tab.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;There is no fingerprint reader, instead Windows Hello uses the camera and IR
sensor to use your face to authenticate. The utility of the camera privacy
switch is somewhat diminished because it also prevents Windows Hello from
working. If there was a separate fingerprint reader the camera could remain
disabled unless it was specifically needed.&lt;&#x2F;p&gt;
&lt;p&gt;Waking from suspend is nearly instant. If opening the laptop it’s usually
resumed and ready to go before you’ve finished moving the display into
position.&lt;&#x2F;p&gt;

  




&lt;figure class=&quot;text-center&quot;&gt;
  &lt;video controls preload=&quot;metadata&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;wake-from-sleep.m4v&quot; poster=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;wake-from-sleep.m4v.jpg&quot; loop=&quot;loop&quot; style=&quot;max-height: 450px&quot; aria-label=&quot;Video showing the laptop waking from sleep quickly. Windows Hello uses the camera to grant access without having to type in a password.&quot;&gt;&lt;&#x2F;video&gt;
  &lt;figcaption&gt;Video showing the laptop waking from sleep. Windows Hello uses the camera to grant access without having to type in a password.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;compatibility-gaming&quot;&gt;Compatibility &amp;amp; Gaming&lt;&#x2F;h3&gt;
&lt;p&gt;The big question with a new architecture&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-2-1&quot;&gt;&lt;a href=&quot;#fn-2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; is how much does it impact
day-to-day activities. As with most things in the tech world: it depends. For a
typical computer user the situation is quite good. Most, if not all the
software included with Windows is Arm native. I do all my browsing with Firefox
and a native Arm version of it has been &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theverge.com&#x2F;2019&#x2F;4&#x2F;11&#x2F;18305849&#x2F;mozilla-firefox-windows-arm-laptops-beta&quot;&gt;available for many
years&lt;&#x2F;a&gt;. The note-taking tool &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;obsidian.md&#x2F;&quot;&gt;Obsidian&lt;&#x2F;a&gt; has an Arm native version,
as does &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;1password.community&#x2F;discussion&#x2F;comment&#x2F;713004&quot;&gt;1Password&lt;&#x2F;a&gt; (in preview), &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jetbrains.com&#x2F;rust&#x2F;&quot;&gt;Rust Rover&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rufus.ie&#x2F;en&#x2F;&quot;&gt;Rufus&lt;&#x2F;a&gt; to name a few more.&lt;&#x2F;p&gt;
&lt;p&gt;However, there’s still plenty of software out there that assumes Windows = x86.
For that there’s the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;arm&#x2F;apps-on-arm-x86-emulation&quot;&gt;Prism x86 emulator built into Windows&lt;&#x2F;a&gt;. This
allows most x86 Windows applications to run seamlessly on Windows Arm, albeit
with some hit to performance. The emulation is often not noticeable, aside from
an initial delay when first launching an application.&lt;&#x2F;p&gt;
&lt;p&gt;I took note of the architecture of everything I installed. Of the 27 things I
installed 44% were native.&lt;&#x2F;p&gt;
&lt;p&gt;Most x86 software I tried such as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;inkscape.org&#x2F;&quot;&gt;Inkscape&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;handbrake.fr&#x2F;&quot;&gt;Handbrake&lt;&#x2F;a&gt; ran fine. It
wasn’t all perfect though. I installed &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.stardewvalley.net&#x2F;&quot;&gt;Stardew Valley&lt;&#x2F;a&gt; using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gog.com&#x2F;galaxy&quot;&gt;GOG Galaxy&lt;&#x2F;a&gt;,
which went fine but when clicking the Play button it never started. There was a
&lt;code&gt;stardewvalley.exe&lt;&#x2F;code&gt; process in Task Manager, but it never opened a window.
Curiously, if the exe is run directly from Explorer then it works fine.&lt;&#x2F;p&gt;
&lt;p&gt;I also tried &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.factorio.com&#x2F;&quot;&gt;Factorio&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.paradoxinteractive.com&#x2F;games&#x2F;cities-skylines&#x2F;about&quot;&gt;Cities Skylines&lt;&#x2F;a&gt; installed via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;&quot;&gt;Steam&lt;&#x2F;a&gt; (all x86
executables). Factorio ran great at the native resolution of the display and
did a constant 60fps, at least in the early game. With a reduced (from native)
resolution of 1600×900 Cities Skylines ran acceptably at 25–30fps in a city
with a population of a bit under 10k.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;factorio.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;factorio.100949ed11bd7d63.jpg&quot; alt=&quot;Screenshot of Factorio. The view is fairly zoomed out. There a lot of trees in the bottom left of the image and the FPS counter is showing 60 FPS.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Screenshot of Factorio taken while moving diagonally down and to the left.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;As you can probably guess I’m not much of a gamer and this is not a gaming PC,
but for older, or lighter games it does just fine under emulation. I didn’t
find a native game to try out, but I then I didn’t really try hard to find one
either.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wsl-virtualisation&quot;&gt;WSL &amp;amp; Virtualisation&lt;&#x2F;h3&gt;
&lt;p&gt;Windows is a strange beast, an outlier in a world that has mostly settled on
UNIX&#x2F;POSIX inspired systems. Being the outlier there are a plethora of ways
that Microsoft and the community have come up with to make it integrate with
the rest of the computing world. The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;wsl&#x2F;about&quot;&gt;Windows Subsystem for Linux&lt;&#x2F;a&gt; (WSL)
is one of them, and it works great on this system.&lt;&#x2F;p&gt;
&lt;p&gt;I installed &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.debian.org&#x2F;&quot;&gt;Debian GNU&#x2F;Linux&lt;&#x2F;a&gt; as well as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;&quot;&gt;Chimera Linux&lt;&#x2F;a&gt; in WSL.
Debian was installed using the built-in mechanism &lt;code&gt;wsl --install -d Debian&lt;&#x2F;code&gt;.
Chimera was installed manually by downloading the &lt;code&gt;aarch64&lt;&#x2F;code&gt; root file system,
gunzipping it and then importing it. Both distros work well. It was
particularly satisfying to &lt;code&gt;apt install x11-apps&lt;&#x2F;code&gt; in the Debian install, then
run &lt;code&gt;xeyes&lt;&#x2F;code&gt; and have it just work™.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;xeyes.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;xeyes.7e1668e04624fbef.jpg&quot; alt=&quot;Screen shot with an xeyes window above a terminal window showing apt output from installing x11-apps and running xeyes.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;xeyes running via WSL.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;WSL2 uses Hyper-V virtualisation under the covers but frustratingly creating
virtual machines manually with Hyper-V requires Windows Pro and the 7x comes
with Windows Home. Through a website of dubious legitimacy I was able to
purchase a Windows Pro key for considerably less than the &lt;span class=&quot;money&quot; data-amount=&quot;169&quot;&gt;AU$169&lt;&#x2F;span&gt;
 Microsoft was asking on the Microsoft Store for a Home to Pro upgrade.&lt;&#x2F;p&gt;
&lt;p&gt;After the updates were applied I fired up Hyper-V Manager and tried booting the
Chimera Linux &lt;code&gt;aarch64&lt;&#x2F;code&gt; ISO. The grub menu is shown promptly but after
selecting an entry it seems to hang. I also tried a Debian ISO but got the same
result. Some searching online revealed that I was &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;Surface&#x2F;comments&#x2F;1dmzpzt&#x2F;running_linux_in_hyperv_on_snapdragon_x&#x2F;&quot;&gt;not alone&lt;&#x2F;a&gt;.
I let the Debian installer go for a while. It turns out that it is running,
just at a glacial pace.&lt;&#x2F;p&gt;
&lt;p&gt;This doesn’t seem to be an issue with Windows on Arm in general. I also have
a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;arm&#x2F;dev-kit&#x2F;&quot;&gt;Windows Dev Kit 2023&lt;&#x2F;a&gt; WIth a Snapdragon 8cx Gen 3 CPU and
Hyper-V works fine on it. Hopefully this is just a bug&#x2F;early issue that will
be resolved.&lt;&#x2F;p&gt;
&lt;p&gt;It’s also worth noting that Hyper-V is the only option for Windows on Arm.
VMWare and Virtual Box only work on x86 systems.&lt;&#x2F;p&gt;
&lt;p&gt;In desperation, I managed to get &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.qemu.org&#x2F;&quot;&gt;QEMU&lt;&#x2F;a&gt; running in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.msys2.org&#x2F;&quot;&gt;Msys2&lt;&#x2F;a&gt; environment.
However, while the &lt;code&gt;qemu-system-aarch64&lt;&#x2F;code&gt; binary is native it is emulating a
system and there is no acceleration available. For lightweight systems such as
Chimera Linux this works, but it’s not ideal.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;development&quot;&gt;Development&lt;&#x2F;h3&gt;
&lt;p&gt;The development experience on this laptop is a bit of a mixed bag, which I will
detail below.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;rust&quot;&gt;Rust&lt;&#x2F;h4&gt;
&lt;p&gt;Pretty much all my personal projects are implemented in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; and for the most
part it works great. The one gotcha is that when using the MSVC toolchain on
Windows for Arm the &lt;code&gt;ring&lt;&#x2F;code&gt; crate requires that &lt;code&gt;clang&lt;&#x2F;code&gt; is installed. This is
straightforward to achieve with the Visual Studio Installer, but it’s not quite
the just works experience you get on x86 Windows. &lt;code&gt;rustup&lt;&#x2F;code&gt; is an x86 binary but
the toolchain it installs is a native &lt;code&gt;aarch64&lt;&#x2F;code&gt; one.&lt;&#x2F;p&gt;
&lt;p&gt;Windows compatible Rust projects that don’t have C&#x2F;C++ dependencies tend to
build and run fine. I worked on a few my projects without issue and also
published some releases to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;&quot;&gt;crates.io&lt;&#x2F;a&gt;. It is more
challenging if C&#x2F;C++ dependencies are involved. There’s multiple ways to
approach them such as &lt;code&gt;vcpkg&lt;&#x2F;code&gt; but I have so far avoided the issue. If I run
into a project that has tricky dependencies I think I’ll use WSL.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;python&quot;&gt;Python&lt;&#x2F;h4&gt;
&lt;p&gt;I tried to install &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pyinfra.com&#x2F;&quot;&gt;pyinfra&lt;&#x2F;a&gt;, which as the name suggests is implemented in
Python. Running &lt;code&gt;python&lt;&#x2F;code&gt; in a PowerShell session opens up the Windows Store to
the Python page if it’s not already installed. Initially I installed this
version, but I noticed that it had installed the x86 version. Searching with
&lt;code&gt;winget&lt;&#x2F;code&gt; revealed a native version, so I uninstalled the Store version and
reinstalled via &lt;code&gt;winget&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I then tried to install &lt;code&gt;pyinfa&lt;&#x2F;code&gt; into a venv and was met with failure.
Pre-built wheels were not available for some packages, so it was attempting to
build from source. I had Rust installed for my other work but &lt;code&gt;cryptography&lt;&#x2F;code&gt;
failed because it couldn’t find OpenSSL. I installed it via &lt;code&gt;vcpkg&lt;&#x2F;code&gt; but it
still complained about not being able to find OpenSSL despite my attempts to
point it at it. &lt;code&gt;pynacl&lt;&#x2F;code&gt; also failed to build because it couldn’t find &lt;code&gt;make&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There has been &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pyca&#x2F;pynacl&#x2F;issues&#x2F;775&quot;&gt;an issue open on the &lt;code&gt;pynacl&lt;&#x2F;code&gt; repo&lt;&#x2F;a&gt; since 2022
asking for Arm support. An issue requesting Arm Windows support for
&lt;code&gt;cryptography&lt;&#x2F;code&gt; was previously closed with the note:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;We won’t ship a wheel for a platform we can’t test in CI and GitHub does not
currently offer arm64 windows runners. When they do we’ll revisit this
though!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Since &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.blog&#x2F;2024-06-03-arm64-on-github-actions-powering-faster-more-efficient-build-systems&#x2F;&quot;&gt;GitHub have announced Arm Linux and Windows runners&lt;&#x2F;a&gt; I
opened a new issue in &lt;code&gt;cryptography&lt;&#x2F;code&gt; asking for Arm Windows support. I didn’t
notice that the Arm runners are currently only available to GitHub Enterprise
and Team plans though. So the &lt;code&gt;cryptography&lt;&#x2F;code&gt; folks are still waiting for
general availability before they are willing to tackle the issue.&lt;&#x2F;p&gt;
&lt;p&gt;It’s pretty strange that Microsoft own GitHub and are making this push for
developers to support Windows on Arm but still haven’t made Arm GitHub Actions
runners available to the wider open-source community.&lt;&#x2F;p&gt;
&lt;p&gt;At this point I suspected that the Microsoft Store may have been on to
something when it installed the x86 version of Python. I uninstalled Python
once again and replaced it with the Store version. This time &lt;code&gt;pyinfa&lt;&#x2F;code&gt; installed
and ran fine. Sadly it was at this point that I discovered that Hyper-V was
broken as described earlier, so my &lt;code&gt;pyinfra&lt;&#x2F;code&gt; experiments had to move to my
Linux system.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;node-js&quot;&gt;Node.js&lt;&#x2F;h4&gt;
&lt;p&gt;I hear JavaScript is pretty popular these days. I didn’t have a particular need
to run &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nodejs.org&#x2F;&quot;&gt;Node.js&lt;&#x2F;a&gt; but wanted to try it out on this system. I installed a native
Node.js via &lt;code&gt;winget&lt;&#x2F;code&gt; and tried to build a couple of projects. Both failed to
build due to a change that was made to address &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nodejs.org&#x2F;en&#x2F;blog&#x2F;vulnerability&#x2F;april-2024-security-releases-2&quot;&gt;a security issue on Windows in
April 2024&lt;&#x2F;a&gt;—The joys of being on the odd-one-out OS. I then
thought I’d try building the TypeScript compiler—another Microsoft project.
This was immediately blocked by the lack of a native binary for the &lt;code&gt;dprint&lt;&#x2F;code&gt;
package:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Error: Cannot find module ‘@dprint&#x2F;win32-arm64&#x2F;package.json’&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Again pretty wild that an extremely popular Microsoft project still doesn’t
build on Windows for Arm despite the platform being years old at this point.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;dprint&lt;&#x2F;code&gt; is actually a Rust project, and in this case &lt;code&gt;npm&lt;&#x2F;code&gt; is used to install
it. Unlike the Python packages the &lt;code&gt;dprint&lt;&#x2F;code&gt; npm package didn’t try to build a
binary when a pre-built one was unavailable. I opened an issue on the repo
suggesting that an Arm Windows binary be published.&lt;&#x2F;p&gt;
&lt;p&gt;The maintainer was amazingly responsive and had a fix released the following
day. They encountered issues building a native binary, but a script was changed
to recognise &lt;code&gt;win32-arm64&lt;&#x2F;code&gt; and install the pre-built x86 binary, which worked.&lt;&#x2F;p&gt;
&lt;p&gt;After opening the issue on the &lt;code&gt;dprint&lt;&#x2F;code&gt; repo I was having flashbacks to my
Python experience, so I uninstalled node and used the installer on the Node.js
website to install the x86 version. Perhaps unsurprisingly this worked fine,
and I was able to build the TypeScript compiler.&lt;&#x2F;p&gt;
&lt;p&gt;The takeaway from this and the Python experience (and likely Ruby too) is that
these ecosystems are not ready for Windows on Arm yet. Unless your project and
its dependencies have no dependencies on native binaries&#x2F;libraries&#x2F;extensions
then you’re you’re better off using the x86 version for now.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;c-c-c&quot;&gt;C&#x2F;C++&#x2F;C#&lt;&#x2F;h4&gt;
&lt;p&gt;Yes one of these is not like the others, but they all start with C, so together
they go! I built a couple of C# projects that I came across on GitHub without
drama in Visual Studio Community Edition.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt; The following section is mostly me complaining about Windows and is
not specific to Windows on Arm. Feel free to skip.&lt;&#x2F;p&gt;
&lt;p&gt;Most open-source projects I come across implemented in C or C++ use Makefiles,
autotools, cmake, or meson to build. This isn’t specific to Windows on Arm but
as far as I can tell you’re more or less sweet out of luck when it comes to
Makefiles and autotools on Windows—you pretty much have to use a third-party
toolchain like Msys2 to build these projects. &lt;code&gt;camke&lt;&#x2F;code&gt; and &lt;code&gt;meson&lt;&#x2F;code&gt; projects
might work…&lt;&#x2F;p&gt;
&lt;p&gt;I did some searching for a &lt;code&gt;make&lt;&#x2F;code&gt; implementation that would run natively, by
that I mean in PowerShell and not some other environment like Msys2. I did find
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sourceforge.net&#x2F;projects&#x2F;ezwinports&#x2F;&quot;&gt;ezwinports&lt;&#x2F;a&gt;, which seems to be a
heroic effort by a single person, Eli Zaretskii to port various UNIX tools to
Windows. I looked into installing the GNU &lt;code&gt;make&lt;&#x2F;code&gt; port but there was this note
about installing &lt;code&gt;libgcc&lt;&#x2F;code&gt; and &lt;code&gt;libstdc++&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Warning: all the ports produced since the year 2021 onwards depend on
the libgcc DLL, and some depend on libstdc++-6.dll, which are not
provided in the zip files.  For the reasons, see below.  If you don’t
have these DLLs on your system, you can download them from this site:&lt;&#x2F;p&gt;
&lt;p&gt;https:&#x2F;&#x2F;osdn.net&#x2F;projects&#x2F;mingw&#x2F;releases&lt;&#x2F;p&gt;
&lt;p&gt;Specifically, download and install these two archives:&lt;&#x2F;p&gt;
&lt;p&gt;https:&#x2F;&#x2F;osdn.net&#x2F;projects&#x2F;mingw&#x2F;downloads&#x2F;72215&#x2F;libgcc-9.2.0-3-mingw32-dll-1.tar.xz&#x2F;
https:&#x2F;&#x2F;osdn.net&#x2F;projects&#x2F;mingw&#x2F;downloads&#x2F;72210&#x2F;libstdc%2B%2B-9.2.0-3-mingw32-dll-6.tar.xz&#x2F;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I have no idea where these are supposed to go and wasn’t really in the mood for
going down this path, so I gave up and concluded if I ran into projects that
needed &lt;code&gt;make&lt;&#x2F;code&gt; or autotools I’d just use Linux (via WSL).&lt;&#x2F;p&gt;
&lt;p&gt;I did revisit the topic about a week later though as I wanted to test an
extremely basic Makefile in one of my projects. I found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tqdm&#x2F;py-make&quot;&gt;pymake&lt;&#x2F;a&gt;, which seems
to have been created specifically to improve the &lt;code&gt;make&lt;&#x2F;code&gt; experience on Windows.
I was able to &lt;code&gt;pip install py-make&lt;&#x2F;code&gt; and do what I needed to do in my project.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update 26 Jul 2024:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aus.social&#x2F;@voltagex&#x2F;112832696768580115&quot;&gt;Adam on Mastodon pointed out&lt;&#x2F;a&gt; that GNU make
has a &lt;code&gt;bat&lt;&#x2F;code&gt; file for building with MSVC on Windows. I tried this and it built
successfully without the need to install any other dependencies. It did however
build an x86 binary and not an Arm one.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;End rant&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I tried to find a nice little C or C++ project to test with that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Used cmake or meson&lt;&#x2F;li&gt;
&lt;li&gt;Was Windows compatible&lt;&#x2F;li&gt;
&lt;li&gt;Didn’t have extra dependencies to deal with&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Those were hard to find. Eventually I settled on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;janet-lang.org&#x2F;&quot;&gt;Janet&lt;&#x2F;a&gt; scripting
language, which built quickly and easily once I worked out what a Visual Studio
Command Prompt was:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;D:\Source\janet&amp;gt;janet.exe&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Janet 1.35.2-local windows&#x2F;aarch64&#x2F;msvc - &amp;#39;(doc)&amp;#39; for help&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;repl:1:&amp;gt; (print &amp;quot;Hello from Janet&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Hello from Janet&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;repl:2:&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;performance&quot;&gt;Performance&lt;&#x2F;h3&gt;
&lt;p&gt;From feel alone the laptop performs very well. Actions are snappy and
responsive. Web-browsing even on heavy websites does not bog down.&lt;&#x2F;p&gt;
&lt;p&gt;First things first, these are the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;browser.geekbench.com&#x2F;v6&#x2F;cpu&#x2F;6896181&quot;&gt;GeekBench scores I got on the Yoga 7x&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;2,457 Single-Core, 13,088 Multi-Core&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For reference, my other systems:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;2,966 Single-Core, 20,174 Multi-Core - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;browser.geekbench.com&#x2F;v6&#x2F;cpu&#x2F;3355715&quot;&gt;Desktop Linux system with AMD Ryzen 7950X&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;1,938 Single-Core, 6,348 Multi-Core - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;browser.geekbench.com&#x2F;v6&#x2F;cpu&#x2F;6906110&quot;&gt;HP Aero Laptop&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I wanted to find a relatively easy to reproduce “real-world” benchmark to
include in this post so that other people can run the same benchmark to get an
idea of how this system performs comparatively. I settled on the time to build
the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gleam.run&#x2F;&quot;&gt;Gleam&lt;&#x2F;a&gt; programming language tooling. If you want to play along at home
this is what you need to do (assuming you have Rust installed):&lt;&#x2F;p&gt;
&lt;p&gt;Windows:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;gleam-lang&#x2F;gleam&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cd gleam&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git checkout v1.2.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cargo fetch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cargo clean; Measure-Command { cargo build --release --locked }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Not Windows:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;gleam-lang&#x2F;gleam&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cd gleam&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git checkout v1.2.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cargo fetch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cargo clean &amp;amp;&amp;amp; time cargo build --release --locked&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Some friends also graciously ran the test for me too. These are the results I
collected. In each case the last line was run multiple times and I selected the
fastest run, rounded to the nearest second.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Device&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;CPU&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Topology&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Arch&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;OS&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;rustc&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Time&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;img class=&quot;os-logo&quot; src=&quot;tux.svg&quot; width=&quot;24&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Yoga 7x&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;X Elite&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;12c&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;ARM&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Debian 12 (WSL)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.79.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;0:52&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img class=&quot;os-logo&quot; src=&quot;windows.svg&quot; width=&quot;24&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Yoga 7x&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;X Elite&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;12c&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;ARM&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Windows 11&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.79.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;0:60&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img class=&quot;os-logo&quot; src=&quot;openbsd.svg&quot; width=&quot;24&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Yoga 7x&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;X Elite&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;12c&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;ARM&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;OpenBSD -current&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.79.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;4:04&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt; &lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img class=&quot;os-logo&quot; src=&quot;tux.svg&quot; width=&quot;24&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Desktop&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.amd.com&#x2F;en&#x2F;products&#x2F;processors&#x2F;desktops&#x2F;ryzen&#x2F;7000-series&#x2F;amd-ryzen-9-7950x.html&quot;&gt;Ryzen 9 7950X&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;16c&#x2F;32t&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;x86&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Arch Linux&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.79.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;0:23&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img class=&quot;os-logo&quot; src=&quot;apple.svg&quot; width=&quot;24&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;MacBook Pro&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;M3 Max&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;12p&#x2F;4e&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;ARM&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;macOS 14.5&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.79.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;0:34&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img class=&quot;os-logo&quot; src=&quot;tux.svg&quot; width=&quot;24&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Desktop&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.amd.com&#x2F;en&#x2F;products&#x2F;processors&#x2F;desktops&#x2F;ryzen&#x2F;9000-series&#x2F;amd-ryzen-9-9900x.html&quot;&gt;Ryzen 9 3900X&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;12c&#x2F;24t&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;x86&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Arch Linux&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.77.1&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;0:44&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img class=&quot;os-logo&quot; src=&quot;tux.svg&quot; width=&quot;24&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Dell XPS 15&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ark.intel.com&#x2F;content&#x2F;www&#x2F;us&#x2F;en&#x2F;ark&#x2F;products&#x2F;232128&#x2F;intel-core-i7-13700h-processor-24m-cache-up-to-5-00-ghz.html&quot;&gt;i7-13700H&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;6p&#x2F;8e*&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;x86&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Ubuntu 22.04 (WSL)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.79.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;0:55&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img class=&quot;os-logo&quot; src=&quot;apple.svg&quot; width=&quot;24&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;MacBook Pro&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;M1 Pro&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;8p&#x2F;2e&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;ARM&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;macOS 13.6.7&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.76.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1:02&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img class=&quot;os-logo&quot; src=&quot;tux.svg&quot; width=&quot;24&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;support.hp.com&#x2F;lv-en&#x2F;document&#x2F;c08303941&quot;&gt;HP Aero&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.amd.com&#x2F;en&#x2F;support&#x2F;downloads&#x2F;drivers.html&#x2F;processors&#x2F;ryzen&#x2F;ryzen-5000-series&#x2F;amd-ryzen-7-5800u.html#amd_support_product_spec&quot;&gt;Ryzen 7 5800U&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;8c&#x2F;16t&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;x86&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Arch Linux&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.79.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1:09&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img class=&quot;os-logo&quot; src=&quot;windows.svg&quot; width=&quot;24&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;support.hp.com&#x2F;lv-en&#x2F;document&#x2F;c08303941&quot;&gt;HP Aero&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.amd.com&#x2F;en&#x2F;support&#x2F;downloads&#x2F;drivers.html&#x2F;processors&#x2F;ryzen&#x2F;ryzen-5000-series&#x2F;amd-ryzen-7-5800u.html#amd_support_product_spec&quot;&gt;Ryzen 7 5800U&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;8c&#x2F;16t&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;x86&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Windows 11&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.79.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;2:03&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img class=&quot;os-logo&quot; src=&quot;windows.svg&quot; width=&quot;24&quot;&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;arm&#x2F;dev-kit&#x2F;&quot;&gt;Dev Kit 2023&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.qualcomm.com&#x2F;products&#x2F;mobile&#x2F;snapdragon&#x2F;pcs-and-tablets&#x2F;snapdragon-mobile-compute-platforms&#x2F;snapdragon-8cx-gen-3-compute-platform&quot;&gt;8cx Gen3&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;8c&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;ARM&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Windows 11&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.79.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;2:39&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;small&gt;
&lt;p&gt;&lt;strong&gt;Key:&lt;&#x2F;strong&gt; c = core, t = thread, p = performance core, e = efficiency core&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Notes:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;As part of collecting these results I have noticed that the build
time is heavily influenced by the memory allocator and possibly libc used by
&lt;code&gt;rustc&lt;&#x2F;code&gt;. On the exact same system with different Linux&#x2F;allocator combinations
I’ve seen times ranging from 24s to 1m55s, so as with all benchmarks they may
be complete rubbish.&lt;&#x2F;li&gt;
&lt;li&gt;The tests I performed on the Yoga 7x were all in the Balanced power
mode. I did try the Best Performance mode and it was about 3 seconds faster.
However, it got hotter and the fan ran for longer after the build was finished.
Since part of my motivation for wanting an Arm system is cooler, quieter
computing I’m going to stick with the Balanced setting.&lt;&#x2F;li&gt;
&lt;li&gt;For Windows testing the code was checked out onto a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blogs.windows.com&#x2F;windowsdeveloper&#x2F;2023&#x2F;06&#x2F;01&#x2F;dev-drive-performance-security-and-control-for-developers&#x2F;&quot;&gt;Dev Drive&lt;&#x2F;a&gt; volume.&lt;&#x2F;li&gt;
&lt;li&gt;* This machine was configured to use 14 cores in WSL and was not using
a Dev Drive.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;small&gt;
&lt;p&gt;Overall I think the Snapdragon X Elite does very well. It’s in between an M1
Pro and an M3 Max, it handily beat my outgoing laptop (comparing Windows
times), and it’s not far off a much more power hungry desktop AMD CPU from a
few years ago.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;copilot-ai&quot;&gt;Copilot &amp;amp; AI&lt;&#x2F;h3&gt;
&lt;p&gt;The Yoga 7x is a so-called Copilot+PC because the SoC includes a neural
processing unit (NPU) capable of more than 40 trillion Int8 operations per
second (TOPS)—Qualcomm claim up to 45 TOPS. I have very little interest in
these Copilot features. Although, I was honestly curious to try out Windows
Recall after they announced the improvements to it. Alas, they canned it for
the initial release and probably for good reason.&lt;&#x2F;p&gt;
&lt;p&gt;There is a dedicated Copilot key on the keyboard as mandated by Microsoft.
Pressing it opens an app with a chat interface a-la ChatGPT. It turns out the
Copilot “app” is just an Edge “App”, an app-like shortcut for opening a
website, in this case &lt;code&gt;copilot.microsoft.com&lt;&#x2F;code&gt;. As a result it can be a bit
janky and is completely dependent on an internet connection to render the UI
and respond to prompts.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;copilot-offline.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;copilot-offline.png&quot; width=&quot;630&quot; alt=&quot;Screenshot of the Copilot app showing &amp;#x27;You&amp;#x27;re offline&amp;#x27; and no other UI.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Copilot &amp;#x27;app&amp;#x27; when you&amp;#x27;re offline.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I did use it a few times when seeking answers for how to do something I know
how to do on Linux in Windows. The responses were generally helpful. One detail
I particularly like is that the response is marked up with numbered links
intended to provide a source for the information. Belying its Edge
underpinnings clicking these links opens in Edge instead of the default
browser.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;copilot-response.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;copilot-response.ba09a5bb538c4d8d.jpg&quot; alt=&quot;Screenshot of the Copilot app with a response to a query about PowerShell. The response includes several numbered footnotes, each of which is a link.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Copilot response with links to sources.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;non-windows-operating-systems&quot;&gt;Non-Windows Operating Systems&lt;&#x2F;h3&gt;
&lt;p&gt;As I mentioned at the start of this post I intend to run Linux on the 7x as
soon as that is viable. Qualcomm has been upstreaming support to the Linux
kernel for some time and looking over the Linux kernel mailing list there is a
bunch more being proposed for Linux 6.11.&lt;&#x2F;p&gt;
&lt;p&gt;Just to be sure Linux was definitely not functional yet I tried booting a
Chimera Linux and Ubuntu-daily ISO. Perhaps worth noting that the 7x runs good
old UEFI and after turning off Secure Boot loaded up grub off these Linux
install disks just fine. However, after selecting an entry in grub it would say
loading Linux then promptly reboot… definitely not working yet.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update Dec 2024:&lt;&#x2F;strong&gt; There’s still some rough edges, but Linux now works.
See
&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;linux-on-yoga-7x-snapdragon&#x2F;&quot;&gt;my post on running Ubuntu&lt;&#x2F;a&gt;
for more details.&lt;&#x2F;p&gt;
&lt;p&gt;All was not lost though. The keen eyed among you may have noticed the OpenBSD
entry in the benchmark table. Yep OpenBSD runs on it right now. After resizing
the Windows partition I was able to install a recent snapshot of OpenBSD
-current.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;openbsd-fvwm.png&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;openbsd-fvwm.a8539848316c7ba5.jpg&quot; alt=&quot;Screenshot of OpenBSD running fvwm. There are Firefox and Alacritty windows. The Firefox window is showing the OpenBSD homepage. The Alacritty window is showing the output of neofetch.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;OpenBSD running on the Yoga 7x.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Now it’s all still pretty bleeding edge and I can’t say the experience is
particularly good at this point. It runs pretty hot with the fan going most of
the time. The built-in Wi-Fi didn’t work, so I had to use a USB Wi-FI dongle.
There is no GPU acceleration and I couldn’t work out how to get fvwm to honor
the X DPI settings so everything was tiny, at least Firefox honored it.
Complaints aside this is proof, only a month or so after release that
alternative OSes are pretty easy to run on these systems.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h3&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;yoga-7x-snapdragon-developer-review&amp;#x2F;yoga-7x-yoga-detail.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;yoga-7x-yoga-detail.a982a13abdfac7ed.jpg&quot; alt=&quot;Photo of the laptop at and angle showing the YOGA detail on the right-side palm rest.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;YOGA&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Overall I’m very happy with the Yoga 7x. It has mostly met my expectations for
a device this early in its release cycle. It is not without its quirks, bugs,
and compromises, but I have a pretty high threshold for those things. Those
compromises mean that computing on the PC Arm platform will not be for
everyone, but I’m glad it’s now an option for the non-Mac users.&lt;&#x2F;p&gt;
&lt;p&gt;It’s also worth noting that the X1E-78-100 CPU in the 7x is the bottom of
the X Elite range, there’s two others with higher clock speeds announced
by Qualcomm that should make for pretty nice machines too.&lt;&#x2F;p&gt;
&lt;p&gt;If there’s anything about the Snapdragon X experience that I didn’t cover feel
free to get in contact and I’ll do my best to answer any questions.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;comments&quot;&gt;Comments&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lobste.rs&#x2F;s&#x2F;p0hx8s&#x2F;developer_s_review_snapdragon_x_laptop&quot;&gt;Lobsters&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=40971564&quot;&gt;Hacker News&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;old.reddit.com&#x2F;r&#x2F;Lenovo&#x2F;comments&#x2F;1e47g09&#x2F;a_developers_review_of_a_snapdragon_x_laptop&#x2F;&quot;&gt;Reddit&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;credits&quot;&gt;Credits&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.x-rates.com&#x2F;historical&#x2F;?from=AUD&amp;amp;amount=1&amp;amp;date=2024-05-29&quot;&gt;Foreign exchange rates&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;commons.wikimedia.org&#x2F;wiki&#x2F;File:Apple_logo_dark_grey.svg&quot;&gt;Apple logo&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;commons.wikimedia.org&#x2F;wiki&#x2F;File:Windows_logo_-_2021.svg&quot;&gt;Windows logo&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;commons.wikimedia.org&#x2F;wiki&#x2F;File:OpenBSD_Logo.svg&quot;&gt;OpenBSD mascot&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;commons.wikimedia.org&#x2F;wiki&#x2F;File:Tux-shaded.svg&quot;&gt;Tux&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;The 15-inch is the closest match in display resolution. &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;While Windows on Arm is not new the X Elite CPUs are the first ones to be widely adopted by PC manufacturers. &lt;a href=&quot;#fr-2-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</description>
    </item>
    
    <item>
      <title>Why Chimera Linux</title>
      <pubDate>Thu, 04 Jul 2024 08:48:55 +1000</pubDate>
      <atom:published>2024-07-04T08:48:55+10:00</atom:published>
      <atom:updated>2024-07-26T09:58:10+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;why-chimera-linux&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;why-chimera-linux&#x2F;</guid>
      <description>&lt;p&gt;I received a reply to my &lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;tech-stack&#x2F;&quot;&gt;Tech Stack 2024&lt;&#x2F;a&gt;
post asking: Why Chimera Linux? I wrote a response that turned out longer than
anticipated and figured I may as well post it here too. I’m not trying to
convince you to use Chimera with this post, just note down why it appeals to
me. That’s really the crux of it: there’s dozens of distros out there all with
different goals and values and Chimera really speaks to me, for you it might be
something else.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Below is a lightly edited version of my email response.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;I like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;&quot;&gt;Chimera Linux&lt;&#x2F;a&gt; because it’s the closest distro I’ve found to what I would build
if I was building my own (something I’ve tinkered with a few times over the
years).&lt;&#x2F;p&gt;
&lt;p&gt;I like that it is a comparatively small and easy to understand system without
giving up quite as much as you do with Alpine Linux, which to be clear, I like
as well and use on my server. The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chimera-linux&#x2F;chimerautils&quot;&gt;userland from FreeBSD&lt;&#x2F;a&gt; is capable and easy
to understand code wise, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;davmac.org&#x2F;projects&#x2F;dinit&#x2F;&quot;&gt;Dinit&lt;&#x2F;a&gt; provides an init system with process monitoring,
dependency tracking, and a service file format that doesn’t require writing
shell scripts like in FreeBSD and Alpine. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;musl.libc.org&#x2F;&quot;&gt;musl&lt;&#x2F;a&gt; is designed to be secure and
uses quite straightforward implementations of libc functions while sticking
closely to the POSIX standard. See the recent OpenSSH vulnerability where
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fosstodon.org&#x2F;@musl&#x2F;112711796005712271&quot;&gt;remote code execution was not possible on musl based systems&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I really like the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chimera-linux&#x2F;cports&#x2F;blob&#x2F;master&#x2F;Usage.md&quot;&gt;cbuild&lt;&#x2F;a&gt; system for building packages. It uses a real
programming language (Python) to define packages and share library code. This
makes package templates easier to write and understand over Make and shell
based packaging systems. Packages are built in an isolated sandbox, preventing
them depending on the host system accidentally—this is definitely an advantage
over building packages on Arch. Most run-time dependencies are automatically
determined so you don’t have to list all those out in the package template.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.alpinelinux.org&#x2F;alpine&#x2F;apk-tools&quot;&gt;apk&lt;&#x2F;a&gt; is fast (although not as fast as Pacman when doing updates&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;). It has a
clever way of tracking packages where &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;docs&#x2F;apk&#x2F;world&quot;&gt;the world file&lt;&#x2F;a&gt; specifies all the packages
that should be present and it uses a solver to determine what needs to be
installed&#x2F;removed. The neat bit is that when you &lt;code&gt;apk del&lt;&#x2F;code&gt; a package it can remove all
packages that are no longer specifically requested, whereas in Arch it’s easy
to end up with orphaned packages that are dead weight and require &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Pacman&#x2F;Tips_and_tricks#Removing_unused_packages_(orphans)&quot;&gt;manual
maintenance to clean up&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Chimera is also a rolling distro (like Arch) so things stay up to date. There’s
a low barrier to submitting new packages and updates, you don’t have to have a
special “committer” or “developer” account, you can just open a pull request
like any other open-source project. Packages in the repo are built
automatically with a build bot server for all supported architectures, whereas
I believe Arch is still working towards automated packaging. Additionally first
class support for multiple CPU architectures allows me to run the same system
on different devices I use such as Raspberry Pis, RISC-V single board
computers, and hopefully eventually
&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;yoga-7x-snapdragon-developer-review&#x2F;&quot;&gt;my new ARM based Snapdragon X Elite laptop&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Having written that all out I guess Chimera feels like a distro that is
full-featured but also simple enough that you can poke around and understand
all the parts. It’s also easy to get involved with the project.&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;Pacman downloads in parallel (5 at a time in my config), then does the actual upgrades. &lt;code&gt;apk&lt;&#x2F;code&gt; fetches one package at at time and then stages the upgrade before moving on to the next one. There’s a couple of related open issues:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.alpinelinux.org&#x2F;alpine&#x2F;apk-tools&#x2F;-&#x2F;issues&#x2F;10963&quot;&gt;https:&#x2F;&#x2F;gitlab.alpinelinux.org&#x2F;alpine&#x2F;apk-tools&#x2F;-&#x2F;issues&#x2F;10963&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.alpinelinux.org&#x2F;alpine&#x2F;apk-tools&#x2F;-&#x2F;issues&#x2F;5977&quot;&gt;https:&#x2F;&#x2F;gitlab.alpinelinux.org&#x2F;alpine&#x2F;apk-tools&#x2F;-&#x2F;issues&#x2F;5977&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
 &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</description>
    </item>
    
    <item>
      <title>How Much Is a Browser Worth?</title>
      <pubDate>Wed, 03 Jul 2024 17:22:01 +1000</pubDate>
      <atom:published>2024-07-03T17:22:01+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;how-much-is-a-browser-worth&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;how-much-is-a-browser-worth&#x2F;</guid>
      <description>&lt;p&gt;Apparently people are excited about funding independent browser efforts this
week. I have little interest in funding yet another browser built in C++ in
2024 but &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;servo.org&#x2F;&quot;&gt;Servo&lt;&#x2F;a&gt; is still alive. Since Mozilla refuse to let us directly
fund Firefox I shall set up a recurring donation to Servo.&lt;&#x2F;p&gt;
&lt;p&gt;The next question is how much is a web browser worth to me? Based on minutes
spent using a browser, quite a lot!&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Once upon a time you could pay for Netscape. It seems that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;19961115062838&#x2F;http:&#x2F;&#x2F;merchant.netscape.com&#x2F;netstore&#x2F;NAVIGATORS&#x2F;STANDARD&#x2F;STANDARD_ITEMS&#x2F;leaf&#x2F;product1.html&quot;&gt;in 1996 Netscape
3.0 cost US$49&lt;&#x2F;a&gt;. Adjusting for inflation that’s US$97 in 2024. Let’s
round that up to an even US$100. At the time of writing &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;numbat.dev&#x2F;&quot;&gt;Numbat&lt;&#x2F;a&gt; tells me
that’s AU$150 or AU$12.50 per month, which seems reasonable.&lt;&#x2F;p&gt;
&lt;p&gt;Some other services for comparison (I don’t actually have an active YouTube
Premium subscription):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Netflix: AU$18.99&#x2F;m&lt;&#x2F;li&gt;
&lt;li&gt;YouTube Premium: AU$16.99&#x2F;m&lt;&#x2F;li&gt;
&lt;li&gt;JetBrains Rust Rover: US$129&#x2F;y (AU$194, AU$16&#x2F;m)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A browser is a least as useful as these, almost certainly more. So, I’ve set up
a US$15&#x2F;m (AU$22.50) recurring sponsorship on GitHub. I encourage you to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;servo&quot;&gt;do
the same&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Tech Stack 2024</title>
      <pubDate>Sat, 01 Jun 2024 10:13:48 +1000</pubDate>
      <atom:published>2024-06-01T10:13:48+10:00</atom:published>
      <atom:updated>2024-06-04T07:49:36+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;tech-stack&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;tech-stack&#x2F;</guid>
      <description>&lt;p&gt;Inspired by Alex Chan’s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.alexchantastic.com&#x2F;tools-of-the-trade&quot;&gt;Tools of the trade&lt;&#x2F;a&gt; post I thought I’d note
down my current tech stack and then revisit it in a few years to see how things
evolve. As per Alex’s post I’ll break it down into three sections: software,
(development) tech stack, and hardware.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;tech-stack&amp;#x2F;desktop.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;desktop.867444be07aa6e80.jpg&quot; alt=&quot;A photo of my desk. There&amp;#x27;s two displays, the one on the right is rotated into a portait orientation, the left on is on a wooden monitor stand. In front of the monitors are: a PS4 controller, TI-89, tenkeyless mechanical keyboard, mouse, and Kobo e-Reader.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;My desk. The computer is behind the displays.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h3 id=&quot;software&quot;&gt;Software&lt;&#x2F;h3&gt;
&lt;p&gt;My dotfiles are public so if you’re curious about the configuration of some
of the tools mentioned below check out &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;dotfiles&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;dotfiles&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;linux&quot;&gt;Linux&lt;&#x2F;h4&gt;
&lt;p&gt;I use Linux for everything. On my desktop machine, which I use for work as well
as personal computing I run &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;archlinux.org&#x2F;&quot;&gt;Arch Linux&lt;&#x2F;a&gt;. On my laptop, which is more of an
ancillary device that I use for tinkering and occasional travel I run &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;&quot;&gt;Chimera
Linux&lt;&#x2F;a&gt;. My goal is to run Chimera on the desktop too (it actually dual boots
already) but I need to get my work development environment working on it before
I can do that. My desktop is my primary computer, the rest of the post will
focus on that.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;awesome&quot;&gt;Awesome&lt;&#x2F;h4&gt;
&lt;p&gt;I’ve been using the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;awesomewm.org&#x2F;&quot;&gt;Awesome window manager&lt;&#x2F;a&gt; since 2019. It’s
deceptively good. When I try out other desktop environments it’s surprising how
many little details that Awesome gets right. It’s also very stable, meaning I
don’t get surprise updates to my UI in new releases.&lt;&#x2F;p&gt;
&lt;p&gt;However the writing is on the wall for X11. My current arrangement of two 4K
24&quot; displays running at 2x scaling with one of them rotated to a portrait
orientation is really pushing the limits of X11. It’s my goal to switch to a
Wayland based system at some point in the future. Most likely System 76’s
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.system76.com&#x2F;tags&#x2F;COSMIC%20DE&quot;&gt;COSMIC desktop&lt;&#x2F;a&gt;, which I’ve been periodically testing during its
development.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;alacritty&quot;&gt;Alacritty&lt;&#x2F;h4&gt;
&lt;p&gt;Somewhere around 75% of the windows I have open at any time are terminal
windows. I like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alacritty.org&#x2F;&quot;&gt;Alacritty&lt;&#x2F;a&gt; because it’s devoid of UI chrome and features like
tabs. Awesome takes care of all window organisation so there is no need for
tabs, splits and things like that. I also like that it’s responsive to use.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;z-shell&quot;&gt;Z Shell&lt;&#x2F;h4&gt;
&lt;p&gt;I’ve used the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.zsh.org&#x2F;&quot;&gt;Z shell&lt;&#x2F;a&gt; since at least 2008. I’ve tried out some of the
newer shells but quickly run into limitations or things I miss from Z shell.
Some things I like about it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Extensive, full-featured completion support&lt;&#x2F;li&gt;
&lt;li&gt;Spelling correction&#x2F;did you mean&lt;&#x2F;li&gt;
&lt;li&gt;Synced history: history across all shells is added to history after every
command and available in all running shells.&lt;&#x2F;li&gt;
&lt;li&gt;Path expansion: &lt;code&gt;ls &#x2F;h&#x2F;w&#x2F;P&#x2F;rss&amp;lt;Tab&amp;gt;&lt;&#x2F;code&gt; → &lt;code&gt;ls &#x2F;home&#x2F;wmoore&#x2F;Projects&#x2F;rsspls&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Command stack (&lt;code&gt;Esc-q&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;espanso&quot;&gt;Espanso&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;espanso.org&#x2F;&quot;&gt;Espanso&lt;&#x2F;a&gt; is a text expander. It allows me to do things like type &lt;code&gt;;mdl&lt;&#x2F;code&gt; and
have that be replaced with a Markdown link using the contents of the clipboard
(E.g. &lt;code&gt;[](&amp;lt;clipboard url&amp;gt;)&lt;&#x2F;code&gt;), or &lt;code&gt;;em&lt;&#x2F;code&gt; and have that be replaced with my email
address.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;cli&quot;&gt;CLI&lt;&#x2F;h4&gt;
&lt;p&gt;I use a bunch of CLI tools, aside from standard POSIX&#x2F;UNIX tools I use these a lot:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;bat&quot;&gt;bat&lt;&#x2F;a&gt; — &lt;code&gt;cat&lt;&#x2F;code&gt; with syntax highlighting&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;fd&quot;&gt;fd&lt;&#x2F;a&gt; — file finder&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;junegunn&#x2F;fzf&quot;&gt;fzf&lt;&#x2F;a&gt; — fuzzy finder (integrated with zsh and vim)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lsd-rs&#x2F;lsd&quot;&gt;lsd&lt;&#x2F;a&gt; — &lt;code&gt;ls&lt;&#x2F;code&gt; but better&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;ripgrep&quot;&gt;ripgrep&lt;&#x2F;a&gt; — clever regex search&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonas&#x2F;tig&quot;&gt;tig&lt;&#x2F;a&gt; — git TUI&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Morganamilo&#x2F;paru&quot;&gt;paru&lt;&#x2F;a&gt; — &lt;code&gt;pacman&lt;&#x2F;code&gt; wrapper&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;obsidian&quot;&gt;Obsidian&lt;&#x2F;h4&gt;
&lt;p&gt;I note down ideas, thoughts, howtos, and things in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;obsidian.md&#x2F;&quot;&gt;Obsidian&lt;&#x2F;a&gt;. I picked it
because the notes store is just plain Markdown files and they sync between
Linux and iOS.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;zfs&quot;&gt;ZFS&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;code&gt;&#x2F;home&lt;&#x2F;code&gt; on my system is a mirror of two 2Tb NVMe drives. I use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;openzfs.org&#x2F;&quot;&gt;ZFS&lt;&#x2F;a&gt; for its
data integrity (resistance to bit rot), redundancy (I can lose a drive an not
lose data), compression (&lt;code&gt;compressratio&lt;&#x2F;code&gt; is 1.40x at the time of writing), and
lightweight snapshots.&lt;&#x2F;p&gt;
&lt;p&gt;I use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;zfs-autosnap&quot;&gt;zfs-autosnap&lt;&#x2F;a&gt; to periodically take snapshots of &lt;code&gt;&#x2F;home&lt;&#x2F;code&gt;, which allows me
roll back files if I get them into an undesirable state.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;text-editing&quot;&gt;Text Editing&lt;&#x2F;h4&gt;
&lt;p&gt;I’m currently in a bit of transitional period with text editors:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jetbrains.com&#x2F;rust&#x2F;&quot;&gt;Rust Rover&lt;&#x2F;a&gt; for Rust&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;zed.dev&#x2F;&quot;&gt;Zed&lt;&#x2F;a&gt; migration in progress for Rust&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;neovim.io&#x2F;&quot;&gt;Neovim&lt;&#x2F;a&gt; for everything else, it’s hard to beat&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I have started using Zed for Rust development. Ideally it would be my primary
editor but it doesn’t support enough languages yet. I imagine I will shift more
tasks to it as it continues to improve.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;1password&quot;&gt;1Password&lt;&#x2F;h4&gt;
&lt;p&gt;I returned to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;1password.com&#x2F;&quot;&gt;1Password&lt;&#x2F;a&gt; when they released the Linux version. Not a lot say
here, it’s reliable and works across all the devices I use.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;firefox&quot;&gt;Firefox&lt;&#x2F;h4&gt;
&lt;p&gt;Like everyone I use a web browser a lot. I use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;getfirefox.com&#x2F;&quot;&gt;Firefox&lt;&#x2F;a&gt; as it’s fast, works well,
is mostly user focused, has built-in ad blocking, and is not a made by Google.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;support.mozilla.org&#x2F;en-US&#x2F;kb&#x2F;containers&quot;&gt;Multi-account containers&lt;&#x2F;a&gt; are a killer Firefox feature
that allows me to segregate things like Google and Facebook off into their own
little container. This separates them so that for my usual browsing I’m not
logged into a Google or Facebook account so it’s harder for them to associate my
activities across the web and on first-party sites (like Google Maps) with my
account.&lt;&#x2F;p&gt;
&lt;p&gt;I’ve read some people say that they can’t use Firefox because too many websites
don’t work with it but I don’t run into this problem at all with the sites I
visit.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;stylus&quot;&gt;Stylus&lt;&#x2F;h5&gt;
&lt;p&gt;I don’t use a lot of browser extensions but &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;openstyles&#x2F;stylus&quot;&gt;Stylus&lt;&#x2F;a&gt; is a great one. It lets me
apply custom styles to websites (or all sites). This enables me to fix poor
font choices or hide that annoying Sign-In With Google across all websites.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;shotwell&quot;&gt;Shotwell&lt;&#x2F;h4&gt;
&lt;p&gt;I recently got a new DSLR and did some research to find a decent
non-destructive photo editor and library manager. I settled on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.gnome.org&#x2F;Apps&#x2F;Shotwell&quot;&gt;Shotwell&lt;&#x2F;a&gt; and
while I still dearly miss &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20150407001931&#x2F;http:&#x2F;&#x2F;www.apple.com&#x2F;aperture&#x2F;&quot;&gt;Aperture&lt;&#x2F;a&gt;, Shotwell gets the job done.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;keyd&quot;&gt;keyd&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rvaiya&#x2F;keyd&quot;&gt;keyd&lt;&#x2F;a&gt; lets me customise and remap keys on my keyboard at the system level so
that it works everywhere. I previously relied solely on programmable mechanical
keyboards for this functionality but I’ve now been able to use a board that
does not have a customisable firmware with the help of &lt;code&gt;keyd&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;copyq&quot;&gt;CopyQ&lt;&#x2F;h4&gt;
&lt;p&gt;Clipboard history is an essential part of any desktop computing environment and
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hluk.github.io&#x2F;CopyQ&#x2F;&quot;&gt;CopyQ&lt;&#x2F;a&gt; is what provides it for me.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dev-stack&quot;&gt;Dev Stack&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;rust&quot;&gt;Rust&lt;&#x2F;h4&gt;
&lt;p&gt;Pretty much all software I write these days in done with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt;. I also use a
lot of software written in Rust as it tends to be efficient with resources,
reliable, and easy for me to delve into the code if needed.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;mold&quot;&gt;mold&lt;&#x2F;h4&gt;
&lt;p&gt;Linking is slow, I do a lot of linking working on personal and work projects.
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rui314&#x2F;mold&quot;&gt;Mold&lt;&#x2F;a&gt; makes this faster.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;rocket&quot;&gt;Rocket&lt;&#x2F;h4&gt;
&lt;p&gt;For building web applications my go-to is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rocket.rs&#x2F;&quot;&gt;Rocket&lt;&#x2F;a&gt;. It’s not quite as batteries
included as something like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rubyonrails.org&#x2F;&quot;&gt;Rails&lt;&#x2F;a&gt; but it includes a lot of stuff that’s
missing from other Rust options.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;zola&quot;&gt;Zola&lt;&#x2F;h4&gt;
&lt;p&gt;For blogs and simple static sites I love &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt;. It’s full-featured, customisable,
and super fast.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;gleam&quot;&gt;Gleam&lt;&#x2F;h4&gt;
&lt;p&gt;On occasions I need to write code targeting JavaScript. When that is non-trivial
I’ve been reaching for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gleam.run&#x2F;&quot;&gt;Gleam&lt;&#x2F;a&gt;. It’s kinda of like Elm but actively maintained
and with fewer restrictions.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hardware&quot;&gt;Hardware&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;desktop-computer&quot;&gt;Desktop Computer&lt;&#x2F;h4&gt;
&lt;p&gt;My computer is a desktop machine that I assembled myself in 2023:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;AMD Ryzen 9 7950X 16-Core&#x2F;32-Thread Processor&lt;&#x2F;li&gt;
&lt;li&gt;64 Gb DDR5 6000 MT&#x2F;s RAM&lt;&#x2F;li&gt;
&lt;li&gt;Storage:
&lt;ul&gt;
&lt;li&gt;Root disk: Crucial T700 1TB PCIe Gen5 NVMe M.2 SSD&lt;&#x2F;li&gt;
&lt;li&gt;&#x2F;home: 2x WD_BLACK 2TB SN850X PCIe Gen4 NVMe in ZFS mirror&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;AMD Radeon RX 6700 XT GPU with 12Gb VRAM&lt;&#x2F;li&gt;
&lt;li&gt;2x Dell 24&quot; 4K displays, one in portrait orientation&lt;&#x2F;li&gt;
&lt;li&gt;Razer Kiyo web cam&lt;&#x2F;li&gt;
&lt;li&gt;Audio Technica ATR2100X microphone&lt;&#x2F;li&gt;
&lt;li&gt;Logi Lift vertical mouse + BenQ ZOWIE FK2-C mouse&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.keebmonkey.com&#x2F;en-au&#x2F;products&#x2F;wk870?syclid=ck4d58iusvis73cshbug&quot;&gt;WK870 mechanical keyboard&lt;&#x2F;a&gt; with Gateron G Pro 2.0 brown switches&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;All of this is packed into a monstrous &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.fractal-design.com&#x2F;products&#x2F;cases&#x2F;torrent&#x2F;&quot;&gt;Fractal Design Torrent case&lt;&#x2F;a&gt;
with a lot of slow, quiet fans to try to keep all the hot things under control.
This build was optimised for my development activities (both work and personal)
with occasional gaming. For future comparisons it does a clean &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.princexml.com&#x2F;&quot;&gt;Prince&lt;&#x2F;a&gt; build&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;
in 1m50s and scores 2966&#x2F;20174 in Geekbench 6.&lt;&#x2F;p&gt;
&lt;p&gt;In early 2024 I started having trouble with pain in my hands from mousing. For
nearly 20 odd years I’ve alternated mouse hands to load balance the wear on my
hands. However this was an issue in both. That led me to try a vertical mouse,
which didn’t initially help. I then tried a wrist rest but it was super hard
and the pain remained. I got a different softer one and now the vertical mouse is
comfortable and my hands have stopped complaining.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;laptop&quot;&gt;Laptop&lt;&#x2F;h4&gt;
&lt;p&gt;My laptop is a 13“ HP Pavilion Aero Laptop 13-be0203AU I bought it because it was
cheap (~AU$1000), had an 8 core AMD CPU, and was lightweight (~1kg). The case
construction is not great as parts of it are painted plastic, which has dings and
scratches on it now. In contrast to my old 2013 MacBook Pro, which still looks
new.&lt;&#x2F;p&gt;
&lt;p&gt;I’ve pre-ordered a Lenovo Yoga Slim 7x (14“, Gen 9) Snapdragon to replace this
machine. It’s got one of the new Snapdragon X Elite ARM CPUs in it. I expect
I’ll have to make do with Windows &amp;amp; WSL until Linux support for this new
hardware catches up.&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;&lt;code&gt;time .&#x2F;bin&#x2F;build prince&lt;&#x2F;code&gt; with &lt;code&gt;mold&lt;&#x2F;code&gt; as linker. &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</description>
    </item>
    
    <item>
      <title>Exporting YouTube Subscriptions to OPML and Watching via RSS</title>
      <pubDate>Mon, 06 May 2024 10:38:22 +1000</pubDate>
      <atom:published>2024-05-06T10:38:22+10:00</atom:published>
      <atom:updated>2024-06-06T08:24:45+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;youtube-subscriptions-opml&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;youtube-subscriptions-opml&#x2F;</guid>
      <description>&lt;p&gt;This post describes how I exported my 500+ YouTube subscriptions to an OPML
file so that I could import them into my RSS reader. I go into fine detail
about the scripts and tools I used. If you just want to see the end result the
code is in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;youtube-to-opml&quot;&gt;this repository&lt;&#x2F;a&gt;, which describes the steps needed to run it.&lt;&#x2F;p&gt;
&lt;p&gt;I was previously a YouTube Premium subscriber but I cancelled it when they
jacked up the already high prices. Since then I’ve been watching videos in
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;newpipe.net&#x2F;&quot;&gt;NewPipe&lt;&#x2F;a&gt; on my Android tablet or via an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;invidious.io&#x2F;&quot;&gt;Invidious&lt;&#x2F;a&gt; instance on real
computers.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;To import my subscriptions into NewPipe I was able to use the
&lt;code&gt;subscriptions.csv&lt;&#x2F;code&gt; file included in the Google Takeout dump of my YouTube
data. This worked fine initially but imposed some friction when adding new
subscriptions.&lt;&#x2F;p&gt;
&lt;p&gt;If I only subscribed to new channels in NewPipe they were only
accessible on my tablet. If I added them to YouTube then I had to remember to
also add them in NewPipe, which was inconvenient if I wasn’t using the tablet
at the time. Inevitably the subscriptions would drift out of sync and I would have to
periodically re-import the subscriptions from YouTube into NewPipe. This was
cumbersome as it doesn’t seem to have a way to do this incrementally. Last time
I had to nuke all its data in order to re-import.&lt;&#x2F;p&gt;
&lt;p&gt;To solve these problems I wanted to manage my subscriptions in my RSS reader,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;feedbin.com&#x2F;&quot;&gt;Feedbin&lt;&#x2F;a&gt;. This way Feedbin would track my subscriptions and new&#x2F;viewed videos
in a way that would sync between all my devices. Notably this is possible
because Google actually publishes an RSS feed for each YouTube channel.&lt;&#x2F;p&gt;
&lt;p&gt;To do that I needed to export all my subscriptions to an OPML file that Feedbin
could import. I opted to do that without requesting another Google Takeout dump
as they take a long time to generate and also result in multiple gigabytes of
archives I have to download (it includes all the videos I’ve uploaded to my
personal account) just to get at the &lt;code&gt;subscriptions.csv&lt;&#x2F;code&gt; file within.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generating-opml&quot;&gt;Generating OPML&lt;&#x2F;h3&gt;
&lt;p&gt;I started
by visiting my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;feed&#x2F;channels&quot;&gt;subscriptions page&lt;&#x2F;a&gt; and using some JavaScript to
generate a JSON array of all the channels I am subscribed to:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;copy&lt;&#x2F;span&gt;&lt;span&gt;(JSON&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;(Array&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Set&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Array&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;prototype&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;map&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;call&lt;&#x2F;span&gt;&lt;span&gt;(document&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;querySelectorAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;a.channel-link&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, (&lt;&#x2F;span&gt;&lt;span&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; link&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;href)))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt; !&lt;&#x2F;span&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;channel&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; null&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This snippet:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;queries the page for all channel links&lt;&#x2F;li&gt;
&lt;li&gt;gets the link URL of each matching element&lt;&#x2F;li&gt;
&lt;li&gt;Creates a &lt;code&gt;Set&lt;&#x2F;code&gt; from them to de-duplicate them&lt;&#x2F;li&gt;
&lt;li&gt;Turns the set back into an &lt;code&gt;Array&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;filters out ones that contain &lt;code&gt;&#x2F;channel&#x2F;&lt;&#x2F;code&gt; to exclude some links like Trending
that also appear on that page&lt;&#x2F;li&gt;
&lt;li&gt;Turns the Array into pretty printed JSON&lt;&#x2F;li&gt;
&lt;li&gt;Copies it to the clipboard&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With the list of channel URLs on my clipboard I pasted this into a
&lt;code&gt;subscriptions.json&lt;&#x2F;code&gt; file. The challenge now was that these URLs were of the
channel pages like:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;https:&#x2F;&#x2F;www.youtube.com&#x2F;@mooretech&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;but the RSS URL of a channel is like:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;https:&#x2F;&#x2F;www.youtube.com&#x2F;feeds&#x2F;videos.xml?channel_id=&amp;lt;CHANNEL_ID&amp;gt;&lt;&#x2F;code&gt;,&lt;&#x2F;p&gt;
&lt;p&gt;which means I needed to determine the channel id for each page. To do that
without futzing around with Google API keys and APIs I needed to download the
HTML of each channel page.&lt;&#x2F;p&gt;
&lt;p&gt;First I generated a config file for &lt;code&gt;curl&lt;&#x2F;code&gt; from the JSON file:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;jaq --raw-output &amp;#39;.[] | (split(&amp;quot;&#x2F;&amp;quot;) | last) as $name | &amp;quot;url \(.)\noutput \($name).html&amp;quot;&amp;#39; subscriptions.json &amp;gt; subscriptions.curl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;01mf02&#x2F;jaq&quot;&gt;jaq&lt;&#x2F;a&gt; is an alternative implementation of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jqlang.github.io&#x2F;jq&#x2F;&quot;&gt;jq&lt;&#x2F;a&gt; that I use. This &lt;code&gt;jaq&lt;&#x2F;code&gt; expression does the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.[]&lt;&#x2F;code&gt; iterate over each element of the &lt;code&gt;subscriptions.json&lt;&#x2F;code&gt; array.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;(split(&quot;&#x2F;&quot;) | last) as $$name&lt;&#x2F;code&gt; split the URL on &lt;code&gt;&#x2F;&lt;&#x2F;code&gt; and take the last element, storing it in a variable called &lt;code&gt;$name&lt;&#x2F;code&gt;.
&lt;ul&gt;
&lt;li&gt;for a URL like &lt;code&gt;https:&#x2F;&#x2F;www.youtube.com&#x2F;@mooretech&lt;&#x2F;code&gt; this stores &lt;code&gt;@mooretech&lt;&#x2F;code&gt; in &lt;code&gt;$name&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&quot;url \(.)\noutput \($$name).html&quot;&lt;&#x2F;code&gt; generates the output text interpolating the channel page url and channel name.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This results in lines like this for each entry in &lt;code&gt;subscriptions.json&lt;&#x2F;code&gt;, output
to &lt;code&gt;subscriptions.curl&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;url https:&#x2F;&#x2F;www.youtube.com&#x2F;@mooretech&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;output @mooretech.html&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I then ran &lt;code&gt;curl&lt;&#x2F;code&gt; against this file to download all the pages:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;curl --location --output-dir html --create-dirs --rate 1&#x2F;s --config subscriptions.curl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--location&lt;&#x2F;code&gt; tells curl to follow redirects, for some reason three of my subscriptions redirected to alternate names when accessed.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--output-dir&lt;&#x2F;code&gt; tells curl to output the files into the &lt;code&gt;html&lt;&#x2F;code&gt; directory.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--create-dirs&lt;&#x2F;code&gt; tells curl to create output directories if they don’t exist (just the &lt;code&gt;html&lt;&#x2F;code&gt; one in this case).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--rate 1&#x2F;s&lt;&#x2F;code&gt; tells curl to only download at a rate of 1 page per second—I was concerned YouTube might block me if I requested the pages too quickly.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--config subscriptions.curl&lt;&#x2F;code&gt; tells curl to read additional command line arguments from the &lt;code&gt;subscriptions.curl&lt;&#x2F;code&gt; file generated above.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Now that I had the HTML for each channel I needed to extract the channel id
from it. While I was processing each HTML file I also extracted the channel
title for use later. For each HTML file I ran this script on it. I called the
script &lt;code&gt;generate-json-opml&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;#!&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;set&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; -eu&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;URL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;NAME&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;echo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$URL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; awk&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; -F &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;{ print $NF }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;HTML&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;html&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;NAME&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.html&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;CHANNEL_ID&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;scraper&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; -a content&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;meta[property=&amp;quot;og:url&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$HTML&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; awk&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; -F &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;{ print $NF }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;TITLE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;scraper&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; -a content&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;meta[property=&amp;quot;og:title&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$HTML&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;XML_URL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;www.youtube.com&#x2F;feeds&#x2F;videos.xml?channel_id=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;CHANNEL_ID&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;json_escape&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;  echo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; jaq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; --raw-input .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;JSON_TITLE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;json_escape&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$TITLE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;JSON_XML_URL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;json_escape&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$XML_URL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;JSON_URL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;json_escape&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$URL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;printf&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;{&amp;quot;title&amp;quot;: %s, &amp;quot;xmlUrl&amp;quot;: %s, &amp;quot;htmlUrl&amp;quot;: %s}\n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$JSON_TITLE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$JSON_XML_URL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$JSON_URL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; json&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$NAME&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let’s break that down:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The channel URL is stored in &lt;code&gt;URL&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;The channel name is determined by using &lt;code&gt;awk&lt;&#x2F;code&gt; to split the URL on &lt;code&gt;&#x2F;&lt;&#x2F;code&gt; and take the last element.&lt;&#x2F;li&gt;
&lt;li&gt;The path to the downloaded HTML page is stored in &lt;code&gt;HTML&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;The channel id is determined by finding the &lt;code&gt;&amp;lt;meta&amp;gt;&lt;&#x2F;code&gt; tag in the html with a &lt;code&gt;property&lt;&#x2F;code&gt; attribute of &lt;code&gt;og:url&lt;&#x2F;code&gt; (the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ogp.me&#x2F;&quot;&gt;OpenGraph metadata&lt;&#x2F;a&gt; URL property). This URL is again split on &lt;code&gt;&#x2F;&lt;&#x2F;code&gt; and the last element stored in &lt;code&gt;CHANNEL_ID&lt;&#x2F;code&gt;.
&lt;ul&gt;
&lt;li&gt;Querying the HTML is done with a tool called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;causal-agent&#x2F;scraper&quot;&gt;scraper&lt;&#x2F;a&gt; that allows you to use CSS selectors to extract parts of a HTML document.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;The channel title is done similarly by extracting the value of the &lt;code&gt;og:title&lt;&#x2F;code&gt; metadata.&lt;&#x2F;li&gt;
&lt;li&gt;The URL of the RSS feed for the channel is stored in &lt;code&gt;XML_URL&lt;&#x2F;code&gt; using &lt;code&gt;CHANNEL_ID&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;A function to escape strings destined for JSON is defined. This makes use of &lt;code&gt;jaq&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;TITLE&lt;&#x2F;code&gt;, &lt;code&gt;XML_URL&lt;&#x2F;code&gt;, and &lt;code&gt;URL&lt;&#x2F;code&gt; are escaped.&lt;&#x2F;li&gt;
&lt;li&gt;Finally we generate a JSON object with the title, URL, and RSS URL and write it into a &lt;code&gt;json&lt;&#x2F;code&gt; directory under the name of the channel.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aus.social&#x2F;@popcorncx&#x2F;112392881683597817&quot;&gt;Stephen pointed out on Mastodon&lt;&#x2F;a&gt; that the HTML contains the usual
&lt;code&gt;&amp;lt;link rel=&quot;alternate&quot;&lt;&#x2F;code&gt; tag for RSS auto-discovery. I did check for that initially but
I think the Firefox dev tools where having a bad time with the large size of the YouTube
pages and didn’t show me any matches at the time. Anyway, that could have been used to
find the feed URL directly instead of building it from the &lt;code&gt;og:url&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ok, almost there. That script had to be run for each of the channel URLs.
First I generated a file with just a plain text list of the channel URLs:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;jaq --raw-output &amp;#39;.[]&amp;#39; subscriptions.json &amp;gt; subscriptions.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then I used &lt;code&gt;xargs&lt;&#x2F;code&gt; to process them in parallel:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;xargs -n1 --max-procs=$(nproc) --arg-file subscriptions.txt --verbose .&#x2F;generate-json-opml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This does the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-n1&lt;&#x2F;code&gt; read one line from &lt;code&gt;subscriptions.txt&lt;&#x2F;code&gt; to be passed as the argument to &lt;code&gt;generate-json-opml&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--max-procs=$(nproc)&lt;&#x2F;code&gt; run up the number of cores my machine has in parallel.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--arg-file subscriptions.txt&lt;&#x2F;code&gt; read arguments for &lt;code&gt;generate-json-opml&lt;&#x2F;code&gt; from &lt;code&gt;subscriptions.txt&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--verbose&lt;&#x2F;code&gt; show the commands being run.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.&#x2F;generate-json-opml&lt;&#x2F;code&gt; the command to run (this is the script above).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Finally all those JSON files need to be turned into an OPML file. For this I
used Python:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; email&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;utils&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; glob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; xml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;etree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;ElementTree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; ET&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;opml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;Element&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;opml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;head&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;SubElement&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;opml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;head&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;title&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;SubElement&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;head&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;title&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;text&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;YouTube Subscriptions&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dateCreated&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;SubElement&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;head&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;dateCreated&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dateCreated&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;text&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; email&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;utils&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;formatdate&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;timeval&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=None&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; localtime&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=True&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;SubElement&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;opml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;youtube&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;SubElement&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;outline&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, {&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;YouTube&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;YouTube&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; path&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; glob&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;glob&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;json&#x2F;*.json&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    with&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        info&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; json&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;load&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;SubElement&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;youtube&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;outline&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;span&gt; info&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;rss&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;span&gt; text&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;info&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;indent&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;opml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;tostring&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;opml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; encoding&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;unicode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;span&gt; xml_declaration&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=True&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This generates an OPML file (which is XML) using the ElementTree library. The
OPML file has this structure:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #5E81AC;&quot;&gt;xml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; version&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; encoding&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;utf-8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;opml&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;head&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    &amp;lt;title&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;YouTube Subscriptions&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;title&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    &amp;lt;dateCreated&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Sun, 05 May 2024 15:57:23 +1000&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;dateCreated&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;&#x2F;head&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;body&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    &amp;lt;outline&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; title&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;YouTube&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; text&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;YouTube&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      &amp;lt;outline&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; title&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;MooreTech&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; xmlUrl&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;www.youtube.com&#x2F;feeds&#x2F;videos.xml?channel_id=UCLi0H57HGGpAdCkVOb_ykVg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; htmlUrl&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;www.youtube.com&#x2F;@mooretech&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;rss&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; text&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;MooreTech&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    &amp;lt;&#x2F;outline&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;&#x2F;body&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;opml&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It does the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Generates the top level OPML structure.&lt;&#x2F;li&gt;
&lt;li&gt;For each JSON file, read and parse the JSON and then use that to generate an &lt;code&gt;outline&lt;&#x2F;code&gt; entry for that channel.&lt;&#x2F;li&gt;
&lt;li&gt;Indent the OPML document.&lt;&#x2F;li&gt;
&lt;li&gt;Write it to stdout using a Unicode encoding with an XML declaration (&lt;code&gt;&amp;lt;?xml version=&#x27;1.0&#x27; encoding=&#x27;utf-8&#x27;?&amp;gt;&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Whew that was a lot! With the OPML file generated I was finally able to import
all my subscriptions into Feedbin.&lt;&#x2F;p&gt;
&lt;p&gt;All the code is available in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;youtube-to-opml&quot;&gt;this
repository&lt;&#x2F;a&gt;. In practice I used a
&lt;code&gt;Makefile&lt;&#x2F;code&gt; to run the various commands so that I didn’t have to remember them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;watching-videos-from-feedbin&quot;&gt;Watching videos from Feedbin&lt;&#x2F;h3&gt;
&lt;p&gt;Now that Feedbin is the source of truth for subscriptions, how do I actually
watch them? I set up the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;play.google.com&#x2F;store&#x2F;apps&#x2F;details?id=com.seazon.feedme&quot;&gt;FeedMe&lt;&#x2F;a&gt; app on my Android tablet. In the settings I
enabled the NewPipe integration and set it to open the video page when tapped:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;youtube-subscriptions-opml&amp;#x2F;feedme-settings.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;youtube-subscriptions-opml&amp;#x2F;feedme-settings.png&quot; alt=&quot;Screenshot of the FeedMe integration settings. There are lots of apps listed. The entry for NewPipe is turned on.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Screenshot of the FeedMe integration settings&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Now when viewing an item in FeedMe there is a NewPipe button that I can tap to
watch it:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;youtube-subscriptions-opml&amp;#x2F;feedme.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;youtube-subscriptions-opml&amp;#x2F;feedme.png&quot; alt=&quot;Screenshot of FeedMe viewing a video item. In the top left there is a NewPipe button, which when tapped opens the video in NewPipe.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Screenshot of FeedMe viewing a video item&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;closing-thoughts-future-work&quot;&gt;Closing Thoughts &amp;amp; Future Work&lt;&#x2F;h3&gt;
&lt;p&gt;Could I have done all the processing to generate the OPML file with a single
Python file? Yes, but I rarely write Python so I preferred to just cobble
things together from tools I already knew.&lt;&#x2F;p&gt;
&lt;p&gt;Should I ever become a YouTube Premium subscriber again I can continue to
use this workflow and watch the videos from the YouTube embeds that
Feedbin generates, or open the item in the YouTube app instead of NewPipe.&lt;&#x2F;p&gt;
&lt;p&gt;At some point I’d like to work out how to get Feedbin to filter out YouTube
Shorts. It has the ability to automatically filter items matching any of the
supported &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;feedbin.com&#x2F;help&#x2F;search-syntax&#x2F;&quot;&gt;search syntax&lt;&#x2F;a&gt; but I’m not sure if Shorts are
easily identifiable.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update 6 June 2024:&lt;&#x2F;strong&gt; Feedbin has a &lt;code&gt;media_duration&lt;&#x2F;code&gt; search term. I was able
to use that in an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;feedbin.com&#x2F;blog&#x2F;2013&#x2F;11&#x2F;06&#x2F;actions-workflows-for-your-rss-feeds&#x2F;&quot;&gt;action&lt;&#x2F;a&gt; to filter out YouTube items less than 90 seconds long,
successfully filtering out Shorts.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;youtube-subscriptions-opml&amp;#x2F;feedbin-shorts-filter.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;youtube-subscriptions-opml&amp;#x2F;feedbin-shorts-filter.png&quot; width=&quot;532&quot; alt=&quot;Screenshot of the Feedbin settings UI. It shows a new action with name &amp;quot;Filter out YouTube Shorts&amp;quot;, the search term is &amp;quot;media_duration:&amp;lt;90&amp;quot; and Article is in Tag has &amp;quot;YouTube&amp;quot; ticked.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Shorts filter in Feedbin&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Lastly, what about desktop usage? When I’m on a real computer I read my RSS via
the Feedbin web app. It supports &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;feedbin.com&#x2F;help&#x2F;sharing-read-it-later-services&#x2F;&quot;&gt;custom sharing
integrations&lt;&#x2F;a&gt;. In order to open a video on an Invidious
instance I need to rewrite it from a URL like:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=u1wfCnRINkE&quot;&gt;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=u1wfCnRINkE&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;to one like:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;invidious.perennialte.ch&#x2F;watch?v=u1wfCnRINkE&quot;&gt;https:&#x2F;&#x2F;invidious.perennialte.ch&#x2F;watch?v=u1wfCnRINkE&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I can’t do that
directly with a Feedbin custom sharing service definition but it would be
trivial to set up a little redirector application to do it. I even published &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=d-tsfUVg4II&quot;&gt;a
video on building a very similar thing&lt;&#x2F;a&gt; last year. Alternatively
I could install a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.invidious.io&#x2F;redirector&#x2F;&quot;&gt;redirector browser
plugin&lt;&#x2F;a&gt;, although that would require set
up on each of the computers and OS installs I use so I prefer the former
option.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.decentralised.social&#x2F;@wezm&#x2F;112391817575822540&quot;&gt;Fediverse&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lobste.rs&#x2F;s&#x2F;n3dnfa&#x2F;exporting_youtube_subscriptions_opml&quot;&gt;Lobsters&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;!-- * [Hacker News](https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=36742534) --&gt;
</description>
    </item>
    
    <item>
      <title>7bit Projects: Dew Point Forecast, MacBinary, RSS Please, Titlecase</title>
      <pubDate>Sat, 04 May 2024 16:20:49 +1000</pubDate>
      <atom:published>2024-05-04T16:20:49+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;7bit-projects&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;7bit-projects&#x2F;</guid>
      <description>&lt;p&gt;Today I compiled my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;7bit.org&#x2F;titlecase&#x2F;&quot;&gt;titlecase&lt;&#x2F;a&gt; Rust crate to Web Assembly and wrapped a
web-page around it so that it can be used online. It’s published on my
“projects domain”, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;7bit.org&#x2F;&quot;&gt;7bit.org&lt;&#x2F;a&gt;. After I published it I realised I hadn’t
written about the other projects that are on &lt;code&gt;7bit.org&lt;&#x2F;code&gt;. They are
Dew Point Forecast, MacBinary, RSS Please, and Titlecase.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;A few years ago I decided that it was a bad idea to speculatively register
domains for projects because:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;I don’t finish all (*cough* most) projects&lt;&#x2F;li&gt;
&lt;li&gt;They get expensive&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;In an attempt to curb spending on domain names I searched for a short,
nondescript domain to use as a dumping ground for projects that were complete,
but not significant enough to warrant their own domain. Thus &lt;code&gt;7bit.org&lt;&#x2F;code&gt; was
born.&lt;&#x2F;p&gt;
&lt;p&gt;So far I’ve published four projects to it, all of which are open-source.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;titlecase&quot;&gt;Titlecase&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;7bit.org&#x2F;titlecase&#x2F;&quot;&gt;Titlecase&lt;&#x2F;a&gt; is one of the earliest Rust crates I wrote. The first commit was in
2017. It converts text into title case. Specifically it uses &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;daringfireball.net&#x2F;2008&#x2F;05&#x2F;title_case&quot;&gt;a style described
by John Gruber&lt;&#x2F;a&gt; for post titles on his website &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;daringfireball.net&#x2F;&quot;&gt;Daring Fireball&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Instead of simply capitalizing each word &lt;code&gt;titlecase&lt;&#x2F;code&gt; does the following
(amongst other things):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Lower case small words like an, of, or in.&lt;&#x2F;li&gt;
&lt;li&gt;Don’t capitalize words like iPhone.&lt;&#x2F;li&gt;
&lt;li&gt;Don’t interfere with file paths, URLs, domains, and email addresses.&lt;&#x2F;li&gt;
&lt;li&gt;Always capitalize the first and last words, even if they are small words
or surrounded by quotes.&lt;&#x2F;li&gt;
&lt;li&gt;Don’t interfere with terms like “Q&amp;amp;A”, or “AT&amp;amp;T”.&lt;&#x2F;li&gt;
&lt;li&gt;Capitalize small words after a colon.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I have compiled it to WebAssembly so that it can be &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;7bit.org&#x2F;titlecase&#x2F;&quot;&gt;used online&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;


  



&lt;figure class=&quot;text-center figure-border&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;7bit.org&amp;#x2F;titlecase&amp;#x2F;&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;7bit-projects&amp;#x2F;Convert%20Text%20to%20Titlecase%20-%207bit.org.png&quot; width=&quot;739&quot; alt=&quot;Screenshot of the Titlecase web page showing the result of converting the text ‘an iPhone review: &amp;quot;our take in 10 minutes&amp;quot;’ to title case with the tool. The resulting text is ‘An iPhone Review: &amp;quot;Our Take in 10 Minutes&amp;quot;’.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Screenshot of the Titlecase web page&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;rss-please&quot;&gt;RSS Please&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rsspls.7bit.org&#x2F;&quot;&gt;RSS Please&lt;&#x2F;a&gt; is a tool to generate RSS feeds from web pages. It uses
CSS selectors to extract parts of the page to generate the feed from.
It’s implemented as a command line tool in Rust.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;rsspls.7bit.org&amp;#x2F;&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;7bit-projects&amp;#x2F;RSS%20Please.png&quot; alt=&quot;Screenshot of the RSS Please website. It has a teal backgound and features a screenshot of the tool running in a terminal. There is an orange RSS logo in the top left and download button in the bottom left.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Screenshot of the RSS Please website&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;dew-point-forecast&quot;&gt;Dew Point Forecast&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dewpoint.7bit.org&#x2F;&quot;&gt;Dew Point Forecast&lt;&#x2F;a&gt; provides weather forecasts that include the dew point. The
dew point relates to how humid it feels. It’s a better measure than relative
humidity as the value does not vary with the air temperature the way the
relative humidity percentage does.&lt;&#x2F;p&gt;
&lt;p&gt;I built this one after moving to Queensland in 2021 and it was the first
project published to &lt;code&gt;7bit.org&lt;&#x2F;code&gt;. It’s implemented in Rust using the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rocket.rs&#x2F;&quot;&gt;Rocket&lt;&#x2F;a&gt;
web framework.&lt;&#x2F;p&gt;


  



&lt;figure class=&quot;text-center figure-border&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;dewpoint.7bit.org&amp;#x2F;&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;7bit-projects&amp;#x2F;Dew%20Point%20-%20Forecast%20for%20Brisbane%20City.png&quot; width=&quot;739&quot; alt=&quot;Screenshot of the dew point forecast for Brisbane, Australia. There are boxes for current conditions, Sat 4 May, Sun 5 May, and Mon 6 May. Each box contains values for temperature, dew point, sunrise, sunset, UV index, and relative humidity.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Dewpoint forecast for Brisbane&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;macbinary&quot;&gt;MacBinary&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;7bit.org&#x2F;macbinary&#x2F;&quot;&gt;MacBinary&lt;&#x2F;a&gt; allows inspection and downloading of individual components in
MacBinary encoded files that were used by the classic Mac OS. It’s available as
a Rust crate for use in third-party projects. I have compiled it to WebAssembly
for use on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;7bit.org&#x2F;macbinary&#x2F;&quot;&gt;the web page&lt;&#x2F;a&gt;. I wrote more about this project in &lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;rust-classic-mac-os-app&#x2F;#macbinary&quot;&gt;this
post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;


  



&lt;figure class=&quot;text-center figure-border&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;7bit.org&amp;#x2F;macbinary&amp;#x2F;&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;7bit-projects&amp;#x2F;Decode%20MacBinary%20Online%20-%207bit.org.png&quot; width=&quot;739&quot; alt=&quot;Screenshot of the MacBinary webpage. It is showing the parsed information from a file called &amp;#x27;bbedit_lite_612.smi_.bin&amp;#x27;. Several resources are lists such as &amp;#x27;BNDL&amp;#x27;, and &amp;#x27;CODE&amp;#x27;.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Screenshot of the MacBinary web page&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.decentralised.social&#x2F;@wezm&#x2F;112391817575822540&quot;&gt;Fediverse&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
    </item>
    
    <item>
      <title>Testing a $4 Micro SD Card From AliExpress</title>
      <pubDate>Sun, 07 Apr 2024 15:06:25 +1000</pubDate>
      <atom:published>2024-04-07T15:06:25+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;testing-aliexpress-sdcards&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;testing-aliexpress-sdcards&#x2F;</guid>
      <description>&lt;p&gt;I needed three low capacity micro SD cards for an upcoming project. There’s
plenty of these available on AliExpress but its very difficult to know if you
if the actual capacity will match the packaging. I did some research and came
across &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=efwDleEJY2w&quot;&gt;this interesting video&lt;&#x2F;a&gt; that tested 16 different cards. Their
recommendation was the Lexar ones. So I found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.aliexpress.com&#x2F;item&#x2F;1005005956657740.html&quot;&gt;some 32Gb ones for
AU$4.13&lt;&#x2F;a&gt; and placed an order&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;testing-aliexpress-sdcards&amp;#x2F;lexar-32gb-micro-sd-card.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;lexar-32gb-micro-sd-card.e715553a425386b1.jpg&quot; width=&quot;440&quot; alt=&quot;Photo of the micro SD card in its packaging.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;The card being tested.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;As per the video’s suggestion I tested one with an open-source tool called
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;AltraMayor&#x2F;f3&quot;&gt;F3 (Fight Flash Fraud)&lt;&#x2F;a&gt; when they arrived. F3 verifies the capacity against
what the drive advertises and verifies that that amount of data can be written
and read back without error.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;The card passed all tests. The packaging said up to 100MB&#x2F;s read and is marked
C10&#x2F;U1&#x2F;V10, which is minimum 10MB&#x2F;s sequential write. The tests showed a
write speed of 30.7MB&#x2F;s and a read speed of 90.9MB&#x2F;s, which feels within the
values advertised.&lt;&#x2F;p&gt;
&lt;p&gt;No idea what the endurance of the flash memory will end up being but I intend
to use them in a low-write environment, where failure doesn’t really matter. If
the data the cards was storing was not easily replaced, such as photos, I
would still purchase from a repeatable local seller. However, for holding an
operating system image for a non-critical single board computer these seem
fine.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;full-results&quot;&gt;Full Results&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ doas f3probe --destructive --time-ops &#x2F;dev&#x2F;sde&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;F3 probe 8.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Copyright (C) 2010 Digirati Internet LTDA.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;This is free software; see the source for copying conditions.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;WARNING: Probing normally takes from a few seconds to 15 minutes, but&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;         it can take longer. Please be patient.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Good news: The device `&#x2F;dev&#x2F;sde&amp;#39; is the real thing&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Device geometry:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	         *Usable* size: 29.54 GB (61952000 blocks)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	        Announced size: 29.54 GB (61952000 blocks)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	                Module: 32.00 GB (2^35 Bytes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	Approximate cache size: 0.00 Byte (0 blocks), need-reset=no&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	   Physical block size: 512.00 Byte (2^9 Bytes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Probe time: 1&amp;#39;02&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; Operation: total time &#x2F; count = avg time&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Read: 347.8ms &#x2F; 4815 = 72us&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     Write: 1&amp;#39;02&amp;quot; &#x2F; 4192321 = 14us&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     Reset: 0us &#x2F; 1 = 0us&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ f3write &#x2F;run&#x2F;media&#x2F;wmoore&#x2F;8BF0-E09E&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;F3 write 8.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Copyright (C) 2010 Digirati Internet LTDA.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;This is free software; see the source for copying conditions.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Free space: 29.53 GB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 1.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 2.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 3.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 4.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 5.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 6.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 7.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 8.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 9.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 10.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 11.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 12.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 13.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 14.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 15.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 16.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 17.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 18.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 19.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 20.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 21.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 22.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 23.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 24.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 25.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 26.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 27.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 28.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 29.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Creating file 30.h2w ... OK!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Free space: 0.00 Byte&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Average writing speed: 30.71 MB&#x2F;s&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ f3read &#x2F;run&#x2F;media&#x2F;wmoore&#x2F;8BF0-E09E&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;F3 read 8.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Copyright (C) 2010 Digirati Internet LTDA.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;This is free software; see the source for copying conditions.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  SECTORS      ok&#x2F;corrupted&#x2F;changed&#x2F;overwritten&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 1.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 2.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 3.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 4.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 5.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 6.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 7.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 8.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 9.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 10.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 11.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 12.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 13.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 14.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 15.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 16.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 17.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 18.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 19.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 20.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 21.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 22.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 23.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 24.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 25.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 26.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 27.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 28.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 29.h2w ... 2097152&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Validating file 30.h2w ... 1102208&#x2F;        0&#x2F;      0&#x2F;      0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Data OK: 29.53 GB (61919616 sectors)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Data LOST: 0.00 Byte (0 sectors)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	       Corrupted: 0.00 Byte (0 sectors)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	Slightly changed: 0.00 Byte (0 sectors)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	     Overwritten: 0.00 Byte (0 sectors)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Average reading speed: 90.93 MB&#x2F;s&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;While these were branded Lexar there’s really no way to know when ordering if they are
genuine or not. The main thing I care about is whether they hold as much data as they say. &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</description>
    </item>
    
    <item>
      <title>Building a Hybrid Native Application With Gleam and Tauri</title>
      <pubDate>Mon, 19 Feb 2024 09:56:49 +1000</pubDate>
      <atom:published>2024-02-19T09:56:49+10:00</atom:published>
      <atom:updated>2024-02-21T10:05:19+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;gleam-tauri&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2024&#x2F;gleam-tauri&#x2F;</guid>
      <description>&lt;p&gt;I took a few hours this weekend to experiment with building a hybrid
native app with Gleam and Tauri. This post is a summary of that project. If
you’d just like to see the code, I have published that at:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;gleam-tauri-experiment&quot;&gt;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;gleam-tauri-experiment&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;gleam-tauri&amp;#x2F;screenshot.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;gleam-tauri&amp;#x2F;screenshot.png&quot; width=&quot;650&quot; alt=&quot;Screenshot of the application showing a name field, minus button, plus button, Greet button and the current time.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Screenshot of the application.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gleam.run&#x2F;&quot;&gt;Gleam&lt;&#x2F;a&gt; is statically typed functional language originally written to target
the Erlang virtual machine. Now it also has a JavaScript back-end that allows
Gleam code to run in the browser as well as in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nodejs.org&#x2F;&quot;&gt;node.js&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;deno.com&#x2F;&quot;&gt;Deno&lt;&#x2F;a&gt;. The generated
JavaScript is quite readable similar to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;elm-lang.org&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rescript-lang.org&#x2F;&quot;&gt;ReScript&lt;&#x2F;a&gt;&#x2F;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;reasonml.github.io&#x2F;&quot;&gt;ReasonML&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Gleam appeals to me as an option for writing front-end code because it’s
stricter than TypeScript, has nominal types, is fast to compile, has a nice
all-in-one developer experience like cargo with the &lt;code&gt;gleam&lt;&#x2F;code&gt; CLI.&lt;&#x2F;p&gt;
&lt;p&gt;One of the things that makes writing front-end applications in Gleam feasible
is the delightful &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lustre-labs&#x2F;lustre&quot;&gt;Lustre&lt;&#x2F;a&gt; package. It’s an implementation of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;guide.elm-lang.org&#x2F;architecture&#x2F;&quot;&gt;Elm
architecture&lt;&#x2F;a&gt; in Gleam. If you’ve used Elm a Lustre application will look
extremely familiar. In this context Gleam is kind of like an actively
maintained Elm without the restrictions on interop with existing JavaScript
code.&lt;&#x2F;p&gt;
&lt;p&gt;To get started here’s some Gleam code that demonstrates a decent chunk of the
language:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; gleam&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;io&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; gleam&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;list&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; gleam&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub type&lt;&#x2F;span&gt;&lt;span&gt; Temperature {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  F(Float)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  C(Float)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub type&lt;&#x2F;span&gt;&lt;span&gt; Celsius {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Celsius(Float)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; temps &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; [C(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;22.0&lt;&#x2F;span&gt;&lt;span&gt;), C(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;5.0&lt;&#x2F;span&gt;&lt;span&gt;), F(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0.0&lt;&#x2F;span&gt;&lt;span&gt;), C(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0.0&lt;&#x2F;span&gt;&lt;span&gt;), F(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;32.0&lt;&#x2F;span&gt;&lt;span&gt;)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  io.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;debug&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;avg&lt;&#x2F;span&gt;&lt;span&gt;(temps))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; avg&lt;&#x2F;span&gt;&lt;span&gt;(measurements: List(Temperature)) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Celsius {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; sum &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    list.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;fold&lt;&#x2F;span&gt;&lt;span&gt;(measurements, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0.0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(sum, val) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      let&lt;&#x2F;span&gt;&lt;span&gt; Celsius(c) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; to_c&lt;&#x2F;span&gt;&lt;span&gt;(val)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      sum &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;+.&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; length &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    list.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;(measurements)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; int.to_float&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Celsius(sum &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;.&lt;&#x2F;span&gt;&lt;span&gt; length)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; to_c&lt;&#x2F;span&gt;&lt;span&gt;(temp: Temperature)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Celsius {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  case&lt;&#x2F;span&gt;&lt;span&gt; temp {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    C(c) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Celsius(c)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    F(f) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Celsius({ f &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 32.0&lt;&#x2F;span&gt;&lt;span&gt; } &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1.8&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When run it outputs:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Celsius(1.8444444444444443)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The generated JavaScript (as of Gleam v1.0.0-rc2) is shown below. While it’s
certainly longer than what you might naively write in JavaScript directly it’s
pretty clear what’s going on.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import * as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; $int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;..&#x2F;gleam_stdlib&#x2F;gleam&#x2F;int.mjs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import * as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; $io&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;..&#x2F;gleam_stdlib&#x2F;gleam&#x2F;io.mjs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import * as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; $list&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;..&#x2F;gleam_stdlib&#x2F;gleam&#x2F;list.mjs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; toList&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; CustomType&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; $CustomType&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; divideFloat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.&#x2F;gleam.mjs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; F&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; $CustomType&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;x0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    super&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; x0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; C&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; $CustomType&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;x0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    super&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; x0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Celcius&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; $CustomType&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;x0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    super&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; x0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; to_c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;temp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  if&lt;&#x2F;span&gt;&lt;span&gt; (temp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; instanceof&lt;&#x2F;span&gt;&lt;span&gt; C)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; temp[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Celcius&lt;&#x2F;span&gt;&lt;span&gt;(c)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; else&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; temp[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Celcius&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;divideFloat&lt;&#x2F;span&gt;&lt;span&gt;((f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 32.0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1.8&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; avg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;measurements&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; sum&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; $list&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;fold&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    measurements&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;    0.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;sum&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      let&lt;&#x2F;span&gt;&lt;span&gt; $&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; to_c&lt;&#x2F;span&gt;&lt;span&gt;(val)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      let&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; $[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      return&lt;&#x2F;span&gt;&lt;span&gt; sum&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; length&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; _pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; $list&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;(measurements)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; $int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;to_float&lt;&#x2F;span&gt;&lt;span&gt;(_pipe)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span&gt;)()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  return new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Celcius&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;divideFloat&lt;&#x2F;span&gt;&lt;span&gt;(sum&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; length))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; temps&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; toList&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; C&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;22.0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; C&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;5.0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; F&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0.0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; C&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0.0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; F&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;32.0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span&gt; $io&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;debug&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;avg&lt;&#x2F;span&gt;&lt;span&gt;(temps))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;building-a-hybrid-native-app&quot;&gt;Building a Hybrid Native App&lt;&#x2F;h3&gt;
&lt;aside class=&quot;float-right&quot;&gt;
  &lt;div class=&quot;emoji text-center&quot;&gt;💡&lt;&#x2F;div&gt;
  &lt;strong&gt;Version Information&lt;&#x2F;strong&gt;

  &lt;p&gt;I used the following pre-release versions of Gleam and Tauri:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Gleam 1.0.0-rc2&lt;&#x2F;li&gt;
&lt;li&gt;Tauri 2.0.0-beta.1.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tauri.app&#x2F;&quot;&gt;Tauri&lt;&#x2F;a&gt; is a framework for building hybrid native applications. By that I mean
an application that uses native code for the back-end and web technology for the
user interface. This is similar to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.electronjs.org&#x2F;&quot;&gt;Electron&lt;&#x2F;a&gt; except that Tauri does not include
a copy of Chromium in every application, instead relying on the system web view
on the host operating system.&lt;&#x2F;p&gt;
&lt;p&gt;You implement your application logic in Rust and communicate with the UI
by emitting and listening to events. The end result is a cross-platform desktop
app that is a lot smaller than if it were built with Electron.&lt;&#x2F;p&gt;
&lt;p&gt;This weekend I decided to try combining these things to see how feasible it
would be to build a hybrid desktop app with Gleam and Tauri. I started by
following &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tauri.app&#x2F;v1&#x2F;guides&#x2F;getting-started&#x2F;setup&#x2F;vite&quot;&gt;the Tauri guide for setting up a Vite project&lt;&#x2F;a&gt;. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;vitejs.dev&#x2F;&quot;&gt;Vite&lt;&#x2F;a&gt;
is a bundler that takes care of transforming source files on the front-end as
well is providing a nice auto-reloading development experience.&lt;&#x2F;p&gt;
&lt;p&gt;Once that was working I initialised a Gleam project in the same directory:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gleam new --name gleamdemo gleam-demo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt; I originally called my application &lt;code&gt;videopls&lt;&#x2F;code&gt; there are still some
references to it in the code.&lt;&#x2F;p&gt;
&lt;p&gt;I then followed &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;erikarow.land&#x2F;notes&#x2F;gleam-vite&quot;&gt;Erika Rowland’s guide to using Gleam with Vite&lt;&#x2F;a&gt;. This
resulted in a simple counter demo running in the Tauri window. At this point
the Gleam code was almost identical to Erika’s post.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;gleam-tauri&amp;#x2F;phase1.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2024&amp;#x2F;gleam-tauri&amp;#x2F;phase1.png&quot; width=&quot;650&quot; alt=&quot;Screenshot of the application showing a counter with plus and minus buttons&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Phase 1 complete.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Now came the uncharted waters: how to integrate &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tauri.app&#x2F;v1&#x2F;references&#x2F;architecture&#x2F;inter-process-communication&#x2F;#commands&quot;&gt;Tauri’s command
system&lt;&#x2F;a&gt; to invoke commands in the back-end. Commands are a sort
of in-process communication mechanism where the UI can invoke a function
implemented in Rust on the back-end.&lt;&#x2F;p&gt;
&lt;p&gt;I added a Tauri command to the back-end:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; src-tauri&#x2F;src&#x2F;main.rs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;&quot;&gt;#[tauri::command]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; greet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt;    format!&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Hello, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I then needed to be able to use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;beta.tauri.app&#x2F;references&#x2F;v2&#x2F;js&#x2F;core&#x2F;namespacecore&#x2F;#invoke&quot;&gt;the &lt;code&gt;invoke&lt;&#x2F;code&gt; function&lt;&#x2F;a&gt; from the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;@tauri-apps&#x2F;api&quot;&gt;@tauri-apps&#x2F;api npm package&lt;&#x2F;a&gt;. Following the pattern I observed
in other Gleam packages I created a JavaScript file to act as a bridge between
Gleam and &lt;code&gt;@tauri-apps&#x2F;api&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; src&#x2F;ffi&#x2F;commands.js&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; invoke&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;@tauri-apps&#x2F;api&#x2F;core&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Ok&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;..&#x2F;..&#x2F;build&#x2F;dev&#x2F;javascript&#x2F;videopls&#x2F;gleam.mjs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; greet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  try&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Ok&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; invoke&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;greet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;, {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; catch&lt;&#x2F;span&gt;&lt;span&gt; (error)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Error&lt;&#x2F;span&gt;&lt;span&gt;(error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I could then define the external function in the Gleam code and call it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; src&#x2F;demo.gleam&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;external&lt;&#x2F;span&gt;&lt;span&gt;(javascript, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;.&#x2F;ffi&#x2F;commands.js&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;greet&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; greet&lt;&#x2F;span&gt;&lt;span&gt;(name: String)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Promise(Result(String, String))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The next challenge was &lt;code&gt;greet&lt;&#x2F;code&gt; is an async function, so it returns a promise,
which does not integrate into a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lustre.build&#x2F;api&#x2F;lustre#simple&quot;&gt;lustre.simple&lt;&#x2F;a&gt; application well. Fortunately
there is the less simple &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lustre.build&#x2F;api&#x2F;lustre#application&quot;&gt;lustre.application&lt;&#x2F;a&gt; that adds effects. After looking
at some existing code I was finally able to come up with a working solution.
The full Gleam code is shown below. &lt;code&gt;get_greeting&lt;&#x2F;code&gt; and &lt;code&gt;do_get_greeting&lt;&#x2F;code&gt; being
the main parts of interest.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; src&#x2F;demo.gleam&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; gleam&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; gleam&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;javascript&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;promise.{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt; Promise}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; lustre&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; lustre&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;attribute &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;as&lt;&#x2F;span&gt;&lt;span&gt; attr&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; lustre&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;element.{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt; Element}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; lustre&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;element&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;html&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; lustre&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;event&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; lustre&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;effect.{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt; Effect}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; app &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; lustre.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;application&lt;&#x2F;span&gt;&lt;span&gt;(init, update, view)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let assert&lt;&#x2F;span&gt;&lt;span&gt; Ok(dispatch) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; lustre.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;start&lt;&#x2F;span&gt;&lt;span&gt;(app, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;#app&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, Nil)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  dispatch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt; Model {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Model(count: Int, greeting: String, name: String)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; init&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(Model, Effect(Msg)) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  #(Model(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;), effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;none&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub type&lt;&#x2F;span&gt;&lt;span&gt; Msg {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Increment&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Decrement&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Greet&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  GotGreeting(String)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  UpdateName(String)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; update&lt;&#x2F;span&gt;&lt;span&gt;(model: Model, msg: Msg)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(Model, Effect(Msg)) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  case&lt;&#x2F;span&gt;&lt;span&gt; msg {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Increment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(Model(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;model, count: model.count &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;), effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;none&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Decrement&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(Model(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;model, count: model.count &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;), effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;none&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Greet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(model, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;get_greeting&lt;&#x2F;span&gt;&lt;span&gt;(model.name))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    GotGreeting(greeting) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Model(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;model, greeting: greeting),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;none&lt;&#x2F;span&gt;&lt;span&gt;(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    UpdateName(name) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(Model(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;model, name: name), effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;none&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; get_greeting&lt;&#x2F;span&gt;&lt;span&gt;(name: String)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Effect(Msg) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;do_get_greeting&lt;&#x2F;span&gt;&lt;span&gt;(name, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; do_get_greeting&lt;&#x2F;span&gt;&lt;span&gt;(name: String, dispatch: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(Msg)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Nil) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Nil {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;  greet&lt;&#x2F;span&gt;&lt;span&gt;(name)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; promise.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(response) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span&gt; response {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Ok(greeting) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; GotGreeting(greeting)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Error(err) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; GotGreeting(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;Error: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; err)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; promise.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;tap&lt;&#x2F;span&gt;&lt;span&gt;(dispatch)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;external&lt;&#x2F;span&gt;&lt;span&gt;(javascript, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;.&#x2F;ffi&#x2F;commands.js&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;greet&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; greet&lt;&#x2F;span&gt;&lt;span&gt;(name: String)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Promise(Result(String, String))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; update_name&lt;&#x2F;span&gt;&lt;span&gt;(text: String)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Msg {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  UpdateName(text)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; -- VIEW&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; view&lt;&#x2F;span&gt;&lt;span&gt;(model: Model)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Element(Msg) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; count &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; int.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(model.count)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;([], [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;h1&lt;&#x2F;span&gt;&lt;span&gt;([], [element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;Gleam + Vite + Tauri&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;([attr.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;field text-center&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)], [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;label&lt;&#x2F;span&gt;&lt;span&gt;([attr.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;greet_name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)], [element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;Name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        attr.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;type_&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;text&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        attr.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;greet_name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        event.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;on_input&lt;&#x2F;span&gt;&lt;span&gt;(update_name),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;([attr.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;text-center&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)], [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(model.greeting &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; &amp;quot; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; count &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; &amp;quot; ✨&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;([attr.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;text-center&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)], [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;([event.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;on_click&lt;&#x2F;span&gt;&lt;span&gt;(Decrement)], [element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;-&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;([event.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;on_click&lt;&#x2F;span&gt;&lt;span&gt;(Increment)], [element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;+&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;([event.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;on_click&lt;&#x2F;span&gt;&lt;span&gt;(Greet)], [element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;Greet&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I added a &lt;code&gt;Greet&lt;&#x2F;code&gt; message for when the “Greet” button is clicked. The &lt;code&gt;update&lt;&#x2F;code&gt;
function that doesn’t update the model but calls &lt;code&gt;get_greeting&lt;&#x2F;code&gt; as its
side-effect. That builds an &lt;code&gt;Effect&lt;&#x2F;code&gt; from &lt;code&gt;do_get_greeting&lt;&#x2F;code&gt;, which calls the
FFI function and maps the &lt;code&gt;Result&lt;&#x2F;code&gt; to a &lt;code&gt;GotGreeting&lt;&#x2F;code&gt; message containing the
greeting or an error message.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;update&lt;&#x2F;code&gt; handles the &lt;code&gt;GotGreeting&lt;&#x2F;code&gt; message by updating the model, which in
turn updates the UI. I’m skipping over the &lt;code&gt;Model&lt;&#x2F;code&gt;, &lt;code&gt;view&lt;&#x2F;code&gt;, &lt;code&gt;update&lt;&#x2F;code&gt;
architecture of this Lustre application since it’s basically the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;guide.elm-lang.org&#x2F;architecture&#x2F;&quot;&gt;Elm
architecture&lt;&#x2F;a&gt;. A similar pattern is seen in Reason React, ReScript, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;redux.js.org&#x2F;tutorials&#x2F;fundamentals&#x2F;part-3-state-actions-reducers&quot;&gt;React
with actions and reducers&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;At this point I had worked out how to invoke Rust functions in the back-end via
Tauri commands but I wanted to take it step further. In a real application you
can imagine that the back-end might be performing actions that it needs to tell
the UI about. For example, when updated data is available after a sync.
To do this Tauri provides a way for both parts of the application to emit
events with a payload, and listen for those events. It’s all very similar to how
events work in JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to test this out by periodically having the back-end emit an event and
have the UI listen for the event and update as a result. I decided to have
the back-end emit the current time each second as a UNIX timestamp. Working out
how to do this on back-end stumped me for a bit but I eventually worked out I
could spawn a thread in the &lt;code&gt;setup&lt;&#x2F;code&gt; function:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; src-tauri&#x2F;src&#x2F;main.rs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;time&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Duration&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Instant&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SystemTime&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; UNIX_EPOCH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt; tauri&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;EventTarget&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt; tauri&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Manager&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    tauri&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Builder&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt;app&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; app&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; app&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;handle&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            std&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;thread&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;spawn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;move ||&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;                loop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;                    let&lt;&#x2F;span&gt;&lt;span&gt; now&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; SystemTime&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;now&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;                    let&lt;&#x2F;span&gt;&lt;span&gt; duration&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; now&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;duration_since&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;UNIX_EPOCH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    app&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;emit_to&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;EventTarget&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(), &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;tick&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;span&gt; duration&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;as_secs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;                        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    std&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;thread&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Duration&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;from_secs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;                }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;            });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;            Ok&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;        })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;invoke_handler&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;tauri&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt;generate_handler!&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;greet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;tauri&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt;generate_context!&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;error while running tauri application&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In a production application you’d want a mechanism for cleanly shutting the
thread down but for experimentation purposes I skipped that. Now I needed to
listen for the &lt;code&gt;tick&lt;&#x2F;code&gt; event in the UI. I added another glue function to the FFI
file:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; src&#x2F;ffi&#x2F;commands.js&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; listenForTick&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;handler&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; listen&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;tick&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;, (&lt;&#x2F;span&gt;&lt;span&gt;event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    handler&lt;&#x2F;span&gt;&lt;span&gt;(event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;payload)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And added a function to the Gleam code to call it and dispatch a message when
it was received:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; src&#x2F;demo.gleam&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; bind_clock&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Effect(Msg) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(dispatch) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    listen_for_tick&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(time) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;      tick&lt;&#x2F;span&gt;&lt;span&gt;(time)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; dispatch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As a first pass I just rendered the number in the UI but I then extended it to
parse the timestamp into a JavaScript Date and render the stringified version
of it. Surprisingly the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hexdocs.pm&#x2F;gleam_javascript&#x2F;&quot;&gt;gleam_javascript&lt;&#x2F;a&gt; package doesn’t have Date bindings
yet so I created some for what I needed:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; src&#x2F;ffi&#x2F;js_extra.js&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; from_unix&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;timestamp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Date&lt;&#x2F;span&gt;&lt;span&gt;(timestamp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1000&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; date_to_string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;date&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; date&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I think in an ideal world simple bindings like this (especially &lt;code&gt;toString&lt;&#x2F;code&gt;)
would be able to be expressed solely though the &lt;code&gt;@external&lt;&#x2F;code&gt; attribute. That
doesn’t seem to be possible yet but if it is please let me know.&lt;&#x2F;p&gt;
&lt;p&gt;I bound those in Gleam:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; src&#x2F;demo.gleam&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub type&lt;&#x2F;span&gt;&lt;span&gt; Date&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;external&lt;&#x2F;span&gt;&lt;span&gt;(javascript, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;.&#x2F;ffi&#x2F;js_extra.js&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;from_unix&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; new_date&lt;&#x2F;span&gt;&lt;span&gt;(timestamp: Int)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Date&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;external&lt;&#x2F;span&gt;&lt;span&gt;(javascript, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;.&#x2F;ffi&#x2F;js_extra.js&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;date_to_string&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; date_to_string&lt;&#x2F;span&gt;&lt;span&gt;(date: Date)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and updated the application to use them. The result is a clock at the bottom of
the page that updates each second:&lt;&#x2F;p&gt;
&lt;div class=&quot;text-center&quot;&gt;
    &lt;video src=&quot;gleam-tauri2-2024-02-19_15.26.34.mp4&quot; width=&quot;659&quot; height=&quot;359&quot; controls&gt;&lt;&#x2F;video&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;The final Gleam application looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; src&#x2F;demo.gleam&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; gleam&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; gleam&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;javascript&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;promise.{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt; Promise}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; lustre&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; lustre&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;attribute &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;as&lt;&#x2F;span&gt;&lt;span&gt; attr&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; lustre&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;element.{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt; Element}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; lustre&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;element&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;html&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; lustre&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;event&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; lustre&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;effect.{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt; Effect}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; app &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; lustre.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;application&lt;&#x2F;span&gt;&lt;span&gt;(init, update, view)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let assert&lt;&#x2F;span&gt;&lt;span&gt; Ok(dispatch) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; lustre.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;start&lt;&#x2F;span&gt;&lt;span&gt;(app, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;#app&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, Nil)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  dispatch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt; Model {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Model(count: Int, greeting: String, name: String, time: Int)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; init&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(Model, Effect(Msg)) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  #(Model(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;), &lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;bind_clock&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub type&lt;&#x2F;span&gt;&lt;span&gt; Msg {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Increment&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Decrement&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Greet&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  GotGreeting(String)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  UpdateName(String)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Tick(Int)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; update&lt;&#x2F;span&gt;&lt;span&gt;(model: Model, msg: Msg)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(Model, Effect(Msg)) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  case&lt;&#x2F;span&gt;&lt;span&gt; msg {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Increment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(Model(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;model, count: model.count &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;), effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;none&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Decrement&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(Model(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;model, count: model.count &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;), effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;none&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Greet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(model, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;get_greeting&lt;&#x2F;span&gt;&lt;span&gt;(model.name))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    GotGreeting(greeting) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Model(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;model, greeting: greeting),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;none&lt;&#x2F;span&gt;&lt;span&gt;(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    UpdateName(name) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(Model(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;model, name: name), effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;none&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Tick(time) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; #(Model(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;model, time: time), effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;none&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; get_greeting&lt;&#x2F;span&gt;&lt;span&gt;(name: String)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Effect(Msg) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;do_get_greeting&lt;&#x2F;span&gt;&lt;span&gt;(name, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; do_get_greeting&lt;&#x2F;span&gt;&lt;span&gt;(name: String, dispatch: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(Msg)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Nil) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Nil {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;  greet&lt;&#x2F;span&gt;&lt;span&gt;(name)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; promise.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(response) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span&gt; response {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Ok(greeting) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; GotGreeting(greeting)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Error(err) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; GotGreeting(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;Error: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; err)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; promise.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;tap&lt;&#x2F;span&gt;&lt;span&gt;(dispatch)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; bind_clock&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Effect(Msg) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  effect.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(dispatch) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    listen_for_tick&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(time) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;      tick&lt;&#x2F;span&gt;&lt;span&gt;(time)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; dispatch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;external&lt;&#x2F;span&gt;&lt;span&gt;(javascript, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;.&#x2F;ffi&#x2F;commands.js&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;greet&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; greet&lt;&#x2F;span&gt;&lt;span&gt;(name: String)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Promise(Result(String, String))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt; UnlistenFn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  fn&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;external&lt;&#x2F;span&gt;&lt;span&gt;(javascript, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;.&#x2F;ffi&#x2F;commands.js&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;listenForTick&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; listen_for_tick&lt;&#x2F;span&gt;&lt;span&gt;(handler: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(Int)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Nil) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Promise(UnlistenFn)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub type&lt;&#x2F;span&gt;&lt;span&gt; Date&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;external&lt;&#x2F;span&gt;&lt;span&gt;(javascript, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;.&#x2F;ffi&#x2F;js_extra.js&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;from_unix&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; new_date&lt;&#x2F;span&gt;&lt;span&gt;(timestamp: Int)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Date&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;external&lt;&#x2F;span&gt;&lt;span&gt;(javascript, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;.&#x2F;ffi&#x2F;js_extra.js&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;date_to_string&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; date_to_string&lt;&#x2F;span&gt;&lt;span&gt;(date: Date)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; update_name&lt;&#x2F;span&gt;&lt;span&gt;(text: String)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Msg {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  UpdateName(text)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; tick&lt;&#x2F;span&gt;&lt;span&gt;(time: Int)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Msg {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Tick(time)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; -- VIEW&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; view&lt;&#x2F;span&gt;&lt;span&gt;(model: Model)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Element(Msg) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; count &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; int.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(model.count)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; time &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    model.time&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; new_date&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; date_to_string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;([], [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;h1&lt;&#x2F;span&gt;&lt;span&gt;([], [element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;Gleam + Vite + Tauri&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;([attr.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;field text-center&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)], [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;label&lt;&#x2F;span&gt;&lt;span&gt;([attr.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;greet_name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)], [element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;Name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        attr.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;type_&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;text&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        attr.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;greet_name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        event.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;on_input&lt;&#x2F;span&gt;&lt;span&gt;(update_name),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;([attr.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;text-center&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)], [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(model.greeting &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; &amp;quot; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; count &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; &amp;quot; ✨&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;([attr.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;text-center&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)], [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;([event.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;on_click&lt;&#x2F;span&gt;&lt;span&gt;(Decrement)], [element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;-&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;([event.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;on_click&lt;&#x2F;span&gt;&lt;span&gt;(Increment)], [element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;+&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;([event.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;on_click&lt;&#x2F;span&gt;&lt;span&gt;(Greet)], [element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;Greet&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    html.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;([attr.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;clock text-center&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)], [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      element.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;Clock: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; time),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;I successfully built a hybrid native application with Gleam and Tauri. While
what I built is clearly experimental code I think it was enough to work out the
approach and patterns you could use to build a larger application. Using Gleam
to build a web components or web front-ends seems quite feasible.&lt;&#x2F;p&gt;
&lt;p&gt;Some unanswered questions I have from this experiment are:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Does binding to external functions in the JS platform or npm packages always
require some JS glue code? It seems it does at the moment.&lt;&#x2F;li&gt;
&lt;li&gt;What is the right way to import &lt;code&gt;gleam.mjs&lt;&#x2F;code&gt; from JavaScript code?&lt;&#x2F;li&gt;
&lt;li&gt;What is the structure of the Gleam &lt;code&gt;build&lt;&#x2F;code&gt; directory?
&lt;ul&gt;
&lt;li&gt;I see &lt;code&gt;dev&lt;&#x2F;code&gt; and &lt;code&gt;prod&lt;&#x2F;code&gt; sub-directories.&lt;&#x2F;li&gt;
&lt;li&gt;Is the &lt;code&gt;prod&lt;&#x2F;code&gt; on used when targeting JavaScript
&lt;ul&gt;
&lt;li&gt;I can’t see any equivalent of Cargo’s &lt;code&gt;--release&lt;&#x2F;code&gt; in the &lt;code&gt;gleam&lt;&#x2F;code&gt; CLI help.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The full project code is available here:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;gleam-tauri-experiment&quot;&gt;https:&#x2F;&#x2F;forge.wezm.net&#x2F;wezm&#x2F;gleam-tauri-experiment&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;thanks&quot;&gt;Thanks&lt;&#x2F;h4&gt;
&lt;p&gt;Special thanks to the following folks:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hayleigh-dot-dev&quot;&gt;Hayleigh Thompson&lt;&#x2F;a&gt; for building Lustre.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Enderchief&quot;&gt;Enderchief&lt;&#x2F;a&gt; for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Enderchief&#x2F;gleam-tools&#x2F;tree&#x2F;master&#x2F;packages&#x2F;vite-gleam&quot;&gt;vite-gleam&lt;&#x2F;a&gt;, which makes it super easy to integrate Gleam code with Vite.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;erikarow.land&#x2F;&quot;&gt;Erika Rowland&lt;&#x2F;a&gt; for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;erikarow.land&#x2F;notes&#x2F;gleam-vite&quot;&gt;her Gleam Vite guide&lt;&#x2F;a&gt;. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;erikarow.land&#x2F;notes&#x2F;esgleam-embed&quot;&gt;The follow up on &lt;code&gt;esgleam&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is also good.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
    </item>
    
    <item>
      <title>systemd-sysusers and Chimera Linux</title>
      <pubDate>Mon, 18 Dec 2023 11:25:57 +1000</pubDate>
      <atom:published>2023-12-18T11:25:57+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;systemd-sysusers-and-chimera-linux&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;systemd-sysusers-and-chimera-linux&#x2F;</guid>
      <description>&lt;p&gt;I use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chimera-linux.org&#x2F;&quot;&gt;Chimera Linux&lt;&#x2F;a&gt; as the primary OS on my laptop (as opposed to my desktop,
which is still running Arch Linux for now). Chimera was created in 2021 and
reached alpha status in June 2023. Chimera was built from scratch and as the
name suggests it comprised of a motley crew of components:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Kernel: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.kernel.org&#x2F;&quot;&gt;Linux&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Toolchain: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;llvm.org&#x2F;&quot;&gt;LLVM&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;libc: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;musl.libc.org&#x2F;&quot;&gt;Musl&lt;&#x2F;a&gt; with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;releases.llvm.org&#x2F;17.0.1&#x2F;docs&#x2F;ScudoHardenedAllocator.html&quot;&gt;Scudo allocator&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Core userland: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chimera-linux&#x2F;chimerautils&quot;&gt;FreeBSD (with some NetBSD and OpenBSD too)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Init: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;davmac.org&#x2F;projects&#x2F;dinit&#x2F;&quot;&gt;Dinit&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Package manager: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.alpinelinux.org&#x2F;alpine&#x2F;apk-tools&quot;&gt;apk&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Package builder: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chimera-linux&#x2F;cports&#x2F;blob&#x2F;e11b91cfa66cc5c45657de4b33215ccdff51b1b7&#x2F;Usage.md&quot;&gt;cbuild&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The project and its development is proving very useful to me for seeing how a
Linux distribution is built and evolved over time. Watching it progress (and
helping a little by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkgs.chimera-linux.org&#x2F;packages?name=&amp;amp;arch=x86_64&amp;amp;origin=&amp;amp;maintainer=Wesley+Moore&quot;&gt;maintaining some packages&lt;&#x2F;a&gt;) has helped expose
some lesser known (to me) components that make up a typical Linux system, and
their role.&lt;&#x2F;p&gt;
&lt;p&gt;Recently &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;software&#x2F;systemd&#x2F;man&#x2F;latest&#x2F;systemd-sysusers.html&quot;&gt;systemd-sysusers&lt;&#x2F;a&gt; was introduced. Some folks might find this
surprising as Chimera does not use systemd for the role of pid 1&#x2F;init. As
mentioned above it uses &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;davmac.org&#x2F;projects&#x2F;dinit&#x2F;&quot;&gt;Dinit&lt;&#x2F;a&gt; for this. Some standalone parts of systemd are
used though. Currently:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;udev&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;systemd-tmpfiles&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;and now, &lt;code&gt;systemd-sysusers&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I had not encountered &lt;code&gt;systemd-sysusers&lt;&#x2F;code&gt; previously (even though it’s probably
used on the systemd based distros I’ve used before), so I thought I’d jot down
what I learned about it and how it’s used (at the time of writing) in Chimera.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h3 id=&quot;what-is-systemd-sysusers&quot;&gt;What is systemd-sysusers?&lt;&#x2F;h3&gt;
&lt;p&gt;As the name implies &lt;code&gt;systemd-sysusers&lt;&#x2F;code&gt; is designed to make managing system
users (and groups) easier. System users are users that a typically created for
system processes to use for privilege separation. For example, the CUPS
printing system runs as the &lt;code&gt;_cups&lt;&#x2F;code&gt; user.&lt;&#x2F;p&gt;
&lt;p&gt;In &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;systemd&#x2F;systemd&#x2F;commit&#x2F;1b99214789101976d6bbf75c351279584b071998&quot;&gt;the commit that introduced sysusers&lt;&#x2F;a&gt; into systemd Lennart
Poettering included this description:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;systemd-sysusers is a tool to reconstruct &#x2F;etc&#x2F;passwd and &#x2F;etc&#x2F;group
from static definition files that take a lot of inspiration from
tmpfiles snippets. These snippets should carry information about system
users only. To make sure it is not misused for normal users these
snippets only allow configuring UID and gecos field for each user, but
do not allow configuration of the home directory or shell, which is
necessary for real login users.&lt;&#x2F;p&gt;
&lt;p&gt;The purpose of this tool is to enable state-less systems that can
populate &#x2F;etc with the minimal files necessary, solely from static data
in &#x2F;usr. systemd-sysuser is additive only, and will never override
existing users.&lt;&#x2F;p&gt;
&lt;p&gt;This tool will create these files directly, and not via some user
database abtsraction layer. This is appropriate as this tool is supposed
to run really early at boot, and is only useful for creating system
users, and system users cannot be stored in remote databases anyway.&lt;&#x2F;p&gt;
&lt;p&gt;The tool is also useful to be invoked from RPM scriptlets, instead of
useradd. This allows moving from imperative user descriptions in RPM to
declarative descriptions.&lt;&#x2F;p&gt;
&lt;p&gt;The UID&#x2F;GID for a user&#x2F;group to be created can either be chosen dynamic,
or fixed, or be read from the owner of a file in the file system, in
order to support reconstructing the correct IDs for files that shall be
owned by them.&lt;&#x2F;p&gt;
&lt;p&gt;This also adds a minimal user definition file, that should be
sufficient for most basic systems. Distributions are expected to patch
these files and augment the contents, for example with fixed UIDs for
the users where that’s necessary.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Chimera is using it for the scenario described for RPM. Specifically
snippets in &lt;code&gt;&#x2F;usr&#x2F;lib&#x2F;sysusers.d&lt;&#x2F;code&gt; are used to describe the system
users and groups that should exist.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;systemd-sysusers-in-chimera-linux&quot;&gt;systemd-sysusers in Chimera Linux&lt;&#x2F;h3&gt;
&lt;p&gt;Before the introduction of &lt;code&gt;systemd-sysusers&lt;&#x2F;code&gt; packages declared system users
and groups that were required in the package &lt;code&gt;template.py&lt;&#x2F;code&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chimera-linux&#x2F;cports&#x2F;blob&#x2F;8973e62759641602e29a8cb2b639dc886731ab49&#x2F;src&#x2F;cbuild&#x2F;hooks&#x2F;pre_pkg&#x2F;099_scriptlets.py&quot;&gt;&lt;code&gt;cbuild&lt;&#x2F;code&gt; would
generate scripts&lt;&#x2F;a&gt; from a template that added the users and groups,
as well as disable them when packages are uninstalled. These scripts had to
handle things like the user&#x2F;group already existing, tools for adding users or
groups missing, failures creating users&#x2F;groups, etc.&lt;&#x2F;p&gt;
&lt;p&gt;The generated scripts were then embedded into the final apk package as scripts
tied to “pre-install”, “pre-upgrade”, and “post-deinstall” actions.&lt;&#x2F;p&gt;
&lt;p&gt;For example, here is the scripts section (formatted by &lt;code&gt;adbdump&lt;&#x2F;code&gt; as YAML) for the
&lt;code&gt;chrony&lt;&#x2F;code&gt; NTP client&#x2F;server:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  pre-install&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    #!&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    _chrony_homedir=&#x2F;var&#x2F;lib&#x2F;chrony&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    system_users=_chrony&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    _system_accounts_invoke() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        local USERADD USERMOD&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        [ -z &amp;quot;$system_users&amp;quot; -a -z &amp;quot;$system_groups&amp;quot; ] &amp;amp;&amp;amp; return 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        if command -v useradd &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            USERADD=&amp;quot;useradd&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        if command -v usermod &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            USERMOD=&amp;quot;usermod&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        show_acct_details() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            echo &amp;quot;   Account: $1&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            echo &amp;quot;   Description: &amp;#39;$2&amp;#39;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            echo &amp;quot;   Homedir: &amp;#39;$3&amp;#39;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            echo &amp;quot;   Shell: &amp;#39;$4&amp;#39;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ -n &amp;quot;$5&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;   Additional groups: &amp;#39;$5&amp;#39;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        group_add() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            local _pretty_grname _grname _gid&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            if ! command -v groupadd &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                echo &amp;quot;WARNING: cannot create $1 system group (missing groupadd)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                echo &amp;quot;The following group must be created manually: $1&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                return 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            _grname=&amp;quot;${1%:*}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            _gid=&amp;quot;${1##*:}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ &amp;quot;${_grname}&amp;quot; = &amp;quot;${_gid}&amp;quot; ] &amp;amp;&amp;amp; _gid=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            _pretty_grname=&amp;quot;${_grname}${_gid:+ (gid: ${_gid})}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            groupadd -r ${_grname} ${_gid:+-g ${_gid}} &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            case $? in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                0) echo &amp;quot;Created ${_pretty_grname} system group.&amp;quot; ;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                9) ;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                *) echo &amp;quot;ERROR: failed to create system group ${_pretty_grname}!&amp;quot;; return 1;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            esac&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            return 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        # System groups required by a package.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        for grp in ${system_groups}; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            group_add $grp || return 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        # System user&#x2F;group required by a package.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        for acct in ${system_users}; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            _uname=&amp;quot;${acct%:*}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            _uid=&amp;quot;${acct##*:}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ &amp;quot;${_uname}&amp;quot; = &amp;quot;${_uid}&amp;quot; ] &amp;amp;&amp;amp; _uid=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            eval homedir=&amp;quot;\$${_uname}_homedir&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            eval shell=&amp;quot;\$${_uname}_shell&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            eval descr=&amp;quot;\$${_uname}_descr&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            eval groups=&amp;quot;\$${_uname}_groups&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            eval pgroup=&amp;quot;\$${_uname}_pgroup&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ -z &amp;quot;$homedir&amp;quot; ] &amp;amp;&amp;amp; homedir=&amp;quot;&#x2F;var&#x2F;empty&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ -z &amp;quot;$shell&amp;quot; ] &amp;amp;&amp;amp; shell=&amp;quot;&#x2F;usr&#x2F;bin&#x2F;nologin&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ -z &amp;quot;$descr&amp;quot; ] &amp;amp;&amp;amp; descr=&amp;quot;${_uname} user&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ -n &amp;quot;$groups&amp;quot; ] &amp;amp;&amp;amp; user_groups=&amp;quot;-G $groups&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            if [ -n &amp;quot;${_uid}&amp;quot; ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                use_id=&amp;quot;-u ${_uid} -g ${pgroup:-${_uid}}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                _pretty_uname=&amp;quot;${_uname} (uid: ${_uid})&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                use_id=&amp;quot;-g ${pgroup:-${_uname}}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                _pretty_uname=&amp;quot;${_uname}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            if [ -z &amp;quot;$USERADD&amp;quot; -o -z &amp;quot;$USERMOD&amp;quot; ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                echo &amp;quot;WARNING: cannot create ${_uname} system account (missing useradd or usermod)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                echo &amp;quot;The following system account must be created:&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                show_acct_details &amp;quot;${_pretty_uname}&amp;quot; &amp;quot;${descr}&amp;quot; &amp;quot;${homedir}&amp;quot; &amp;quot;${shell}&amp;quot; &amp;quot;${groups}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                continue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            group_add ${pgroup:-${acct}} || return 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            ${USERADD} -c &amp;quot;${descr}&amp;quot; -d &amp;quot;${homedir}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                ${use_id} ${pgroup:+-N} -s &amp;quot;${shell}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                ${user_groups} -r ${_uname} &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            case $? inhttps:&#x2F;&#x2F;github.com&#x2F;systemd&#x2F;systemd&#x2F;commit&#x2F;1b99214789101976d6bbf75c351279584b071998&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                0)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    echo &amp;quot;Created ${_pretty_uname} system user.&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    ${USERMOD} -L ${_uname} &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    if [ $? -ne 0 ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        echo &amp;quot;WARNING: unable to lock password for ${_uname} system account&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    ;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                9)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    ${USERMOD} -c &amp;quot;${descr}&amp;quot; -d &amp;quot;${homedir}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        -s &amp;quot;${shell}&amp;quot; -g &amp;quot;${pgroup:-${_uname}}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        ${user_groups} ${_uname} &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    if [ $? -eq 0 ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        echo &amp;quot;Updated ${_uname} system user.&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        echo &amp;quot;WARNING: unable to modify ${_uname} system account&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        echo &amp;quot;Please verify that account is compatible with these settings:&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        show_acct_details &amp;quot;${_pretty_uname}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                            &amp;quot;${descr}&amp;quot; &amp;quot;${homedir}&amp;quot; &amp;quot;${shell}&amp;quot; &amp;quot;${groups}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        continue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    ;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                *)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    echo &amp;quot;ERROR: failed to create system user ${_pretty_uname}!&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    return 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    ;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            esac&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        return 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    _system_accounts_invoke &amp;#39;chrony&amp;#39; &amp;#39;4.4&amp;#39; || exit $?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  post-deinstall&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    #!&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    _chrony_homedir=&#x2F;var&#x2F;lib&#x2F;chrony&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    system_users=_chrony&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    _system_accounts_invoke() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        local USERMOD&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        [ -z &amp;quot;$system_users&amp;quot; ] &amp;amp;&amp;amp; return 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        if command -v usermod &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            USERMOD=&amp;quot;usermod&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        for acct in ${system_users}; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            _uname=&amp;quot;${acct%:*}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            comment=&amp;quot;$( (getent passwd &amp;quot;${_uname}&amp;quot; | cut -d: -f5 | head -n1) 2&amp;gt;&#x2F;dev&#x2F;null )&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            comment=&amp;quot;${comment:-user} - removed package ${1}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            if [ -z &amp;quot;$USERMOD&amp;quot; ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                echo &amp;quot;WARNING: cannot disable ${_uname} system user (missing usermod)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                continue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            ${USERMOD} -L -d &#x2F;var&#x2F;empty -s &#x2F;usr&#x2F;bin&#x2F;false \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                -c &amp;quot;${comment}&amp;quot; ${_uname} &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            if [ $? -eq 0 ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                echo &amp;quot;Disabled ${_uname} system user.&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        return 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    _system_accounts_invoke &amp;#39;chrony&amp;#39; &amp;#39;4.4&amp;#39; || exit $?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  pre-upgrade&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    #!&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    _chrony_homedir=&#x2F;var&#x2F;lib&#x2F;chrony&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    system_users=_chrony&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    _system_accounts_invoke() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        local USERADD USERMOD&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        [ -z &amp;quot;$system_users&amp;quot; -a -z &amp;quot;$system_groups&amp;quot; ] &amp;amp;&amp;amp; return 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        if command -v useradd &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            USERADD=&amp;quot;useradd&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        if command -v usermod &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            USERMOD=&amp;quot;usermod&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        show_acct_details() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            echo &amp;quot;   Account: $1&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            echo &amp;quot;   Description: &amp;#39;$2&amp;#39;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            echo &amp;quot;   Homedir: &amp;#39;$3&amp;#39;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            echo &amp;quot;   Shell: &amp;#39;$4&amp;#39;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ -n &amp;quot;$5&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;   Additional groups: &amp;#39;$5&amp;#39;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        group_add() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            local _pretty_grname _grname _gid&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            if ! command -v groupadd &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                echo &amp;quot;WARNING: cannot create $1 system group (missing groupadd)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                echo &amp;quot;The following group must be created manually: $1&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                return 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            _grname=&amp;quot;${1%:*}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            _gid=&amp;quot;${1##*:}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ &amp;quot;${_grname}&amp;quot; = &amp;quot;${_gid}&amp;quot; ] &amp;amp;&amp;amp; _gid=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            _pretty_grname=&amp;quot;${_grname}${_gid:+ (gid: ${_gid})}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            groupadd -r ${_grname} ${_gid:+-g ${_gid}} &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            case $? in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                0) echo &amp;quot;Created ${_pretty_grname} system group.&amp;quot; ;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                9) ;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                *) echo &amp;quot;ERROR: failed to create system group ${_pretty_grname}!&amp;quot;; return 1;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            esac&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            return 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        # System groups required by a package.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        for grp in ${system_groups}; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            group_add $grp || return 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        # System user&#x2F;group required by a package.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        for acct in ${system_users}; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            _uname=&amp;quot;${acct%:*}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            _uid=&amp;quot;${acct##*:}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ &amp;quot;${_uname}&amp;quot; = &amp;quot;${_uid}&amp;quot; ] &amp;amp;&amp;amp; _uid=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            eval homedir=&amp;quot;\$${_uname}_homedir&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            eval shell=&amp;quot;\$${_uname}_shell&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            eval descr=&amp;quot;\$${_uname}_descr&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            eval groups=&amp;quot;\$${_uname}_groups&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            eval pgroup=&amp;quot;\$${_uname}_pgroup&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ -z &amp;quot;$homedir&amp;quot; ] &amp;amp;&amp;amp; homedir=&amp;quot;&#x2F;var&#x2F;empty&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ -z &amp;quot;$shell&amp;quot; ] &amp;amp;&amp;amp; shell=&amp;quot;&#x2F;usr&#x2F;bin&#x2F;nologin&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ -z &amp;quot;$descr&amp;quot; ] &amp;amp;&amp;amp; descr=&amp;quot;${_uname} user&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            [ -n &amp;quot;$groups&amp;quot; ] &amp;amp;&amp;amp; user_groups=&amp;quot;-G $groups&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            if [ -n &amp;quot;${_uid}&amp;quot; ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                use_id=&amp;quot;-u ${_uid} -g ${pgroup:-${_uid}}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                _pretty_uname=&amp;quot;${_uname} (uid: ${_uid})&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                use_id=&amp;quot;-g ${pgroup:-${_uname}}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                _pretty_uname=&amp;quot;${_uname}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            if [ -z &amp;quot;$USERADD&amp;quot; -o -z &amp;quot;$USERMOD&amp;quot; ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                echo &amp;quot;WARNING: cannot create ${_uname} system account (missing useradd or usermod)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                echo &amp;quot;The following system account must be created:&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                show_acct_details &amp;quot;${_pretty_uname}&amp;quot; &amp;quot;${descr}&amp;quot; &amp;quot;${homedir}&amp;quot; &amp;quot;${shell}&amp;quot; &amp;quot;${groups}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                continue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            group_add ${pgroup:-${acct}} || return 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            ${USERADD} -c &amp;quot;${descr}&amp;quot; -d &amp;quot;${homedir}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                ${use_id} ${pgroup:+-N} -s &amp;quot;${shell}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                ${user_groups} -r ${_uname} &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            case $? in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                0)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    echo &amp;quot;Created ${_pretty_uname} system user.&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    ${USERMOD} -L ${_uname} &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    if [ $? -ne 0 ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        echo &amp;quot;WARNING: unable to lock password for ${_uname} system account&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    ;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                9)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    ${USERMOD} -c &amp;quot;${descr}&amp;quot; -d &amp;quot;${homedir}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        -s &amp;quot;${shell}&amp;quot; -g &amp;quot;${pgroup:-${_uname}}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        ${user_groups} ${_uname} &amp;gt;&#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    if [ $? -eq 0 ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        echo &amp;quot;Updated ${_uname} system user.&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        echo &amp;quot;WARNING: unable to modify ${_uname} system account&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        echo &amp;quot;Please verify that account is compatible with these settings:&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        show_acct_details &amp;quot;${_pretty_uname}&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                            &amp;quot;${descr}&amp;quot; &amp;quot;${homedir}&amp;quot; &amp;quot;${shell}&amp;quot; &amp;quot;${groups}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                        continue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    ;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                *)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    echo &amp;quot;ERROR: failed to create system user ${_pretty_uname}!&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    return 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;                    ;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            esac&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        return 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    _system_accounts_invoke &amp;#39;chrony&amp;#39; &amp;#39;4.4&amp;#39; || exit $?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see this not super pretty and the “pre-install” &amp;amp; “pre-upgrade” scripts are
duplicated.&lt;&#x2F;p&gt;
&lt;p&gt;Enter &lt;code&gt;systemd-sysusers&lt;&#x2F;code&gt;. Now the &lt;code&gt;chrony&lt;&#x2F;code&gt; package includes a file
&lt;code&gt;sysusers.conf&lt;&#x2F;code&gt;, which is installed into &lt;code&gt;&#x2F;usr&#x2F;lib&#x2F;sysusers.d&#x2F;chrony.conf&lt;&#x2F;code&gt; when the package
is installed:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# Create chrony system user&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;u _chrony - &amp;quot;chrony user&amp;quot; &#x2F;var&#x2F;lib&#x2F;chrony &#x2F;usr&#x2F;bin&#x2F;nologin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When building the package &lt;code&gt;cbuild&lt;&#x2F;code&gt; detects the presence of a file installed
into &lt;code&gt;usr&#x2F;lib&#x2F;sysusers.d&lt;&#x2F;code&gt; and adds a runtime dependency on the &lt;code&gt;systemd-utils&lt;&#x2F;code&gt;
package, which contains the &lt;code&gt;systemd-sysusers&lt;&#x2F;code&gt; binary.&lt;&#x2F;p&gt;
&lt;p&gt;In turn, the &lt;code&gt;systemd-utils&lt;&#x2F;code&gt; package contains a trigger.
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chimera-linux&#x2F;cports&#x2F;blob&#x2F;2ff6e8bdd6e3e5f6663f0aa19200f7ce75d84cc2&#x2F;Packaging.md#hooks-and-triggers&quot;&gt;Triggers&lt;&#x2F;a&gt; are a concept built into the &lt;code&gt;apk&lt;&#x2F;code&gt; package manager.
A package can have one trigger script that is run whenever a package changes
the contents of a “monitored” directory. In this case the &lt;code&gt;systemd-utils&lt;&#x2F;code&gt;
trigger is run whenever &lt;code&gt;&#x2F;usr&#x2F;lib&#x2F;syusers.d&lt;&#x2F;code&gt; or &lt;code&gt;&#x2F;usr&#x2F;lib&#x2F;tmpfiles.d&lt;&#x2F;code&gt; changes
(&lt;code&gt;systemd-tmpfiles&lt;&#x2F;code&gt; is a story for another day).&lt;&#x2F;p&gt;
&lt;p&gt;As far as the &lt;code&gt;systemd-sysusers&lt;&#x2F;code&gt; part of the trigger script is concerned it
runs &lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;systemd-sysusers&lt;&#x2F;code&gt;, which uses the declarative contents of
&lt;code&gt;&#x2F;usr&#x2F;lib&#x2F;syusers.d&lt;&#x2F;code&gt; to determine what system users and groups should exist and
be active, then makes changes as needed.&lt;&#x2F;p&gt;
&lt;p&gt;This is the &lt;code&gt;adbdump&lt;&#x2F;code&gt; of &lt;code&gt;systemd-utils-254-r5.44c71395.apk&lt;&#x2F;code&gt; showing the
trigger script and its monitored directories:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  trigger&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    #!&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    # package script&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    set -e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    # invoking sysusers is always harmless&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &#x2F;usr&#x2F;bin&#x2F;systemd-sysusers || :&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    # always create&#x2F;remove&#x2F;set&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    TMPFILES_ARGS=&amp;quot;--create --remove&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    # a little heuristical but unassuming with userland&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    # the idea is that if &#x2F;run is mounted, it&amp;#39;s probably a running system&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    # (doesn&amp;#39;t matter if container or real) and has pseudo-filesystems&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    # in place, otherwise we avoid messing with them&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    if [ ! -r &#x2F;proc&#x2F;self&#x2F;mounts -o ! -x &#x2F;usr&#x2F;bin&#x2F;awk ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        # bare system, don&amp;#39;t mess with pseudofs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        TMPFILES_ARGS=&amp;quot;$TMPFILES_ARGS -E&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        RUN_FSTYPE=$(&#x2F;usr&#x2F;bin&#x2F;awk &amp;#39;{if ($2 == &amp;quot;&#x2F;run&amp;quot;) print $1;}&amp;#39; &#x2F;proc&#x2F;self&#x2F;mounts)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        RUN_FSTYPE=$(&#x2F;usr&#x2F;bin&#x2F;awk &amp;#39;{if ($2 == &amp;quot;&#x2F;run&amp;quot;) print $1;}&amp;#39; &#x2F;proc&#x2F;self&#x2F;mounts)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        if [ &amp;quot;$RUN_FSTYPE&amp;quot; != &amp;quot;tmpfs&amp;quot; ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            # &#x2F;run is not mounted or is something bad, don&amp;#39;t mess with pseudofs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;            TMPFILES_ARGS=&amp;quot;$TMPFILES_ARGS -E&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;        fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    &#x2F;usr&#x2F;bin&#x2F;systemd-tmpfiles $TMPFILES_ARGS || :&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;triggers&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt; # 2 items&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; &#x2F;usr&#x2F;lib&#x2F;sysusers.d&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; &#x2F;usr&#x2F;lib&#x2F;tmpfiles.d&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see individual packages are now much simpler and all the complexity
of managing the system users and groups is delegated to &lt;code&gt;systemd-sysusers&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;An added benefit of the removal of hook scripts is package actions (such as
add&#x2F;delete) become more atomic. For example if a “pre-install” hook script is
run and makes changes, then the install step fails the changes made by the
script will not be rolled back. In contrast trigger scripts are only run after
the package action has successfully been committed. If something fails during
the package action the transaction will be rolled back before any scripts are
run.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wrap-up&quot;&gt;Wrap Up&lt;&#x2F;h3&gt;
&lt;p&gt;I found learning about this change valuable for better understanding how a
hidden aspect of package management works, I hope you did too. If you’d like to
see more posts like this feel free to let me know.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Fixing OpenBSD panic dc_atapi_start: not ready in KVM</title>
      <pubDate>Sun, 17 Sep 2023 12:13:24 +1000</pubDate>
      <atom:published>2023-09-17T12:13:24+10:00</atom:published>
      <atom:updated>2023-09-22T11:56:57+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;openbsd-db-atapi-start-not-ready&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;openbsd-db-atapi-start-not-ready&#x2F;</guid>
      <description>&lt;p&gt;I tried creating an OpenBSD 7.3 virtual machine on my new computer (Arch Linux
host) and the installer kept crashing with the error:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;openbsd-db-atapi-start-not-ready&amp;#x2F;openbsd_panic_dc_atapi_start_not_ready.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;openbsd-db-atapi-start-not-ready&amp;#x2F;openbsd_panic_dc_atapi_start_not_ready.png&quot; alt=&quot;Screenshot of the installer crash.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Screenshot of the installer crash.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dc_atapi_start: not ready, st = 50&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fatal protection fault in supervisor mode trap type 4 code 0 rip ffffffff810089d9 cs 8 rflags 10282 cr2 287eb3000 cpl 6 rsp ffff800014fd11a0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gssbase Oxffffffff818fbff0 kgsbase Ox0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;panic: trap type 4, code=0, pc=ffffffff810089d9&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;syncing disks...12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 _&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;I did a bunch of searching online and tried a several different suggestions but that one that
worked for me was from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;openbsd&#x2F;comments&#x2F;12jzg2y&#x2F;when_i_tried_to_install_openbsd_73_in_qemu_i&#x2F;jhhk1gx&#x2F;&quot;&gt;this Reddid thread&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;tinneriw31&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Switch the virtual cd drive from ide to sata. Worked for me. Exact same issue.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;virt-manager.org&#x2F;&quot;&gt;virt-manager&lt;&#x2F;a&gt; to manage VMs. These are the steps to do that when creating the VM:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create the VM and at the last step choose “Customize configuration before install”&lt;&#x2F;li&gt;
&lt;li&gt;Click on the “IDE CDROM 1” tab and change “Disk bus” to SATA&lt;&#x2F;li&gt;
&lt;li&gt;Then click Apply, and then Begin installation in the top left.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;After that the VM installed successfully.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;openbsd-db-atapi-start-not-ready&amp;#x2F;virt-manager-customize-configuration.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;openbsd-db-atapi-start-not-ready&amp;#x2F;virt-manager-customize-configuration.png&quot; width=&quot;504&quot; alt=&quot;Screenshot of step 5 in the new virtual machine wizard in virt-manager showing the &amp;#x27;Customize configuration before install&amp;#x27; option checked.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Customize configuration before install.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;openbsd-db-atapi-start-not-ready&amp;#x2F;virt-manager-sata-cd-rom.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;openbsd-db-atapi-start-not-ready&amp;#x2F;virt-manager-sata-cd-rom.png&quot; width=&quot;1028&quot; alt=&quot;Screenshot of the virt-manager CD ROM tab showing &amp;#x27;Disk bus: SATA&amp;#x27; selected.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Disk bus: SATA&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
</description>
    </item>
    
    <item>
      <title>Australian and New Zealand Makers on YouTube</title>
      <pubDate>Tue, 11 Apr 2023 10:04:27 +1000</pubDate>
      <atom:published>2023-04-11T10:04:27+10:00</atom:published>
      <atom:updated>2023-04-14T12:16:17+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;anz-youtube-makers&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;anz-youtube-makers&#x2F;</guid>
      <description>&lt;p&gt;I decided I wanted to add some more local folks into my YouTube subscriptions.
I &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.decentralised.social&#x2F;@wezm&#x2F;110165869937514088&quot;&gt;put the call out on Mastodon&lt;&#x2F;a&gt; for suggestions for folks doing videos
about machining, woodworking, electronics, software, that type of thing. I
received a number of helpful replies and thought it might be useful to collect
the list (as well as ones I’m already subscribed to) on this page in case
others are looking for new channels to check out.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@AndrewNewton&quot;&gt;Andrew Newton&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@artisanmakes&quot;&gt;Artisan Makes&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@BensWorx&quot;&gt;BensWorx&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@Clickspring&quot;&gt;Clickspring&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@CuttingEdgeEngineering&quot;&gt;Cutting Edge Engineering Australia&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@EEVblog&quot;&gt;EEVblog&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@iforce2d&quot;&gt;iforce2d&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@LukeTowan&quot;&gt;Luke Towan&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@machsuper&quot;&gt;Mach Super&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@MakersMuse&quot;&gt;Maker’s Muse&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@MattKC&quot;&gt;MattKC&lt;&#x2F;a&gt; (Matt moved to the US but he still counts 🙂)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@MooreTech&quot;&gt;MooreTech&lt;&#x2F;a&gt; – It’s me!&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@PaskMakes&quot;&gt;Pask Makes&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@primitivetechnology9550&quot;&gt;Primitive Technology&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@psivewri&quot;&gt;Psivewri&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@SuperHouseTV&quot;&gt;SuperHouseTV&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@UnexpectedMaker&quot;&gt;Unexpected Maker&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;That’s the list for now. If you have other suggestions (especially if they can
mix up the diversity of the list a bit) please let me know.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Building a Classic Mac OS App in Rust</title>
      <pubDate>Fri, 31 Mar 2023 13:26:07 +1000</pubDate>
      <atom:published>2023-03-31T13:26:07+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;rust-classic-mac-os-app&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;rust-classic-mac-os-app&#x2F;</guid>
      <description>&lt;p&gt;Instead of using my funemployment to build useful things I have continued to
build things for old versions of Mac OS. Through some luck and a little
persistence I have actually managed to get Rust code running on classic Mac OS
(I’ve tried Mac OS 7.5 and 8.1). In this post I’ll cover how I got here and
show a little network connected demo application I built—just in time for
the end of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.marchintosh.com&#x2F;&quot;&gt;#MARCHintosh&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Before I get into the details this is where we’re headed:&lt;&#x2F;p&gt;





&lt;figure class=&quot;text-center&quot;&gt;
  &lt;video controls preload=&quot;auto&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;rust-classic-mac-os-app&amp;#x2F;ferris-weather-2023-03-31_12.37.49_edit.mp4&quot; poster=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;rust-classic-mac-os-app&amp;#x2F;ferris-weather-2023-03-31_12.37.49_edit.mp4.png&quot;  style=&quot;max-height: 480px&quot; aria-label=&quot;Video showing the Ferris Weather application in operation. Initially there is a window with a Ferris icon, the text, &amp;#x27;An application that exercises my Open Transport Rust bindings and HTTP client&amp;#x27;, and a button &amp;#x27;Get Weather&amp;#x27;. Clicking the button results in an alert that says: &amp;#x27;The temperature in Brisbane is 26.9°C&amp;#x27;.&quot;&gt;&lt;&#x2F;video&gt;
  &lt;figcaption&gt;Ferris Weather, a weather app built with Rust (and some C)&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;derez-redux&quot;&gt;DeRez Redux&lt;&#x2F;h3&gt;
&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;derez&#x2F;&quot;&gt;the last post&lt;&#x2F;a&gt; I got Nim code running on Mac
OS and toyed with DeRez. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;vulpine.club&#x2F;@Ninji&#x2F;110053455721324087&quot;&gt;The author of &lt;code&gt;mpu-emu&lt;&#x2F;code&gt; replied on Mastodon&lt;&#x2F;a&gt;
letting me know that DeRez &lt;em&gt;should&lt;&#x2F;em&gt; run via &lt;code&gt;mpw-emu&lt;&#x2F;code&gt; on Linux as the
filesystem layer transparently handles &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;MacBinary&quot;&gt;MacBinary&lt;&#x2F;a&gt; files.&lt;&#x2F;p&gt;
&lt;p&gt;I spent some time in the debugger and worked out that &lt;code&gt;mpw-emu&lt;&#x2F;code&gt; supported
MacBinary III but &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;autc04&#x2F;Retro68&quot;&gt;Retro68&lt;&#x2F;a&gt; produced MacBinary II files. I contributed code
to &lt;code&gt;mpw-emu&lt;&#x2F;code&gt; to add MacBinary II support and enabled some latent support for
UNIX paths. After that DeRez did work (almost):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ mpw-emu ~&#x2F;Documents&#x2F;Classic\ Mac&#x2F;Shared\ 2&#x2F;DeRez.bin Root:home:wmoore:Projects:classic-mac-rust:cmake-build-retro68ppc:Dialog.bin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[2023-03-31T04:51:34Z ERROR emulator] Unimplemented call to InterfaceLib::SetFScaleDisable @10012C6C&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[2023-03-31T04:51:34Z ERROR stdio] Unimplemented format character: P&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[2023-03-31T04:51:34Z ERROR emulator] Unimplemented call to InterfaceLib::SecondsToDate @1000B2A4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;data &amp;#39;DITL&amp;#39; (128) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;0007 0000 0000 00A0 00E6 00B4 0136 0404&amp;quot;            &#x2F;* .......†.Ê.¥.6.. *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;5175 6974 0000 0000 009B 00E1 00B9 013B&amp;quot;            &#x2F;* Quit.....õ.·.π.; *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;0000 0000 0000 0046 000A 005A 0136 0818&amp;quot;            &#x2F;* .......F...Z.6.. *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;436F 6E76 6572 7369 6F6E 2070 6F77 6572&amp;quot;            &#x2F;* Conversion power *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;6564 2062 7920 5E30 0000 0000 001E 000A&amp;quot;            &#x2F;* ed by ^0........ *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;003E 002A A002 0080 0000 0000 0014 0032&amp;quot;            &#x2F;* .&amp;gt;.*†..Ä.......2 *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;0024 007D 8807 4365 6C73 6975 7300 0000&amp;quot;            &#x2F;* .$.}à.Celsius... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;0000 0014 00AA 0024 00F5 8809 4661 7265&amp;quot;            &#x2F;* .....™.$.ıà∆Fare *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;6E68 6569 7400 0000 0000 0029 0036 0039&amp;quot;            &#x2F;* nheit......).6.9 *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;0081 1002 3235 0000 0000 002B 00AE 003B&amp;quot;            &#x2F;* .Å..25.....+.Æ.; *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;00F9 1002 3737&amp;quot;                                     &#x2F;* .˘..77 *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;* etc *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Success! DeRez running on Linux… only thing is that when you point at the type
definitions to get structured output instead of hex dumps it hits an
unimplemented function in &lt;code&gt;mpw-emu&lt;&#x2F;code&gt;. It’s on my to-do list to fix that:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ mpw-emu ~&#x2F;Documents&#x2F;Classic\ Mac&#x2F;Shared\ 2&#x2F;DeRez.bin Root:home:wmoore:Projects:classic-mac-rust:cmake-build-retro68ppc:Dialog.bin Root:home:wmoore:Source:github.com:autc04:Retro68:InterfacesAndLibraries:Interfaces\&amp;amp;Libraries:Interfaces:RIncludes:Carbon.r&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[2023-03-31T04:53:07Z ERROR emulator] Unimplemented call to InterfaceLib::SetFScaleDisable @10012C6C&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[2023-03-31T04:53:07Z ERROR stdio] Unimplemented format character: P&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[2023-03-31T04:53:07Z ERROR emulator] Unimplemented call to InterfaceLib::SecondsToDate @1000B2A4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[2023-03-31T04:53:07Z ERROR emulator] Unimplemented call to StdCLib::fseek @10006A8C&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;File &amp;quot;Root:home:wmoore:Source:github.com:autc04:Retro68:InterfacesAndLibraries:Interfaces&amp;amp;Libraries:Interfaces:RIncludes:CoreServices.r&amp;quot;; Line 0; ### &#x2F;home&#x2F;wmoore&#x2F;Documents&#x2F;Classic Mac&#x2F;Shared 2&#x2F;DeRez.bin - Can&amp;#39;t FSeek on file Root:home:wmoore:Source:github.com:autc04:Retro68:InterfacesAndLibraries:Interfaces&amp;amp;Libraries:Interfaces:RIncludes:CoreServices.r.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;File &amp;quot;Root:home:wmoore:Source:github.com:autc04:Retro68:InterfacesAndLibraries:Interfaces&amp;amp;Libraries:Interfaces:RIncludes:CoreServices.r&amp;quot;; Line 0; ### &#x2F;home&#x2F;wmoore&#x2F;Documents&#x2F;Classic Mac&#x2F;Shared 2&#x2F;DeRez.bin - Fatal Error, can&amp;#39;t recover.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;macbinary&quot;&gt;MacBinary&lt;&#x2F;h3&gt;
&lt;p&gt;Poking at the MacBinary code in &lt;code&gt;mpw-emu&lt;&#x2F;code&gt; got me wondering if there was already
a MacBinary crate that could be used. Turns out there wasn’t so I somehow
nerd-sniped myself into &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lib.rs&#x2F;crates&#x2F;macbinary&quot;&gt;building one&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The first challenge was finding a decent specification for the three versions
of MacBinary. I was eventually I was able to dig up the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20050307030202&#x2F;http:&#x2F;&#x2F;www.lazerware.com&#x2F;formats&#x2F;macbinary&#x2F;macbinary.html&quot;&gt;MacBinary I&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20050305042909&#x2F;http:&#x2F;&#x2F;www.lazerware.com&#x2F;formats&#x2F;macbinary&#x2F;macbinary_ii.html&quot;&gt;MacBinary II&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20050305044255&#x2F;http:&#x2F;&#x2F;www.lazerware.com&#x2F;formats&#x2F;macbinary&#x2F;macbinary_iii.html&quot;&gt;MacBinary III&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I then set about building the parser. I reused the binary parser code from
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;yeslogic&#x2F;allsorts&quot;&gt;Allsorts&lt;&#x2F;a&gt; since I was already familiar with that code. I hit another roadblock
when it came to the CRC in the header. Nothing describes the actual CRC
algorithm used. I tried the CRC reversing tool &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;reveng.sourceforge.io&#x2F;&quot;&gt;CRC RevEng&lt;&#x2F;a&gt; without
success. A lot of existing code seemed to use an implementation that originated
in a late 80’s UNIX utility, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;web.mit.edu&#x2F;~mkgray&#x2F;jik&#x2F;sipbsrc&#x2F;src&#x2F;mcvert&#x2F;mcvert.c&quot;&gt;mcvert&lt;&#x2F;a&gt;, that has unclear licensing. I wanted to
use the Rust &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lib.rs&#x2F;crates&#x2F;crc&quot;&gt;crc crate&lt;&#x2F;a&gt; instead.&lt;&#x2F;p&gt;
&lt;p&gt;I eventually stumbled on the blog post,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;entropymine.wordpress.com&#x2F;2019&#x2F;02&#x2F;13&#x2F;detecting-macbinary-format&#x2F;&quot;&gt;Detecting MacBinary format&lt;&#x2F;a&gt;,
which included the line:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that the spec does not even tell you what CRC algorithm to use — you
have to be a detective to figure it out. (It’s the one sometimes called
CRC16-CCITT.)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;That was the tip I needed and with a little trial an error I eventually worked
out that it was &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;reveng.sourceforge.io&#x2F;crc-catalogue&#x2F;16.htm#crc.cat.crc-16-ibm-3740&quot;&gt;CRC-16&#x2F;XMODEM&lt;&#x2F;a&gt; also known as &lt;code&gt;CRC-16&#x2F;CCITT-FALSE&lt;&#x2F;code&gt;. In
hindsight I could probably have worked this out from the discussion of XMODEM
in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20050307030202&#x2F;http:&#x2F;&#x2F;www.lazerware.com&#x2F;formats&#x2F;macbinary&#x2F;macbinary.html&quot;&gt;MacBinary I spec&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;With that sorted I was able to wrap up the parser and do some testing. I could
now read the resource and data forks and figured it would be interesting to be
able to parse the resource data too, so I added a resource fork parser as well.&lt;&#x2F;p&gt;
&lt;p&gt;I wrote the parsers in a way that does not require heap allocation—only
borrowing from the underlying data. Due to this it was straightforward to make
the crate compatible with &lt;code&gt;no_std&lt;&#x2F;code&gt;, which allows it to be used in embedded
environments and WebAssembly.&lt;&#x2F;p&gt;
&lt;p&gt;As something of a test-bed I created some
WebAssembly bindings and built a page that allows you to inspect MacBinary
files online, with all parsing done client-side via the crate. You can
try it out at: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;7bit.org&#x2F;macbinary&#x2F;&quot;&gt;https:&#x2F;&#x2F;7bit.org&#x2F;macbinary&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;rust-on-mac-os&quot;&gt;Rust on Mac OS&lt;&#x2F;h3&gt;
&lt;p&gt;Now that I was well and truly in the classic Mac space again I took another
stab at compiling Rust for PPC Mac OS
(&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;rust-on-ppc-classic-mac-os&#x2F;&quot;&gt;see this post for my previous attempt&lt;&#x2F;a&gt;).
It seemed that using the
&lt;code&gt;powerpc-ibm-aix&lt;&#x2F;code&gt; LLVM target was most likely to produce a compatible library
(Apple used AIX conventions for PPC Mac OS). Problem was that it was hitting
unimplemented code in LLVM:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;LLVM ERROR: relocation for paired relocatable term is not yet supported&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I set about trying to work out how this code path was being hit and ran &lt;code&gt;rustc&lt;&#x2F;code&gt;
in a debugger. Unsurprisingly there were no debug symbols so I built &lt;code&gt;rustc&lt;&#x2F;code&gt;
and LLVM from source. This was my &lt;code&gt;config.toml&lt;&#x2F;code&gt; for the Rust repo:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;llvm&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;release-debuginfo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;download-ci-llvm&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;link-jobs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After repeatedly running out of disk space and memory compiling LLVM (the
binaries with debug info are huge) I eventually had new Rust compiler.&lt;&#x2F;p&gt;
&lt;p&gt;Some of the LLVM binaries:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.rwxr-xr-x 2.0G wmoore 26 Mar 20:05 llc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.rwxr-xr-x 2.1G wmoore 26 Mar 20:10 llvm-opt-fuzzer&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.rwxr-xr-x 2.1G wmoore 26 Mar 20:04 bugpoint&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.rwxr-xr-x 2.2G wmoore 26 Mar 20:09 llvm-lto2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.rwxr-xr-x 2.2G wmoore 26 Mar 20:06 llvm-lto&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.rwxr-xr-x 2.2G wmoore 26 Mar 20:11 opt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.rwxr-xr-x 2.3G wmoore 26 Mar 20:11 llvm-reduce&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I linked the new compiler into &lt;code&gt;rustup&lt;&#x2F;code&gt; and then repeated my previous steps in
the debugger… except this time the code compiled and did not hit the
unimplemented LLVM code. This was my first lucky break. I’m not sure what
changed but it was now happily compiling the code. I switched to a recent
nightly compiler and that worked too! No need to build from source.&lt;&#x2F;p&gt;
&lt;p&gt;I repeated the step described in my original post of using
&lt;code&gt;powerpc-linux-gnu-objcopy&lt;&#x2F;code&gt; to convert the static library archive (&lt;code&gt;.a&lt;&#x2F;code&gt;) to a
format that Retro68 would accept. After some fighting with &lt;code&gt;binutils&lt;&#x2F;code&gt; I was
finally able to get it to link!&lt;&#x2F;p&gt;
&lt;iframe src=&quot;https:&#x2F;&#x2F;mastodon.decentralised.social&#x2F;@wezm&#x2F;110098546361010915&#x2F;embed&quot; class=&quot;mastodon-embed&quot; style=&quot;max-width: 100%; border: 0&quot; width=&quot;400&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;&#x2F;iframe&gt;&lt;script src=&quot;https:&#x2F;&#x2F;mastodon.decentralised.social&#x2F;embed.js&quot; async=&quot;async&quot;&gt;&lt;&#x2F;script&gt;
&lt;p&gt;I rebuilt the temperature converter that I’d built in Nim in Rust (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;classic-mac-rust&quot;&gt;source code&lt;&#x2F;a&gt;) and ran into
more linker&#x2F;&lt;code&gt;binutils&lt;&#x2F;code&gt; issues. After a &lt;em&gt;lot&lt;&#x2F;em&gt; of trial-and-error and some more
luck I was able to solve that by using the updated &lt;code&gt;binutils&lt;&#x2F;code&gt; on the
&lt;code&gt;gcc12-update branch&lt;&#x2F;code&gt; branch of Retro68. I now had a working temperature
converter:&lt;&#x2F;p&gt;





&lt;figure class=&quot;text-center&quot;&gt;
  &lt;video controls preload=&quot;auto&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;rust-classic-mac-os-app&amp;#x2F;classic-mac-rust-2023-03-28_20.00.31.mp4&quot; poster=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;rust-classic-mac-os-app&amp;#x2F;classic-mac-rust-2023-03-28_20.00.31.mp4.png&quot;  style=&quot;max-height: 480px&quot; aria-label=&quot;Video of the temperature converter converting values to and from Celsius, running on Mac OS 8.1 (in emulator).&quot;&gt;&lt;&#x2F;video&gt;
  &lt;figcaption&gt;The temperature converter application ported to Rust&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;It worked on Mac OS 7.5 too:&lt;&#x2F;p&gt;


  


  


&lt;figure class=&quot;text-center figure-border figure-pixelated&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;rust-classic-mac-os-app&amp;#x2F;rust-on-mac-os-7.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;rust-classic-mac-os-app&amp;#x2F;rust-on-mac-os-7.png&quot; alt=&quot;Screenshot of the temperature converter application running on Mac OS 7.5 (in emulator).&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Temperature converter application running on Mac OS 7.5&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The Rust version is a bit more efficient than the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;classic-mac-nim&quot;&gt;Nim version&lt;&#x2F;a&gt; as
it avoids some copying and heap allocation. That latter of which because I’m
coding in a &lt;code&gt;no_std&lt;&#x2F;code&gt; environment without a heap.&lt;&#x2F;p&gt;
&lt;p&gt;The Rust standard library is divided into three main parts (crates):&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;core&lt;&#x2F;code&gt; for things that do not require heap allocation, I&#x2F;O, etc.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;alloc&lt;&#x2F;code&gt; for things that use heap allocation but not I&#x2F;O etc.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;std&lt;&#x2F;code&gt;, the rest: files, networking, threads, etc. &lt;code&gt;std&lt;&#x2F;code&gt; re-exports the
other two.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;By defining a custom allocator that called &lt;code&gt;malloc&lt;&#x2F;code&gt; and &lt;code&gt;free&lt;&#x2F;code&gt; provided by the
Retro68 environment I was able to use the &lt;code&gt;alloc&lt;&#x2F;code&gt; crate in addition to &lt;code&gt;core&lt;&#x2F;code&gt;.
This gained me access &lt;code&gt;String&lt;&#x2F;code&gt;, &lt;code&gt;Vec&lt;&#x2F;code&gt;, and friends.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;networking&quot;&gt;Networking&lt;&#x2F;h4&gt;
&lt;p&gt;I now wanted to build something a little more involved than a single dialog. I
set about building bindings to Open Transport, Apple’s network stack introduced
with PCI Power Macs (like my 9500).&lt;&#x2F;p&gt;
&lt;p&gt;Due to its heritage most of the Mac OS toolbox functions use the Pascal calling
convention, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;llvm&#x2F;llvm-project&#x2F;blob&#x2F;bd20a344bbf813b2c39b57ad1a5248bff915ce25&#x2F;clang&#x2F;lib&#x2F;CodeGen&#x2F;CGCall.cpp#L60&quot;&gt;which LLVM does not support&lt;&#x2F;a&gt;. To bridge the C (and
Rust) world to this Pascal world I had to create &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Trampoline_(computing)&quot;&gt;trampoline functions&lt;&#x2F;a&gt; in C
for each toolbox function that I wanted to call from Rust (if there’s a better
way to do this I’d love to know how). This works because &lt;code&gt;gcc&lt;&#x2F;code&gt; in Retro68
understands both C and Pascal calling conventions. I appended an underscore to
each of the wrapper functions. For example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;OSStatus&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; OTConnect_&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;EndpointRef ref&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; TCall &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;sndCall&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; TCall &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;rcvCall&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; OTConnect&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ref&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; sndCall&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; rcvCall&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I used the “Downloading a URL With HTTP” example from the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.apple.com&#x2F;library&#x2F;archive&#x2F;documentation&#x2F;mac&#x2F;NetworkingOT&#x2F;NetworkingOpenTransport.pdf&quot;&gt;Networking With Open Transport&lt;&#x2F;a&gt;
book as a guide for the functions I needed. Once the bindings were created I
implemented the &lt;code&gt;TcpClientStack&lt;&#x2F;code&gt; trait from the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;embedded-nal&#x2F;latest&#x2F;embedded_nal&#x2F;&quot;&gt;embedded-nal&lt;&#x2F;a&gt; (embedded network abstraction layer)
crate against Open Transport. Next I used this with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lib.rs&#x2F;crates&#x2F;http_io&quot;&gt;http_io&lt;&#x2F;a&gt; crate to be able
to make HTTP requests.&lt;&#x2F;p&gt;
&lt;p&gt;As an initial test I wrote an app to fetch a friend’s website (since it’s
available over plain HTTP) and show an alert with the number of bytes read.
Amazingly this worked on the first try: the Open Transport bindings, the
&lt;code&gt;TcpClientStack&lt;&#x2F;code&gt; implementation, the HTTP client, and my test code all worked!&lt;&#x2F;p&gt;
&lt;p&gt;Finally I used my newfound networking abilities to build &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;ferris-weather&quot;&gt;Ferris
Weather&lt;&#x2F;a&gt;, the application shown at the start of the post. This
uses the HTTP client to fetch a JSON file containing weather observations,
parses it with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;serde.rs&#x2F;&quot;&gt;serde&lt;&#x2F;a&gt; and then shows an alert with the most recent
observation. I also drew a little 1-bit &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rustacean.net&#x2F;&quot;&gt;Ferris the Rustacean&lt;&#x2F;a&gt; in
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ResEdit&quot;&gt;ResEdit&lt;&#x2F;a&gt; for it.&lt;&#x2F;p&gt;
&lt;p&gt;The idea for this was prompted by the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.bom.gov.au&#x2F;&quot;&gt;Australian Bureau of Meteorology&lt;&#x2F;a&gt;
still being accessible over HTTP. Unfortunately it wasn’t working and after a
lot of debugging I eventually discovered that I triggering their anti-scraping
blocker for some reason. To work around this I copied a snapshot of the JSON to
my own server. So, unfortunately the data shown by the application does not
update but you still get the idea.&lt;&#x2F;p&gt;


  


  


&lt;figure class=&quot;text-center figure-border figure-pixelated&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;rust-classic-mac-os-app&amp;#x2F;Ferris%20Weather.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;rust-classic-mac-os-app&amp;#x2F;Ferris%20Weather.png&quot; alt=&quot;Screenshot of the Ferris Weather application showing an alert with the temperature in Brisbane.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Ferris Weather&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;So there you have it, that’s how I built an application in Rust (and some C)
for classic Mac OS. The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;ferris-weather&quot;&gt;source code to Ferris Weather&lt;&#x2F;a&gt; is on
GitHub.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;next&quot;&gt;Next&lt;&#x2F;h3&gt;
&lt;p&gt;My intention is to take a bit of a break from classic Mac OS for a bit and work
on some other projects—ones that might be useful to people in this century—but
there are some things I want to look at when I come back to it:&lt;&#x2F;p&gt;
&lt;p&gt;First is TLS support for the HTTP client. I think this should be relatively
straightforward with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lib.rs&#x2F;crates&#x2F;embedded-tls&quot;&gt;embedded-tls&lt;&#x2F;a&gt; crate.&lt;&#x2F;p&gt;
&lt;p&gt;Next I’d like to improve how Open Transport is used. I think with either
the synchronous, non-blocking mode I’m using now or the asynchronous mode it
should be possible to tie it into the async Rust ecosystem, which would allow
it to play nicer with the event loop and cooperative multi-tasking.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, so far I’ve been working without the full Rust standard library, only
&lt;code&gt;core&lt;&#x2F;code&gt; and &lt;code&gt;alloc&lt;&#x2F;code&gt;. It seems like it should be possible to implement a lot of
the remaining standard library (io, networking), on top of the Mac OS toolbox,
but that’s a lot of work and will have to wait for another time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hire-me&quot;&gt;Hire Me&lt;&#x2F;h3&gt;
&lt;p&gt;As mentioned at the start of this post I’m currently taking a break from
employment but I will be looking for a new role next month, so if you’re looking
for a Rust developer get in touch.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>How to use DeRez</title>
      <pubDate>Mon, 20 Mar 2023 12:44:38 +1000</pubDate>
      <atom:published>2023-03-20T12:44:38+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;derez&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;derez&#x2F;</guid>
      <description>&lt;p&gt;After my post on trying to run
&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;rust-on-ppc-classic-mac-os&#x2F;&quot;&gt;Rust on Classic Mac OS post&lt;&#x2F;a&gt; I continued trying to
find a modern language that I can use to build classic Mac OS software. I’ve
had some success with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nim-lang.org&#x2F;&quot;&gt;Nim&lt;&#x2F;a&gt; and built a little
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;classic-mac-nim&#x2F;tree&#x2F;39e6ed7c2b31c20b775782319cde8ae5a43e1512&quot;&gt;temperature converter application&lt;&#x2F;a&gt;. As part of this I wanted to be able to use
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ResEdit&quot;&gt;ResEdit&lt;&#x2F;a&gt; to edit the layout of the dialog. The problem was that I need a way
to convert the modified resources back into the textual representation used in
the source code. In this post I describe how I did this with &lt;code&gt;DeRez&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  &lt;video controls preload=&quot;auto&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;derez&amp;#x2F;mac-nim-temp.mp4&quot; poster=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;derez&amp;#x2F;mac-nim-temp.mp4.png&quot;  style=&quot;max-height: 227px&quot; aria-label=&quot;Video of the temperature converter application.&quot;&gt;&lt;&#x2F;video&gt;
  &lt;figcaption&gt;Video of the temperature converter application.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;To build the temperature converter I started with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;autc04&#x2F;Retro68&#x2F;tree&#x2F;5f882506013a0a8a4335350197a1b7c91763494e&#x2F;Samples&#x2F;Dialog&quot;&gt;Dialog sample&lt;&#x2F;a&gt; from
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;autc04&#x2F;Retro68&quot;&gt;Retro68&lt;&#x2F;a&gt;, which looks like this:&lt;&#x2F;p&gt;



  


&lt;figure class=&quot;text-center figure-pixelated&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;derez&amp;#x2F;Dialog.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;derez&amp;#x2F;Dialog.png&quot; alt=&quot;Screenshot of the Dialog sample from Retro68. It has a static text item, edit text item, check box, two radio buttons, and a Quit button.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Dialog Sample&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I opened it up in ResEdit and edited the &lt;code&gt;DITL&lt;&#x2F;code&gt; (Dialog Template) resource to
add the icon and temperature fields. I also added a new &lt;code&gt;ICON&lt;&#x2F;code&gt; resource and
drew a little thermometer:&lt;&#x2F;p&gt;



  


&lt;figure class=&quot;text-center figure-pixelated&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;derez&amp;#x2F;DITL.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;derez&amp;#x2F;DITL.png&quot; alt=&quot;Screenshot of the ResEdit DITL editor editing the DITL resource for my temperature converter.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Editing DITL resource in ResEdit&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;With the changes made, I now wanted to convert the binary resources stored in
the resource fork back into the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;autc04&#x2F;Retro68&#x2F;blob&#x2F;5f882506013a0a8a4335350197a1b7c91763494e&#x2F;Samples&#x2F;Dialog&#x2F;dialog.r&quot;&gt;textual format used in the source code&lt;&#x2F;a&gt;.
I believe the format is called &lt;code&gt;Rez&lt;&#x2F;code&gt;, here’s a snippet of it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;resource &amp;#39;DITL&amp;#39; (128) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 190-10-20, 320-10-80, 190-10, 320-10 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		Button { enabled, &amp;quot;Quit&amp;quot; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 190-10-20-5, 320-10-80-5, 190-10+5, 320-10+5 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		UserItem { enabled };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 10, 10, 30, 310 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		StaticText { enabled, &amp;quot;Static Text Item&amp;quot; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 40, 10, 56, 310 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		EditText { enabled, &amp;quot;Edit Text Item&amp;quot; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 70, 10, 86, 310 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		CheckBox { enabled, &amp;quot;Check Box&amp;quot; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 90, 10, 106, 310 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		RadioButton { enabled, &amp;quot;Radio 1&amp;quot; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 110, 10, 126, 310 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		RadioButton { enabled, &amp;quot;Radio 2&amp;quot; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This turned out to be a bit of journey and the motivation for this blog post.
As part of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Macintosh_Programmer%27s_Workshop&quot;&gt;Macintosh Programmers Workshop&lt;&#x2F;a&gt; (MPW) theres is a tool
called &lt;code&gt;DeRez&lt;&#x2F;code&gt; that does what I want. First up I had to work out how to operate
MPW. It’s an editable shell where you run commands with ⌘-Return. Once I worked
that out I could run &lt;code&gt;DeRez&lt;&#x2F;code&gt; on my edited application but I only got the
fallback hexadecimal representation of the resources, not the structured output
I wanted:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;data &amp;#39;DITL&amp;#39; (128) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;0007 0000 0000 00A0 00E6 00B4 0136 0404&amp;quot;            &#x2F;* ....... .æ.´.6.. *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;5175 6974 0000 0000 009B 00E1 00B9 013B&amp;quot;            &#x2F;* Quit......á.¹.; *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;0000 0000 0000 0046 000A 005A 0136 0808&amp;quot;            &#x2F;* .......F...Z.6.. *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;4865 6C6C 6F20 5E30 0000 0000 001E 000A&amp;quot;            &#x2F;* Hello ^0........ *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;003E 002A A002 0597 0000 0000 0014 0032&amp;quot;            &#x2F;* .&amp;gt;.* .........2 *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;0024 007D 8807 4365 6C73 6975 7300 0000&amp;quot;            &#x2F;* .$.}.Celsius... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;0000 0014 00AA 0024 00F5 8809 4661 7265&amp;quot;            &#x2F;* .....ª.$.õÆFare *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;6E68 6569 7400 0000 0000 0029 0036 0039&amp;quot;            &#x2F;* nheit......).6.9 *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;0081 1009 4564 6974 2054 6578 7400 0000&amp;quot;            &#x2F;* ..ÆEdit Text... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;0000 002B 00AE 003B 00F9 1009 4564 6974&amp;quot;            &#x2F;* ...+.®.;.ù.ÆEdit *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	$&amp;quot;2054 6578 7400&amp;quot;                                     &#x2F;*  Text. *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;Help DeRez&lt;&#x2F;code&gt; in MPW didn’t shed much light on the problem but after a lot of
searching online I eventually found some extra details in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.manpagez.com&#x2F;man&#x2F;1&#x2F;DeRez&#x2F;&quot;&gt;man page for
&lt;code&gt;DeRez&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; shipped on Mac OS X. Specifically:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The type declarations for the standard
Macintosh resources are contained in the &lt;code&gt;Carbon.r&lt;&#x2F;code&gt; resource header file,
contained in the Carbon framework.  You may use the ${RIncludes} shell
environment variable to define a default path to resource header files.
If you do not specify any type declaration files, &lt;code&gt;DeRez&lt;&#x2F;code&gt; produces data
statements in hexadecimal form.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;and&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also specify resource description
files containing type declarations.  For each type declaration file on
the command line, DeRez applies the following search rules:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;DeRez tries to open the file with the name specified as is.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;If rule 1 fails and the filename contains no colons or begins with a
colon, DeRez appends the filename to each of the pathnames specified by
the {RIncludes} environment variable and tries to open the file.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;With this information I was able to construct a command that worked:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;DeRez -i &amp;#39;Macintosh HD:MPW-GM:Interfaces&amp;amp;Libraries:Interfaces:RIncludes:&amp;#39; &amp;quot;Macintosh HD:Retro68:Retro68App&amp;quot; Carbon.r&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;-i&lt;&#x2F;code&gt; sets the include path for type declarations and &lt;code&gt;Carbon.r&lt;&#x2F;code&gt; tells it to use
that file for resource descriptions. Running the command I was now rewarded
with textual resources:&lt;&#x2F;p&gt;



  


&lt;figure class=&quot;text-center figure-pixelated&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;derez&amp;#x2F;MPW.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;derez&amp;#x2F;MPW.png&quot; alt=&quot;Screenshot of an MPW worksheet on Mac OS 8 showing the output of running DeRez on an application.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;DeRez output in MPW&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;To get the text out of the VM I copied and pasted it into a new document in
&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.barebones.com&#x2F;products&#x2F;bbedit&#x2F;index.html&quot;&gt;BBEdit&lt;&#x2F;a&gt; (version 5.0) and saved it with Unix line endings to the Unix folder
that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sheepshaver.cebix.net&#x2F;&quot;&gt;SheepShaver&lt;&#x2F;a&gt; shares with the host and with that I was able to update the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;classic-mac-nim&#x2F;blob&#x2F;39e6ed7c2b31c20b775782319cde8ae5a43e1512&#x2F;dialog.r&quot;&gt;resource file in my temperature converter project&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;honorable-mention&quot;&gt;Honorable Mention&lt;&#x2F;h3&gt;
&lt;p&gt;Whilst trying to work out how to do all this I was also reminded of Ninji’s
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Treeki&#x2F;mpw-emu&quot;&gt;mpw-emu&lt;&#x2F;a&gt; project (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wuffs.org&#x2F;blog&#x2F;emulating-mac-compilers&quot;&gt;detailed write-up on their blog&lt;&#x2F;a&gt;). It
combines an emulator with implementations of library functions in order to be
able to run MPW tools directly (outside a Mac OS emulator). It has gained
support for &lt;code&gt;DeRez&lt;&#x2F;code&gt; so you can run &lt;code&gt;DeRez&lt;&#x2F;code&gt; directly on a host system like
Linux.&lt;&#x2F;p&gt;
&lt;p&gt;I &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;MacBinary&quot;&gt;MacBinaried&lt;&#x2F;a&gt; &lt;code&gt;DeRez&lt;&#x2F;code&gt; in SheepShaver and copied it to my Linux host. Then with a bit
of fussing with &lt;code&gt;mpw-emu&lt;&#x2F;code&gt; Rust code I was able to run it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ mpw-emu ~&#x2F;Documents&#x2F;Classic\ Mac&#x2F;Shared\ 2&#x2F;DeRez.bin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[2023-03-20T02:11:07Z ERROR emulator] Unimplemented call to InterfaceLib::SetFScaleDisable @10012C6C&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[2023-03-20T02:11:07Z ERROR stdio] Unimplemented format character: P&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[2023-03-20T02:11:07Z ERROR emulator] Unimplemented call to InterfaceLib::SecondsToDate @1000B2A4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;### &#x2F;home&#x2F;wmoore&#x2F;Documents&#x2F;Classic Mac&#x2F;Shared 2&#x2F;DeRez.bin - No filename to de-compile was specified.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;### &#x2F;home&#x2F;wmoore&#x2F;Documents&#x2F;Classic Mac&#x2F;Shared 2&#x2F;DeRez.bin - Usage: &#x2F;home&#x2F;wmoore&#x2F;Documents&#x2F;Classic Mac&#x2F;Shared 2&#x2F;DeRez.bin resourceFile [-c] [-d name[=value]] [-e] [-i path] [-m n] [-noResolve [output | include]] [-only type[(id[:id])]] [-p] [-rd] [-s type[(id[:id])]] [-script japanese | tradChinese | simpChinese | korean] [-u name] [file…].&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Amazing!&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately I don’t think &lt;code&gt;DeRez&lt;&#x2F;code&gt; will work this way outside a macOS host. It
needs to be able to read the resource fork of the application I edited with
ResEdit and that is not preserved on Linux:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ mpw-emu ~&#x2F;Documents&#x2F;Classic\ Mac&#x2F;Shared\ 2&#x2F;DeRez.bin Dialog.APPL&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[2023-03-20T02:14:05Z ERROR emulator] Unimplemented call to InterfaceLib::SetFScaleDisable @10012C6C&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[2023-03-20T02:14:05Z ERROR stdio] Unimplemented format character: P&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[2023-03-20T02:14:05Z ERROR emulator] Unimplemented call to InterfaceLib::SecondsToDate @1000B2A4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;### &#x2F;home&#x2F;wmoore&#x2F;Documents&#x2F;Classic Mac&#x2F;Shared 2&#x2F;DeRez.bin - The resource fork of &amp;quot;Dialog.APPL&amp;quot; is empty and uninitialized.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you’re on macOS I think that this would actually work. Although now I think
about it Xcode ships (or at least used to) a native version of &lt;code&gt;DeRez&lt;&#x2F;code&gt; so now
I’m not sure what Ninji’s motivation for making it work in &lt;code&gt;mpw-emu&lt;&#x2F;code&gt; was.
Perhaps it is possible to use on Linux somehow…&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Creating a Podcast From a Mastodon Account With XSLT</title>
      <pubDate>Wed, 01 Mar 2023 18:33:33 +1000</pubDate>
      <atom:published>2023-03-01T18:33:33+10:00</atom:published>
      <atom:updated>2023-03-01T21:54:06+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;xslt-podcast&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;xslt-podcast&#x2F;</guid>
      <description>&lt;aside class=&quot;float-right&quot;&gt;
  &lt;div class=&quot;emoji text-center&quot;&gt;💡&lt;&#x2F;div&gt;
  &lt;strong&gt;Just want the feed?&lt;&#x2F;strong&gt;

  &lt;p&gt;Here you go:&lt;br&gt;
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;files.wezm.net&#x2F;atprewind.rss&quot;&gt;ATPrewind podcast feed&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;I recently discovered the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;social.tupo.space&#x2F;@ATPrewind&quot;&gt;ATPrewind account on Mastodon&lt;&#x2F;a&gt;. It’s an account
sharing “gems discovered while re-listening to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@atpfm&quot;&gt;@atpfm&lt;&#x2F;a&gt; from the very first
episode. By &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;social.tupo.space&#x2F;@joshua&quot;&gt;@joshua&lt;&#x2F;a&gt;”. ATP is a tech Podcast that’s been running for about 10
years. Each post (so far) from ATPrewind includes a short clip from the show in the
form of a little video.&lt;&#x2F;p&gt;
&lt;p&gt;This post describes how I was nerd sniped into creating a podcast from the ATPrewind posts.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;It all started when &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.decentralised.social&#x2F;@wezm&#x2F;109940341596949214&quot;&gt;I posted the following&lt;&#x2F;a&gt; on Mastodon:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ahh this ATP Rewind account is gold https:&#x2F;&#x2F;social.tupo.space&#x2F;@ATPrewind&lt;&#x2F;p&gt;
&lt;p&gt;Keep up the great work @joshua&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@kgrz&#x2F;109942702497796855&quot;&gt;Kashyap replied&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Indeed! This should also be a podcast 🙃&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This was a great idea and it got me thinking about how to do it with the least
amount of effort.&lt;&#x2F;p&gt;
&lt;p&gt;In this day and age I imagine many programmers would reach for
their favourite programming language and code up something to generate a
podcast feed (real podcasts are just RSS). Perhaps using the Mastodon API or
similar and then work out a way to host their program.&lt;&#x2F;p&gt;
&lt;p&gt;With &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=d-tsfUVg4II&quot;&gt;my recent experience with Deno Deploy&lt;&#x2F;a&gt; fresh in my mind I
considered using it. However I opted for a decidedly late 90s solution: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.w3.org&#x2F;TR&#x2F;xslt-30&#x2F;&quot;&gt;XSLT&lt;&#x2F;a&gt;.
According to Wikipedia “XSLT is a language originally designed for transforming
XML documents into other XML documents”. The ‘originally’ refers to fact that
you can now generate any text with it, not just XML.
Since it was created in the era of “XML ALL THE THINGS”, XSL templates are
themselves XML documents. It also makes extensive use of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.w3.org&#x2F;TR&#x2F;xpath-31&#x2F;&quot;&gt;XPath&lt;&#x2F;a&gt; expressions to
select nodes and extract their content.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;creating-a-podcast-feed&quot;&gt;Creating a Podcast Feed&lt;&#x2F;h3&gt;
&lt;p&gt;Every Mastodon account has an RSS feed so I created an XSL template to process
the ATPrewind RSS feed and add the missing elements required to turn it into a
valid podcast feed. With some help from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;leancrew.com&#x2F;all-this&#x2F;2022&#x2F;08&#x2F;filtering-my-rss-reading&#x2F;&quot;&gt;this Dr. Drang&lt;&#x2F;a&gt; post this is
what I came up with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #5E81AC;&quot;&gt;xml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; version&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; encoding&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;utf-8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ?&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;stylesheet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; version&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; xmlns&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;http:&#x2F;&#x2F;www.w3.org&#x2F;1999&#x2F;XSL&#x2F;Transform&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; xmlns&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;media&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;http:&#x2F;&#x2F;search.yahoo.com&#x2F;mrss&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;output&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; method&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;xml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; encoding&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;utf-8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; indent&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;yes&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&amp;lt;!-- First, get everything. --&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;template&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; match&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;node() | @*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;   &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;copy&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;       &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;apply-templates&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; select&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;node() | @*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;   &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;copy&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;template&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&amp;lt;!-- Update items to include title and enclosure elements. --&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;template&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; match&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;rss&#x2F;channel&#x2F;item&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;   &amp;lt;item&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;     &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;apply-templates&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; select&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;node()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;     &amp;lt;title&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Post on &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;value-of&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; select&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;pubDate&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&amp;gt;&amp;lt;&#x2F;title&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;     &amp;lt;enclosure&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; url&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;{media:content&#x2F;@url}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; length&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;{media:content&#x2F;@fileSize}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;audio&#x2F;mp4; codecs=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;quot&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;mp4a.40.2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;quot&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;&#x2F;item&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;template&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;stylesheet&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;XML noise aside this document should be fairly self explanatory. It copies all
nodes from the source RSS feed, then processes each &lt;code&gt;item&lt;&#x2F;code&gt; element. To each one
it adds a &lt;code&gt;title&lt;&#x2F;code&gt; element derived from the text “Post on” and the publication
date of the post, and an &lt;code&gt;enclosure&lt;&#x2F;code&gt; element derived from the &lt;code&gt;media:content&lt;&#x2F;code&gt;
element.&lt;&#x2F;p&gt;
&lt;p&gt;I lie a bit by saying that the enclosure MIME type is &lt;code&gt;audio&#x2F;mp4; codecs=&quot;mp4a.40.2&quot;&lt;&#x2F;code&gt;. I.e. AAC-LC in MP4 container for the video in each post.
Running &lt;code&gt;curl -L https:&#x2F;&#x2F;social.tupo.space&#x2F;@ATPrewind.rss | xsltproc podcast.xsl&lt;&#x2F;code&gt; produces the podcast feed.&lt;&#x2F;p&gt;
&lt;p&gt;It took a few tries to get it to work but eventually I was able to convince
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;overcast.fm&#x2F;&quot;&gt;Overcast&lt;&#x2F;a&gt; that it was a real podcast. One trick that I’m exploiting here is
that the MP4 container for video and audio is the same, the audio only version
is just lacking the video stream. I figured that podcast players might still be
able to play the video and at least for Overcast it works:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;xslt-podcast&amp;#x2F;overcast-screenshot.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;xslt-podcast&amp;#x2F;overcast-screenshot.png&quot; width=&quot;393&quot; alt=&quot;Screenshot of Overcast showing the podcast &amp;#x27;episodes&amp;#x27;.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Screenshot of Overcast showing the &amp;#x27;episodes&amp;#x27;.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;deployment&quot;&gt;Deployment&lt;&#x2F;h3&gt;
&lt;p&gt;To deploy this contraption I created a Docker image with &lt;code&gt;curl&lt;&#x2F;code&gt; and &lt;code&gt;libxslt&lt;&#x2F;code&gt;
installed:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;docker&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; wezm-alpine:3.17.2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;# UID needs to match owner of &#x2F;home&#x2F;rss&#x2F;feeds volume&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ARG&lt;&#x2F;span&gt;&lt;span&gt; PUID=1000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ARG&lt;&#x2F;span&gt;&lt;span&gt; PGID=1000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ARG&lt;&#x2F;span&gt;&lt;span&gt; USER=rss&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span&gt; addgroup -g ${PGID} ${USER} &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    adduser -D -u ${PUID} -G ${USER} -h &#x2F;home&#x2F;${USER} -D ${USER}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span&gt; apk --update add curl libxslt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;entrypoint.sh &#x2F;home&#x2F;${USER}&#x2F;entrypoint.sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;podcast.xsl &#x2F;home&#x2F;${USER}&#x2F;podcast.xsl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;WORKDIR&lt;&#x2F;span&gt;&lt;span&gt; &#x2F;home&#x2F;${USER}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;USER&lt;&#x2F;span&gt;&lt;span&gt; ${USER}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;VOLUME&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;&#x2F;home&#x2F;rss&#x2F;feeds&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ENTRYPOINT&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;.&#x2F;entrypoint.sh&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I made a small script as the entrypoint to the container:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;#!&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;set&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; -e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;trap&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;exit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; TERM INT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;while&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; true&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;  curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; -L https:&#x2F;&#x2F;social.tupo.space&#x2F;@ATPrewind.rss&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; xsltproc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; podcast.xsl -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; &#x2F;home&#x2F;rss&#x2F;feeds&#x2F;atprewind.rss&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;  sleep&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 3600&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt; # 1 hour&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It regenerates the podcast feed each hour. I bind mount the directory for
files.wezm.net into the container in my Docker Compose config, which takes
advantage of the fact that nginx is already serving that directory.&lt;&#x2F;p&gt;
&lt;p&gt;Some may argue that the Docker part of this is not in the spirit of, “least
amount of effort”, but I already have all this set up on my server so adding
one more container is very little effort. I’ve written previously about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;technical&#x2F;2019&#x2F;02&#x2F;alpine-linux-docker-infrastructure&#x2F;&quot;&gt;my
Alpine Linux server&lt;&#x2F;a&gt; if you’d like to read more.&lt;&#x2F;p&gt;
&lt;p&gt;Once I pushed the Docker image the podcast was live. The URL is:&lt;br&gt;
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;files.wezm.net&#x2F;atprewind.rss&quot;&gt;https:&#x2F;&#x2F;files.wezm.net&#x2F;atprewind.rss&lt;&#x2F;a&gt; if you’d like to subscribe.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;future-work&quot;&gt;Future Work&lt;&#x2F;h3&gt;
&lt;p&gt;I whipped all this up before work today and made an assumption that might not
always hold: there is a single video media attachment on each post. So far
that’s true but I should update the XSL template to only try to generate an
&lt;code&gt;enclosure&lt;&#x2F;code&gt; element if the attachment is present. It would also be a good idea
to filter for audio and video only in case a post appears with images.&lt;&#x2F;p&gt;
&lt;p&gt;For now I shall eagerly look forward to the next post appearing in Overcast.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Trying to Run Rust on Classic Mac OS</title>
      <pubDate>Mon, 27 Feb 2023 10:06:28 +1000</pubDate>
      <atom:published>2023-02-27T10:06:28+10:00</atom:published>
      <atom:updated>2023-03-26T14:27:05+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;rust-on-ppc-classic-mac-os&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;rust-on-ppc-classic-mac-os&#x2F;</guid>
      <description>&lt;p&gt;I recently acquired a Power Macintosh 9500&#x2F;150 and after cleaning it up and
building a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;erichelgeson&#x2F;BlueSCSI&quot;&gt;BlueSCSI&lt;&#x2F;a&gt; to replace the failed hard drive it’s now in a
semi-operational state. This weekend I thought I’d see if I could build a Mac
app for it that called some Rust code. This post details my trials and
tribulations.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;I started by building &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;autc04&#x2F;Retro68&quot;&gt;Retro68&lt;&#x2F;a&gt;, which is a modernish GCC based toolchain
that allows cross-compiling applications for 68K and PPC Macs. With Retro68
built I set up a VM in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sheepshaver.cebix.net&#x2F;&quot;&gt;SheepShaver&lt;&#x2F;a&gt; running Mac OS 8.1. Using the LaunchAAPL
and LaunchAAPLServer tools that come with Retro68 I was able to build the
sample applications and launch them in the emulated Mac.&lt;&#x2F;p&gt;
&lt;p&gt;With the basic workflow working I set about creating a Rust project that built
a static library with one very basic exported function. It just returns a
static &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;String_(computer_science)#Length-prefixed&quot;&gt;Pascal string&lt;&#x2F;a&gt; when called.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;&quot;&gt;#![no_std]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;&quot;&gt;#![feature(lang_items)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt; core&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;panic&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;PanicInfo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;static&lt;&#x2F;span&gt;&lt;span&gt; MSG&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\x04&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Rust&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;&quot;&gt;#[no_mangle]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub unsafe extern&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;C&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; hello_rust&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt; *const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; u8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    MSG&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;as_ptr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;&quot;&gt;#[panic_handler]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; panic&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;_panic&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;PanicInfo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt; !&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    loop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;&quot;&gt;#[lang = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;eh_personality&lt;&#x2F;span&gt;&lt;span style=&quot;color: #5E81AC;&quot;&gt;&amp;quot;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;extern&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;C&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; eh_personality&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;() {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;&quot;&gt;#[cfg(test)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;mod&lt;&#x2F;span&gt;&lt;span&gt; tests&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    use super::*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;&quot;&gt;    #[test]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; test_msg_is_pascal_string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt;        assert_eq!&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;MSG&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;],&lt;&#x2F;span&gt;&lt;span&gt; MSG&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;try_into&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;());&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Classic Mac OS is not a target that Rust knows about so I created a custom
target JSON definition named &lt;code&gt;powerpc-apple-macos.json&lt;&#x2F;code&gt; based on prior work by
kmeisthax in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;autc04&#x2F;Retro68&#x2F;discussions&#x2F;123#discussioncomment-597268&quot;&gt;this GitHub discussion&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;arch&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;powerpc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;data-layout&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;E-m:a-p:32:32-i64:64-n32&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;executables&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; true&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;llvm-target&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;powerpc-unknown-none&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;max-atomic-width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 32&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;os&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;macosclassic&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;vendor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;apple&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;target-endian&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;big&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;target-pointer-width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;32&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;linker&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;powerpc-apple-macos-gcc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;linker-flavor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;gcc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;linker-is-gnu&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I was able to build the static library with this cargo invocation:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cargo +nightly build --release -Z build-std=core --target powerpc-apple-macos.json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It’s using nightly because it’s using unstable features to build &lt;code&gt;core&lt;&#x2F;code&gt; and the
&lt;code&gt;eh_personality&lt;&#x2F;code&gt; lang item in the code.&lt;&#x2F;p&gt;
&lt;p&gt;This successfully compiles and produces
&lt;code&gt;target&#x2F;powerpc-apple-macos&#x2F;release&#x2F;libclassic_mac_rust.a&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I used the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;autc04&#x2F;Retro68&#x2F;tree&#x2F;5f882506013a0a8a4335350197a1b7c91763494e&#x2F;Samples&#x2F;Dialog&quot;&gt;Dialog sample&lt;&#x2F;a&gt; from Retro68 as the basis of my Mac app. Here it is
running prior to Rust integration:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;rust-on-ppc-classic-mac-os&amp;#x2F;dialog-sample.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;rust-on-ppc-classic-mac-os&amp;#x2F;dialog-sample.png&quot; alt=&quot;Screenshot of SheepShaver running Mac OS 8.1. It shows some Finder windows with a frontmost dialog that has a text label, text field, radio buttons, check box and Quit button.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Dialog Sample&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;This is my tweaked version of the C file:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    Copyright 2015 Wolfgang Thaller.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    This file is part of Retro68.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    Retro68 is free software: you can redistribute it and&#x2F;or modify&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    it under the terms of the GNU General Public License as published by&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    the Free Software Foundation, either version 3 of the License, or&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    (at your option) any later version.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    Retro68 is distributed in the hope that it will be useful,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    GNU General Public License for more details.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    You should have received a copy of the GNU General Public License&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    along with Retro68.  If not, see &amp;lt;http:&#x2F;&#x2F;www.gnu.org&#x2F;licenses&#x2F;&amp;gt;.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Quickdraw.h&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Dialogs.h&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Fonts.h&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#ifndef&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; TARGET_API_MAC_CARBON&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    &#x2F;* NOTE: this is checking whether the Dialogs.h we use *knows* about Carbon,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;             not whether we are actually compiling for Cabon.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;             If Dialogs.h is older, we add a define to be able to use the new name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;             for NewUserItemUPP, which used to be NewUserItemProc. *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;define&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; NewUserItemUPP&lt;&#x2F;span&gt;&lt;span style=&quot;color: #5E81AC;&quot;&gt; NewUserItemProc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#endif&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;extern&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; ConstStringPtr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; hello_rust&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pascal &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; ButtonFrameProc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;DialogRef dlg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; DialogItemIndex itemNo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    DialogItemType type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    Handle&lt;&#x2F;span&gt;&lt;span&gt; itemH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Rect box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    GetDialogItem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;dlg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;itemH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    InsetRect&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    PenSize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    FrameRoundRect&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; !&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;TARGET_API_MAC_CARBON&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    InitGraf&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;qd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;thePort&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    InitFonts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    InitWindows&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    InitMenus&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    TEInit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    InitDialogs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;NULL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#endif&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    DialogPtr dlg &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; GetNewDialog&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;128&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,(&lt;&#x2F;span&gt;&lt;span&gt;WindowPtr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    InitCursor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    SelectDialogItemText&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;dlg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;32767&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ConstStr255Param param1 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; hello_rust&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    ParamText&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;param1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    DialogItemType type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    Handle&lt;&#x2F;span&gt;&lt;span&gt; itemH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Rect box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    GetDialogItem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;dlg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;itemH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    SetDialogItem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;dlg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Handle&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; NewUserItemUPP&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;ButtonFrameProc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ControlHandle cb&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; radio1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; radio2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    GetDialogItem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;dlg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;itemH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cb &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;ControlHandle&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;itemH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    GetDialogItem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;dlg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;itemH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    radio1 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;ControlHandle&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;itemH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    GetDialogItem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;dlg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;itemH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    radio2 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;ControlHandle&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;itemH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    SetControlValue&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;radio1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    short&lt;&#x2F;span&gt;&lt;span&gt; item&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    do&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;        ModalDialog&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;NULL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;item&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;item &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt; item &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;        {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;            if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;item &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;                SetControlValue&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;cb&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; !&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;GetControlValue&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;cb&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;            if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;item &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ||&lt;&#x2F;span&gt;&lt;span&gt; item &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;            {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;                SetControlValue&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;radio1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; item &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;                SetControlValue&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;radio2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; item &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; while&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;item &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;!=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    FlushEvents&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;everyEvent&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And this is the resource file (&lt;code&gt;dialog.r&lt;&#x2F;code&gt;):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	Copyright 2015 Wolfgang Thaller.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	This file is part of Retro68.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	Retro68 is free software: you can redistribute it and&#x2F;or modify&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	it under the terms of the GNU General Public License as published by&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	the Free Software Foundation, either version 3 of the License, or&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	(at your option) any later version.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	Retro68 is distributed in the hope that it will be useful,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	GNU General Public License for more details.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	You should have received a copy of the GNU General Public License&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	along with Retro68.  If not, see &amp;lt;http:&#x2F;&#x2F;www.gnu.org&#x2F;licenses&#x2F;&amp;gt;.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#include &amp;quot;Dialogs.r&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;resource &amp;#39;DLOG&amp;#39; (128) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	{ 50, 100, 240, 420 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	dBoxProc,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	visible,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	noGoAway,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	0,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	128,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;quot;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	centerMainScreen&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;resource &amp;#39;DITL&amp;#39; (128) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 190-10-20, 320-10-80, 190-10, 320-10 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		Button { enabled, &amp;quot;Quit&amp;quot; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 190-10-20-5, 320-10-80-5, 190-10+5, 320-10+5 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		UserItem { enabled };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 10, 10, 30, 310 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		StaticText { enabled, &amp;quot;Hello ^0&amp;quot; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 40, 10, 56, 310 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		EditText { enabled, &amp;quot;Edit Text Item&amp;quot; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 70, 10, 86, 310 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		CheckBox { enabled, &amp;quot;Check Box&amp;quot; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 90, 10, 106, 310 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		RadioButton { enabled, &amp;quot;Radio 1&amp;quot; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{ 110, 10, 126, 310 },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		RadioButton { enabled, &amp;quot;Radio 2&amp;quot; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#include &amp;quot;Processes.r&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;resource &amp;#39;SIZE&amp;#39; (-1) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	reserved,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	acceptSuspendResumeEvents,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	reserved,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	canBackground,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	doesActivateOnFGSwitch,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	backgroundAndForeground,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	dontGetFrontClicks,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	ignoreChildDiedEvents,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	is32BitCompatible,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#ifdef TARGET_API_MAC_CARBON&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    isHighLevelEventAware,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	notHighLevelEventAware,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#endif&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	onlyLocalHLEvents,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	notStationeryAware,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	dontUseTextEditServices,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	reserved,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	reserved,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	reserved,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#ifdef TARGET_API_MAC_CARBON&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	500 * 1024,	&#x2F;&#x2F; Carbon apparently needs additional memory.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	500 * 1024&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	100 * 1024,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	100 * 1024&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#endif&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The main differences are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;extern&lt;&#x2F;code&gt; declaration for the Rust function&lt;&#x2F;li&gt;
&lt;li&gt;Using the string returned from &lt;code&gt;hello_rust&lt;&#x2F;code&gt; to set &lt;code&gt;ParamText&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Changing the StaticText control’s text to “Hello ^0” in order to make use of
the &lt;code&gt;ParamText&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Adding &lt;code&gt;target_link_libraries(Dialog ${CMAKE_SOURCE_DIR}&#x2F;target&#x2F;powerpc-apple-macos&#x2F;release&#x2F;libclassic_mac_rust.a)&lt;&#x2F;code&gt;
to &lt;code&gt;CMakeLists.txt&lt;&#x2F;code&gt; to have CMake link with the Rust library.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;rust-on-ppc-classic-mac-os&amp;#x2F;ParamText.jpg&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;rust-on-ppc-classic-mac-os&amp;#x2F;ParamText.jpg&quot; alt=&quot;Photo of ParamText documentation from my copy of Inside Macintosh Volume Ⅰ&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;ParamText documentation from my copy of Inside Macintosh Volume Ⅰ&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Now when building the project we get…&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ninja: Entering directory `cmake-build-retro68ppc&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[1&#x2F;4] Linking C executable Dialog.xcoff&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;FAILED: Dialog.xcoff&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;: &amp;amp;&amp;amp; &#x2F;home&#x2F;wmoore&#x2F;Source&#x2F;github.com&#x2F;autc04&#x2F;Retro68-build&#x2F;toolchain&#x2F;bin&#x2F;powerpc-apple-macos-gcc  -Wl,-gc-sections CMakeFiles&#x2F;Dialog.dir&#x2F;dialog.obj -o Dialog.xcoff  &#x2F;home&#x2F;wmoore&#x2F;Projects&#x2F;classic-mac-rust&#x2F;target&#x2F;powerpc-apple-macos&#x2F;release&#x2F;libclassic_mac_rust.a &amp;amp;&amp;amp; :&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;home&#x2F;wmoore&#x2F;Source&#x2F;github.com&#x2F;autc04&#x2F;Retro68-build&#x2F;toolchain&#x2F;lib&#x2F;gcc&#x2F;powerpc-apple-macos&#x2F;9.1.0&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;powerpc-apple-macos&#x2F;bin&#x2F;ld:&#x2F;home&#x2F;wmoore&#x2F;Projects&#x2F;classic-mac-rust&#x2F;target&#x2F;powerpc-apple-macos&#x2F;release&#x2F;libclassic_mac_rust.a: file format not recognized; treating as linker script&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;home&#x2F;wmoore&#x2F;Source&#x2F;github.com&#x2F;autc04&#x2F;Retro68-build&#x2F;toolchain&#x2F;lib&#x2F;gcc&#x2F;powerpc-apple-macos&#x2F;9.1.0&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;powerpc-apple-macos&#x2F;bin&#x2F;ld:&#x2F;home&#x2F;wmoore&#x2F;Projects&#x2F;classic-mac-rust&#x2F;target&#x2F;powerpc-apple-macos&#x2F;release&#x2F;libclassic_mac_rust.a:1: syntax error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;collect2: error: ld returned 1 exit status&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ninja: build stopped: subcommand failed.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It doesn’t like &lt;code&gt;libclassic_mac_rust.a&lt;&#x2F;code&gt;. Some investigation shows that the objects in the library
are in ELF format. &lt;code&gt;powerpc-apple-macos-objcopy --info&lt;&#x2F;code&gt; shows that Retro68 does not handle
ELF:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;BFD header file version (GNU Binutils) 2.31.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;xcoff-powermac&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; (header big endian, data big endian)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  powerpc:common&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  rs6000:6000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;srec&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; (header endianness unknown, data endianness unknown)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  powerpc:common&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  rs6000:6000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;symbolsrec&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; (header endianness unknown, data endianness unknown)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  powerpc:common&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  rs6000:6000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;verilog&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; (header endianness unknown, data endianness unknown)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  powerpc:common&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  rs6000:6000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;tekhex&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; (header endianness unknown, data endianness unknown)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  powerpc:common&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  rs6000:6000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;binary&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; (header endianness unknown, data endianness unknown)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  powerpc:common&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  rs6000:6000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ihex&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; (header endianness unknown, data endianness unknown)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  powerpc:common&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  rs6000:6000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;               xcoff-powermac srec symbolsrec verilog tekhex binary ihex&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;powerpc:common xcoff-powermac srec symbolsrec verilog tekhex binary ihex&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   rs6000:6000 xcoff-powermac srec symbolsrec verilog tekhex binary ihex&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It looks like it really only supports &lt;code&gt;xcoff-powermac&lt;&#x2F;code&gt;, which was derived from
rs6000 AIX. At this point I tried to find a way to convert my ELF objects to
XCOFF. I eventually stumbled across
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;discuss.haiku-os.org&#x2F;t&#x2F;xcoff-pef&#x2F;12445&#x2F;15&quot;&gt;this thread on the Haiku forum&lt;&#x2F;a&gt;
that mentions that &lt;code&gt;powerpc-linux-gnu-binutils&lt;&#x2F;code&gt; on Debian knows about
&lt;code&gt;aixcoff-rs6000&lt;&#x2F;code&gt;. So I fired up a Debian docker container and tried converting
my &lt;code&gt;.a&lt;&#x2F;code&gt;, and it worked:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;docker run --rm -it -v $(pwd):&#x2F;src debian:testing&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;apt update&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;apt install binutils-powerpc-linux-gnu&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;powerpc-linux-gnu-objcopy -O aixcoff-rs6000 &#x2F;src&#x2F;target&#x2F;powerpc-apple-macos&#x2F;release&#x2F;libclassic_mac_rust.a &#x2F;src&#x2F;target&#x2F;powerpc-apple-macos&#x2F;release&#x2F;libclassic_mac_rust.obj&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Examining the objects in the new archive showed that they were now in the same
format as the objects generated by Retro68. I updated the &lt;code&gt;CMakeLists.txt&lt;&#x2F;code&gt; to
point at the new library and tried building again:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;home&#x2F;wmoore&#x2F;Source&#x2F;github.com&#x2F;autc04&#x2F;Retro68-build&#x2F;toolchain&#x2F;lib&#x2F;gcc&#x2F;powerpc-apple-macos&#x2F;9.1.0&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;powerpc-apple-macos&#x2F;bin&#x2F;ld: &#x2F;home&#x2F;wmoore&#x2F;Projects&#x2F;classic-mac-rust&#x2F;target&#x2F;powerpc-apple-macos&#x2F;release&#x2F;libclassic_mac_rust.obj(classic_mac_rust-80e61781bab75910.classic_mac_rust.9ba2ce33-cgu.0.rcgu.o): class 2 symbol `hello_rust&amp;#39; has no aux entries&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we get further. It can read the &lt;code&gt;.a&lt;&#x2F;code&gt; now and even sees the &lt;code&gt;hello_rust&lt;&#x2F;code&gt;
symbol but it
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;autc04&#x2F;Retro68&#x2F;blob&#x2F;5f882506013a0a8a4335350197a1b7c91763494e&#x2F;binutils&#x2F;bfd&#x2F;xcofflink.c#L1461-L1478&quot;&gt;looks like it’s looking for an aux entry to determine the symbol type&lt;&#x2F;a&gt;
but not finding one. AUX entries are an
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.ibm.com&#x2F;docs&#x2F;en&#x2F;aix&#x2F;7.2?topic=formats-xcoff-object-file-format&quot;&gt;XCOFF&lt;&#x2F;a&gt;
thing.&lt;&#x2F;p&gt;
&lt;p&gt;One other thing I tried was setting the &lt;code&gt;llvm-target&lt;&#x2F;code&gt; in the custom target JSON
to &lt;code&gt;powerpc-ibm-aix&lt;&#x2F;code&gt;. Due to the heritage of PPC Mac OS the ABI is the same
(Apple used the AIX toolchain, which is why object files use XCOFF even though
executables use PEF). This target would be ideal as it would use the right ABI
and emit XCOFF by default.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately it runs into unimplemented parts of LLVM’s XCOFF implementation:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;LLVM ERROR: relocation for paired relocatable term is not yet supported&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Rust uses a fork&#x2F;snapshot of LLVM but the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;llvm-project&#x2F;blob&#x2F;5ef9f9948fca7cb39dd6c1935ca4e819fb7a0db2&#x2F;llvm&#x2F;lib&#x2F;MC&#x2F;XCOFFObjectWriter.cpp&quot;&gt;issue is still present in LLVM master&lt;&#x2F;a&gt;.
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;belkadan.com&#x2F;blog&#x2F;2020&#x2F;04&#x2F;Swift-on-Mac-OS-9&#x2F;&quot;&gt;This post on writing a Mac OS 9 application in Swift&lt;&#x2F;a&gt; goes down a
similar path using the AIX target and also mentions patching the Swift compiler
to avoid the unsupported parts of LLVMs XCOFF implementation. That’s an avenue
for future experimentation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;rustc-codegen-gcc&quot;&gt;rustc_codegen_gcc&lt;&#x2F;h3&gt;
&lt;p&gt;At this point I decided to try a different approach.
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rustc_codegen_gcc&quot;&gt;rustc_codegen_gcc&lt;&#x2F;a&gt; is a
codegen plugin that uses &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gcc.gnu.org&#x2F;onlinedocs&#x2F;jit&#x2F;&quot;&gt;libgccjit&lt;&#x2F;a&gt; for code generation instead of LLVM. The
motivation of the project is promising for my use case:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The primary goal of this project is to be able to compile Rust code on
platforms unsupported by LLVM.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I found the instructions for using &lt;code&gt;rustc_codegen_gcc&lt;&#x2F;code&gt; a bit difficult to
follow, especially when trying to build a cross-compiler.&lt;&#x2F;p&gt;
&lt;p&gt;I eventually managed to rebuild Retro68 with &lt;code&gt;libgccjit&lt;&#x2F;code&gt; enabled and then coax
&lt;code&gt;rustc_codegen_gcc&lt;&#x2F;code&gt; to use it. Unsurprisingly that quickly failed as Retro68 is
based on GCC 9.1 and  &lt;code&gt;rustc_codegen_gcc&lt;&#x2F;code&gt; is building against GCC master and
there were many missing symbols.&lt;&#x2F;p&gt;
&lt;p&gt;Undeterred I noted that there is a WIP GCC 12.2 branch in the Retro68 repo so I
built that and tweaked  &lt;code&gt;rustc_codegen_gcc&lt;&#x2F;code&gt; to disable the &lt;code&gt;master&lt;&#x2F;code&gt; cargo
feature that should in theory allow it to build against a GCC release. This did
in fact allow me to get a bit further but I ran into more issues in the step
that attempts to build &lt;code&gt;compiler-rt&lt;&#x2F;code&gt; and &lt;code&gt;core&lt;&#x2F;code&gt;. Eventually I gave up on this
route too. I was probably too far off the well tested configuration of x86,
against GCC master.&lt;&#x2F;p&gt;
&lt;p&gt;Future work here is to trying building a &lt;code&gt;powerpc-ibm-aix&lt;&#x2F;code&gt; libgccjit from GCC
master and see if that works.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wrap-up&quot;&gt;Wrap Up&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;turbolent&#x2F;status&#x2F;1617231570573873152&quot;&gt;Bastian on Twitter&lt;&#x2F;a&gt;
has had some success compiling Rust to Web Assembly, Web Assembly to C89, C89
to Mac OS 9 binary, which is definitely cool but I would still love to be able
to generate native PPC code directly from &lt;code&gt;rustc&lt;&#x2F;code&gt; somehow.&lt;&#x2F;p&gt;
&lt;p&gt;This is where I have parked this project for now. I actually only discovered
the post on building a Mac OS 9 application with Swift while writing this post.
There are perhaps some ideas in there that I could explore further.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Debugging a Docker Core Dump</title>
      <pubDate>Sat, 25 Feb 2023 10:39:49 +1000</pubDate>
      <atom:published>2023-02-25T10:39:49+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;systemd-gdb-coredumps&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;systemd-gdb-coredumps&#x2F;</guid>
      <description>&lt;p&gt;On my main machine I use an excellent cross-platform tool called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;stepchowfun&#x2F;docuum&quot;&gt;Docuum&lt;&#x2F;a&gt; that
automatically cleans up unused docker images. This allows me to use Docker
without the need to periodically wonder why I’m out of disk space, run &lt;code&gt;docker system prune&lt;&#x2F;code&gt; and recover half my disk.&lt;&#x2F;p&gt;
&lt;p&gt;I installed Docuum via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aur.archlinux.org&#x2F;packages&#x2F;docuum&quot;&gt;the AUR package&lt;&#x2F;a&gt; (although tweaked to build the
latest Docuum release) and ran it via the bundled systemd service definition.
This worked great for a while but some time back it started failing. Every time
Docuum would try to check for things to clean up I’d see the following in the
system journal:&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Feb 25 10:03:12 ryzen docuum[77751]: [2023-02-25 10:03:12 +10:00 INFO] Performing an initial vacuum on startup…&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Feb 25 10:03:12 ryzen kernel: audit: type=1326 audit(1677283392.831:2): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=77763 comm=&amp;quot;docker&amp;quot; exe=&amp;quot;&#x2F;usr&#x2F;bin&#x2F;docke&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Feb 25 10:03:12 ryzen systemd[1]: Created slice Slice &#x2F;system&#x2F;systemd-coredump.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Feb 25 10:03:12 ryzen systemd[1]: Started Process Core Dump (PID 77768&#x2F;UID 0).&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Feb 25 10:03:13 ryzen systemd-coredump[77769]: [🡕] Process 77763 (docker) of user 0 dumped core.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               Stack trace of thread 77763:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               #0  0x00005568dbcb5c4e n&#x2F;a (docker + 0x243c4e)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               #1  0x00005568dbd35a3b n&#x2F;a (docker + 0x2c3a3b)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               #2  0x00005568dbd3482f n&#x2F;a (docker + 0x2c282f)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               #3  0x00005568dbd6c2ee n&#x2F;a (docker + 0x2fa2ee)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               #4  0x00005568dbcfafa8 n&#x2F;a (docker + 0x288fa8)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               #5  0x00005568dbcfaef1 n&#x2F;a (docker + 0x288ef1)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               #6  0x00005568dbced953 n&#x2F;a (docker + 0x27b953)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               #7  0x00005568dbd1eb41 n&#x2F;a (docker + 0x2acb41)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               ELF object binary architecture: AMD x86-64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Feb 25 10:03:13 ryzen docuum[77751]: [2023-02-25 10:03:13 +10:00 ERROR] Unable to list images.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Feb 25 10:03:13 ryzen docuum[77751]: [2023-02-25 10:03:13 +10:00 INFO] Retrying in 5 seconds…&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Feb 25 10:03:13 ryzen systemd[1]: systemd-coredump@0-77768-0.service: Deactivated successfully.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This would repeat every 5 seconds. I ignored this for a while but finally decided
to investigate it today. To find the failing command I ran
&lt;code&gt;coredumpctl list&lt;&#x2F;code&gt; then identified one of the docker crashes and ran
&lt;code&gt;coredumpctl info&lt;&#x2F;code&gt; with its PID:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ coredumpctl info 78255&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;           PID: 78255 (docker)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;           UID: 0 (root)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;           GID: 0 (root)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        Signal: 31 (SYS)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     Timestamp: Sat 2023-02-25 10:03:23 AEST (44min ago)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Command Line: docker image ls --all --no-trunc --format $&amp;#39;{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.CreatedAt}}&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Executable: &#x2F;usr&#x2F;bin&#x2F;docker&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; Control Group: &#x2F;system.slice&#x2F;docuum.service&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          Unit: docuum.service&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;         Slice: system.slice&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       Boot ID: 0ac9f0dd246548949c3a90a0e7494665&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Machine ID: affcb0b7a7d1464385d65464d9be450e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Hostname: ryzen&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       Storage: &#x2F;var&#x2F;lib&#x2F;systemd&#x2F;coredump&#x2F;core.docker.0.0ac9f0dd246548949c3a90a0e7494665.78255.1677283403000000.zst (inaccessible)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       Message: Process 78255 (docker) of user 0 dumped core.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Strangely I could run the command myself (&lt;code&gt;Command Line&lt;&#x2F;code&gt; line) just fine. I
figured I needed to see where in docker it was crashing. I learned how to
access systemd coredumps with gdb and ran: &lt;code&gt;sudo coredumpctl gdb 78255&lt;&#x2F;code&gt;. &lt;code&gt;sudo&lt;&#x2F;code&gt;
is needed because the core dump belongs to root due the crashing &lt;code&gt;docker&lt;&#x2F;code&gt;
process belonging to root. This didn’t yield much extra info as debug symbols
were not present for the binary. It did identify why it crashed though:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Program terminated with signal SIGSYS, Bad system call.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Knowing that docker was implemented in Go and that they make system calls
manually on Linux I wondered if this was some sort of Go bug—although given
the popularity of Docker this did seem unlikely.&lt;&#x2F;p&gt;
&lt;p&gt;To get more info I needed debug symbols. Arch Linux makes these available via
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Debuginfod&quot;&gt;debuginfod&lt;&#x2F;a&gt; and gdb can
automatically download debuginfo files if &lt;code&gt;DEBUGINFOD_URLS&lt;&#x2F;code&gt; is set. I reran
&lt;code&gt;gdb&lt;&#x2F;code&gt;, telling &lt;code&gt;sudo&lt;&#x2F;code&gt; to pass the &lt;code&gt;DEBUGINFOD_URLS&lt;&#x2F;code&gt; environment variable
through (I had already set &lt;code&gt;DEBUGINFOD_URLS=https:&#x2F;&#x2F;debuginfod.archlinux.org&#x2F;&lt;&#x2F;code&gt;
in my Zsh config some time ago):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sudo --preserve-env=DEBUGINFOD_URLS coredumpctl gdb 78255&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now there was a proper backtrace:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#0  runtime&#x2F;internal&#x2F;syscall.Syscall6 () at runtime&#x2F;internal&#x2F;syscall&#x2F;asm_linux_amd64.s:36&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#1  0x00005588e7b93c33 in syscall.RawSyscall6 (num=160, a1=7, a2=94046491589710, a3=7, a4=824634289408, a5=0, a6=0, r1=&amp;lt;optimized out&amp;gt;, r2=&amp;lt;optimized out&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    errno=&amp;lt;optimized out&amp;gt;) at runtime&#x2F;internal&#x2F;syscall&#x2F;syscall_linux.go:38&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#2  0x00005588e7c13a3b in syscall.RawSyscall (trap=160, a1=7, a2=94046491589710, a3=7, r1=&amp;lt;optimized out&amp;gt;, r2=&amp;lt;optimized out&amp;gt;, err=&amp;lt;optimized out&amp;gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    at syscall&#x2F;syscall_linux.go:62&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#3  0x00005588e7c1282f in syscall.Setrlimit (resource=&amp;lt;optimized out&amp;gt;, rlim=&amp;lt;optimized out&amp;gt;, err=...) at syscall&#x2F;zsyscall_linux_amd64.go:1326&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#4  0x00005588e7c4a2ee in os.init.1 () at os&#x2F;rlimit.go:30&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#5  0x00005588e7bd8fa8 in runtime.doInit (t=0x5588e91a7be0 &amp;lt;os.[inittask]&amp;gt;) at runtime&#x2F;proc.go:6506&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#6  0x00005588e7bd8ef1 in runtime.doInit (t=0x5588e91a9900 &amp;lt;main.[inittask]&amp;gt;) at runtime&#x2F;proc.go:6483&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#7  0x00005588e7bcb953 in runtime.main () at runtime&#x2F;proc.go:233&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#8  0x00005588e7bfcb41 in runtime.goexit () at runtime&#x2F;asm_amd64.s:1598&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#9  0x0000000000000000 in ?? ()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So the issue seems to be a call to &lt;code&gt;setrlimit&lt;&#x2F;code&gt;. Looking at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;golang&#x2F;go&#x2F;blob&#x2F;203e59ad41bd288e1d92b6f617c2f55e70d3c8e3&#x2F;src&#x2F;syscall&#x2F;zsyscall_linux_amd64.go#L1335&quot;&gt;the code&lt;&#x2F;a&gt;
and searching the Go issue tracker didn’t turn up anyone else having this
issue, which pointed at an issue on my system.&lt;&#x2F;p&gt;
&lt;p&gt;I’m honestly not sure what led me to the next step but I decided to take a look
at the Docuum service definition. I was surprised to see that it was more
complicated than most definitions I’m used to seeing:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;Unit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;LRU eviction of Docker images&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;Documentation&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;https:&#x2F;&#x2F;github.com&#x2F;stepchowfun&#x2F;docuum&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;DefaultDependencies&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;After&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;docker.service docker.socket&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;Requires&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;docker.service docker.socket&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;Service&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;Type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;simple&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;Environment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;DOCUUM_THRESHOLD&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;10GB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;EnvironmentFile&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;-&#x2F;etc&#x2F;default&#x2F;docuum&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ExecStart&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;usr&#x2F;bin&#x2F;docuum --threshold $DOCUUM_THRESHOLD&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ProtectSystem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;full&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;PrivateTmp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;PrivateDevices&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;PrivateNetwork&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;CapabilityBoundingSet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;KeyringMode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;private&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;RestrictNamespaces&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;~cgroup ipc net mnt pid user uts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;RestrictAddressFamilies&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;AF_UNIX&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ReadWritePaths&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;var&#x2F;run&#x2F;docker.sock&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;DeviceAllow&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;IPAddressDeny&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;any&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;NoNewPrivileges&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;PrivateTmp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;PrivateDevices&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;PrivateMounts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;PrivateUsers&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ProtectControlGroups&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ProtectSystem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;strict&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ProtectHome&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;tmpfs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ProtectKernelModules&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ProtectKernelTunables&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;RestrictSUIDSGID&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;SystemCallArchitectures&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;native&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;SystemCallFilter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;@system-service&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;SystemCallFilter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;~@privileged @resources&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;RestrictRealtime&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;LockPersonality&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;MemoryDenyWriteExecute&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;UMask&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;0077&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ProtectHostname&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;Install&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;WantedBy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;multi-user.target&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Suspiciously it seemed to be doing some sandboxing and filtering system calls
(&lt;code&gt;SystemCallFilter&lt;&#x2F;code&gt;). A bit more research pointed me to &lt;code&gt;systemd-analyze syscall-filter&lt;&#x2F;code&gt;, which lists which system calls belong to the predefined system
call sets (&lt;code&gt;@privileged&lt;&#x2F;code&gt;, &lt;code&gt;@resources&lt;&#x2F;code&gt;, etc.).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;setrlimit&lt;&#x2F;code&gt; was listed under &lt;code&gt;@resources&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@resources&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    # Alter resource settings&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ioprio_set&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    mbind&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    migrate_pages&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    move_pages&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    nice&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    sched_setaffinity&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    sched_setattr&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    sched_setparam&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    sched_setscheduler&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    set_mempolicy&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    setpriority&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    setrlimit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;software&#x2F;systemd&#x2F;man&#x2F;systemd.exec.html#System%20Call%20Filtering&quot;&gt;systemd docs for &lt;code&gt;SystemCallFilter&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; also mentioned:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the first character of the list is “~”, the effect is inverted: only the
listed system calls will result in immediate process termination
(deny-listing)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;So we finally had our culprit: the service definition was denying system calls in
the &lt;code&gt;@resources&lt;&#x2F;code&gt; set and at some point &lt;code&gt;docker&lt;&#x2F;code&gt; had started making &lt;code&gt;setrlimit&lt;&#x2F;code&gt; calls,
which were resulting in termination.&lt;&#x2F;p&gt;
&lt;p&gt;The fix was simple enough: I removed &lt;code&gt;@resources&lt;&#x2F;code&gt; from the deny list, rebuilt
the package, and then re-enabled the &lt;code&gt;docuum&lt;&#x2F;code&gt; service (I’d previously disabled
it due to the constant crashes). I was pleased to see it start successfully and
begin vacuuming up a few months of Docker image detritus.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;This small debugging session taught me a number of things: I learned how to
find and list core dumps managed by systemd, how to open them in GDB with
symbols present, and that systemd has powerful, fine-grained system call
sandboxing.&lt;&#x2F;p&gt;
&lt;p&gt;I was ultimately able to resolve the issue and get Docuum working again.
I have published my patched version of the AUR package to my personal AUR
repo in case it’s useful to anyone else:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;aur&#x2F;tree&#x2F;master&#x2F;docuum&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;aur&#x2F;tree&#x2F;master&#x2F;docuum&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Hide Sign in With Google Pop Up</title>
      <pubDate>Fri, 20 Jan 2023 20:48:02 +1000</pubDate>
      <atom:published>2023-01-20T20:48:02+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;hide-sign-in-with-google&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;hide-sign-in-with-google&#x2F;</guid>
      <description>&lt;p&gt;Inspired by
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rachsmith.com&#x2F;remove-youtube-shorts&#x2F;&quot;&gt;Rach Smith’s post on using userstyles to hide YouTube shorts&lt;&#x2F;a&gt;
I came up with some CSS to hide those
annoying Sign in with Google pop-ups.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;I never want to sign in with Google and use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;support.mozilla.org&#x2F;en-US&#x2F;kb&#x2F;containers&quot;&gt;Firefox Multi-Account Containers&lt;&#x2F;a&gt;
to ensure that the bulk of my browsing is done without ever being
signed in to a Google account. This means that I see a lot of these pop ups
encouraging me to sign in, so Google can track me more.&lt;&#x2F;p&gt;

&lt;figure class=&quot;text-center figure-border no-overlay&quot;&gt;

  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2023&amp;#x2F;hide-sign-in-with-google&amp;#x2F;sign-in-with-google.png&quot; width=&quot;357&quot; alt=&quot;Screenshot of the pop up&quot; &#x2F;&gt;
  
  &lt;figcaption&gt;Be gone evil pop up&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;openstyles&#x2F;stylus&quot;&gt;Stylus&lt;&#x2F;a&gt; to manage user styles. I created a new rule that applies to all
sites and put this CSS in there:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;credential_picker_container&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;has&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;iframe&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;*=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;accounts.google.com&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;]) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    display&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; none;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that it uses the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;CSS&#x2F;:has&quot;&gt;&lt;code&gt;:has&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; selector, which is not on by default in Firefox
at the time of writing. In Firefox 103 onwards you can enable it by toggling
&lt;code&gt;layout.css.has-selector.enabled&lt;&#x2F;code&gt; (the usual caveats about poking around in
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;support.mozilla.org&#x2F;en-US&#x2F;kb&#x2F;about-config-editor-firefox&quot;&gt;&lt;code&gt;about:config&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; apply).&lt;&#x2F;p&gt;
&lt;p&gt;If you do browse while signed in to a Google account, Aranjedeath on the Fediverse
pointed out that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hachyderm.io&#x2F;@Aranjedeath&#x2F;109720032830494381&quot;&gt;you can turn them off in your account settings&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>divmod, Rust, x86, and Optimisation</title>
      <pubDate>Wed, 11 Jan 2023 19:48:09 +1000</pubDate>
      <atom:published>2023-01-11T19:48:09+10:00</atom:published>
      <atom:updated>2023-01-11T21:11:28+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;divmod&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2023&#x2F;divmod&#x2F;</guid>
      <description>&lt;p&gt;While reviewing some Rust code that did something like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; %&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I lamented the lack of a &lt;code&gt;divmod&lt;&#x2F;code&gt; method in Rust (that would return both the
quotient and remainder). My colleague &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;brendanzab&quot;&gt;Brendan&lt;&#x2F;a&gt; pointed out that he actually
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;commit&#x2F;f39152e07baf03fc1ff4c8b2c1678ac857b4a512&quot;&gt;added it&lt;&#x2F;a&gt; back in 2013 but it was moved out of the standard
library before the 1.0 release.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;I also learned that the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.felixcloutier.com&#x2F;x86&#x2F;div&quot;&gt;&lt;code&gt;div&lt;&#x2F;code&gt; instruction on x86&lt;&#x2F;a&gt; provides the remainder
so there is potentially some benefit to combining the operation. I suspected
that LLVM was probably able to optimise the separate operations and a trip to
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust.godbolt.org&#x2F;&quot;&gt;the Compiler Explorer&lt;&#x2F;a&gt; confirmed it.&lt;&#x2F;p&gt;
&lt;p&gt;This function:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; divmod&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; %&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Compiles to the following assembly, which I have annotated with my
understanding of each line (Note: I’m still learning x86 assembly):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;; rdi = numerator, rsi = denominator&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;example&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;divmod&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        test    rsi&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rsi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;     ; check for denominator of zero&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        je&lt;&#x2F;span&gt;&lt;span&gt;      .LBB0_5      &lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;; jump to div zero panic if zero&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        mov     rax&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rdi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;     ; load rax with numerator&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        or      rax&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rsi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;     ; or rax with denominator&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        shr     rax&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;32&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;      ; shift rax right 32-bits&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        je&lt;&#x2F;span&gt;&lt;span&gt;      .LBB0_2      &lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;; if the result of the shift sets the zero flag then numerator and&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;                             ; denominator are 32-bit since none of the upper 32-bits are set.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;                             ; jump to 32-bit division implementation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        mov     rax&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rdi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;     ; move numerator into rax&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        xor     edx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;edx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;     ; zero edx (I&amp;#39;m not sure why, might be relevant to the calling&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;                             ; convention and is used by the caller?)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        div     rsi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;          ; divide rax by rsi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        ret&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;                  ; return, quotient is in rax, remainder in rdx&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;; 32 bit implementation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;LBB0_2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        mov     eax&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;edi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;     ; move edi to eax (32-bit regs)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        xor     edx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;edx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;     ; zero edx&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        div     esi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;          ; divide eax by esi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        ret&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;; div zero panic&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;LBB0_5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        push    rax&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        lea     rdi&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rip&lt;&#x2F;span&gt;&lt;span&gt; + &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;str.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        lea     rdx&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rip&lt;&#x2F;span&gt;&lt;span&gt; + .L__unnamed_1]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        mov     esi&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;25&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        call    qword&lt;&#x2F;span&gt;&lt;span&gt; ptr [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rip&lt;&#x2F;span&gt;&lt;span&gt; + &lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;core&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;panicking&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;:panic@GOTPCREL]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        ud2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;L__unnamed_2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .ascii  &amp;quot;&#x2F;app&#x2F;example.rs&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;L__unnamed_1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .quad   .L__unnamed_2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .asciz  &amp;quot;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;017&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;000&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;000&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;000&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;000&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;000&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;000&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;000&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;002&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;000&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;000&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;000&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;006&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;000&lt;&#x2F;span&gt;&lt;span&gt;\&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;000&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;str.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .ascii  &amp;quot;attempt to divide by zero&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I found it interesting that after checking for a zero denominator there’s an
additional check to see if the values fit into 32-bits, and if so it jumps to an
instruction sequence that uses 32-bit registers. According to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gmplib.org&#x2F;~tege&#x2F;x86-timing.pdf&quot;&gt;the testing done
in this report&lt;&#x2F;a&gt; 32-bit &lt;code&gt;div&lt;&#x2F;code&gt; has lower latency—particularly on older
models.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;del&gt;I wasn’t able to work out why each implementation zeros &lt;code&gt;edx&lt;&#x2F;code&gt;. If you know,
send me a message and I’ll update the post.&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bikeshed.vibber.net&#x2F;@brion&#x2F;109670222269686433&quot;&gt;Brion Vibber on the Fediverse&lt;&#x2F;a&gt; provided this explanation as to why &lt;code&gt;edx&lt;&#x2F;code&gt;
is being zeroed:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;iirc rdx &#x2F; edx is the top word for the x86 division operation, which takes a double-word numerator – the inverse of multiplication producing a double-word output.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This makes sense and looking back at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.felixcloutier.com&#x2F;x86&#x2F;div&quot;&gt;the docs&lt;&#x2F;a&gt; it does say that:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;32-bit: Unsigned divide EDX:EAX by r&#x2F;m32, with result stored in EAX := Quotient, EDX := Remainder.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;64-bit: Unsigned divide RDX:RAX by r&#x2F;m64, with result stored in RAX := Quotient, RDX := Remainder.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust.godbolt.org&#x2F;z&#x2F;hj9rb4Txa&quot;&gt;View the Example on Compiler Explorer&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Garage Door Monitor Update</title>
      <pubDate>Mon, 17 Oct 2022 15:04:44 +1000</pubDate>
      <atom:published>2022-10-17T15:04:44+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;garage-door-monitor-update&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;garage-door-monitor-update&#x2F;</guid>
      <description>&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;garage-door-monitor&#x2F;&quot;&gt;The garage door monitor that I built earlier in the year&lt;&#x2F;a&gt;
has by all accounts been running perfectly since I installed it. Recently I
implemented a couple of new features that I’ve wished for over the last few
months.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;The first new feature is a bit more visibility into the state of the system. I
added uptime and memory info to the web page:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor-update&amp;#x2F;webpage.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor-update&amp;#x2F;webpage.png&quot; width=&quot;625&quot; alt=&quot;Screenshot of the web page served by the garage door monitor. It shows the state of the door, uptime of the device, and memory usage information.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Screenshot of the web page now including uptime and memory info.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The second and more practical addition is a subsequent notification when the
door is closed again after it was left open for longer that the trigger time (5
minutes).&lt;&#x2F;p&gt;


  



&lt;figure class=&quot;text-center figure-border&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor-update&amp;#x2F;new-mattermost-notification.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor-update&amp;#x2F;new-mattermost-notification.png&quot; width=&quot;403&quot; alt=&quot;Screenshot of the message posted to Mattermost by the garage door monitor. It reads: Garage door closed after 5 minutes open.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;The new notification&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Since I set up the garage door monitor I haven’t accidentally left it open.
However, I have triggered the notification when washing the car or packing for
a road trip. It’s this latter scenario that I wanted to address. The issue is
that because the notification is triggered during packing if I did set off and
forget to close the door I wouldn’t know. One way to deal with this would be
recurring notifications. I chose another option though.&lt;&#x2F;p&gt;
&lt;p&gt;When the door is closed after notifying that it was left open, it sends another
message saying that it’s now closed. The benefit of this approach is that the
message log now properly tracks the state of the door, so if I’m a few kms down
the road and wonder if I closed the door it’s just a matter of checking the
channel in Mattermost.&lt;&#x2F;p&gt;
&lt;p&gt;Before I powered it off to update the Buildroot image on the SD card I hooked
up a serial console and checked its uptime. I was pleased to see 100% uptime
since it was first installed:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 02:03:14 up 183 days, 20:26,  load average: 0.00, 0.00, 0.00&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That’s all for now, on to the next few hundred days of service!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;garage-door-monitor&quot;&gt;Garage door monitor source on GitHub&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Resuming Read Rust Tweeting</title>
      <pubDate>Mon, 11 Jul 2022 11:15:32 +1000</pubDate>
      <atom:published>2022-07-11T11:15:32+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;resuming-read-rust-tweeting&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;resuming-read-rust-tweeting&#x2F;</guid>
      <description>&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;read_rust&quot;&gt;Read Rust Twitter account&lt;&#x2F;a&gt; crossed over 10K followers in the
last few days. Amazingly 4350 of those coming after I stopped regular posting.
This got me thinking about the account and how I might be able
to use it to benefit the community while avoiding the overhead that led me to
&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;slowing-read-rust-posting&#x2F;&quot;&gt;winding things down in Sep 2020&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;In that post I noted:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Whilst I think there is value in the curation and archiving of posts on Read
Rust, the website doesn’t see a lot of use. I think most of the value for
people is following the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;read_rust&quot;&gt;Twitter&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;botsin.space&#x2F;@readrust&quot;&gt;Mastodon&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.facebook.com&#x2F;readrust&#x2F;&quot;&gt;Facebook&lt;&#x2F;a&gt; accounts.
However, there’s a fair amount of overlap between posts shared on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;&quot;&gt;&#x2F;r&#x2F;rust&lt;&#x2F;a&gt;,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;rustlang&quot;&gt;@rustlang&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;this-week-in-rust.org&#x2F;&quot;&gt;This Week in Rust&lt;&#x2F;a&gt;. So, I think that if folks keep an
eye on one or more of those they will still see most posts of note.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Since then the @rustlang Twitter account has become much less active, so
perhaps there is room for the Read Rust Twitter account as well. My plan is to
resume using @read_rust, but I will not be seeking out content to post like I
did previously. I’ll retweet interesting things that I come across during my
usual reading and will &lt;em&gt;consider&lt;&#x2F;em&gt; sharing posts that mention @read_rust.
Hopefully that represents a sustainable time commitment.&lt;&#x2F;p&gt;
&lt;p&gt;Onwards! 🦀&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Generating RSS Feeds From Web Pages With RSS Please</title>
      <pubDate>Mon, 04 Jul 2022 09:54:29 +1000</pubDate>
      <atom:published>2022-07-04T09:54:29+10:00</atom:published>
      <atom:updated>2022-07-24T09:28:15+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;generate-rss-from-webpage&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;generate-rss-from-webpage&#x2F;</guid>
      <description>&lt;p&gt;Sometimes I come across a web page that I’d like to revisit when there’s
new content. Typically, I do this by subscribing to the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;RSS&quot;&gt;RSS feed&lt;&#x2F;a&gt; in
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;feedbin.com&#x2F;&quot;&gt;Feedbin&lt;&#x2F;a&gt;. Unfortunately some sites don’t provide an RSS feed, which is why I
built &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;rsspls&quot;&gt;RSS Please&lt;&#x2F;a&gt; (&lt;code&gt;rsspls&lt;&#x2F;code&gt;). RSS Please allows you to generate an RSS
feed by extracting specific parts of a web page. In this post I give a bit of
background on the tool and how I’m running it in my Docker infrastructure.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h3 id=&quot;background&quot;&gt;Background&lt;&#x2F;h3&gt;
&lt;p&gt;Sometimes an RSS feed isn’t available on a website. If the site is open source
I will often try to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pulls?q=is%3Apr+author%3Awezm++rss+is%3Aclosed+&quot;&gt;open a PR to add or enable one&lt;&#x2F;a&gt;. That’s not always
possible though. Other times the page may be one that the author wouldn’t
naturally think to provide a feed for, but one would still be useful.&lt;&#x2F;p&gt;
&lt;p&gt;As an example, when we were looking to buy a house I noticed that listings
would often go live on agent’s websites several days or more before they were
published to the big aggregators. The market was very competitive so I was
regularly visiting all the real estate agent websites to run my search, and
check for new listings. At the time I used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;feedfry.com&#x2F;&quot;&gt;Feedfry&lt;&#x2F;a&gt; to create RSS feeds from
the search results. I could then subscribe to them in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;feedbin.com&#x2F;&quot;&gt;Feedbin&lt;&#x2F;a&gt;. Paired with
the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;feedbin.com&#x2F;notifier&quot;&gt;Feedbin Notifier app&lt;&#x2F;a&gt; I received a notification on my phone
whenever there was a new listing matching my search criteria.&lt;&#x2F;p&gt;
&lt;p&gt;Feedfry is free with ads or paid subscription. I paid while house shopping but
let that lapse afterwards. I don’t begrudge them funding the service with ads or
subscriptions but I figured I could probably put something together and
self-host it. At the same time providing a bit more control over how the
elements of the page were extracted to generate the feed. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;rsspls&quot;&gt;RSS Please&lt;&#x2F;a&gt;
is the result.&lt;&#x2F;p&gt;
&lt;p&gt;RSS Please is an open-source command line application implemented in Rust. It
has no runtime dependencies and runs on UNIX-like platforms including FreeBSD,
Linux, and macOS. Once I resolve &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;rsspls&#x2F;issues&#x2F;4&quot;&gt;this issue&lt;&#x2F;a&gt; it will run on
Windows too. The following sections describe how it’s configured and how I’m
running it on my server.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;configuration&quot;&gt;Configuration&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;rsspls&lt;&#x2F;code&gt; configuration file allows a number of feeds to be defined. It
uses &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Learn&#x2F;CSS&#x2F;Building_blocks&#x2F;Selectors&quot;&gt;CSS Selectors&lt;&#x2F;a&gt; to describe how parts of each page will be extracted
to produce a feed. As an example here’s a configuration that builds a feed
from this site—although I already have an RSS feed at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;rss.xml&quot;&gt;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;rss.xml&lt;&#x2F;a&gt; if you want to subscribe.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;# The configuration must start with the [rsspls] section&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;rsspls&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;output&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;tmp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[[&lt;&#x2F;span&gt;&lt;span&gt;feed&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;# The title of the channel in the feed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;title&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Example WezM.net Feed&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;# The output filename within the output directory to write this feed to.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;filename&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;wezm.rss&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;feed.config&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;www.wezm.net&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;item&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;heading&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;h3 a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;summary&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.post-body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;date&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;time&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The configuration format is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;toml.io&#x2F;&quot;&gt;TOML&lt;&#x2F;a&gt;. The &lt;code&gt;item&lt;&#x2F;code&gt; key selects &lt;code&gt;article&lt;&#x2F;code&gt; elements
from the page. &lt;code&gt;heading&lt;&#x2F;code&gt;, &lt;code&gt;summary&lt;&#x2F;code&gt;, and &lt;code&gt;date&lt;&#x2F;code&gt; are selectors upon the element
selected by &lt;code&gt;item&lt;&#x2F;code&gt;. &lt;code&gt;summary&lt;&#x2F;code&gt; and &lt;code&gt;date&lt;&#x2F;code&gt; are optional. &lt;code&gt;heading&lt;&#x2F;code&gt; is expected to
select an element with a &lt;code&gt;href&lt;&#x2F;code&gt; attribute, which is used as the link for the
item in the feed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;running-it&quot;&gt;Running It&lt;&#x2F;h3&gt;
&lt;p&gt;Once installed running &lt;code&gt;rsspls&lt;&#x2F;code&gt; will update the configured feeds. Caching is
used to skip updates when the origin server indicates nothing has changed since
last time. By default &lt;code&gt;rsspls&lt;&#x2F;code&gt; looks for its configuration file in
&lt;code&gt;$XDG_CONFIG_HOME&#x2F;rsspls&#x2F;feeds.toml&lt;&#x2F;code&gt;, defaulting to
&lt;code&gt;~&#x2F;.config&#x2F;rsspls&#x2F;feeds.toml&lt;&#x2F;code&gt; if &lt;code&gt;XDG_CONFIG_HOME&lt;&#x2F;code&gt; is not set. Alternatively
the path can be supplied with &lt;code&gt;--config&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;generate-rss-from-webpage&amp;#x2F;rsspls-output.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;generate-rss-from-webpage&amp;#x2F;rsspls-output.png&quot; width=&quot;335&quot; alt=&quot;Screenshot of the output when running rsspls. It has several log messages prefixed with INFO describing the actions taken&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;rsspls prints informational messages when updating feeds&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;deployment&quot;&gt;Deployment&lt;&#x2F;h3&gt;
&lt;p&gt;Since I
&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;alpine-linux-docker-infrastructure-three-years&#x2F;&quot;&gt;host my things with Docker + Compose&lt;&#x2F;a&gt;
I’m running &lt;code&gt;rsspls&lt;&#x2F;code&gt; with Docker as well, but that’s not required. There are
plenty of other ways you could go about it. E.g. you could have &lt;code&gt;cron&lt;&#x2F;code&gt; run
&lt;code&gt;rsspls&lt;&#x2F;code&gt; on your computer and &lt;code&gt;rsync&lt;&#x2F;code&gt; the feeds to a server. Some RSS aggregators
like &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;lzone.de&#x2F;liferea&#x2F;&quot;&gt;Liferea&lt;&#x2F;a&gt; even let you subscribe to local files.&lt;&#x2F;p&gt;
&lt;p&gt;I create a Docker image from the &lt;code&gt;rsspls&lt;&#x2F;code&gt; binaries I publish:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;docker&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; wezm-alpine:3.16.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;# UID needs to match owner of &#x2F;home&#x2F;rsspls&#x2F;feeds volume&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ARG&lt;&#x2F;span&gt;&lt;span&gt; PUID=1000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ARG&lt;&#x2F;span&gt;&lt;span&gt; PGID=1000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ARG&lt;&#x2F;span&gt;&lt;span&gt; USER=rsspls&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span&gt; addgroup -g ${PGID} ${USER} &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    adduser -D -u ${PUID} -G ${USER} -h &#x2F;home&#x2F;${USER} -D ${USER}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ARG&lt;&#x2F;span&gt;&lt;span&gt; RSSPLS_VERSION=0.2.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span&gt; cd &#x2F;usr&#x2F;local&#x2F;bin &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    wget -O - https:&#x2F;&#x2F;releases.wezm.net&#x2F;rsspls&#x2F;${RSSPLS_VERSION}&#x2F;rsspls-${RSSPLS_VERSION}-x86_64-unknown-linux-musl.tar.gz | tar zxf - &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    mkdir &#x2F;home&#x2F;${USER}&#x2F;feeds &amp;amp;&amp;amp; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    chown ${USER}:${USER} &#x2F;home&#x2F;${USER}&#x2F;feeds&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;entrypoint.sh &#x2F;home&#x2F;${USER}&#x2F;entrypoint.sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;WORKDIR&lt;&#x2F;span&gt;&lt;span&gt; &#x2F;home&#x2F;${USER}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;USER&lt;&#x2F;span&gt;&lt;span&gt; ${USER}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;VOLUME&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;&#x2F;home&#x2F;rsspls&#x2F;feeds&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ENTRYPOINT&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;.&#x2F;entrypoint.sh&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It uses my standard &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alpinelinux.org&#x2F;&quot;&gt;Alpine&lt;&#x2F;a&gt; base image which is built from the “Mini root
filesystem” they publish and does not require any other packages to be
installed.&lt;&#x2F;p&gt;
&lt;p&gt;I use an entry point script to run &lt;code&gt;rsspls&lt;&#x2F;code&gt; every 12 hours:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;#!&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;set&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; -e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;trap&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;exit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; TERM INT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;while&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; true&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;  rsspls&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; --config &#x2F;etc&#x2F;rsspls.toml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;  sleep&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1036800&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt; # 12 hours&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In my &lt;code&gt;docker-compose.yml&lt;&#x2F;code&gt; I have the following:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  rsspls&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    image&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; example.com&#x2F;rsspls&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    volumes&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; .&#x2F;rsspls&#x2F;rsspls.toml:&#x2F;etc&#x2F;rsspls.toml:ro&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; .&#x2F;volumes&#x2F;www&#x2F;rsspls.wezm.net:&#x2F;home&#x2F;rsspls&#x2F;feeds&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    restart&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; unless-stopped&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;.&#x2F;volumes&#x2F;www&#x2F;rsspls.wezm.net&lt;&#x2F;code&gt; path is shared with the container running &lt;code&gt;nginx&lt;&#x2F;code&gt;, so
the generated feeds are accessible at &lt;code&gt;rsspls.wezm.net&lt;&#x2F;code&gt;—although I’m not making them
obvious to visitors (there’s no directory index so visiting that domain will just give
a 403 Forbidden error).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;This was a fun project to put together over a weekend. I get a lot of
satisfaction from building and self-hosting tools to solve my own problems. Not
everyone has the time or desire to do that though so if you’re looking for
similar functionality check out &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;feed43.com&#x2F;&quot;&gt;Feed43&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;feedfry.com&#x2F;&quot;&gt;Feedfry&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As mentioned the tool is open-source (MIT or Apache 2.0). Check out the repo at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;rsspls&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;rsspls&lt;&#x2F;a&gt; and if you like what you see maybe give it a
star.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Monitoring My Garage Door With a Raspberry Pi, Rust, and a 13Mb Linux System</title>
      <pubDate>Wed, 20 Apr 2022 06:38:27 +1000</pubDate>
      <atom:published>2022-04-20T06:38:27+10:00</atom:published>
      <atom:updated>2022-10-17T15:51:20+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;garage-door-monitor&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;garage-door-monitor&#x2F;</guid>
      <description>&lt;p&gt;I’ve accidentally left our garage door open a few times. To combat this I built
a monitor that sends an alert via Mattermost when the door has been left open
for more than 5 minutes. This turned out to be a super fun project. I used
parts on hand as much as possible, implemented the monitoring application in
Rust, and then built a stripped down Linux image to run it.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;



&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;garage-door-monitor.jpg&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;garage-door-monitor.jpg&quot; alt=&quot;Photo of the garage door monitor stuck to the wall. It has wires going to the power supply and reed switch, which is visible on the right of the photo.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;What we&amp;#x27;ll be building in this post. (In reality the reed switch and magnet are better aligned than they appear in the photo)&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;overview-goals&quot;&gt;Overview &amp;amp; Goals&lt;&#x2F;h3&gt;
&lt;p&gt;The garage door state is monitored via a reed switch. If the door is left open
for more than 5 minutes a message is posted to a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mattermost&#x2F;mattermost-server&quot;&gt;Mattermost&lt;&#x2F;a&gt; channel. The
monitor has a web server that allows the current state to be viewed (on
the local network only). There’s also a JSON endpoint that I use to put the
door state in the status bar of my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;awesomeWM&#x2F;awesome&quot;&gt;Awesome&lt;&#x2F;a&gt; desktop. An LED on the device
flashes to indicate status around once every second. All code and designs
are published in a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;garage-door-monitor&quot;&gt;git repo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;


  



&lt;figure class=&quot;text-center figure-border&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;mattermost-notification.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;mattermost-notification.png&quot; width=&quot;380&quot; alt=&quot;Screenshot of the message posted to Mattermost by the garage door monitor. It reads: Garage door has been open for 5 minutes.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Mattermost message posted by the monitor. A notification will be shown on my desktop or mobile if I&amp;#x27;m away from my computer.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;When I embarked on this project I researched the garage door opener that opens
and closes the door. The installation document mentions an “E-Serial port” but
it’s listed as an input. I concluded that the opener doesn’t readily expose the
door state and proceeded with tracking this myself.&lt;&#x2F;p&gt;
&lt;p&gt;I had a few goals in mind, which shaped the approach:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Notify both my partner and I when the door is left open.&lt;&#x2F;li&gt;
&lt;li&gt;Use parts I had on hand as much as possible.&lt;&#x2F;li&gt;
&lt;li&gt;Minimise upkeep&#x2F;maintenance of the final result.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;For notifications I made use of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mattermost&#x2F;mattermost-server&quot;&gt;Mattermost&lt;&#x2F;a&gt; instance I host. I created a
channel called House and then created an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.mattermost.com&#x2F;developer&#x2F;webhooks-incoming.html&quot;&gt;incoming webhook&lt;&#x2F;a&gt; locked
to that channel. The garage door monitor uses this web hook to post messages.&lt;&#x2F;p&gt;
&lt;p&gt;It was important that the monitor be able to send notifications so that we
would be alerted even if we were away from home. For example, if we left in the
car but forgot to close the door. Without this requirement it would have been
enough to wire an LED directly to the reed switch that could be observed from
inside the house.&lt;&#x2F;p&gt;
&lt;p&gt;Making https requests means the monitor needs to be able to access the
internet. With the parts I had on hand this meant an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ESP32&quot;&gt;Espressif ESP32&lt;&#x2F;a&gt;
or Raspberry Pi Zero W. The ESP32 would be perfect except I prefer to write
software in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; and after some investigation I concluded Rust on ESP32 is
still immature, although &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mabez.dev&#x2F;blog&#x2F;posts&#x2F;esp-rust-04-04-2022&#x2F;&quot;&gt;improving every month&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So Pi Zero it was. The Pi Zero in question is actually the one I used for my
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;technical&#x2F;2019&#x2F;01&#x2F;linux-conf-au-rust-epaper-badge&#x2F;&quot;&gt;e-Paper conference badge in 2019&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;software&quot;&gt;Software&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;garage-door-monitor&#x2F;tree&#x2F;main&#x2F;app&quot;&gt;The software&lt;&#x2F;a&gt; came together quickly. I used a thread per task approach
where there is a dedicated thread for each of:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;General-purpose input&#x2F;output (GPIO)
&lt;ul&gt;
&lt;li&gt;Monitors the door state and controls the status LED.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;State management
&lt;ul&gt;
&lt;li&gt;Receives updates from the GPIO thread via a channel and detects door state
transitions.&lt;&#x2F;li&gt;
&lt;li&gt;Updates the global state when a change is detected and notes the time the
door was opened if the state transitions to open.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Notification thread
&lt;ul&gt;
&lt;li&gt;Checks the global state every 5 seconds and sends a notification if the
door has been open for more than 5 minutes.&lt;&#x2F;li&gt;
&lt;li&gt;Notes the notification time to prevent repeatedly sending the notification.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;HTTP server
&lt;ul&gt;
&lt;li&gt;Serves the web page and JSON endpoint.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Main thread
&lt;ul&gt;
&lt;li&gt;Polls a flag every 5 seconds in order to detect signals and shutdown
cleanly (mainly useful during development).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Acquiring access to the GPIO pins requires elevated privileges, so after that’s
done the application drops privileges to a regular user.&lt;&#x2F;p&gt;
&lt;p&gt;All logging is done via syslog to avoid the need to manage an application
specific log file. The logging was mainly used during development. The
only way to see it is via the serial console.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;web-server&quot;&gt;Web Server&lt;&#x2F;h4&gt;
&lt;p&gt;A failure mode of this whole arrangement is that if it stops working for some
reason I won’t know unless I observe the status LED. To remedy this I included
a web server that allows the status of the device to be checked remotely (on
the local network) via a web page.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;webpage.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;webpage.png&quot; width=&quot;700&quot; alt=&quot;Screenshots of the web page served by the garage door monitor. The left side shows the closed state with a green dot emoji and the right one shows the open state with a red dot emoji and the text: Opened 1 minute ago.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Screenshots of the web page when the door is closed and open.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I then extended this with a JSON endpoint &lt;code&gt;&#x2F;door.json&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;secs_since_notified&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; null&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;open_for&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 9&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I use the JSON endpoint in my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;bar&quot;&gt;status bar&lt;&#x2F;a&gt; to show the door status:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;awesome-status-bar.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;awesome-status-bar.png&quot; width=&quot;398&quot; alt=&quot;Screenshot of my status bar showing the garage door state on the far right.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Screenshot of my status bar showing the garage door state on the far right.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;ul&gt;
&lt;li&gt;🚪: Closed&lt;&#x2F;li&gt;
&lt;li&gt;🪟: Open&lt;&#x2F;li&gt;
&lt;li&gt;🚪❓: State unknown&lt;&#x2F;li&gt;
&lt;li&gt;🚪⁉️: Error fetching status&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;shrinking-hardening-buildroot&quot;&gt;Shrinking&#x2F;​Hardening&#x2F;​Buildroot&lt;&#x2F;h3&gt;
&lt;p&gt;The trouble with using a Raspberry Pi is now I have another computer I have to
maintain: apply package updates, upgrade the OS, worry about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;sahajsarup&#x2F;status&#x2F;1515938046793105410?s=20&amp;amp;t=ZMKyMKS6v0niLz5xlvYS6g&quot;&gt;wearing out the SD
card&lt;&#x2F;a&gt;, etc. Raspberry Pi OS was great for getting the project off the ground but
I wanted to slim it down so it was running the bare minimum amount of software.
I also wanted it to run from RAM so that I didn’t have to worry about the SD
card wearing out or file-system corruption from unclean shutdowns.&lt;&#x2F;p&gt;
&lt;p&gt;The aim was to turn the Pi into a glorified microcontroller that I could feel
comfortable running for years without needing to worry about applying updates
etc. Yes it’s still possible that there is a vulnerability in the limited
amount of software it runs but I feel like I’ve reduced the attack surface
enough that I can happily let this thing run and it’ll be fine.&lt;&#x2F;p&gt;
&lt;p&gt;I used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;buildroot.org&#x2F;&quot;&gt;Buildroot&lt;&#x2F;a&gt; to build a custom Linux file system
image. I started with the base Buildroot Raspberry Pi Zero configuration and
then trial-and-error disabled kernel features and tested that it still worked.
The image started off at about 30Mb and ended up at 13Mb. The smallest microSD
card I could get my hands on was 256Mb (thanks Kim), so the aim wasn’t really
the smallest image possible, more the most basic kernel configuration that
would still run the application.&lt;&#x2F;p&gt;
&lt;p&gt;In the end I disabled a lot of kernel features, including:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Ethernet devices, IPv6, Bluetooth, BPF, netfilter, cifs, nfs, PPP and other
network protocols and devices&lt;&#x2F;li&gt;
&lt;li&gt;Most file systems, SCSI&lt;&#x2F;li&gt;
&lt;li&gt;I2C, 1-wire, sensors devices&lt;&#x2F;li&gt;
&lt;li&gt;Display support&lt;&#x2F;li&gt;
&lt;li&gt;Quotas, accounting&lt;&#x2F;li&gt;
&lt;li&gt;USB, virtio, sdio, IR, PPS, HID&lt;&#x2F;li&gt;
&lt;li&gt;MD (LVM&#x2F;RAID) and MMC block devices&lt;&#x2F;li&gt;
&lt;li&gt;Sound and multimedia devices, game controllers, etc.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The resulting image boots and is interactive in under 5 seconds. The monitoring
application itself starts running about 13 seconds after power is applied, as
shown in the video below. The time between boot and the application running is
taken by bringing up the Wi-Fi connection and syncing the clock.&lt;&#x2F;p&gt;

  


  


&lt;figure class=&quot;text-center&quot;&gt;
  &lt;video controls preload=&quot;none&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;power-on.m4v&quot; poster=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;power-on.m4v.jpg&quot;  style=&quot;max-height: 360px&quot; aria-label=&quot;Video showing power being connected to the Raspberry Pi and the time passing until the status LED starts blinking&quot;&gt;&lt;&#x2F;video&gt;
  &lt;figcaption&gt;Power on to status LED blinking in about 13 seconds.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The image runs entirely from the initial RAM disk embedded in the kernel image.
The SD card is not even mounted, nor can it be mounted as the kernel lacks the
necessary file-system support. The Raspberry Pi bootloader loads the kernel and
initial RAM disk into memory from the SD card, then hands over to the kernel.&lt;&#x2F;p&gt;
&lt;p&gt;The final image includes the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Linux kernel&lt;&#x2F;li&gt;
&lt;li&gt;musl libc&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;busybox.net&#x2F;&quot;&gt;Busybox&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Raspberry Pi Firmware&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.openntpd.org&#x2F;&quot;&gt;OpenNTPD&lt;&#x2F;a&gt; — for syncing the clock&lt;&#x2F;li&gt;
&lt;li&gt;wpa_supplicant — for Wi-Fi&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.libslack.org&#x2F;daemon&#x2F;&quot;&gt;daemon&lt;&#x2F;a&gt; — process supervisor to restart
the &lt;code&gt;garage-door-monitor&lt;&#x2F;code&gt; binary if it exits for some reason.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;garage-door-monitor&#x2F;tree&#x2F;main&#x2F;app&quot;&gt;garage-door-monitor&lt;&#x2F;a&gt; — the application itself&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;rsdate&quot;&gt;rsdate&lt;&#x2F;a&gt; — for setting the clock at boot&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Since the image lacks &lt;code&gt;ssh&lt;&#x2F;code&gt;, debugging was done over a serial console (which
is protected by a password) while I iterated on the Buildroot image.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.openntpd.org&#x2F;&quot;&gt;OpenNTPD&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;rsdate&quot;&gt;rsdate&lt;&#x2F;a&gt; are used for time keeping. Semi-accurate time is
required to make &lt;code&gt;https&lt;&#x2F;code&gt; connections to Mattermost.
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;openntpd-portable&#x2F;openntpd-openbsd&#x2F;commit&#x2F;796283a2f842a878168c01bd8d42e34b10c1fef3&quot;&gt;OpenNTPD removed its functionality for syncing the time at startup&lt;&#x2F;a&gt;
and since the Pi Zero lacks a real-time clock it boots up with the time set to
the distant past. The delta is greater that OpenNTPD is willing to adjust for
so the time remains in the past. To address this I built &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;rsdate&quot;&gt;rsdate&lt;&#x2F;a&gt;, a small Rust
tool that sets the system clock to the time retrieved from an ntp server. This
is run at boot and afterward OpenNTPD takes care of keeping it in sync.&lt;&#x2F;p&gt;
&lt;p&gt;Buildroot has built-in support for Rust so as part of the building the image it
also cross-compiles the monitoring application and &lt;code&gt;rsdate&lt;&#x2F;code&gt; and bakes them into
the image.&lt;&#x2F;p&gt;
&lt;p&gt;I ran the image in my study for a couple of weeks occasionally flipping the
door state by adding or removing a resistor on a breadboard to help ensure it
was working properly.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hardware&quot;&gt;Hardware&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;wiring&quot;&gt;Wiring&lt;&#x2F;h4&gt;
&lt;p&gt;The diagram below shows the limited wiring required:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;wiring-diagram.svg&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;wiring-diagram.svg&quot; alt=&quot;Wiring diagram&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Wiring diagram&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;ul&gt;
&lt;li&gt;The Pi is powered through header pins 4 (5V) and 6 (GND).&lt;&#x2F;li&gt;
&lt;li&gt;A reed switch is connected between pin 1 (3.3V) and pin 38 via a 10kΩ
resistor. Internal pull-downs are enabled on this pin.&lt;&#x2F;li&gt;
&lt;li&gt;The anode of a 3mm LED is connected to header pin 40 via a 220Ω current
limiting resistor. The cathode is connected to pin 39 (GND)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The pin for reading the reed switch is configured so that if the reed switch
is missing or broken then it will be detected as the door being open and
an alert eventually sent.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;status-led&quot;&gt;Status LED&lt;&#x2F;h4&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;soldering-led.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;soldering-led.8512d81ec9969a42.jpg&quot; alt=&quot;Photo of alligator clip holding a piece of solid write in place for soldering between the Raspberry Pi header and LED cathode.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Soldering the LED to the Raspberry Pi header.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The LED periodically blinks to show the state of the device. This allows
correct operation to easily be observed in person.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;No blinking: something is wrong
&lt;ul&gt;
&lt;li&gt;Perhaps the application is not running or there was a permission issue
accessing GPIO. In the latter case other components of the application like
the web-server continue running so there are other ways to check what is
happening.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;One blink: Door closed&lt;&#x2F;li&gt;
&lt;li&gt;Two blinks: Door open&lt;&#x2F;li&gt;
&lt;li&gt;Three blinks: Door state unknown&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

  


  


&lt;figure class=&quot;text-center&quot;&gt;
  &lt;video controls preload=&quot;none&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;one-flash.m4v&quot; poster=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;one-flash.m4v.jpg&quot;  style=&quot;max-height: 338px&quot; aria-label=&quot;Video of the status LED blinking once every second.&quot;&gt;&lt;&#x2F;video&gt;
  &lt;figcaption&gt;One blink for door closed.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;

  


  


&lt;figure class=&quot;text-center&quot;&gt;
  &lt;video controls preload=&quot;none&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;two-flash.m4v&quot; poster=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;two-flash.m4v.jpg&quot;  style=&quot;max-height: 338px&quot; aria-label=&quot;Video of the status LED blinking twice.&quot;&gt;&lt;&#x2F;video&gt;
  &lt;figcaption&gt;Two blinks for door open.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h4 id=&quot;installation&quot;&gt;Installation&lt;&#x2F;h4&gt;
&lt;p&gt;Our house came with a security system and there is a camera mounted on the
front of the garage. The cable that runs to the camera goes right past where I
wanted to put the garage door monitor and it carries 12V DC. To power the Pi from
that I bought a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.aliexpress.com&#x2F;item&#x2F;32949780662.html?spm=a2g0o.order_detail.0.0.4ed6f19cubMzT1&quot;&gt;UBEC on AliExpress&lt;&#x2F;a&gt; for US$1.94.&lt;&#x2F;p&gt;
&lt;p&gt;The UBEC is a tiny switch-mode power supply intended for powering the receiver
and servos in remote control planes, etc. It’s perfect for the Pi too: It’s
rated at 3 amps, outputs 5V and can take a broad range of input voltages.&lt;&#x2F;p&gt;
&lt;p&gt;I cut the camera cable and spliced a little connector into it for the BEC to
connect to. This turned out to be extremely fiddly and time consuming,
requiring three attempts before I got a result I was happy with.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;splicing-power.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;splicing-power.dbe4fe0bb04c2506.jpg&quot; style=&quot;max-height: 600px&quot; alt=&quot;Photo of three exposed wires that have been soldered being held with helping hands.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Splicing the BEC connector into the video camera cable.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;spliced-power.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;spliced-power.8260bec0c5f5488c.jpg&quot; style=&quot;max-height: 600px&quot; alt=&quot;Photo of the camera cable completed with heatshrink tubing over the joint&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;The completed splice with heatshrink tubing over the joint.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The only other part I had to buy for this project was the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jaycar.com.au&#x2F;security-alarm-reed-switch&#x2F;p&#x2F;LA5072?pos=12&amp;amp;queryId=70baa88bc7dd47cd48c78012da5a86be&amp;amp;sort=relevance&quot;&gt;reed switch&lt;&#x2F;a&gt;.
I picked this up from the local Jaycar for about AU$5. I mounted it between a
bracket that holds the runner for the garage door and the door itself. The gap
was too wide for the reed switch to operate so I designed a spacer in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freecadweb.org&#x2F;&quot;&gt;FreeCAD&lt;&#x2F;a&gt;
and 3D printed it. It’s a block with 3mm holes and slots in the sides for nuts to slide
into. You can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;garage-door-monitor&#x2F;blob&#x2F;main&#x2F;hardware&#x2F;Reed%20Mount.stl&quot;&gt;view the rendered model on GitHub&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The first revision of the spacer was too tall and the slots for the nuts
weren’t quite big enough—I’m still new to 3D modeling and 3D printing. I
tweaked the design (yay for parametric modeling) and printed a second version,
which worked well.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;reed-switch-mounting.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;reed-switch-mounting.c133311596354a58.jpg&quot; style=&quot;max-height: 600px&quot; alt=&quot;Photo of the reed switch mounted on it&amp;#x27;s 3D printed mounting block.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;The reed switch mounted on the 3D printed spacer block.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I used super sticky double sided tape to attach everything to the wall and
bracket.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;tape.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;tape.3728f93177c3cd65.jpg&quot; width=&quot;600&quot; alt=&quot;Photo of a roll of double sided body mounting tape on a table with mini screwdrivers to the left, a roll of solder above, and a Raspberry Pi Zero with an LED attached to the right.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Mounting tape I had left over from another job around the house.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;garage-door-monitor&amp;#x2F;prepared-for-sticking.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;prepared-for-sticking.ab4730df847577c5.jpg&quot; width=&quot;600&quot; alt=&quot;Photo of reed switch, UBEC, and Raspberry Pi Zero with double sided tape attached.&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;All taped and ready to stick on.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Before connecting the Pi to power I tested the BEC output and reed switch
behaviour with a multimeter. I used washers under the reed switch to fine tune
the gap between it and the magnet attached to the garage door to ensure it
switched properly. Once all seemed well I connected the Pi and tested that the
monitoring, web server, and notifications were working as expected—they were!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;This was a fun project as it combined several of my interests: hardware and
electronics, software, web dev, and operating systems. I’m pleased with the end
result. It’s certainly not the smallest Linux system in the world but
definitely a lot smaller than Raspberry Pi OS Lite.&lt;&#x2F;p&gt;
&lt;p&gt;The true test will come if&#x2F;when I leave the door open and receive a
notification, making all this worth it. I’ll post an update if that
happens.&lt;&#x2F;p&gt;
&lt;p&gt;All the code, hardware designs, and Buildroot configuration&#x2F;overlay is
available in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;garage-door-monitor&quot;&gt;the git repository&lt;&#x2F;a&gt; if you’re interested.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;garage-door-monitor-update&#x2F;&quot;&gt;See part two&lt;&#x2F;a&gt; for a couple of new
features I added a few months later.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Fixing Monospace Text in Kobo eReaders</title>
      <pubDate>Sun, 10 Apr 2022 09:14:50 +1000</pubDate>
      <atom:published>2022-04-10T09:14:50+10:00</atom:published>
      <atom:updated>2024-11-14T18:10:31+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;monospace-kobo-ereader&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;monospace-kobo-ereader&#x2F;</guid>
      <description>&lt;p&gt;After verifying with friends that eBook readers do a decent job of rendering
technical content I purchased a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;au.kobobooks.com&#x2F;products&#x2F;kobo-libra-2&quot;&gt;Kobo Libra 2&lt;&#x2F;a&gt; this week. I loaded up some books
and started reading… but something was off. Sure enough, after verifying the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.w3.org&#x2F;publishing&#x2F;epub&#x2F;&quot;&gt;EPUB&lt;&#x2F;a&gt; with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;calibre-ebook.com&#x2F;&quot;&gt;Calibre&lt;&#x2F;a&gt; on my computer I confirmed that the Kobo was not rendering
text with CSS rules like &lt;code&gt;font-family: monospace&lt;&#x2F;code&gt; in a monospace font.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;&lt;strong&gt;Update Nov 2024:&lt;&#x2F;strong&gt; Oliver Fink wrote in to tell me that the issue is that
the font now has to have the font family name “Courier”, not just have a
file name of Courier. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;memos.mountmerlin.com&#x2F;m&#x2F;Gy8venDj6xej3vabw2idwx&quot;&gt;Check out their write-up&lt;&#x2F;a&gt; for all the details.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally Roman Frołow wrote in with a tip for a tool that can turn off
behaviour that overrides the font family in KePub books, which could also
result in monospace fonts not showing up in books. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;kobo&#x2F;comments&#x2F;12kgo49&#x2F;comment&#x2F;jg4p6gj&#x2F;&quot;&gt;Visit this Reddit
thread&lt;&#x2F;a&gt; for the details.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update Aug 2022:&lt;&#x2F;strong&gt; I was told and have confirmed myself that the workaround
described in this post no longer works with the latest firmware. I tested
version 4.33.19759. If anyone knows how to restore the behaviour please get in
touch.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;monospace-kobo-ereader&#x2F;#instructions&quot;&gt;Skip to Instructions ⮷&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I quickly discovered this is a known issue with Kobo readers dating back years:
for reasons I can’t comprehend they do not include a monospace font on the
device. I read many forum posts about how to add fonts but these were centred
around adding fonts to the font selector for the body text. Eventually I found
the solution via a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.the-ebook-reader.com&#x2F;2019&#x2F;12&#x2F;12&#x2F;new-kobo-firmware-update-4-19-14123-released&#x2F;&quot;&gt;summary of the release notes for a firmware update in
2019&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Path for monospace font changed to match other fonts and name is “Courier”.
This should mean that any correctly sideloaded font whose name starts with
“Courier” will be used when the monospace font face is specified.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;With this knowledge in hand I was able to make it render monospace text in the
one true monospace font, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fsd.it&#x2F;shop&#x2F;fonts&#x2F;pragmatapro&#x2F;&quot;&gt;PragmataPro&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2022&amp;#x2F;monospace-kobo-ereader&amp;#x2F;kobo-monospace.jpg&quot;&gt;
  
  
    
    
      &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&amp;#x2F;processed_images&amp;#x2F;kobo-monospace.58d01ea7ca81bbe9.jpg&quot; alt=&quot;Kobo Libra 2 rendering monospace text in PragmataPro&quot; &#x2F;&gt;
    
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Kobo Libra 2 rendering monospace text in PragmataPro.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;instructions&quot;&gt;Instructions&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;Connect the Kobo to your computer.&lt;&#x2F;li&gt;
&lt;li&gt;Create a folder in the root of the device named &lt;code&gt;fonts&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Put your chosen monospace font in the &lt;code&gt;fonts&lt;&#x2F;code&gt; folder.&lt;&#x2F;li&gt;
&lt;li&gt;Rename the fonts to follow this naming convention: &lt;code&gt;Courier &amp;lt;Font Family&amp;gt;-&amp;lt;Font Weight&amp;gt;.ttf&lt;&#x2F;code&gt;.
&lt;ul&gt;
&lt;li&gt;The leading &lt;code&gt;Courier&lt;&#x2F;code&gt; is required for the eReader to use the font for
monospace text in books and not just show the font as an option in the
reading settings.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;For example I renamed the PragmataPro font files to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Courier PragmataPro Mono-Regular.ttf&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Courier PragmataPro Mono-Bold.ttf&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Courier PragmataPro Mono-Italic.ttf&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Courier PragmataPro Mono-BoldItalic.ttf&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I read in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.mobileread.com&#x2F;forums&#x2F;showthread.php?t=204363&quot;&gt;a forum thread&lt;&#x2F;a&gt; that it’s important that the fonts be
named like this with the actual font family name and the font weight names for
them to work. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;apps.gnome.org&#x2F;en&#x2F;app&#x2F;org.gnome.font-viewer&#x2F;&quot;&gt;GNOME Font Viewer&lt;&#x2F;a&gt; in freedesktop.org environments
or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;support.apple.com&#x2F;en-au&#x2F;guide&#x2F;font-book&#x2F;welcome&#x2F;mac&quot;&gt;Font Book&lt;&#x2F;a&gt; on macOS, or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;yeslogic&#x2F;allsorts-tools&quot;&gt;allsorts-tools&lt;&#x2F;a&gt; on all platforms can show you the
font family name for a font.&lt;&#x2F;p&gt;
&lt;p&gt;Happy reading!&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Alpine Linux and Docker Infrastructure Three Years Later</title>
      <pubDate>Mon, 28 Feb 2022 09:52:10 +1000</pubDate>
      <atom:published>2022-02-28T09:52:10+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;alpine-linux-docker-infrastructure-three-years&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;alpine-linux-docker-infrastructure-three-years&#x2F;</guid>
      <description>&lt;p&gt;Three years ago I published,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;technical&#x2F;2019&#x2F;02&#x2F;alpine-linux-docker-infrastructure&#x2F;&quot;&gt;Rebuilding My Personal Infrastructure With Alpine Linux and Docker&lt;&#x2F;a&gt;,
in which I described how I was hosting various applications using an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alpinelinux.org&#x2F;&quot;&gt;Alpine
Linux&lt;&#x2F;a&gt; host and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.docker.com&#x2F;&quot;&gt;Docker&lt;&#x2F;a&gt; on a virtual machine at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.vultr.com&#x2F;?ref=7903263&quot;&gt;Vultr&lt;&#x2F;a&gt;. I thought it
would be good to write a follow-up on how this worked out.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;The server I set up in 2019 is still running, although it has been through a
few Alpine upgrades in that time. Since there are very few packages installed
on the host, upgrades are painless. When I originally configured the
server Docker Compose was not in the stable Alpine package repos. It was added
in the mid-2019 Alpine 3.10 release. I was glad to be able to remove &lt;code&gt;pip&lt;&#x2F;code&gt; and
not have to manually manage compose updates after migrating to the package from
that point on.&lt;&#x2F;p&gt;
&lt;p&gt;In 2019 the services I was hosting looked like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;images&#x2F;2019&#x2F;services.svg&quot; alt=&quot;Before&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In 2022 the situation is now:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;alpine-linux-docker-infrastructure-three-years&#x2F;services.svg&quot; alt=&quot;After&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As you can see the server is now running more containers. It now hosts a number
of new services including a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pleroma.social&#x2F;&quot;&gt;Pleroma&lt;&#x2F;a&gt; instance and several web-applications I
built in Rust (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;leaf&quot;&gt;leaf&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;Quotes&quot;&gt;quotes&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;dewpoint.7bit.org&quot;&gt;dewpoint&lt;&#x2F;a&gt;). I also retired the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;binarytrance.com&#x2F;&quot;&gt;Binary
Trance&lt;&#x2F;a&gt; Rails instance and migrated from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;acmesh-official&#x2F;acme.sh&quot;&gt;acme.sh&lt;&#x2F;a&gt; to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;go-acme.github.io&#x2F;lego&#x2F;&quot;&gt;lego&lt;&#x2F;a&gt;. I ran a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zedeus&#x2F;nitter&quot;&gt;Nitter&lt;&#x2F;a&gt;
instance for a while but removed it after it bots made it consume a huge amount
of bandwidth. See my &lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2021&#x2F;nitter-bandwidth&#x2F;&quot;&gt;Burning 2.5Tb of Bandwidth Hosting a Nitter
Instance&lt;&#x2F;a&gt; post for more details on
that.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;read-rust&#x2F;&quot;&gt;Read Rust&lt;&#x2F;a&gt; application is implemented in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crystal-lang.org&#x2F;&quot;&gt;Crystal&lt;&#x2F;a&gt;. This one posed a bit
of a challenge. It is the only container I run that is not derived from a
minimal Alpine base image that I build myself. Instead, it uses &lt;code&gt;debian-slim&lt;&#x2F;code&gt;. I
need to use a specific version of the Crystal compiler to build my application,
which means I can’t use the version in the Alpine package repos. Additionally
the Linux binaries that the project published for this version are not
dynamically linked but the bundled &lt;code&gt;libgc.a&lt;&#x2F;code&gt; assumes a &lt;code&gt;glibc&lt;&#x2F;code&gt; based system:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ crystal build src&#x2F;asdf.cr&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;x86_64-alpine-linux-musl&#x2F;10.3.1&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;x86_64-alpine-linux-musl&#x2F;bin&#x2F;ld: &#x2F;crystal-0.34.0-1&#x2F;bin&#x2F;..&#x2F;lib&#x2F;crystal&#x2F;lib&#x2F;libgc.a(pthread_support.o): in function `GC_thr_init&amp;#39;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pthread_support.c:(.text+0x1137): undefined reference to `gnu_get_libc_version&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;⋮&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For this reason I opted to use a Debian base image for this container and that’s
worked fine.&lt;&#x2F;p&gt;
&lt;p&gt;Another notable change is the move from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;acmesh-official&#x2F;acme.sh&quot;&gt;acme.sh&lt;&#x2F;a&gt; to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;go-acme.github.io&#x2F;lego&#x2F;&quot;&gt;lego&lt;&#x2F;a&gt; for managing TLS
certificates. In my original post I noted the following regarding how the
certificates were renewed:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Docker and cron is also a challenge. I ended up solving that with a simple
solution: use the host cron to &lt;code&gt;docker exec&lt;&#x2F;code&gt; acme.sh in the &lt;code&gt;hitch&lt;&#x2F;code&gt; container.
Perhaps not “pure” Docker but a lot simpler than some of the options I saw.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It turned out that this never worked. I could see that cron was running the
script but the certs would not get renewed. For a while I ran the
script manually to renew them, which did work. Eventually I got sick of this, and
thinking &lt;code&gt;acme.sh&lt;&#x2F;code&gt; was to blame I searched for an alternative.&lt;&#x2F;p&gt;
&lt;p&gt;I settled on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;go-acme.github.io&#x2F;lego&#x2F;&quot;&gt;lego&lt;&#x2F;a&gt;, an ACME client implemented in Go. I &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-acme&#x2F;lego&#x2F;issues&#x2F;1150&quot;&gt;discovered and
suggested a fix for a bug&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-acme&#x2F;lego&#x2F;pull&#x2F;1135&quot;&gt;contributed LuaDNS
support&lt;&#x2F;a&gt; in May 2020 and then migrated over to using it instead of
&lt;code&gt;acme.sh&lt;&#x2F;code&gt;. It was in the final stages of test this that I discovered the cron
bug was in my script all along. Adding &lt;code&gt;-T&lt;&#x2F;code&gt; to inhibit &lt;code&gt;docker-compose exec&lt;&#x2F;code&gt;
from allocating a TTY fixed the issue. It’s likely this would have fixed the
issue for &lt;code&gt;acme.sh&lt;&#x2F;code&gt; as well. Ultimately lego felt like the better option as the
code in &lt;code&gt;acme.sh&lt;&#x2F;code&gt; for constructing API requests and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;acmesh-official&#x2F;acme.sh&#x2F;blob&#x2F;2a2d556551e9266a3924da205c1ede55d89a689d&#x2F;dnsapi&#x2F;dns_lua.sh#L117-L126&quot;&gt;parsing their
results&lt;&#x2F;a&gt; seemed quite fragile&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;ProgrammerHumor&#x2F;comments&#x2F;cw58z7&#x2F;it_works_on_my_machine&#x2F;&quot;&gt;Jokes aside&lt;&#x2F;a&gt;, I still find the Docker workflow of
iterating on an image locally then shipping it when it’s working to be quite
pleasant. In summary this server has served me well over the last three years
and I have no immediate plans to rebuild again. It’s been reliable and mostly
hassle free. Hosting on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.vultr.com&#x2F;?ref=7903263&quot;&gt;Vultr&lt;&#x2F;a&gt; has also been reliable and stable over that
whole time with only the odd interruption for network maintenance or network
issues.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>ASCII-centric Usernames</title>
      <pubDate>Thu, 27 Jan 2022 18:38:25 +1000</pubDate>
      <atom:published>2022-01-27T18:38:25+10:00</atom:published>
      <atom:updated>2022-01-27T21:07:32+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;usernames&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2022&#x2F;usernames&#x2F;</guid>
      <description>&lt;p&gt;I’m working on a web-based side project in my spare time. The great thing about
side projects is you get to make all the choices and question the common
wisdom. Recently I’ve been building out the sign-up flow and I started thinking
about usernames—specifically the characters that they may be comprised of.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;I poked around a few sites to see what they did: Twitter, GitHub, Discourse
all restrict your username to a mostly ASCII alphanumeric character set, perhaps
with &lt;code&gt;-&lt;&#x2F;code&gt;, &lt;code&gt;_&lt;&#x2F;code&gt;, and &lt;code&gt;.&lt;&#x2F;code&gt; thrown in.&lt;&#x2F;p&gt;
&lt;p&gt;It struck me that this is fine for me, an English speaker, but must suck for
folks that can’t have a username in their own language. It is however not
without precedent. The Internet’s origins in the US linger on with similar
restrictions on e-mail and DNS (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Punycode&quot;&gt;Punycode&lt;&#x2F;a&gt; is but a workaround) for example.
Some further thinking and research led to some possible reasons for this:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;There’s the obvious precedent set by e-mail and other systems: that’s how
we’ve always done it so it just continues.&lt;&#x2F;li&gt;
&lt;li&gt;Some languages require a dedicated &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Input_method&quot;&gt;input system&lt;&#x2F;a&gt; in order to type
naturally. That means it would be difficult for people without familiarity
with that system to be able to type the username such as might be necessary
when @ mentioning someone.&lt;&#x2F;li&gt;
&lt;li&gt;Similar to above, most keyboards have some way to type the English alphabet.&lt;&#x2F;li&gt;
&lt;li&gt;ASCII alpha-numeric characters are able to be in URLs without &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Percent-encoding&quot;&gt;percent-encoding&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;That last one is the most compelling reason I saw. For an application that has
user profile pages where the username goes in the URL it seems advantageous for
that to be able to happen directly without encoding.&lt;&#x2F;p&gt;
&lt;p&gt;Now this is all very biased by my monolingual, English speaking, Western viewpoint.
Perhaps it is more common to permit native language usernames in applications that
target non-English markets?&lt;&#x2F;p&gt;
&lt;p&gt;I did find a couple of examples that were more permissive with usernames.
Discord happily let me set my username to “🦊 こんにちは”. Slack rejected the
emoji with a cute message, “Of course you want a name with an emoji. Sadly, it
is not to be. Try letters?”, but was otherwise happy with “こんにちは”. In both
cases @ mentioning the user appears to require typing their name, although you
could also find them in the people directory first.&lt;&#x2F;p&gt;
&lt;p&gt;Notably Discord and Slack don’t have public profile pages (that would need a
URL). I’d be curious if there were systems out there with public profile pages
where the usernames are permissive and the name is in the URL (and not the
account id number for example).&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>RustConf 2021</title>
      <pubDate>Sat, 09 Oct 2021 08:33:11 +1000</pubDate>
      <atom:published>2021-10-09T08:33:11+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2021&#x2F;rust-conf&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2021&#x2F;rust-conf&#x2F;</guid>
      <description>&lt;p&gt;A few weeks ago I got up at 2:30am and attended virtual &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rustconf.com&#x2F;&quot;&gt;RustConf
2021&lt;&#x2F;a&gt;. The pre-recorded talks were live-streamed and there was a
dedicated Discord server for discussion and Q&amp;amp;A while the talks ran. It was
overall well organised and a good experience. All the talks were interesting
and well executed.  The Discord chat was fun but I’m not sure it added a lot to
the &lt;em&gt;value&lt;&#x2F;em&gt; of my experience.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;I think if you had a talk that you were particularly interested in, being
present for the conference and having direct access to the speaker might be
valuable. Alternatively it seemed that for folks newer to the language the chat
provided easy access to a lot of people to answer questions and provide
explanations. With this in mind, in the future I think I’ll probably stick to
watching the videos after they’re posted to YouTube (and not mess with my
sleep). Having said that, they plan to return to an in-person conference (in
Portland) next year.&lt;&#x2F;p&gt;
&lt;p&gt;My favourites were:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=DnYQKWs_7EA&amp;amp;list=PL85XCvVPmGQgACNMZlhlRZ4zlKZG_iWH5&amp;amp;index=2&quot;&gt;Deadlocked&lt;&#x2F;a&gt; by Mara Bos&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=CV5CjUlcqsw&amp;amp;list=PL85XCvVPmGQgACNMZlhlRZ4zlKZG_iWH5&amp;amp;index=4&quot;&gt;The Importance of Not Over-Optimizing in Rust&lt;&#x2F;a&gt; by Lily Mara&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=BLy_YF4nmqQ&amp;amp;list=PL85XCvVPmGQgACNMZlhlRZ4zlKZG_iWH5&amp;amp;index=5&quot;&gt;Identifying Pokémon Cards&lt;&#x2F;a&gt; by Hugo Peixoto&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=9H9SO2u6Q20&amp;amp;list=PL85XCvVPmGQgACNMZlhlRZ4zlKZG_iWH5&amp;amp;index=12&quot;&gt;Hacking rustc: Contributing to the Compiler&lt;&#x2F;a&gt; by Esteban Kuber&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can watch all the talks in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;playlist?list=PL85XCvVPmGQgACNMZlhlRZ4zlKZG_iWH5&quot;&gt;RustConf 2021 playlist on YouTube&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The best thing about Esteban’s talk may have been this exchange on Discord:&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2021&amp;#x2F;rust-conf&amp;#x2F;speed.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2021&amp;#x2F;rust-conf&amp;#x2F;speed.png&quot; width=&quot;357&quot; alt=&quot;Screenshot of Discord chat in which someone suggests that Esteban sounds like he&amp;#x27;s speaking at 1.25×. Esteban replies saying that the talk was a couple of minutes over the 15 minutes allocated so he sped it up. One of the conference staff, replied that they would have happily given him the extra couple of minutes.&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;People are really good at detecting sped up speech.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
</description>
    </item>
    
    <item>
      <title>Burning 2.5Tb of Bandwidth Hosting a Nitter Instance</title>
      <pubDate>Thu, 26 Aug 2021 09:10:54 +1000</pubDate>
      <atom:published>2021-08-26T09:10:54+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2021&#x2F;nitter-bandwidth&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2021&#x2F;nitter-bandwidth&#x2F;</guid>
      <description>&lt;p&gt;On 24 August I received an email from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.vultr.com&#x2F;?ref=7903263&quot;&gt;Vultr&lt;&#x2F;a&gt; saying that my server had used 78%
of its 3Tb bandwidth allocation for the month. This was surprising as last time
I looked I only used a small fraction of this allocation across the various
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;technical&#x2F;2019&#x2F;02&#x2F;alpine-linux-docker-infrastructure&#x2F;&quot;&gt;things I host&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;After some investigation I noticed that the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zedeus&#x2F;nitter&quot;&gt;Nitter&lt;&#x2F;a&gt; instance I &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;decentralised.social&#x2F;notice&#x2F;A41E2cjuM14UYFAF7o&quot;&gt;set up six
months ago&lt;&#x2F;a&gt; at &lt;code&gt;nitter.decentralised.social&lt;&#x2F;code&gt; seemed to be
getting a lot of traffic. In particular it seemed that there were several
crawlers including Googlebot and bingbot attempting to index the whole site and
all its media.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Nitter is an alternate UI for Twitter that is simpler, faster, and free of
tracking. I mainly set it up so that I could share Twitter links with my
friends, without them having to visit Twitter proper. It’s obvious in hindsight
but a Nitter instance is basically a proxy for the entirety of Twitter so any
bots that start crawling it are basically trying to suck all of Twitter through
my little server.&lt;&#x2F;p&gt;
&lt;p&gt;Nitter doesn’t have a &lt;code&gt;robots.txt&lt;&#x2F;code&gt; by default so the first thing I did was fork
it and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;nitter&#x2F;commit&#x2F;4e7bd7b8853bf36008a3d1e79ee97deaa68743da&quot;&gt;add one that blocked all robots&lt;&#x2F;a&gt;. Unsurprisingly this
didn’t have an immediate impact and I was concerned that if the traffic kept up
I’d hit my bandwidth limit and start having to pay per Gb thereafter.&lt;&#x2F;p&gt;
&lt;p&gt;I concluded the best option for now was to block most traffic to the instance
until I could work out what to do. Since &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;varnish-cache.org&#x2F;&quot;&gt;Varnish&lt;&#x2F;a&gt; fronts most of my web
traffic I used it to filter requests and return a very basic 404 page for all
but a handful of routes. A day later the impact of this change is obvious in
the usage graphs.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2021&amp;#x2F;nitter-bandwidth&amp;#x2F;daily-bandwidth.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2021&amp;#x2F;nitter-bandwidth&amp;#x2F;daily-bandwidth.png&quot; alt=&quot;Chart showing daily bytes sent and received for the last 30 days. The last day shows a significant drop&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Daily data sent and received for the last 30 days&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2021&amp;#x2F;nitter-bandwidth&amp;#x2F;network-usage.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2021&amp;#x2F;nitter-bandwidth&amp;#x2F;network-usage.png&quot; alt=&quot;Chart showing network activity for the last week with a significant drop in the last two days&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Network activity, last 7 days&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2021&amp;#x2F;nitter-bandwidth&amp;#x2F;cpu-usage.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2021&amp;#x2F;nitter-bandwidth&amp;#x2F;cpu-usage.png&quot; alt=&quot;Chart showing CPU usage for the last week with a significant drop in the last two days&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;CPU usage, last 7 days&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;After letting the changes sit overnight I was still seeing a lot of requests
from user-agents that appear to be Chinese bots of some sort. They almost
exactly matched the user-agents in this blog post: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.johnlarge.co.uk&#x2F;blocking-aggressive-chinese-crawlers-scrapers-bots&#x2F;&quot;&gt;Blocking aggressive Chinese
crawlers&#x2F;scrapers&#x2F;bots&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As a result I added some additional configuration to Varnish to block requests
from these user-agents, as they were clearly not honouring the &lt;code&gt;robots.txt&lt;&#x2F;code&gt; I
added:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sub vcl_recv &lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;req&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;agent &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;~&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;(Mb2345Browser|LieBaoFast|OPPO A33|SemrushBot)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;synth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;403&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;User agent blocked due to excessive traffic.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    # rest of config here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;what-now&quot;&gt;What Now?&lt;&#x2F;h3&gt;
&lt;p&gt;I liked having the Nitter instance for sharing links but now I’m not sure how
to run it in a way that only proxies the things I’m sharing. I don’t really
want to be responsible for all of the content posted to Twitter flowing through
my server. Perhaps there’s a project idea lurking there, or perhaps I just make
my peace with linking to Twitter.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Turning One Hundred Tweets Into a Blog Post</title>
      <pubDate>Tue, 03 Nov 2020 11:40:00 +1100</pubDate>
      <atom:published>2020-11-03T11:40:00+11:00</atom:published>
      <atom:updated>2021-09-30T09:14:36+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;turning-one-hundred-tweets-into-a-blog-post&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;turning-one-hundred-tweets-into-a-blog-post&#x2F;</guid>
      <description>&lt;p&gt;Near the conclusion of my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;search?q=%23100binaries%20from%3A%40wezm&amp;amp;src=typed_query&amp;amp;f=live&quot;&gt;#100binaries&lt;&#x2F;a&gt; Twitter series I started working on
&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;100-rust-binaries&#x2F;&quot;&gt;the blog post that contained all the tweets&lt;&#x2F;a&gt;.
It ended up posing a number of interesting challenges and design decisions, as
well as a couple of Rust binaries. Whilst I don’t think the process was optimal
I thought I’d share the process to show my approach to solving the problem.
Perhaps the tools used and approach taken is interesting to others.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;My initial plan was to use Twitter embeds. Given a tweet URL it’s fairly
easy to turn it into some HTML markup. By including Twitter’s embed JavaScript
on the page the markup turns into rich Twitter embed. However there were a few
things I didn’t like about this option:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The page was going to end up massive, even split across a couple of pages
because the Twitter JS was loading all the images for each tweet up front.&lt;&#x2F;li&gt;
&lt;li&gt;I didn’t like relying on JavaScript for the page to render media.&lt;&#x2F;li&gt;
&lt;li&gt;I didn’t really want to include Twitter’s JavaScript (it’s likely it would be
blocked by visitors with an ad blocker anyway).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So I decided I’d render the content myself. I also decided that I’d host the
original screenshots and videos instead of saving them from the tweets. This
was a little time consuming as they were across a couple of computers and
not named well but I found them all in the end.&lt;&#x2F;p&gt;
&lt;p&gt;To ensure the page wasn’t enormous I used the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTML&#x2F;Element&#x2F;img#attr-loading&quot;&gt;&lt;code&gt;loading=&quot;lazy&quot;&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
attribute on images. This is a relatively new attribute that tells the browser
to delay loading of images until they’re within some threshold of the
view port. It currently works in Firefox and Chrome.&lt;&#x2F;p&gt;
&lt;p&gt;I used &lt;code&gt;preload=&quot;none&quot;&lt;&#x2F;code&gt; on videos to ensure video data was only loaded if the
visitor attempted to play it.&lt;&#x2F;p&gt;
&lt;p&gt;To prevent the blog post from being too long&#x2F;heavy I split it across two pages.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;collecting-all-the-tweet-urls&quot;&gt;Collecting All the Tweet URLs&lt;&#x2F;h3&gt;
&lt;p&gt;With the plan in mind the first step was getting the full list of tweets. For
better or worse I decided to avoid using any of Twitter’s APIs that require
authentication. Instead I turned to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nitter.net&#x2F;about&quot;&gt;nitter&lt;&#x2F;a&gt; (an alternative Twitter
front-end) for its simple markup and JS free rendering.&lt;&#x2F;p&gt;
&lt;p&gt;For each page of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nitter.net&#x2F;search?f=tweets&amp;amp;q=%23100binaries+from%3A%40wezm&quot;&gt;search results for ‘#100binaries from:@wezm’&lt;&#x2F;a&gt; I ran
the following in the JS Console in Firefox:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;tweets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;document&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;querySelectorAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.tweet-date a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;forEach&lt;&#x2F;span&gt;&lt;span&gt;(a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; tweets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;href))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;copy&lt;&#x2F;span&gt;&lt;span&gt;(tweets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;join&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and pasted the result into &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;wezm.net&#x2F;blob&#x2F;master&#x2F;v2&#x2F;content&#x2F;posts&#x2F;2020&#x2F;100-rust-binaries&#x2F;tweets.txt&quot;&gt;tweets.txt&lt;&#x2F;a&gt; in Neovim.&lt;&#x2F;p&gt;
&lt;p&gt;When all pages had be processed I turned the nitter.net URLs back in to
twitter.com URLs: &lt;code&gt;:%s&#x2F;nitter\.net&#x2F;twitter.com&#x2F;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This tells Neovim: for every line (&lt;code&gt;%&lt;&#x2F;code&gt;) substitute (&lt;code&gt;s&lt;&#x2F;code&gt;) &lt;code&gt;nitter.net&lt;&#x2F;code&gt; with
&lt;code&gt;twitter.com&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;turning-tweet-urls-into-tweet-content&quot;&gt;Turning Tweet URLs Into Tweet Content&lt;&#x2F;h3&gt;
&lt;p&gt;Now I needed to turn the tweet URLs into tweet content. In hindsight it may
have been better to use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.twitter.com&#x2F;en&#x2F;docs&#x2F;twitter-api&#x2F;v1&#x2F;tweets&#x2F;post-and-engage&#x2F;api-reference&#x2F;get-statuses-show-id&quot;&gt;Twitter’s GET statuses&#x2F;show&#x2F;:id&lt;&#x2F;a&gt; API to do
this (possibly via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;twitter&#x2F;twurl&quot;&gt;twurl&lt;&#x2F;a&gt;) but that is not what I did. Onwards!&lt;&#x2F;p&gt;
&lt;p&gt;I used the unauthenticated &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.twitter.com&#x2F;en&#x2F;docs&#x2F;twitter-api&#x2F;v1&#x2F;tweets&#x2F;post-and-engage&#x2F;api-reference&#x2F;get-statuses-oembed&quot;&gt;oEmbed API&lt;&#x2F;a&gt; to get some markup for each
tweet. &lt;code&gt;xargs&lt;&#x2F;code&gt; was used to take a line from &lt;code&gt;tweets.txt&lt;&#x2F;code&gt; and make the API
(HTTP) request with &lt;code&gt;curl&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;xargs -I &amp;#39;{url}&amp;#39; -a tweets.txt -n 1 curl https:&#x2F;&#x2F;api.twitter.com&#x2F;1&#x2F;statuses&#x2F;oembed.json\?omit_script\=true\&amp;amp;dnt\=true\&amp;amp;lang\=en\&amp;amp;url\=\{url\} &amp;gt; tweets.json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This tells &lt;code&gt;xargs&lt;&#x2F;code&gt; to replace occurrences of &lt;code&gt;{url}&lt;&#x2F;code&gt; in the command with a line
(&lt;code&gt;-n 1&lt;&#x2F;code&gt;) read from &lt;code&gt;tweets.txt&lt;&#x2F;code&gt; (&lt;code&gt;-a tweets.txt&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;The result of one of these API requests is JSON like this (formatted with
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stedolan.github.io&#x2F;jq&#x2F;&quot;&gt;&lt;code&gt;jq&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; for readability):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1322855912076386304&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;author_name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Wesley Moore&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;author_url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;lt;blockquote class=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;twitter-tweet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; data-lang=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;en&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; data-dnt=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;gt;&amp;lt;p lang=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;en&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; dir=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;ltr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;gt;Day 100 of &amp;lt;a href=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;amp;ref_src=twsrc%5Etfw&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;gt;#100binaries&amp;lt;&#x2F;a&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Today I&amp;amp;#39;m featuring the Rust compiler — the binary that made the previous 99 fast, efficient, user-friendly, easy-to-build, and reliable binaries possible.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Thanks to all the people that have worked on it past, present, and future. &amp;lt;a href=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;t.co&#x2F;aBEdLE87eq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;gt;https:&#x2F;&#x2F;t.co&#x2F;aBEdLE87eq&amp;lt;&#x2F;a&amp;gt; &amp;lt;a href=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;t.co&#x2F;jzyJtIMGn1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;gt;pic.twitter.com&#x2F;jzyJtIMGn1&amp;lt;&#x2F;a&amp;gt;&amp;lt;&#x2F;p&amp;gt;&amp;amp;mdash; Wesley Moore (@wezm) &amp;lt;a href=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1322855912076386304?ref_src=twsrc%5Etfw&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;gt;November 1, 2020&amp;lt;&#x2F;a&amp;gt;&amp;lt;&#x2F;blockquote&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 550&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;height&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; null&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;rich&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;cache_age&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;3153600000&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;provider_name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Twitter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;provider_url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;twitter.com&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The output from &lt;code&gt;xargs&lt;&#x2F;code&gt; is lots of these JSON objects all concatenated
together. I needed to turn &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;wezm.net&#x2F;blob&#x2F;master&#x2F;v2&#x2F;content&#x2F;posts&#x2F;2020&#x2F;100-rust-binaries&#x2F;tweets.json&quot;&gt;tweets.json&lt;&#x2F;a&gt; into an array of objects to make it
valid JSON. I opened up the file in Neovim and:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Added commas between the JSON objects: &lt;code&gt;%s&#x2F;}{&#x2F;},\r{&#x2F;g&lt;&#x2F;code&gt;.
&lt;ul&gt;
&lt;li&gt;This is, substitute &lt;code&gt;}{&lt;&#x2F;code&gt; with &lt;code&gt;},{&lt;&#x2F;code&gt; and a newline (&lt;code&gt;\r&lt;&#x2F;code&gt;), multiple times (&lt;code&gt;&#x2F;g&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Added &lt;code&gt;[&lt;&#x2F;code&gt; and &lt;code&gt;]&lt;&#x2F;code&gt; to start and end of the file.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I then reversed the order of the objects and formatted the document with &lt;code&gt;jq&lt;&#x2F;code&gt; (from within Neovim): &lt;code&gt;%!jq &#x27;.|reverse&#x27; -&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This filters the whole file though a command (&lt;code&gt;%!&lt;&#x2F;code&gt;). The command is &lt;code&gt;jq&lt;&#x2F;code&gt; and it
filters the entire document &lt;code&gt;.&lt;&#x2F;code&gt;, read from stdin (&lt;code&gt;-&lt;&#x2F;code&gt;), through the &lt;code&gt;reverse&lt;&#x2F;code&gt;
filter to reverse the order of the array. &lt;code&gt;jq&lt;&#x2F;code&gt; automatically pretty prints.&lt;&#x2F;p&gt;
&lt;p&gt;It would have been better to have reversed &lt;code&gt;tweets.txt&lt;&#x2F;code&gt; but I didn’t
realise they were in reverse chronological ordering until this point and
doing it this way avoided making another 100 HTTP requests.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;rendering-tweets-json&quot;&gt;Rendering tweets.json&lt;&#x2F;h3&gt;
&lt;p&gt;I created a custom &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;content&#x2F;shortcodes&#x2F;&quot;&gt;Zola shortcode&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;wezm.net&#x2F;blob&#x2F;master&#x2F;v2&#x2F;templates&#x2F;shortcodes&#x2F;tweet_list.html&quot;&gt;tweet_list&lt;&#x2F;a&gt; that reads
&lt;code&gt;tweets.json&lt;&#x2F;code&gt; and renders each item in an ordered list. It evolved over time as
I kept adding more information to the JSON file. It allowed me to see how
the blog post looked as I implemented the following improvements.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;expanding-t-co-links&quot;&gt;Expanding t.co Links&lt;&#x2F;h3&gt;
&lt;aside class=&quot;float-right&quot;&gt;
  &lt;div class=&quot;emoji text-center&quot;&gt;💡&lt;&#x2F;div&gt;
  &lt;strong&gt;You used Rust for this!?&lt;&#x2F;strong&gt;

  &lt;p&gt;This is the sort of thing that would be well suited to a scripting language
too.  These days I tend to reach for Rust, even for little tasks like this.
It’s what I’m most familiar with nowadays and I can mostly write a “script”
like this off the cuff with little need to refer to API docs.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;The markup Twitter returns is full of &lt;code&gt;t.co&lt;&#x2F;code&gt; redirect links. I wanted to avoid
sending my visitors through the Twitter redirect so I needed to expand these
links to their target. I whipped up a little Rust program to do this:
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;expand-t-co&quot;&gt;expand-t-co&lt;&#x2F;a&gt;. It finds all &lt;code&gt;t.co&lt;&#x2F;code&gt; links with a regex
(&lt;code&gt;https:&#x2F;&#x2F;t\.co&#x2F;[a-zA-Z0-9]+&lt;&#x2F;code&gt;) and replaces each occurrence with the target
of the link.&lt;&#x2F;p&gt;
&lt;p&gt;The target URL is determined by making making a HTTP HEAD request for the
&lt;code&gt;t.co&lt;&#x2F;code&gt; URL and noting the value of the &lt;code&gt;Location&lt;&#x2F;code&gt; header. The tool
caches the result in a &lt;code&gt;HashMap&lt;&#x2F;code&gt; to avoid repeating a request for
the same &lt;code&gt;t.co&lt;&#x2F;code&gt; URL if it’s encountered again.&lt;&#x2F;p&gt;
&lt;p&gt;I used  the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;algesten&#x2F;ureq&quot;&gt;ureq&lt;&#x2F;a&gt; crate to make the HTTP requests. Arguably it would have been
better to use an async client so that more requests were made in parallel but
that was added complexity I didn’t want to deal with for a mostly one-off
program.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;adding-the-media&quot;&gt;Adding the Media&lt;&#x2F;h3&gt;
&lt;p&gt;At this point I did a lot of manual work to find all the screenshots and videos
that I shared in the tweets and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;wezm.net&#x2F;tree&#x2F;master&#x2F;v2&#x2F;content&#x2F;posts&#x2F;2020&#x2F;100-rust-binaries&quot;&gt;added them to my blog&lt;&#x2F;a&gt;. I also
renamed them after the tool they depicted. As part of this process I noted the
source of media files that I didn’t create in a &lt;code&gt;&quot;media_source&quot;&lt;&#x2F;code&gt; key in
&lt;code&gt;tweets.json&lt;&#x2F;code&gt; so that I could attribute them. I also added a &lt;code&gt;&quot;media&quot;&lt;&#x2F;code&gt; key with
the name of the media file for each binary.&lt;&#x2F;p&gt;
&lt;p&gt;Some of the externally sourced images were animated GIFs, which lack
playback controls and are very inefficient file size wise. Whenever I encountered an
animated GIF I converted it to an MP4 with &lt;code&gt;ffmpeg&lt;&#x2F;code&gt;, resulting in large space savings:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ffmpeg -i ~&#x2F;Downloads&#x2F;so.gif -movflags faststart -pix_fmt yuv420p -vf &amp;quot;scale=trunc(iw&#x2F;2)*2:trunc(ih&#x2F;2)*2&amp;quot; so.mp4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This converts &lt;code&gt;so.gif&lt;&#x2F;code&gt; to &lt;code&gt;so.mp4&lt;&#x2F;code&gt; and ensures the dimensions are a divisible
by 2, which is apparently a requirement of H.264 streams encapsulated in MP4. I
worked out how to do this from: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;unix.stackexchange.com&#x2F;a&#x2F;294892&#x2F;5444&quot;&gt;https:&#x2F;&#x2F;unix.stackexchange.com&#x2F;a&#x2F;294892&#x2F;5444&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I also wanted to know the media dimensions for each file so that I could have them
scaled properly on the page — most images are HiDPI and need to be presented at
half their pixel width to appear the right size.&lt;&#x2F;p&gt;
&lt;p&gt;For this I used &lt;code&gt;ffprobe&lt;&#x2F;code&gt;, which is part of &lt;code&gt;ffmpeg&lt;&#x2F;code&gt;. I originally planned to
use another tool to handle images (as opposed to videos) but it turns out
&lt;code&gt;ffprobe&lt;&#x2F;code&gt; handles them too.&lt;&#x2F;p&gt;
&lt;p&gt;Since I wanted to update the values of JSON objects in &lt;code&gt;tweets.json&lt;&#x2F;code&gt; I opted to
parse the JSON this time. Again I whipped up a little Rust “script”:
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;add-media-dimensions&quot;&gt;add-media-dimensions&lt;&#x2F;a&gt;. It parses &lt;code&gt;tweets.json&lt;&#x2F;code&gt; and for each object in the
array runs &lt;code&gt;ffprobe&lt;&#x2F;code&gt; on the media file, like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ffprobe -v quiet -print_format json -show_format -show_streams file.mp4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I learned how to do this from: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;11236144&#x2F;38820&quot;&gt;https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;11236144&#x2F;38820&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With this invocation &lt;code&gt;ffprobe&lt;&#x2F;code&gt; produces JSON so &lt;code&gt;add-media-dimensions&lt;&#x2F;code&gt; also
parses that and adds the width and height values to &lt;code&gt;tweets.json&lt;&#x2F;code&gt;. At the end
the updated JSON document is printed to stdout. This turned out to be a handy
sanity check as it detected a couple of copy&#x2F;paste errors and typos in the
manually added &lt;code&gt;&quot;media&quot;&lt;&#x2F;code&gt; values.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cleaning-up-pic-twitter-com-links&quot;&gt;Cleaning Up pic.twitter.com Links&lt;&#x2F;h3&gt;
&lt;p&gt;The oEmbed markup that Twitter returns includes links for each piece of media. Now that
I’m handling that myself these can be deleted. Neovim is used for this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;:%s&#x2F; &amp;lt;a href=\\&amp;quot;https:\&#x2F;\&#x2F;twitter\.com[^&amp;quot;]\+\(photo\|video\)[^&amp;quot;]\+&amp;quot;&amp;gt;pic.twitter.com[^&amp;lt;]\+&amp;lt;\&#x2F;a&amp;gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For each line of the file (&lt;code&gt;%&lt;&#x2F;code&gt;) substitute (&lt;code&gt;s&lt;&#x2F;code&gt;) matches with nothing. And that
took care of them. Yes I’m matching HTML with a regex, no you shouldn’t do this
for something that’s part of a program. For one-off text editing it’s fine
though, especially since you can eyeball the differences with &lt;code&gt;git diff&lt;&#x2F;code&gt;, or in
my case &lt;code&gt;tig status&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;adding-a-hidpi-flag&quot;&gt;Adding a HiDPI Flag&lt;&#x2F;h3&gt;
&lt;p&gt;I initially tried using a heuristic in &lt;code&gt;tweet_list&lt;&#x2F;code&gt; to determine if a media
file was HiDPI or not but there were a few exceptions to the rule. I decided to
add a &lt;code&gt;&quot;hidpi&quot;&lt;&#x2F;code&gt; value to the JSON to indicate if it was HiDPI media or not. A
bit of trial and error with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stedolan.github.io&#x2F;jq&#x2F;&quot;&gt;jq&lt;&#x2F;a&gt; led to this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;jq &amp;#39;map(. + if .width &amp;gt; 776 then {hidpi: true} else {hidpi:false} end)&amp;#39; tweets.json &amp;gt; tweets-hidpi.json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If the image is greater then 776 pixels wide then set the &lt;code&gt;hidpi&lt;&#x2F;code&gt; property to
&lt;code&gt;true&lt;&#x2F;code&gt;, otherwise &lt;code&gt;false&lt;&#x2F;code&gt;. 776 was picked via visual inspection of the rendered
page. Once satisfied with the result I examined the rendered result and flipped
the &lt;code&gt;hidpi&lt;&#x2F;code&gt; value on some items where the heuristic was wrong.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;adding-alt-text&quot;&gt;Adding alt Text&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;didoesdigital.com&#x2F;&quot;&gt;Di&lt;&#x2F;a&gt;, ever my good conscience when it comes to such things enquired at one
point if I’d added &lt;code&gt;alt&lt;&#x2F;code&gt; text to the images. I was on the fence since the
images were mostly there to show what the tools looked like — I didn’t think
they were really essential content — but she made a good argument for including
some &lt;code&gt;alt&lt;&#x2F;code&gt; text even if it was fairly simplistic.&lt;&#x2F;p&gt;
&lt;p&gt;I turned to &lt;code&gt;jq&lt;&#x2F;code&gt; again to add a basic &lt;code&gt;&quot;media_description&quot;&lt;&#x2F;code&gt; to the JSON,
which &lt;code&gt;tweet_list&lt;&#x2F;code&gt; would include as &lt;code&gt;alt&lt;&#x2F;code&gt; text:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;jq &amp;#39;map(. + {media_description: (&amp;quot;Screenshot of &amp;quot; + (.media &#x2F;&#x2F; &amp;quot;????&amp;quot; | sub(&amp;quot;.(png|gif|mp4|jpg)$&amp;quot;; &amp;quot;&amp;quot;)) + &amp;quot; running in a terminal.&amp;quot;)})&amp;#39; tweets.json &amp;gt; tweets-alt.json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For each object in the JSON array it adds a &lt;code&gt;media_description&lt;&#x2F;code&gt; key with a
value derived from the &lt;code&gt;media&lt;&#x2F;code&gt; key (the file name with the extension removed).
If the object doesn’t have a &lt;code&gt;media&lt;&#x2F;code&gt; value then it is defaulted to “????”
(&lt;code&gt;.media &#x2F;&#x2F; &quot;????&quot;&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;After these initial descriptions were added I went though the rendered page and
updated the text of items where the description was incorrect or inadequate.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;video-poster-images&quot;&gt;Video Poster Images&lt;&#x2F;h3&gt;
&lt;p&gt;As it stood all the videos were just white boxes with playback controls since I
has used &lt;code&gt;preload=&quot;none&quot;&lt;&#x2F;code&gt; to limit the data usage of the page. I decided to pay
the cost of the larger page weight and add poster images to each of the videos.
I used &lt;code&gt;ffmpeg&lt;&#x2F;code&gt; to extract the first frame of each video as a PNG:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;for m in *.mp4; do ffmpeg -i $m -vf &amp;quot;select=1&amp;quot; -vframes 1 $m.png; done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I learned how to do this from: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;superuser.com&#x2F;a&#x2F;1010108&quot;&gt;https:&#x2F;&#x2F;superuser.com&#x2F;a&#x2F;1010108&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I then converted the PNGs to JPEGs for smaller files. I could have generated
JPEGs directly from &lt;code&gt;ffmpeg&lt;&#x2F;code&gt; but I didn’t know how to control the quality — I
wanted a relatively low quality for smaller files.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;for f in *.mp4.png; do convert &amp;quot;$f&amp;quot; -quality 60 $f.jpg ; done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This produced files named &lt;code&gt;filename.mp4.png.jpg&lt;&#x2F;code&gt;. I’m yet to memorise how to
manipulate file extensions in &lt;code&gt;zsh&lt;&#x2F;code&gt;, despite having &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;Sasha_Boyd&#x2F;status&#x2F;1300666988608454656&quot;&gt;been told how to do
it&lt;&#x2F;a&gt;, so I did a follow up step to rename them:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;for f in *.mp4; do mv $f.png.jpg $f.jpg ; done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;&#x2F;h3&gt;
&lt;p&gt;Lastly I ran &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pmt.sourceforge.io&#x2F;pngcrush&#x2F;index.html&quot;&gt;&lt;code&gt;pngcrush&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; on all of the PNGs. It reliably reduces the file size
in a lossless manner:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;for f in *.png; do pngcrush -reduce -ow $f; done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With that I did some styling tweaks, added a little commentary and published
&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;100-rust-binaries&#x2F;&quot;&gt;the page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you made it this far, thanks for sticking with it to the end. I’m not sure
how interesting or useful this post is but if you liked it let me know and I
might do more like it in the future.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>One Hundred Rust Binaries - Page 2</title>
      <pubDate>Mon, 02 Nov 2020 13:00:01 +1100</pubDate>
      <atom:published>2020-11-02T13:00:01+11:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;100-rust-binaries&#x2F;page2&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;100-rust-binaries&#x2F;page2&#x2F;</guid>
      <description>&lt;p&gt;This is page two of my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;search?q=%23100binaries%20from%3A%40wezm&amp;amp;src=typed_query&amp;amp;f=live&quot;&gt;#100binaries&lt;&#x2F;a&gt; list containing binaries 51–100.
&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;100-rust-binaries&#x2F;&quot;&gt;See the first page for the introduction and binaries 1–50&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;





  


&lt;ol class=&quot;tweet-list&quot; start=&quot;51&quot;&gt;

  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 51 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Zola by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;20100Prouillet?ref_src=twsrc%5Etfw&quot;&gt;@20100Prouillet&lt;&#x2F;a&gt; is a static site compiler. It comes with support for Sass and syntax highlighting, as well as templates, themes, taxonomies, RSS feeds, and Markdown rendering. I use it for a couple of my own websites. &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.getzola.org&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1304774262616514560?ref_src=twsrc%5Etfw&quot;&gt;September 12, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 52 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;fclones by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;pkolaczk?ref_src=twsrc%5Etfw&quot;&gt;@pkolaczk&lt;&#x2F;a&gt; is a duplicate file finder. It uses several techniques to do the least amount of work possible to determine if files are different. This makes it one of the fastest tools for this job. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pkolaczk&#x2F;fclones&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;pkolaczk&#x2F;fclones&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1305136649706205185?ref_src=twsrc%5Etfw&quot;&gt;September 13, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;..&amp;#x2F;fclones.mp4&quot; poster=&quot;..&amp;#x2F;fclones.mp4.jpg&quot; style=&quot;max-height: 450px&quot;&gt;&lt;&#x2F;video&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 53 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;zoxide by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;ajeetdsouza?ref_src=twsrc%5Etfw&quot;&gt;@ajeetdsouza&lt;&#x2F;a&gt; makes it easier to jump around the file system. It integrates with your shell to learn your most popular directories. You can then jump to them by specifying only part of the path. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ajeetdsouza&#x2F;zoxide&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;ajeetdsouza&#x2F;zoxide&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1305499038490537984?ref_src=twsrc%5Etfw&quot;&gt;September 14, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;..&amp;#x2F;zoxide.mp4&quot; poster=&quot;..&amp;#x2F;zoxide.mp4.jpg&quot; style=&quot;max-height: 450px&quot;&gt;&lt;&#x2F;video&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 54 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;color_blinder by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;dns2utf8?ref_src=twsrc%5Etfw&quot;&gt;@dns2utf8&lt;&#x2F;a&gt; is a tool that renders a set of images simulating 11 kinds of colour blindness. There&amp;#39;s also a GUI version, &lt;br&gt;color_blinder_gtk. &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;dns2utf8&#x2F;color_blinder&quot;&gt;https:&#x2F;&#x2F;gitlab.com&#x2F;dns2utf8&#x2F;color_blinder&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1305861427014627332?ref_src=twsrc%5Etfw&quot;&gt;September 15, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;color_blinder.jpg&quot; loading=&quot;lazy&quot; style=&quot;max-height: 1937px; min-height: 167px&quot; alt=&quot;Screenshot of the set of images generated by color_blinder when applied the Rust home page.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 55 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Bookmark by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;Szymongib?ref_src=twsrc%5Etfw&quot;&gt;@Szymongib&lt;&#x2F;a&gt; is a tool for storing, organising, searching, and opening URL bookmarks. It has a CLI interface as well as an interactive TUI. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Szymongib&#x2F;bookmark&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Szymongib&#x2F;bookmark&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1306223815228350466?ref_src=twsrc%5Etfw&quot;&gt;September 16, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;..&amp;#x2F;bookmark.mp4&quot; poster=&quot;..&amp;#x2F;bookmark.mp4.jpg&quot; style=&quot;max-height: 464px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;Szymongib&amp;#x2F;bookmark&amp;#x2F;blob&amp;#x2F;f46e5361878de972b7f0d11565fbecdb6a66bad9&amp;#x2F;assets&amp;#x2F;bookmark-demo.gif&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;Szymongib&amp;#x2F;bookmark&amp;#x2F;blob&amp;#x2F;f46e5361878de972b7f0d11565fbecdb6a66bad9&amp;#x2F;assets&amp;#x2F;bookmark-demo.gif&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 56 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Artichoke (&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;artichokeruby?ref_src=twsrc%5Etfw&quot;&gt;@artichokeruby&lt;&#x2F;a&gt;) is a Ruby made with Rust that can be compiled to web assembly, and embedded in other applications. It aims to be compatible with MRI Ruby.&lt;a href=&quot;https:&#x2F;&#x2F;www.artichokeruby.org&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.artichokeruby.org&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1306552499210338304?ref_src=twsrc%5Etfw&quot;&gt;September 17, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;artichoke.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 450.5px; min-height: 167px&quot; alt=&quot;Screenshot of Artichoke running a small Ruby program in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;csview by Wenxuan is a high performance, command line CSV viewer with CJK (Chinese, Japanese, Korean) and emoji support. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wfxr&#x2F;csview&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;wfxr&#x2F;csview&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1306910845641908233?ref_src=twsrc%5Etfw&quot;&gt;September 18, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;csview.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 753px; min-height: 167px&quot; alt=&quot;Screenshot of csview rendering a sample CSV file in a terminal using default, reinforced, and rounded styles.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;wfxr&amp;#x2F;i&amp;#x2F;blob&amp;#x2F;e04314806087faf8715a753e70f1a77f10b189d2&amp;#x2F;csview-screenshot.png&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;wfxr&amp;#x2F;i&amp;#x2F;blob&amp;#x2F;e04314806087faf8715a753e70f1a77f10b189d2&amp;#x2F;csview-screenshot.png&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 58 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;pipe-rename by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;MarcusBuffett?ref_src=twsrc%5Etfw&quot;&gt;@marcusbuffett&lt;&#x2F;a&gt; takes a list of files as input and opens &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;search?q=%24EDITOR&amp;amp;src=ctag&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;$EDITOR&lt;&#x2F;a&gt; with that list. You edit the names and then pipe-rename applies the changes accordingly. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;marcusbuffett&#x2F;pipe-rename&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;marcusbuffett&#x2F;pipe-rename&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1307273233759043584?ref_src=twsrc%5Etfw&quot;&gt;September 19, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;..&amp;#x2F;pipe-rename.mp4&quot; poster=&quot;..&amp;#x2F;pipe-rename.mp4.jpg&quot; style=&quot;max-height: 524px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;marcusbuffett&amp;#x2F;pipe-rename&amp;#x2F;blob&amp;#x2F;b734616bab4b4ca4f31de0902479202f33bda545&amp;#x2F;renamer.gif&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;marcusbuffett&amp;#x2F;pipe-rename&amp;#x2F;blob&amp;#x2F;b734616bab4b4ca4f31de0902479202f33bda545&amp;#x2F;renamer.gif&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 59 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Cogsy by cartoon-raccoon is a curses-based command line Discogs client. You can browse and query your collection, view your wantlist, track your listening history, and view your profile — all from the comfort of your terminal. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cartoon-raccoon&#x2F;cogsy&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;cartoon-raccoon&#x2F;cogsy&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1307646659556384768?ref_src=twsrc%5Etfw&quot;&gt;September 20, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;cogsy.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 411px; min-height: 167px&quot; alt=&quot;Screenshot of Cogsy running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;cartoon-raccoon&amp;#x2F;cogsy&amp;#x2F;blob&amp;#x2F;8111b15243398cfe9cec990b88ed19f6155f8b37&amp;#x2F;images&amp;#x2F;screenshots&amp;#x2F;cogsy_main.png&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;cartoon-raccoon&amp;#x2F;cogsy&amp;#x2F;blob&amp;#x2F;8111b15243398cfe9cec990b88ed19f6155f8b37&amp;#x2F;images&amp;#x2F;screenshots&amp;#x2F;cogsy_main.png&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 60 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;git-brws by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;Linda_pp?ref_src=twsrc%5Etfw&quot;&gt;@Linda_pp&lt;&#x2F;a&gt; is a tool to open a repository, file, commit, diff, tag, blame, pull request, issue or project&amp;#39;s website in your web browser from the command line. It supports several repository hosting services. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rhysd&#x2F;git-brws&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rhysd&#x2F;git-brws&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1307997508874854401?ref_src=twsrc%5Etfw&quot;&gt;September 21, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 61 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;tiny by Ömer Sinan Ağacan is a console IRC client with a clean UI, multiple tabs, coloured nicks, and more. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;osa1&#x2F;tiny&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;osa1&#x2F;tiny&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1308355154433814536?ref_src=twsrc%5Etfw&quot;&gt;September 22, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;tiny.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 554px; min-height: 167px&quot; alt=&quot;Screenshot of tiny connected to several IRC channels on chat.freenode.net in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 62 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;ptail by Tom Forbes streams the output from a command and displays a fixed number of lines as the output is generated. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;orf&#x2F;ptail&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;orf&#x2F;ptail&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1308737887463235586?ref_src=twsrc%5Etfw&quot;&gt;September 23, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;..&amp;#x2F;ptail.mp4&quot; poster=&quot;..&amp;#x2F;ptail.mp4.jpg&quot; style=&quot;max-height: 296px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;orf&amp;#x2F;ptail&amp;#x2F;blob&amp;#x2F;b26b089816cf3f495dae26ae0316c91f724667ce&amp;#x2F;images&amp;#x2F;readme.gif&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;orf&amp;#x2F;ptail&amp;#x2F;blob&amp;#x2F;b26b089816cf3f495dae26ae0316c91f724667ce&amp;#x2F;images&amp;#x2F;readme.gif&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 63 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;procs by dalance is an enhanced version of ps. It has coloured output, filtering, paging, watch mode, and can show extra info like container names, and network ports. You can also extensively configure it via a config file.  &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dalance&#x2F;procs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;dalance&#x2F;procs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1309100270475972608?ref_src=twsrc%5Etfw&quot;&gt;September 24, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;procs.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 554px; min-height: 167px&quot; alt=&quot;Screenshot of procs running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 64 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;rsmixer by jantap is a PulseAudio volume mixer. It allows you to adjust the volume, change settings, and displays VU meters for outputs and application generating audio. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jantap&#x2F;rsmixer&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;jantap&#x2F;rsmixer&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1309462656005275648?ref_src=twsrc%5Etfw&quot;&gt;September 25, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;..&amp;#x2F;rsmixer.mp4&quot; poster=&quot;..&amp;#x2F;rsmixer.mp4.jpg&quot; style=&quot;max-height: 450px&quot;&gt;&lt;&#x2F;video&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 65 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;vopono by James McMurray is a tool that uses Linux network namespaces to enable you to run specific applications through a VPN. Different VPN connections can be active simultaneously, whilst keeping your main connection as normal. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jamesmcm&#x2F;vopono&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;jamesmcm&#x2F;vopono&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1309825048551669762?ref_src=twsrc%5Etfw&quot;&gt;September 26, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;vopono.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 708.5px; min-height: 167px&quot; alt=&quot;Screenshot of vopono running in a terminal and two different browsers, one showing the VPN applied, the other not.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;jamesmcm&amp;#x2F;vopono&amp;#x2F;blob&amp;#x2F;ef9653b80aea5f1695f9ca02b06e2ff340f1fae0&amp;#x2F;screenshot.png&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;jamesmcm&amp;#x2F;vopono&amp;#x2F;blob&amp;#x2F;ef9653b80aea5f1695f9ca02b06e2ff340f1fae0&amp;#x2F;screenshot.png&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 66 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;tickrs by Cory Forsstrom is a realtime ticker for stock prices in your terminal. It supports POSIX platforms and Windows, and sources data from Yahoo! Finance. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tarkah&#x2F;tickrs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;tarkah&#x2F;tickrs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1310187431681949696?ref_src=twsrc%5Etfw&quot;&gt;September 27, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;..&amp;#x2F;tickrs.mp4&quot; poster=&quot;..&amp;#x2F;tickrs.mp4.jpg&quot; style=&quot;max-height: 590px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;tarkah&amp;#x2F;tickrs&amp;#x2F;blob&amp;#x2F;a5bc18a470999b5c18c98a7188a477c8e305652b&amp;#x2F;assets&amp;#x2F;demo.gif&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;tarkah&amp;#x2F;tickrs&amp;#x2F;blob&amp;#x2F;a5bc18a470999b5c18c98a7188a477c8e305652b&amp;#x2F;assets&amp;#x2F;demo.gif&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 67 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;git-workspace by Tom Forbes is a tool for synchronising your git projects. It can automatically set upstreams for forks, move deleted repositories to an archive directory, and execute git fetch on all projects in parallel. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;orf&#x2F;git-workspace&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;orf&#x2F;git-workspace&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1310549822408200193?ref_src=twsrc%5Etfw&quot;&gt;September 28, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;..&amp;#x2F;git-workspace.mp4&quot; poster=&quot;..&amp;#x2F;git-workspace.mp4.jpg&quot; style=&quot;max-height: 364px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;orf&amp;#x2F;git-workspace&amp;#x2F;blob&amp;#x2F;8403c57edd172e925b682ee6220653db37dd616c&amp;#x2F;images&amp;#x2F;readme-example.gif&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;orf&amp;#x2F;git-workspace&amp;#x2F;blob&amp;#x2F;8403c57edd172e925b682ee6220653db37dd616c&amp;#x2F;images&amp;#x2F;readme-example.gif&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 68 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;code-minimap by Wenxuan is a blazing fast tool for generating a high level overview of a text file, like that in Sublime Text. Wenxuan has also written a plugin that uses code-minimap to add a minimap to Vim&#x2F;Neovim, pictured here. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wfxr&#x2F;code-minimap&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;wfxr&#x2F;code-minimap&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1310912211557085184?ref_src=twsrc%5Etfw&quot;&gt;September 29, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;..&amp;#x2F;code-minimap.mp4&quot; poster=&quot;..&amp;#x2F;code-minimap.mp4.jpg&quot; style=&quot;max-height: 414px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;wfxr&amp;#x2F;i&amp;#x2F;blob&amp;#x2F;e04314806087faf8715a753e70f1a77f10b189d2&amp;#x2F;minimap-vim.gif&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;wfxr&amp;#x2F;i&amp;#x2F;blob&amp;#x2F;e04314806087faf8715a753e70f1a77f10b189d2&amp;#x2F;minimap-vim.gif&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 69 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;kx by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;onatm?ref_src=twsrc%5Etfw&quot;&gt;@onatm&lt;&#x2F;a&gt; is a tool for interactively switching between Kubernetes contexts. It uses the skim fuzzy finder to list contexts, allowing to you to filter and select the desired option. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;onatm&#x2F;kx&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;onatm&#x2F;kx&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1311274602337722373?ref_src=twsrc%5Etfw&quot;&gt;September 30, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 70 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;kmon by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;orhunp_?ref_src=twsrc%5Etfw&quot;&gt;@orhunp_&lt;&#x2F;a&gt; is a Linux kernel manager and activity monitor. It provides information about the current kernel, loaded modules, and recent kernel messages. It can also load&#x2F;unload&#x2F;reload&#x2F;blacklist modules, and more. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;orhun&#x2F;kmon&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;orhun&#x2F;kmon&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1311636986730512387?ref_src=twsrc%5Etfw&quot;&gt;October 1, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;kmon.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 450px; min-height: 167px&quot; alt=&quot;Screenshot of kmon running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 71 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;so by Sam Tay is a TUI for StackExchange sites, such as StackOverflow. Search for questions, view answers, all from the comfort of your terminal. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;samtay&#x2F;so&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;samtay&#x2F;so&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1311999372113186819?ref_src=twsrc%5Etfw&quot;&gt;October 2, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;..&amp;#x2F;so.mp4&quot; poster=&quot;..&amp;#x2F;so.mp4.jpg&quot; style=&quot;max-height: 424px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;samtay&amp;#x2F;so&amp;#x2F;blob&amp;#x2F;93c13cdbf3fecaf23f21237ecee42d62f62905e0&amp;#x2F;assets&amp;#x2F;demo.gif&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;samtay&amp;#x2F;so&amp;#x2F;blob&amp;#x2F;93c13cdbf3fecaf23f21237ecee42d62f62905e0&amp;#x2F;assets&amp;#x2F;demo.gif&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 72 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;lipl by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;rguignar?ref_src=twsrc%5Etfw&quot;&gt;@rguignar&lt;&#x2F;a&gt; is tool that plots the value produced by a command pipeline over time. Here it is plotting the CPU temperature of my computer, specifically the Tctl value from lm-sensors. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;yxdunc&#x2F;lipl&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;yxdunc&#x2F;lipl&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1312361757780987904?ref_src=twsrc%5Etfw&quot;&gt;October 3, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;lipl.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 450px; min-height: 167px&quot; alt=&quot;Screenshot of lipl plotting the CPU temperature of my computer in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 73 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Cicero by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;eyeplum?ref_src=twsrc%5Etfw&quot;&gt;@eyeplum&lt;&#x2F;a&gt; is a tool for viewing Unicode and glyph information. Given a piece of text it lists the graphemes and information about each code point. When using the TUI it can also render glyphs using fonts on your system. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;eyeplum&#x2F;cicero-tui&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;eyeplum&#x2F;cicero-tui&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1312709046621798402?ref_src=twsrc%5Etfw&quot;&gt;October 4, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;cicero.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 540px; min-height: 167px&quot; alt=&quot;Screenshot of Cicero running in a terminal, displaying the graphemes of the text &amp;#x27;Rust Café 🦀&amp;#x27; and rendering the R glyph in PragmataPro.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 74 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;battop by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;svartalf?ref_src=twsrc%5Etfw&quot;&gt;@svartalf&lt;&#x2F;a&gt; is a cross-platform (Linux, macOS, FreeBSD, and DragonflyBSD) interactive viewer, similar to top, htop and other *top utilities for the battery in your laptop. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;svartalf&#x2F;rust-battop&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;svartalf&#x2F;rust-battop&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1313071434009391107?ref_src=twsrc%5Etfw&quot;&gt;October 5, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;battop.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 500px; min-height: 167px&quot; alt=&quot;Screenshot of battop running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 75 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;XXV by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;chvest?ref_src=twsrc%5Etfw&quot;&gt;@chvest&lt;&#x2F;a&gt; is an interactive hex viewer for the terminal. It allows you to navigate file content and visit specific offsets. It remembers recent files and you can switch between a light and dark theme. &lt;a href=&quot;https:&#x2F;&#x2F;chrisvest.github.io&#x2F;xxv&#x2F;&quot;&gt;https:&#x2F;&#x2F;chrisvest.github.io&#x2F;xxv&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1313433821946273792?ref_src=twsrc%5Etfw&quot;&gt;October 6, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;xxv.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 564px; min-height: 167px&quot; alt=&quot;Screenshot of xxv running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;chrisvest.github.io&amp;#x2F;xxv&amp;#x2F;screenshot.png&quot;&gt;https:&amp;#x2F;&amp;#x2F;chrisvest.github.io&amp;#x2F;xxv&amp;#x2F;screenshot.png&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 76 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;indexa by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;mosmeh?ref_src=twsrc%5Etfw&quot;&gt;@mosmeh&lt;&#x2F;a&gt; is a locate alternative with incremental filtering. After indexa indexes your disk (startlingly quickly), you can use it to interactively find files. Behaviour and appearance can be fine tuned via config file. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mosmeh&#x2F;indexa&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;mosmeh&#x2F;indexa&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1313796212223475712?ref_src=twsrc%5Etfw&quot;&gt;October 7, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;indexa.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 450px; min-height: 167px&quot; alt=&quot;Screenshot of indexa running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 77 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;shy by Chris West is an ssh launcher. It parses your ssh config file and lists hosts to connect to — great for long, generated host names. If your config contains a lot of entries it also allows interactively filtering the list. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;xvxx&#x2F;shy&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;xvxx&#x2F;shy&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1314158604128149505?ref_src=twsrc%5Etfw&quot;&gt;October 8, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;shy.jpg&quot; loading=&quot;lazy&quot; style=&quot;max-height: 277px; min-height: 167px&quot; alt=&quot;Screenshot of shy running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;xvxx&amp;#x2F;shy&amp;#x2F;blob&amp;#x2F;21555eb5259fd498d1d8fb4a4c39cf90a502f443&amp;#x2F;img&amp;#x2F;screen1.jpeg&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;xvxx&amp;#x2F;shy&amp;#x2F;blob&amp;#x2F;21555eb5259fd498d1d8fb4a4c39cf90a502f443&amp;#x2F;img&amp;#x2F;screen1.jpeg&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 78 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;frawk by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;ezrosent?ref_src=twsrc%5Etfw&quot;&gt;@ezrosent&lt;&#x2F;a&gt; is an awk-like language with built in support for CSV and TSV. It features a bytecode interpreter as well as an LLVM-based JIT compiler, and has support for parallelism. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ezrosent&#x2F;frawk&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;ezrosent&#x2F;frawk&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1314520987556175872?ref_src=twsrc%5Etfw&quot;&gt;October 9, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;frawk.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 274px; min-height: 167px&quot; alt=&quot;Screenshot of frawk running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;serial-monitor by Dave Hylands is a command line program which will connect to, and allow you to interact with devices which are connected to your host computer via USB serial adapters. Here it is pictured connected to my LED cube. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dhylands&#x2F;serial-monitor&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;dhylands&#x2F;serial-monitor&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1314883373278654466?ref_src=twsrc%5Etfw&quot;&gt;October 10, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;serial-monitor.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 253px; min-height: 167px&quot; alt=&quot;Screenshot of serial-monitor running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 80 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;gfold by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;nickgeracehacks?ref_src=twsrc%5Etfw&quot;&gt;@nickgeracehacks&lt;&#x2F;a&gt; provides a quick overview of all git repositories in the current, or specified directory. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nickgerace&#x2F;gfold&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;nickgerace&#x2F;gfold&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1315245763358715906?ref_src=twsrc%5Etfw&quot;&gt;October 11, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;gfold.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 450px; min-height: 167px&quot; alt=&quot;Screenshot of gfold running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 81 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;fselect by jhspetersson is a tool for finding files using SQL like queries. As well as file name and metadata like size, fselect can also query inside archives, and metadata like EXIF and ID3. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jhspetersson&#x2F;fselect&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;jhspetersson&#x2F;fselect&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1315608150398050305?ref_src=twsrc%5Etfw&quot;&gt;October 12, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;fselect.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 446px; min-height: 167px&quot; alt=&quot;Screenshot of fselect running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 82 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;lfs by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;DenysSeguret?ref_src=twsrc%5Etfw&quot;&gt;@DenysSeguret&lt;&#x2F;a&gt; is small utility for Linux that lists information about your mounted file systems in a nice table. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Canop&#x2F;lfs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Canop&#x2F;lfs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1315970537907085312?ref_src=twsrc%5Etfw&quot;&gt;October 13, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;lfs.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 240.5px; min-height: 167px&quot; alt=&quot;Screenshot of lfs running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 83 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;dotenv-linter is tool for checking .env files for issues that might cause an application to malfunction. Not only can it identify issues but it can also fix them! &lt;a href=&quot;https:&#x2F;&#x2F;dotenv-linter.github.io&#x2F;#&#x2F;?id=dotenv-linter&quot;&gt;https:&#x2F;&#x2F;dotenv-linter.github.io&#x2F;#&#x2F;?id=dotenv-linter&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1316332930998956032?ref_src=twsrc%5Etfw&quot;&gt;October 14, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;dotenv-linter.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 260px; min-height: 167px&quot; alt=&quot;Screenshot of dotenv-linter running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 84 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;Volta by LinkedIn is a tool for installing and managing JavaScript tools and Node.js toolchains. It allow versions to be pinned to ensure collaborators are using the same tool version with the same Node version. &lt;a href=&quot;https:&#x2F;&#x2F;volta.sh&#x2F;&quot;&gt;https:&#x2F;&#x2F;volta.sh&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1316695319493644289?ref_src=twsrc%5Etfw&quot;&gt;October 15, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 85 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;bottom by Clement Tsang is alternative take on top. It&amp;#39;s a graphical process&#x2F;system monitor with a customisable interface and lots of features. bottom runs on at least Linux, macOS, and Windows. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ClementTsang&#x2F;bottom&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;ClementTsang&#x2F;bottom&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1317420089914871813?ref_src=twsrc%5Etfw&quot;&gt;October 17, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;bottom.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 756px; min-height: 167px&quot; alt=&quot;Screenshot of bottom running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 86 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;huniq by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;dakoraa?ref_src=twsrc%5Etfw&quot;&gt;@dakoraa&lt;&#x2F;a&gt; filters out duplicates on the command line. It&amp;#39;s an alternative to `sort | uniq` or `sort -u`. It trades off memory use for a 5–10× performance boost over `sort | uniq` &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;koraa&#x2F;huniq&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;koraa&#x2F;huniq&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1317782477860229120?ref_src=twsrc%5Etfw&quot;&gt;October 18, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;huniq.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 363px; min-height: 167px&quot; alt=&quot;Screenshot of the output of huniq -h in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 87 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;cargo-wipe by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;faithraven?ref_src=twsrc%5Etfw&quot;&gt;@faithraven&lt;&#x2F;a&gt; is a cargo subcommand that recursively finds and optionally deletes all &amp;quot;target&amp;quot; or &amp;quot;node_modules&amp;quot; folders found in the current path. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mihai-dinculescu&#x2F;cargo-wipe&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;mihai-dinculescu&#x2F;cargo-wipe&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1318153750918221825?ref_src=twsrc%5Etfw&quot;&gt;October 19, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;cargo-wipe.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 450px; min-height: 167px&quot; alt=&quot;Screenshot of cargo-wipe being run on my Projects directory in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 88 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;terminal-typeracer by Darrien Glasser is a typing practice app for the terminal. It presents a passage of text to type and tracks your speed and accuracy as you type it.&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;ttyperacer&#x2F;terminal-typeracer&quot;&gt;https:&#x2F;&#x2F;gitlab.com&#x2F;ttyperacer&#x2F;terminal-typeracer&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1318507257072635908?ref_src=twsrc%5Etfw&quot;&gt;October 20, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;terminal-typeracer.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 450px; min-height: 167px&quot; alt=&quot;Screenshot of terminal-typeracer running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 89 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;Audiobench by Joshua Maros is a modular synthesiser. You can create many different sounds by connecting together modules in any arrangement you can think of. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;joshua-maros&#x2F;audiobench&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;joshua-maros&#x2F;audiobench&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1318869641859600384?ref_src=twsrc%5Etfw&quot;&gt;October 21, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;audiobench.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 508px; min-height: 167px&quot; alt=&quot;Screenshot of Audiobench.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;joshua-maros.github.io&amp;#x2F;audiobench&amp;#x2F;book&amp;#x2F;images&amp;#x2F;default_patch.png&quot;&gt;https:&amp;#x2F;&amp;#x2F;joshua-maros.github.io&amp;#x2F;audiobench&amp;#x2F;book&amp;#x2F;images&amp;#x2F;default_patch.png&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 90 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;t-rec by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;5422m4n?ref_src=twsrc%5Etfw&quot;&gt;@5422m4n&lt;&#x2F;a&gt; is a terminal screen recorder for macOS. It uses native APIs and intelligent idle frame detection to efficiently generate animated GIFs. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sassman&#x2F;t-rec-rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;sassman&#x2F;t-rec-rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1319232031524487169?ref_src=twsrc%5Etfw&quot;&gt;October 22, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;t-rec.gif&quot; loading=&quot;lazy&quot; style=&quot;max-height: 432px; min-height: 167px&quot; alt=&quot;Animated GIF of rust-sloth rendering a 3D model of Pikachu in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 90 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;fhc by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;edu4rdshl?ref_src=twsrc%5Etfw&quot;&gt;@edu4rdshl&lt;&#x2F;a&gt; is the Fast HTTP Checker. It reads a list of  host names from stdin and checks to see which ones respond to a HTTPS or HTTP request. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Edu4rdSHL&#x2F;fhc&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Edu4rdSHL&#x2F;fhc&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1319594423592779777?ref_src=twsrc%5Etfw&quot;&gt;October 23, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;fhc.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 282px; min-height: 167px&quot; alt=&quot;Screenshot of fhc running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 91 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;desed is a debugger for sed scripts. Step forward and backward through the script observing input, output, hold space, and regex matches. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;SoptikHa2&#x2F;desed&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;SoptikHa2&#x2F;desed&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1319956805615865857?ref_src=twsrc%5Etfw&quot;&gt;October 24, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;desed.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 450px; min-height: 167px&quot; alt=&quot;Screenshot of desed running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 93 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; (I did two day 90s so I&amp;#39;m skipping 92)&lt;br&gt;&lt;br&gt;silver by Christopher Knight is a cross-shell powerline-like prompt generator with icons and colours. It supports Bash, Elvish, Fish, Ion, PowerShell, and Zsh. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;reujab&#x2F;silver&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;reujab&#x2F;silver&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1320319194940882945?ref_src=twsrc%5Etfw&quot;&gt;October 25, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;silver.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 255px; min-height: 167px&quot; alt=&quot;Screenshot of silver running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 94 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;fnm (fast node manager) by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;galstar?ref_src=twsrc%5Etfw&quot;&gt;@galstar&lt;&#x2F;a&gt; is, as the name suggests, a tool for installing and switching between multiple Node.js versions. The most recent version is the first release implemented in Rust, which adds Windows support. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Schniz&#x2F;fnm&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Schniz&#x2F;fnm&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1320681581716099074?ref_src=twsrc%5Etfw&quot;&gt;October 26, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;fnm.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 216px; min-height: 167px&quot; alt=&quot;Screenshot of fnm running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 95 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;waitfor by Adam Shirey is a tool that blocks until a condition is met. The condition can be based on time, file existence, or HTTP response status. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aeshirey&#x2F;waitfor&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;aeshirey&#x2F;waitfor&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1321043967891312640?ref_src=twsrc%5Etfw&quot;&gt;October 27, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;waitfor.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 667px; min-height: 167px&quot; alt=&quot;Screenshot of the waitfor documentation showing the various condition flags it accepts.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 96 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;rusty-tags by Daniel Trstenjak is a tool that creates tags (for source code navigation) using ctags for a cargo project. It indexes all direct and indirect dependencies, and the Rust standard library. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dan-t&#x2F;rusty-tags&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;dan-t&#x2F;rusty-tags&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1321406361989689345?ref_src=twsrc%5Etfw&quot;&gt;October 28, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;rusty-tags.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 239px; min-height: 167px&quot; alt=&quot;Screenshot of rusty-tags running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 97 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;SongRec by Marin Moulinier is a Shazam client for Linux. It listens to the microphone or an audio file to determine the song playing. It can be used through its GUI or on the command line. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;marin-m&#x2F;SongRec&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;marin-m&#x2F;SongRec&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1321768747015786496?ref_src=twsrc%5Etfw&quot;&gt;October 29, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;songrec.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 774px; min-height: 167px&quot; alt=&quot;Screenshot of the SongRec GUI after recognising a few songs. There is album art on the left and a history of recognised songs on the right.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 98 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;ddh by Jon Moroney is a fast duplicate file finder. It uses hashing to determine duplicates. Results are written to a file in either a human readable format or machine readable JSON. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;darakian&#x2F;ddh&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;darakian&#x2F;ddh&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1322131138668802048?ref_src=twsrc%5Etfw&quot;&gt;October 30, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;ddh.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 355px; min-height: 167px&quot; alt=&quot;Screenshot of ddh running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 99 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;pueue by Arne Beer is a command-line task management tool for sequential and parallel execution of long-running tasks. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Nukesor&#x2F;pueue&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Nukesor&#x2F;pueue&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1322493522432282625?ref_src=twsrc%5Etfw&quot;&gt;October 31, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;..&amp;#x2F;pueue.mp4&quot; poster=&quot;..&amp;#x2F;pueue.mp4.jpg&quot; style=&quot;max-height: 612px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;Nukesor&amp;#x2F;images&amp;#x2F;blob&amp;#x2F;72c983b374ea32b64e5997477693030001bdd7a6&amp;#x2F;pueue.gif&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;Nukesor&amp;#x2F;images&amp;#x2F;blob&amp;#x2F;72c983b374ea32b64e5997477693030001bdd7a6&amp;#x2F;pueue.gif&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 100 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Today I&amp;#39;m featuring the Rust compiler — the binary that made the previous 99 fast, efficient, user-friendly, easy-to-build, and reliable binaries possible.&lt;br&gt;&lt;br&gt;Thanks to all the people that have worked on it past, present, and future. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1322855912076386304?ref_src=twsrc%5Etfw&quot;&gt;November 1, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;..&amp;#x2F;rustc.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 450px; min-height: 167px&quot; alt=&quot;The Rust logo.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;upload.wikimedia.org&amp;#x2F;wikipedia&amp;#x2F;commons&amp;#x2F;thumb&amp;#x2F;d&amp;#x2F;d5&amp;#x2F;Rust_programming_language_black_logo.svg&amp;#x2F;1200px-Rust_programming_language_black_logo.svg.png&quot;&gt;https:&amp;#x2F;&amp;#x2F;upload.wikimedia.org&amp;#x2F;wikipedia&amp;#x2F;commons&amp;#x2F;thumb&amp;#x2F;d&amp;#x2F;d5&amp;#x2F;Rust_programming_language_black_logo.svg&amp;#x2F;1200px-Rust_programming_language_black_logo.svg.png&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;100-rust-binaries&#x2F;&quot;&gt;« Back to page 1&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>One Hundred Rust Binaries</title>
      <pubDate>Mon, 02 Nov 2020 13:00:00 +1100</pubDate>
      <atom:published>2020-11-02T13:00:00+11:00</atom:published>
      <atom:updated>2020-11-05T08:27:13+11:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;100-rust-binaries&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;100-rust-binaries&#x2F;</guid>
      <description>&lt;p&gt;I recently completed a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;search?q=%23100binaries%20from%3A%40wezm&amp;amp;src=typed_query&amp;amp;f=live&quot;&gt;#100binaries&lt;&#x2F;a&gt; series on Twitter wherein I shared one
open-source &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; tool or application each day, for one hundred days (Jul—Nov
2020). This post lists binaries 1–50.
&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;100-rust-binaries&#x2F;page2&#x2F;&quot;&gt;See page 2 for binaries 51–100&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;All images and videos without an explicit source were created by me for the
series. Most picture the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;alacritty&#x2F;alacritty&quot;&gt;Alacritty&lt;&#x2F;a&gt; terminal emulator running on Linux. I use
the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fsd.it&#x2F;shop&#x2F;fonts&#x2F;pragmatapro&#x2F;&quot;&gt;PragmataPro font&lt;&#x2F;a&gt; and my prompt is generated by
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;edkolev&#x2F;promptline.vim&quot;&gt;vim-promptline&lt;&#x2F;a&gt;. The colour scheme is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chriskempson&#x2F;base16-shell&quot;&gt;Base16 Default Dark&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I also wrote a follow-up post about how this page was built and the
considerations that went into making it as lightweight as possible:
&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;turning-one-hundred-tweets-into-a-blog-post&#x2F;&quot;&gt;Turning One Hundred Tweets Into a Blog Post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;





  


&lt;ol class=&quot;tweet-list&quot;&gt;

  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Kicking off a little series today: &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;! I&amp;#39;m going to share one open source &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;rustlang?ref_src=twsrc%5Etfw&quot;&gt;@rustlang&lt;&#x2F;a&gt; tool or application a day, for the next 100 days!&lt;br&gt;&lt;br&gt;Starting off with hexyl by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;sharkdp86?ref_src=twsrc%5Etfw&quot;&gt;@sharkdp86&lt;&#x2F;a&gt;: a  hex dump tool that uses colour to distinguish categories of bytes. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;hexyl&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;hexyl&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1286870604402978816?ref_src=twsrc%5Etfw&quot;&gt;July 25, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;hexyl.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 337.5px; min-height: 167px&quot; alt=&quot;Screenshot of hexyl running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 2 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;exa by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;cairnrefinery?ref_src=twsrc%5Etfw&quot;&gt;@cairnrefinery&lt;&#x2F;a&gt;: an improved file lister (like ls) with more features and better defaults. It uses colours to distinguish file types and metadata. It knows about symlinks, extended attributes, and git. &lt;a href=&quot;https:&#x2F;&#x2F;the.exa.website&#x2F;&quot;&gt;https:&#x2F;&#x2F;the.exa.website&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1287024806769168384?ref_src=twsrc%5Etfw&quot;&gt;July 25, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;exa.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 338.5px; min-height: 167px&quot; alt=&quot;Screenshot of exa running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 3 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;This one has actually been pictured in the previous two posts: Alacritty by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;i_am_jwilm?ref_src=twsrc%5Etfw&quot;&gt;@i_am_jwilm&lt;&#x2F;a&gt;. Alacritty is a fast GPU accelerated terminal emulator that supports BSD, Linux, macOS, and Windows. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;alacritty&#x2F;alacritty&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;alacritty&#x2F;alacritty&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1287379644661010433?ref_src=twsrc%5Etfw&quot;&gt;July 26, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;alacritty.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 338.5px; min-height: 167px&quot; alt=&quot;Screenshot of Alacritty disiplaying the Alacritty logo.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 4 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Amp by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wastedintel?ref_src=twsrc%5Etfw&quot;&gt;@wastedintel&lt;&#x2F;a&gt; is a modal text editor inspired by vi&#x2F;vim. It comes with usable defaults and includes syntax highlighting, a fuzzy file finder, local symbol jump, and basic git integration out of the box. &lt;a href=&quot;https:&#x2F;&#x2F;amp.rs&#x2F;&quot;&gt;https:&#x2F;&#x2F;amp.rs&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1287742032975257600?ref_src=twsrc%5Etfw&quot;&gt;July 27, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;amp.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 338.5px; min-height: 167px&quot; alt=&quot;Screenshot of Amp editing Rust source code in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 5 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Tokei by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;XamppRocky?ref_src=twsrc%5Etfw&quot;&gt;@XamppRocky&lt;&#x2F;a&gt; quickly calculates and presents statistics about source code such as line and comment count, grouped by language. It also handles files containing multiple languages such as Markdown containing code snippets. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;XAMPPRocky&#x2F;tokei&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;XAMPPRocky&#x2F;tokei&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1288104420052553728?ref_src=twsrc%5Etfw&quot;&gt;July 28, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;tokei.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 338.5px; min-height: 167px&quot; alt=&quot;Screenshot of the output of running Tokei on the Allsorts repository.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 6 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Silicon by Aloxaf: Create a beautiful image of your source code. Handy for sharing code on Twitter!  &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Aloxaf&#x2F;silicon&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Aloxaf&#x2F;silicon&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1288466808991817730?ref_src=twsrc%5Etfw&quot;&gt;July 29, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;silicon.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 323px; min-height: 167px&quot; alt=&quot;Output generated by Silicon for a small Rust program.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 7 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;wool by grapegrip: Live preview Markdown documents as you edit, using GitHub styling. Great for checking READMEs and similar files before pushing to GitHub. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;grapegrip&#x2F;wool&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;grapegrip&#x2F;wool&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1288829195754172422?ref_src=twsrc%5Etfw&quot;&gt;July 30, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 8 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;broot by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;DenysSeguret?ref_src=twsrc%5Etfw&quot;&gt;@DenysSeguret&lt;&#x2F;a&gt; is a swiss army knife of file hierarchies — even huge ones! Interactively navigate, search, sort, and preview file trees with ease to find just the right file or directory. Then open or cd into it. &lt;a href=&quot;https:&#x2F;&#x2F;dystroy.org&#x2F;broot&#x2F;&quot;&gt;https:&#x2F;&#x2F;dystroy.org&#x2F;broot&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1289191583573737475?ref_src=twsrc%5Etfw&quot;&gt;July 31, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;broot.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 338.5px; min-height: 167px&quot; alt=&quot;Screenshot of broot running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 9 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;skim by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;jinzhouz?ref_src=twsrc%5Etfw&quot;&gt;@jinzhouz&lt;&#x2F;a&gt; is a fast and versatile fuzzy finder. It can be plugged into your shell and vim, as well as used in shell pipelines. It can also invoke commands dynamically based on the selection, and show file previews. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lotabout&#x2F;skim&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;lotabout&#x2F;skim&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1289553972655620096?ref_src=twsrc%5Etfw&quot;&gt;August 1, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;skim.mp4&quot; poster=&quot;skim.mp4.jpg&quot; style=&quot;max-height: 338px&quot;&gt;&lt;&#x2F;video&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 10 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Nu (&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;nu_shell?ref_src=twsrc%5Etfw&quot;&gt;@nu_shell&lt;&#x2F;a&gt;) is a command line shell for Linux, macOS, and Windows that operates on structured data in the form of tables instead of unstructured text streams. &lt;a href=&quot;https:&#x2F;&#x2F;www.nushell.sh&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.nushell.sh&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1289916361339092994?ref_src=twsrc%5Etfw&quot;&gt;August 2, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;nu.mp4&quot; poster=&quot;nu.mp4.jpg&quot; style=&quot;max-height: 338px&quot;&gt;&lt;&#x2F;video&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 11 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;viu by Atanas Yankov is a tool for viewing bitmap images in the terminal. Handy for quickly checking the contents of an image file or picking the perfect meme. It even supports animated GIFs. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;atanunq&#x2F;viu&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;atanunq&#x2F;viu&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1290278748223201280?ref_src=twsrc%5Etfw&quot;&gt;August 3, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;viu.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 338.5px; min-height: 167px&quot; alt=&quot;Screenshot of viu rendering Ferris the Rustacean in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 12 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;gitui by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;Extrawurst?ref_src=twsrc%5Etfw&quot;&gt;@Extrawurst&lt;&#x2F;a&gt; is a blazing fast terminal UI for git. Inspect, stage, unstage, and commit changes. Save, apply, drop, and inspect stashes. Browse the commit log, diff committed changes, and more. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;extrawurst&#x2F;gitui&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;extrawurst&#x2F;gitui&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1290641136638480384?ref_src=twsrc%5Etfw&quot;&gt;August 4, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;gitui.mp4&quot; poster=&quot;gitui.mp4.jpg&quot; style=&quot;max-height: 675px&quot;&gt;&lt;&#x2F;video&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 13 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;fd by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;sharkdp86?ref_src=twsrc%5Etfw&quot;&gt;@sharkdp86&lt;&#x2F;a&gt; is an alternative to `find` with sensible defaults. It rapidly lists files and directories with names matching a regex, whilst respecting .gitignore files. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;fd&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;fd&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1291003525712019459?ref_src=twsrc%5Etfw&quot;&gt;August 5, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;fd.mp4&quot; poster=&quot;fd.mp4.jpg&quot; style=&quot;max-height: 337px&quot;&gt;&lt;&#x2F;video&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;ro&quot; dir=&quot;ltr&quot;&gt;Day 14 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Emulsion by Artúr Kovács is a fast and minimal image viewer for Linux, macOS, and Windows. &lt;a href=&quot;https:&#x2F;&#x2F;arturkovacs.github.io&#x2F;emulsion-website&#x2F;&quot;&gt;https:&#x2F;&#x2F;arturkovacs.github.io&#x2F;emulsion-website&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1291365913548079109?ref_src=twsrc%5Etfw&quot;&gt;August 6, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;emulsion.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 528.5px; min-height: 167px&quot; alt=&quot;Screenshot of Emulsion displaying an image of Ferris the Rustacean.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 15 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;rusty-man by Robin Krahl is a command-line viewer for rustdoc documentation that presents the content in a style akin to man pages. &lt;a href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~ireas&#x2F;rusty-man&quot;&gt;https:&#x2F;&#x2F;git.sr.ht&#x2F;~ireas&#x2F;rusty-man&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1291728300960669696?ref_src=twsrc%5Etfw&quot;&gt;August 7, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;rusty-man.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 337.5px; min-height: 167px&quot; alt=&quot;Screenshot of rusty-man rendering the Allsorts docs in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 16 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;diskonaut by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;im_snif?ref_src=twsrc%5Etfw&quot;&gt;@im_snif&lt;&#x2F;a&gt; is a tool for visualising disk space usage. You can navigate the tree and choose what to delete. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;imsnif&#x2F;diskonaut&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;imsnif&#x2F;diskonaut&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1292090686917967873?ref_src=twsrc%5Etfw&quot;&gt;August 8, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;diskonaut.mp4&quot; poster=&quot;diskonaut.mp4.jpg&quot; style=&quot;max-height: 812px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;imsnif&amp;#x2F;diskonaut&amp;#x2F;blob&amp;#x2F;2cf5c7bd061f42443288e538ae75fedf7a846d76&amp;#x2F;demo.gif&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;imsnif&amp;#x2F;diskonaut&amp;#x2F;blob&amp;#x2F;2cf5c7bd061f42443288e538ae75fedf7a846d76&amp;#x2F;demo.gif&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 17 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Spotify TUI by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;AlexKeliris?ref_src=twsrc%5Etfw&quot;&gt;@AlexKeliris&lt;&#x2F;a&gt; is a terminal user interface for Spotify. Play music and podcasts from your library and playlists, and visualise them with the built in spectrum analyser. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Rigellute&#x2F;spotify-tui&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Rigellute&#x2F;spotify-tui&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1292453074410192897?ref_src=twsrc%5Etfw&quot;&gt;August 9, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;spotify-tui.mp4&quot; poster=&quot;spotify-tui.mp4.jpg&quot; style=&quot;max-height: 510px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;user-images.githubusercontent.com&amp;#x2F;12150276&amp;#x2F;75177190-91d4ab00-572d-11ea-80bd-c5e28c7b17ad.gif&quot;&gt;https:&amp;#x2F;&amp;#x2F;user-images.githubusercontent.com&amp;#x2F;12150276&amp;#x2F;75177190-91d4ab00-572d-11ea-80bd-c5e28c7b17ad.gif&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 18 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;dijo by Akshay is a scriptable, curses-based, digital habit tracker. Use it to track activities you want to do each day, or a specific number of times each day. View an overview of your progress by week or month. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NerdyPepper&#x2F;dijo&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;NerdyPepper&#x2F;dijo&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1292815462242357249?ref_src=twsrc%5Etfw&quot;&gt;August 10, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;dijo.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 337.5px; min-height: 167px&quot; alt=&quot;Screenshot of dijo running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 19 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;pastel by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;sharkdp86?ref_src=twsrc%5Etfw&quot;&gt;@sharkdp86&lt;&#x2F;a&gt; is a command-line tool to generate, analyse, convert and manipulate colours. It has a selection of 22 sub-commands for all manner of colour operations. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;pastel&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;pastel&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1293177850233667595?ref_src=twsrc%5Etfw&quot;&gt;August 11, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;pastel.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 339.5px; min-height: 167px&quot; alt=&quot;Screenshot of pastel running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 20 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;DWFV by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;Ptishell?ref_src=twsrc%5Etfw&quot;&gt;@Ptishell&lt;&#x2F;a&gt; is a command line digital waveform viewer for standard Value Change Dump (VCD) files, with vi-like key bindings. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;psurply&#x2F;dwfv&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;psurply&#x2F;dwfv&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1293540239802281985?ref_src=twsrc%5Etfw&quot;&gt;August 12, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;dwfv.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 337.5px; min-height: 167px&quot; alt=&quot;Screenshot of DWFV running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 21 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Zenith by Benjamin Vaisvil is a system monitoring tool like top. It monitors CPU, memory, disk, and network activity and has a process list for viewing and manipulating processes. You can also navigate back through old data. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bvaisvil&#x2F;zenith&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;bvaisvil&#x2F;zenith&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1293902626904354822?ref_src=twsrc%5Etfw&quot;&gt;August 13, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;zenith.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 600px; min-height: 167px&quot; alt=&quot;Screenshot of Zenith running in a terminal displaying CPU, memory, network, disk, and process information.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 22 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;dtool by guoxbin is a collection of mini-tools to assist developers. They including number encoding, string and URL encoding&#x2F;decoding, hashing, encryption&#x2F;decryption, and more. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;guoxbin&#x2F;dtool&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;guoxbin&#x2F;dtool&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1294265015541760006?ref_src=twsrc%5Etfw&quot;&gt;August 14, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;dtool.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 837.5px; min-height: 167px&quot; alt=&quot;Screenshot of the output of dtool --help in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 23 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Castor by Julien Blanchard is, &amp;quot;a browser for the small internet&amp;quot;. It&amp;#39;s is a GUI application implemented with GTK for browsing Gemini, Gopher, and Finger pages. &lt;a href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~julienxx&#x2F;castor&quot;&gt;https:&#x2F;&#x2F;git.sr.ht&#x2F;~julienxx&#x2F;castor&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1294627402069315585?ref_src=twsrc%5Etfw&quot;&gt;August 15, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;castor.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 600px; min-height: 167px&quot; alt=&quot;Screenshot of Castor displaying the Gemini home page.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 24 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;watchexec is a language agnostic, full-featured tool for running a command in response to file-system changes. It&amp;#39;s great for automatically recompiling Rust projects in response to changes.  &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;watchexec&#x2F;watchexec&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;watchexec&#x2F;watchexec&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1294989789918375937?ref_src=twsrc%5Etfw&quot;&gt;August 16, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;watchexec.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 528.5px; min-height: 167px&quot; alt=&quot;Screenshot of the output of watchexec --help in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 25 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;meli by epilys is a mail client for the terminal. It aims for configurability and extensibility with sane defaults, whilst being suitable for both new and power users. &lt;a href=&quot;https:&#x2F;&#x2F;meli.delivery&#x2F;&quot;&gt;https:&#x2F;&#x2F;meli.delivery&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1295352178400526336?ref_src=twsrc%5Etfw&quot;&gt;August 17, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;meli.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 850px; min-height: 167px&quot; alt=&quot;Screenshot of meli running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;meli.delivery&amp;#x2F;images&amp;#x2F;screenshots&amp;#x2F;threads.webp&quot;&gt;https:&amp;#x2F;&amp;#x2F;meli.delivery&amp;#x2F;images&amp;#x2F;screenshots&amp;#x2F;threads.webp&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 26 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;delta by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;dandavison7?ref_src=twsrc%5Etfw&quot;&gt;@dandavison7&lt;&#x2F;a&gt; is viewer for git and diff output that presents diffs with rich styling. It allows extensive configuration to the layout and appearnce of the output and integrates with git. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dandavison&#x2F;delta&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;dandavison&#x2F;delta&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1295714567197270022?ref_src=twsrc%5Etfw&quot;&gt;August 18, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;delta.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 337.5px; min-height: 167px&quot; alt=&quot;Screenshot of delta running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 27 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;sharewifi by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;bradyj?ref_src=twsrc%5Etfw&quot;&gt;@bradyj&lt;&#x2F;a&gt; is tool for macOS that lets you quickly share Wi-Fi connection details. It uses the Keychain and can display a QR code that iOS and Android devices can scan to connect to the network. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bradyjoslin&#x2F;sharewifi&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;bradyjoslin&#x2F;sharewifi&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1296076954622406663?ref_src=twsrc%5Etfw&quot;&gt;August 19, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;sharewifi.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 595px; min-height: 167px&quot; alt=&quot;Screenshot of sharewifi running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 28 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;eva by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;N3rdyP3pp3r?ref_src=twsrc%5Etfw&quot;&gt;@N3rdyP3pp3r&lt;&#x2F;a&gt; is a command line calculator. It has an interactive mode with syntax highlighting and persistent history but can also be used non-interactively. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NerdyPepper&#x2F;eva&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;NerdyPepper&#x2F;eva&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1296439340898426881?ref_src=twsrc%5Etfw&quot;&gt;August 20, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;eva.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 337.5px; min-height: 167px&quot; alt=&quot;Screenshot of eva running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 29 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;bat by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;sharkdp86?ref_src=twsrc%5Etfw&quot;&gt;@sharkdp86&lt;&#x2F;a&gt; is an alternative to cat with beautiful syntax highlighting for a large number of languages, git integration, automatic paging, and yes, it can concatenate files too. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;bat&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;bat&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1296801728927539202?ref_src=twsrc%5Etfw&quot;&gt;August 21, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;bat.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 338.5px; min-height: 167px&quot; alt=&quot;Screenshot of bat showing some Rust code in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 30 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;git-absorb by Stephen Jung automates the `git commit --fixup` workflow. When you have changes you want to meld into previous commits git-absorb will automatically find the right commits and make the fixup commits. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tummychow&#x2F;git-absorb&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;tummychow&#x2F;git-absorb&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1297164116788994048?ref_src=twsrc%5Etfw&quot;&gt;August 22, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 31 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;dust by Andy Boot aims to be more intuitive alternative to `du -sh`. It provides a summary of the top consumers of disk space in a file tree. A visualisation accompanies the results to show how they contribute overall. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bootandy&#x2F;dust&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;bootandy&#x2F;dust&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1297526505304666115?ref_src=twsrc%5Etfw&quot;&gt;August 23, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;dust.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 559.5px; min-height: 167px&quot; alt=&quot;Screenshot of dust running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 32 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;shotgun by the neXromancers is a tool for taking screenshots on X11 based desktops. It was used to take most of the screenshots shared in this series so far. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;neXromancers&#x2F;shotgun&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;neXromancers&#x2F;shotgun&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1297888893715361795?ref_src=twsrc%5Etfw&quot;&gt;August 24, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;shotgun.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 337.5px; min-height: 167px&quot; alt=&quot;Screenshot taken by shotgun of mdcat rendering the shotgun README in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 33 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;ripgrep by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;burntsushi5?ref_src=twsrc%5Etfw&quot;&gt;@burntsushi5&lt;&#x2F;a&gt; is a very fast recursive search tool (like `grep -r`, `ack`, and `ag`) that honours ignore files, and skips binary files by default. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;ripgrep&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;ripgrep&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1298251282352873478?ref_src=twsrc%5Etfw&quot;&gt;August 25, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;ripgrep.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 337.5px; min-height: 167px&quot; alt=&quot;Screenshot of ripgrep running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 34 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;ion by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;redox_os?ref_src=twsrc%5Etfw&quot;&gt;@redox_os&lt;&#x2F;a&gt; is a high performance shell. It&amp;#39;s usable as an interactive shell or for writing scripts using its powerful, user friendly scripting syntax. &lt;a href=&quot;https:&#x2F;&#x2F;doc.redox-os.org&#x2F;ion-manual&#x2F;html&#x2F;&quot;&gt;https:&#x2F;&#x2F;doc.redox-os.org&#x2F;ion-manual&#x2F;html&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1298613669538996226?ref_src=twsrc%5Etfw&quot;&gt;August 26, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;ion.mp4&quot; poster=&quot;ion.mp4.jpg&quot; style=&quot;max-height: 337px&quot;&gt;&lt;&#x2F;video&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 35 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;mdcat by Sebastian Wiesner is a Markdown renderer for the terminal. It renders headings, styled text, quotes, lists, and code blocks. With appropriate terminal support it can also render clickable links, and images. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lunaryorn&#x2F;mdcat&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;lunaryorn&#x2F;mdcat&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1298976057538871297?ref_src=twsrc%5Etfw&quot;&gt;August 27, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;mdcat.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 481px; min-height: 167px&quot; alt=&quot;Screenshot of mdcat rendering a sampel Markdown document in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 36 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;sd by Gregory is an alternative to sed for common search and replace tasks. It supports regex search and replace, as well as literal matching. Also, changes are made in-place when a file path is specified — no more fighting with GNU vs. BSD sed `-i`!&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1299338443990982656?ref_src=twsrc%5Etfw&quot;&gt;August 28, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 37 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;swc is a JavaScript and TypeScript compiler that aims to be a fast alternative to Babel for compiling modern JavaScript into more broadly compatible JavaScript. It also provides bundling and tree shaking for dead code elimination. &lt;a href=&quot;https:&#x2F;&#x2F;swc-project.github.io&#x2F;&quot;&gt;https:&#x2F;&#x2F;swc-project.github.io&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1299700831755931649?ref_src=twsrc%5Etfw&quot;&gt;August 29, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 38 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;espanso by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;terzi_federico?ref_src=twsrc%5Etfw&quot;&gt;@terzi_federico&lt;&#x2F;a&gt; is a cross-platform text expansion tool. Define trigger keywords and when you type them espanso will replace them with what you choose. Replacements can be text, dates, the output of scripts, and more. &lt;a href=&quot;https:&#x2F;&#x2F;espanso.org&#x2F;&quot;&gt;https:&#x2F;&#x2F;espanso.org&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1300063221005447169?ref_src=twsrc%5Etfw&quot;&gt;August 30, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;espanso.mp4&quot; poster=&quot;espanso.mp4.jpg&quot; style=&quot;max-height: 162px&quot;&gt;&lt;&#x2F;video&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 39 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Hyperfine by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;sharkdp86?ref_src=twsrc%5Etfw&quot;&gt;@sharkdp86&lt;&#x2F;a&gt; is a benchmarking tool. It compares commands and provides statistical analysis. It can do warm-up runs and run cache clearing commands before running the tests.   Progress is reported while the test runs. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;hyperfine&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;hyperfine&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1300425609156333568?ref_src=twsrc%5Etfw&quot;&gt;August 31, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;hyperfine.mp4&quot; poster=&quot;hyperfine.mp4.jpg&quot; style=&quot;max-height: 342px&quot;&gt;&lt;&#x2F;video&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 40 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt; &lt;br&gt;&lt;br&gt;verco by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;ahvamolessa?ref_src=twsrc%5Etfw&quot;&gt;@ahvamolessa&lt;&#x2F;a&gt; is git TUI for *BSD, Linux, macOS, and Windows. I features a keyboard centric UI that can be used to perform many common git tasks. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matheuslessarodrigues&#x2F;verco&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;matheuslessarodrigues&#x2F;verco&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1300787997592432642?ref_src=twsrc%5Etfw&quot;&gt;September 1, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;verco.mp4&quot; poster=&quot;verco.mp4.jpg&quot; style=&quot;max-height: 675px&quot;&gt;&lt;&#x2F;video&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;ja&quot; dir=&quot;ltr&quot;&gt;Day 41 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;oha (おはよう) by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hatookov?ref_src=twsrc%5Etfw&quot;&gt;@hatookov&lt;&#x2F;a&gt; is a load testing tool for web applications. It generates load and shows a live dashboard while the test runs, then prints a summary at the end. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hatoo&#x2F;oha&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;hatoo&#x2F;oha&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1301150386217074690?ref_src=twsrc%5Etfw&quot;&gt;September 2, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;oha.mp4&quot; poster=&quot;oha.mp4.jpg&quot; style=&quot;max-height: 431px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;hatoo&amp;#x2F;oha&amp;#x2F;blob&amp;#x2F;10b1dc0103c11e8144f3a61cbb481092d24a2062&amp;#x2F;demo.gif&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;hatoo&amp;#x2F;oha&amp;#x2F;blob&amp;#x2F;10b1dc0103c11e8144f3a61cbb481092d24a2062&amp;#x2F;demo.gif&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 42 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Starship is a fast, customisable shell prompt generator compatible with bash, fish, ion, PowerShell, and zsh. It can surface status information from a vast selection of modules, which you arrange however you want. &lt;a href=&quot;https:&#x2F;&#x2F;starship.rs&#x2F;&quot;&gt;https:&#x2F;&#x2F;starship.rs&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1301512772879110146?ref_src=twsrc%5Etfw&quot;&gt;September 3, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;starship.mp4&quot; poster=&quot;starship.mp4.jpg&quot; style=&quot;max-height: 478px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;starship.rs&amp;#x2F;demo.webm&quot;&gt;https:&amp;#x2F;&amp;#x2F;starship.rs&amp;#x2F;demo.webm&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 43 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;git-trim by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;lee_seongchan?ref_src=twsrc%5Etfw&quot;&gt;@lee_seongchan&lt;&#x2F;a&gt; automates cleaning up git branches whose tracking remote refs are merged or stray. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;foriequal0&#x2F;git-trim&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;foriequal0&#x2F;git-trim&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1301875160211955712?ref_src=twsrc%5Etfw&quot;&gt;September 4, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;git-trim.mp4&quot; poster=&quot;git-trim.mp4.jpg&quot; style=&quot;max-height: 572px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;raw.githubusercontent.com&amp;#x2F;foriequal0&amp;#x2F;git-trim&amp;#x2F;master&amp;#x2F;screencast.png&quot;&gt;https:&amp;#x2F;&amp;#x2F;raw.githubusercontent.com&amp;#x2F;foriequal0&amp;#x2F;git-trim&amp;#x2F;master&amp;#x2F;screencast.png&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 44 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;bandwhich by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;im_snif?ref_src=twsrc%5Etfw&quot;&gt;@im_snif&lt;&#x2F;a&gt; helps you answer the question: what&amp;#39;s using all my bandwidth!!? bandwhich provides a live view of processes and their network utilisation, as well as utilisation by destination address. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;imsnif&#x2F;bandwhich&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;imsnif&#x2F;bandwhich&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1302237547221807104?ref_src=twsrc%5Etfw&quot;&gt;September 5, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
      &lt;video controls muted preload=&quot;none&quot; src=&quot;bandwhich.mp4&quot; poster=&quot;bandwhich.mp4.jpg&quot; style=&quot;max-height: 828px&quot;&gt;&lt;&#x2F;video&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;imsnif&amp;#x2F;bandwhich&amp;#x2F;blob&amp;#x2F;fde53ddb3bcb769bc3474ba3d739d268619bf138&amp;#x2F;demo.gif&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;imsnif&amp;#x2F;bandwhich&amp;#x2F;blob&amp;#x2F;fde53ddb3bcb769bc3474ba3d739d268619bf138&amp;#x2F;demo.gif&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 45 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;xsv by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;burntsushi5?ref_src=twsrc%5Etfw&quot;&gt;@burntsushi5&lt;&#x2F;a&gt; is the Swiss Army knife of CSV! Slice, select, search, join, analyse, and more with its large selection of composable sub-commands. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;xsv&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;xsv&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1302599936366833664?ref_src=twsrc%5Etfw&quot;&gt;September 6, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;xsv.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 450px; min-height: 167px&quot; alt=&quot;Screenshot of xsv running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 46 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;Shellcaster by Jeff Hughes is a terminal based podcast manager. Subscribe, sync, and download episodes for local playback. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jeff-hughes&#x2F;shellcaster&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;jeff-hughes&#x2F;shellcaster&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1302962322516840448?ref_src=twsrc%5Etfw&quot;&gt;September 7, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;shellcaster.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 504px; min-height: 167px&quot; alt=&quot;Screenshot of Shellcaster running in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
        &lt;span class=&quot;media-source&quot;&gt;
          &lt;b&gt;Source:&lt;&#x2F;b&gt;
          &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;jeff-hughes&amp;#x2F;shellcaster&amp;#x2F;blob&amp;#x2F;f6cb4c55c4a6765483d7810a2b6d08a928e799e1&amp;#x2F;img&amp;#x2F;screenshot.png&quot;&gt;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;jeff-hughes&amp;#x2F;shellcaster&amp;#x2F;blob&amp;#x2F;f6cb4c55c4a6765483d7810a2b6d08a928e799e1&amp;#x2F;img&amp;#x2F;screenshot.png&lt;&#x2F;a&gt;
        &lt;&#x2F;span&gt;
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 47 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;cargo-edit by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;killercup?ref_src=twsrc%5Etfw&quot;&gt;@killercup&lt;&#x2F;a&gt; adds three handy cargo sub-commands for editing the Cargo.toml in Rust projects:&lt;br&gt;&lt;br&gt;• add — Add new dependencies&lt;br&gt;• remove — Remove dependencies&lt;br&gt;• upgrade — Upgrade dependencies&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;killercup&#x2F;cargo-edit&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;killercup&#x2F;cargo-edit&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1303324711720488961?ref_src=twsrc%5Etfw&quot;&gt;September 8, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 48 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;yj by Bruce Adams converts YAML to JSON. It works well paired with jq for querying, or any other tool expecting JSON.&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bruceadams&#x2F;yj&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;bruceadams&#x2F;yj&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1303687100437590020?ref_src=twsrc%5Etfw&quot;&gt;September 9, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;yj.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 337.5px; min-height: 167px&quot; alt=&quot;Screenshot of yj transformating a small YAML document into JSON in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 49 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;websocat by Vitaly Shukela is netcat, curl and socat for WebSockets. websocat can act as client, server, or both (proxy). It can even proxy TCP connections over a web socket connection. It supports BSD, Linux, macOS, and Windows. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;vi&#x2F;websocat&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;vi&#x2F;websocat&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1304049488693047298?ref_src=twsrc%5Etfw&quot;&gt;September 10, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
  &lt;&#x2F;li&gt;
  
  &lt;li&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Day 50 of &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;100binaries?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#100binaries&lt;&#x2F;a&gt;&lt;br&gt;&lt;br&gt;tealdeer by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;dbrgn?ref_src=twsrc%5Etfw&quot;&gt;@dbrgn&lt;&#x2F;a&gt; is a tldr pages client. tldr pages are community maintained, concise, example-driven alternatives to man pages for command line tools. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dbrgn&#x2F;tealdeer&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;dbrgn&#x2F;tealdeer&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Wesley Moore (@wezm) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wezm&#x2F;status&#x2F;1304411875426406400?ref_src=twsrc%5Etfw&quot;&gt;September 11, 2020&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;

    
      
        
        
      

      
        &lt;span class=&quot;screenshot&quot;&gt;
          &lt;img src=&quot;tealdeer.png&quot; loading=&quot;lazy&quot; style=&quot;max-height: 338.5px; min-height: 167px&quot; alt=&quot;Screenshot of tealdeer showing the tldr page for ls in a terminal.&quot;&gt;
        &lt;&#x2F;span&gt;
      
      
    
  &lt;&#x2F;li&gt;
  
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;100-rust-binaries&#x2F;page2&#x2F;&quot;&gt;Continue to page 2 »&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Slowing Down Read Rust Posting</title>
      <pubDate>Mon, 07 Sep 2020 10:00:00 +1000</pubDate>
      <atom:published>2020-09-07T10:00:00+10:00</atom:published>
      <atom:updated>2021-10-01T10:44:43+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;slowing-read-rust-posting&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;slowing-read-rust-posting&#x2F;</guid>
      <description>&lt;p&gt;After nearly 3 years and more than 3200 posts I’m going to slow down the
posting frequency on Read Rust. I hope this will free up some spare time and
make it easier to take breaks from social media. I aim to share all of the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.rust-lang.org&#x2F;2020&#x2F;09&#x2F;03&#x2F;Planning-2021-Roadmap.html&quot;&gt;#rust2021&lt;&#x2F;a&gt; posts I can find, but after that I’ll probably only share posts
that seem particularly noteworthy or interesting.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;I started &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;readrust.net&#x2F;&quot;&gt;Read Rust&lt;&#x2F;a&gt; in January 2018 to track the posts being shared as part of
the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.rust-lang.org&#x2F;2018&#x2F;01&#x2F;03&#x2F;new-years-rust-a-call-for-community-blogposts.html&quot;&gt;inaugural call for blog posts&lt;&#x2F;a&gt;. When I started there were
only a handful of new posts each day to triage. Now there are many more and
unless I triage and publish daily they quickly pile up.&lt;&#x2F;p&gt;
&lt;p&gt;Also, I’ve kind of built a reflex of trying to “complete the Internet” each day
by ensuring that I read my whole Twitter feed, and new posts on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;&quot;&gt;&#x2F;r&#x2F;rust&lt;&#x2F;a&gt;. I
would like to break this habit and be able to take breaks from these things,
without feeling like I might miss an important post.&lt;&#x2F;p&gt;
&lt;p&gt;Whilst I think there is value in the curation and archiving of posts on Read
Rust, the website doesn’t see a lot of use. I think most of the value for
people is following the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;read_rust&quot;&gt;Twitter&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;botsin.space&#x2F;@readrust&quot;&gt;Mastodon&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.facebook.com&#x2F;readrust&#x2F;&quot;&gt;Facebook&lt;&#x2F;a&gt; accounts.
However, there’s a fair amount of overlap between posts shared on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;&quot;&gt;&#x2F;r&#x2F;rust&lt;&#x2F;a&gt;,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;rustlang&quot;&gt;@rustlang&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;this-week-in-rust.org&#x2F;&quot;&gt;This Week in Rust&lt;&#x2F;a&gt;. So, I think that if folks keep an
eye on one or more of those they will still see most posts of note.&lt;&#x2F;p&gt;
&lt;p&gt;If you’re not into social media, the full list of more than 450 Rust RSS feeds
I subscribe to is available via an OPML file on the site. So, feel free to use
that to subscribe to a bunch of feeds instead. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;readrust.net&#x2F;rust-blogs.opml&quot;&gt;Rust blogs OPML&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It’s been fun to build, and rebuild the website and surrounding tooling over
the years. Read Rust was initially just an RSS feed but after requests for an
actual web-page I built a small site with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cobalt-org.github.io&#x2F;&quot;&gt;Cobalt static site
compiler&lt;&#x2F;a&gt;. In late 2019 in an effort to streamline the sharing of posts
I rebuilt the site as dynamic web app. In early 2020 I added full text search.&lt;&#x2F;p&gt;
&lt;p&gt;As mentioned in the introduction, from here I plan to share &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.rust-lang.org&#x2F;2020&#x2F;09&#x2F;03&#x2F;Planning-2021-Roadmap.html&quot;&gt;#rust2021&lt;&#x2F;a&gt; posts and
after that posting will be much less frequent. Thanks for reading, and happy
coding 🦀.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;frequently-anticipated-questions&quot;&gt;Frequently Anticipated Questions&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;q-what-about-getting-others-to-help-share-posts&quot;&gt;Q. What about getting others to help share posts?&lt;&#x2F;h4&gt;
&lt;p&gt;I considered this, and it it was actually part of the motivation for the
rebuild in 2019. However, ultimately Rust is now large enough and continuing to
grow such that it’s become less and less feasible to curate the entire
firehose of Rust content.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;q-what-about-making-it-a-sort-of-rss-powered-rust-planet&quot;&gt;Q. What about making it a sort of RSS powered Rust &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Planet_(software)&quot;&gt;planet&lt;&#x2F;a&gt;?&lt;&#x2F;h4&gt;
&lt;p&gt;I think there’s value in curation. Rust is popular enough now that there’s a
lot of low effort posts, or repetitious getting started posts. Also, people
rightly have diverse interests and their blog may not solely contain Rust
posts. So, I’d prefer to keep the archive in the focussed state it’s in now.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;q-what-will-happen-to-the-site-and-social-media-accounts-now&quot;&gt;Q. What will happen to the site and social media accounts now?&lt;&#x2F;h4&gt;
&lt;p&gt;I plan to keep the site up and running indefinitely. I am a strong believer in
not breaking links on the web, and I think I have a pretty decent track record.
For example, this site has been online for 13 years and I still have redirects
in place from the very first version of it. I may still share the occasional
post but in general I hope to free up a bit of time to work on other things.&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Working Around GitHub Browser Sniffing to Get Better Emoji on Linux</title>
      <pubDate>Fri, 19 Jun 2020 18:03:43 +1000</pubDate>
      <atom:published>2020-06-19T18:03:43+10:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;github-emoji-linux&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;github-emoji-linux&#x2F;</guid>
      <description>&lt;p&gt;I have my system configured&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; to use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.joypixels.com&#x2F;&quot;&gt;JoyPixels&lt;&#x2F;a&gt; for emoji, which I consider
vastly more attractive than Noto Color Emoji. Sadly GitHub uses browser
sniffing to detect Linux user-agents and replaces emoji with (badly aligned)
images of Noto Color Emoji. They don’t do this on macOS and Windows. In this
post I explain how I worked around this.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;div class=&quot;text-center&quot;&gt;
  &lt;img src=&quot;github-emoji-before-after.png&quot; style=&quot;max-width: 530px; max-width: min(530px, 100% - 32px); border: 1px solid #e1e4e8; padding: 0 16px; background-color: white;&quot; alt=&quot;Screenshot of GitHub showing two comments, one with emoji set in the Noto Color Emoji font, the other in the JoyPixels Font.&quot;&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;The solution is simple: make GitHub think you’re using a Mac or Windows PC.
There are various ways to change the User-Agent string of Firefox. The easiest
is via &lt;code&gt;about:config&lt;&#x2F;code&gt; but I didn’t want it to be a global change — I
want sites to know that I’m using Linux in logs&#x2F;privacy respecting analytics (I
block most trackers).&lt;&#x2F;p&gt;
&lt;p&gt;I ended up using the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;addons.mozilla.org&#x2F;en-US&#x2F;firefox&#x2F;addon&#x2F;user-agent-string-switcher&#x2F;&quot;&gt;User-Agent Switcher and Manager&lt;&#x2F;a&gt; browser add-on. I
configured its allow list to only include &lt;code&gt;github.com&lt;&#x2F;code&gt;, and use the
&lt;code&gt;User-Agent&lt;&#x2F;code&gt; string for Firefox on macOS. The end result? JoyPixels, just like
I wanted.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;P.S.&lt;&#x2F;strong&gt; If anyone from GitHub sees this. Please stop browser sniffing Linux
visitors.  Linux desktops and browsers have had working emoji support for years
now.&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;I use the term, “configured”, loosely here as all I really did was install the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.archlinux.org&#x2F;packages&#x2F;community&#x2F;any&#x2F;ttf-joypixels&#x2F;&quot;&gt;ttf-joypixels&lt;&#x2F;a&gt; package. &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</description>
    </item>
    
    <item>
      <title>Setting the amdgpu HDMI Pixel Format on Linux</title>
      <pubDate>Sat, 30 May 2020 08:48:30 +1000</pubDate>
      <atom:published>2020-05-30T08:48:30+10:00</atom:published>
      <atom:updated>2021-05-15T10:15:08+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;linux-amdgpu-pixel-format&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;linux-amdgpu-pixel-format&#x2F;</guid>
      <description>&lt;p&gt;This week I discovered some details of digital display technology that I was
previously unaware of: pixel formats. I have two &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.dell.com&#x2F;en-au&#x2F;shop&#x2F;dell-24-ultra-hd-4k-monitor-p2415q&#x2F;apd&#x2F;210-anfp&#x2F;monitors-monitor-accessories&quot;&gt;Dell P2415Q displays&lt;&#x2F;a&gt;
connected to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bitcannon.net&#x2F;page&#x2F;ryzen9-pc&#x2F;&quot;&gt;my computer&lt;&#x2F;a&gt;. One via DisplayPort, the other via HDMI.
The HDMI connected one was misbehaving and showing a dull picture. It turned
out I needed to force the HDMI port of my RX560 graphics card to use RGB output
instead of YCbCr. However, the &lt;code&gt;amdgpu&lt;&#x2F;code&gt; driver does not expose a means to do
this. So, I used an EDID hack to make it look like the display only supported
RGB.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;&lt;strong&gt;tl;dr&lt;&#x2F;strong&gt; You can’t easily configure the pixel format of the Linux &lt;code&gt;amdgpu&lt;&#x2F;code&gt;
driver but you can hack the EDID of your display so the driver chooses RGB.
&lt;a href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;linux-amdgpu-pixel-format&#x2F;#the-fix&quot;&gt;Jump to the instructions&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Previously I had one display at work and one at home, both using DisplayPort
and all was well. However, when I started working from home at the start of
2020 (pre-pandemic) the HDMI connected one has always been a bit flakey. The
screen would go blank for second, then come back on. I tried 3 different HDMI
cables each more premium (and hopefully shielded than the last) without
success.&lt;&#x2F;p&gt;
&lt;p&gt;This week the frustration boiled over and I vented to some friends. I was on
the brink of just rage buying a new graphics card with multiple DisplayPorts,
since I’d never had any trouble with that connection. I received one suggestion
to swap the cables between the two, to rule out a fault with the HDMI connected
display. I was quite confident the display was ok but it was a sensible thing
to try before dropping cash on a new graphics card. So I swapped the cables
over.&lt;&#x2F;p&gt;
&lt;p&gt;After performing &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.dell.com&#x2F;support&#x2F;article&#x2F;en-au&#x2F;sln306595&#x2F;setting-up-the-p2415q-p2715q-monitors-with-hdmi-2-0-that-support-4k-x-2k-60hz?lang=en&quot;&gt;the magical incantation to enable HDMI 2.0&lt;&#x2F;a&gt; and
get 4K 60Hz on the newly HDMI connected display I immediately noticed lag. I
even captured it in a slow motion video on my phone to prove I wasn’t going
crazy. Despite &lt;code&gt;xrandr&lt;&#x2F;code&gt; reporting a 60Hz connection it seemed as though it was
updating at less than that. This led me to compare the menus of the two
displays. It was here I noticed that the good one reported an input colour
format of RGB, the other &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;YPbPr&quot;&gt;YPbPr&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This led to more reading about pixel formats in digital displays — a thing I
was not previously aware of. Turns out that ports like HDMI support multiple
ways of encoding the pixel data, some sacrificing dynamic range for lower
bandwidth. I found this article particularly helpful,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.tomshardware.com&#x2F;features&#x2F;displayport-vs-hdmi-better-for-gaming&quot;&gt;DisplayPort vs. HDMI: Which Is Better For Gaming?&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;My hypothesis at this point was that the lag was being introduced by my display
converting the YPbPr input to its native RGB. So, I looked for a way to change
the pixel format output from the HDMI port of my RX560 graphics card. Turns out
this is super easy on Windows, but &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;drm&#x2F;amd&#x2F;-&#x2F;issues&#x2F;476&quot;&gt;the &lt;code&gt;amdgpu&lt;&#x2F;code&gt; driver on Linux does not
support changing it&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In trying various suggestions in that bug report I rebooted a few times and the
lag mysteriously went away but the pixel format remained the same. At this
point I noticed the display had a grey cast to it, especially on areas of
white. This had been present on the other display when it was connected via
HDMI too but I just put it down to being a couple of years older than the other
one. With my new pixel format knowledge in hand I knew this was was the source
of lack of brightness. So, I was still determined to find a way to force the
HDMI output to RGB.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-fix&quot;&gt;The Fix&lt;&#x2F;h3&gt;
&lt;p&gt;It was at this point I found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;Amd&#x2F;comments&#x2F;8bwul6&#x2F;how_to_switch_the_pixel_format_for_amdgpu_on_linux&#x2F;dxaef7a&#x2F;&quot;&gt;this Reddit post&lt;&#x2F;a&gt; describing
a terrible hack, originally described by Parker Reed in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=tYYMiX7dlak&quot;&gt;this YouTube
video&lt;&#x2F;a&gt;: Copy the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Extended_Display_Identification_Data&quot;&gt;EDID&lt;&#x2F;a&gt; of the display and modify it to make
it seem like the display only supports RGB. The &lt;code&gt;amdgpu&lt;&#x2F;code&gt; driver then chooses
that format instead.  Amazingly enough it worked! I also haven’t experienced
the screen blanking issue since swapping cables. I can’t say for sure if that
is fixed but the HDMI cable is now further away from interference from my Wi-Fi
router, so perhaps that helped.&lt;&#x2F;p&gt;
&lt;p&gt;The following are the steps I took on Arch Linux to use a modified EDID:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Install &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aur.archlinux.org&#x2F;packages&#x2F;wxedid&quot;&gt;wxEDID from the AUR&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Make a copy of the EDID data: &lt;code&gt;cp &#x2F;sys&#x2F;devices&#x2F;pci0000:00&#x2F;0000:00:03.1&#x2F;0000:09:00.0&#x2F;drm&#x2F;card0&#x2F;card0-HDMI-A-1&#x2F;edid Documents&#x2F;edid.bin&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Edit &lt;code&gt;edid.bin&lt;&#x2F;code&gt; with wxEDID and change these values:
&lt;ol&gt;
&lt;li&gt;Find SPF: Supported features -&amp;gt; vsig_format -&amp;gt; replace 0b01 wih 0b00&lt;&#x2F;li&gt;
&lt;li&gt;Find CHD: CEA-861 header -&amp;gt; change the value of YCbCr420 and YCbCr444 to 0&lt;&#x2F;li&gt;
&lt;li&gt;Recalculate the checksum: Options &amp;gt; Recalc Checksum.&lt;&#x2F;li&gt;
&lt;li&gt;Save the file.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt; I had to attempt editing the file a few times as wxEDID kept
segfaulting. Eventually it saved without crashing though.&lt;&#x2F;p&gt;
&lt;p&gt;Now we need to get the kernel to use the modified file:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;sudo mkdir &#x2F;lib&#x2F;firmware&#x2F;edid&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;sudo mv edid.bin &#x2F;lib&#x2F;firmware&#x2F;edid&#x2F;edid.bin&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Edit the kernel command line. I use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Systemd-boot&quot;&gt;systemd-boot&lt;&#x2F;a&gt;, so I edited
&lt;code&gt;&#x2F;boot&#x2F;loader&#x2F;entries&#x2F;arch.conf&lt;&#x2F;code&gt; and added
&lt;code&gt;drm.edid_firmware=HDMI-A-1:edid&#x2F;edid.bin&lt;&#x2F;code&gt; to the command line, making the
full file look like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; title   Arch Linux&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; linux   &#x2F;vmlinuz-linux&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; initrd  &#x2F;amd-ucode.img&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; initrd  &#x2F;initramfs-linux.img&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; options root=PARTUUID=2f693946-c278-ed44-8ba2-67b07c3b6074 resume=UUID=524c0604-c307-4106-97e4-1b9799baa7d5 resume_offset=4564992 drm.edid_firmware=HDMI-A-1:edid&#x2F;edid.bin rw&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Note 1:&lt;&#x2F;strong&gt; &lt;code&gt;HDMI-A-1&lt;&#x2F;code&gt; is the connector to apply the EDID firmware to. It
was determined from the &lt;code&gt;&#x2F;sys&lt;&#x2F;code&gt; path above:&lt;br&gt;
&lt;code&gt;&#x2F;sys&#x2F;devices&#x2F;pci0000:00&#x2F;0000:00:03.1&#x2F;0000:09:00.0&#x2F;drm&#x2F;card0&#x2F;card0-&lt;b&gt;HDMI-A-1&lt;&#x2F;b&gt;&#x2F;edid&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Note 2:&lt;&#x2F;strong&gt; Older kernels (before 4.15 I think) used
&lt;code&gt;drm_kms_helper.edid_firmware&lt;&#x2F;code&gt; as the command line argument instead of
&lt;code&gt;drm.edid_firmware&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Regenerate the initial RAM disk: &lt;code&gt;sudo mkinitcpio -p linux&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Reboot&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;After rebooting the display confirmed it was now using RGB and visually it was
looking much brighter! 🤞 the display blanking issue remains fixed as well. You
can also check the that kernel applied the firmware by looking at the kernel
log (&lt;code&gt;dmesg&lt;&#x2F;code&gt;) for a line like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[    4.956349] [drm] Got external EDID base block and 1 extension from &amp;quot;edid&#x2F;edid.bin&amp;quot; for connector &amp;quot;HDMI-A-1&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</description>
    </item>
    
    <item>
      <title>Software Bounties</title>
      <pubDate>Fri, 22 May 2020 20:07:43 +1000</pubDate>
      <atom:published>2020-05-22T20:07:43+10:00</atom:published>
      <atom:updated>2020-06-19T09:30:00+10:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;bounties&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;bounties&#x2F;</guid>
      <description>&lt;p&gt;I don’t have time to build all the things I’d like to build, so I’m offering
bounties on the following work.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;&lt;strong&gt;Details&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Payment will be made via PayPal when the criteria is met. If you would prefer
another mechanism, feel free to suggest it, but no guarantees.&lt;&#x2F;li&gt;
&lt;li&gt;Amounts are in Australian dollars.&lt;&#x2F;li&gt;
&lt;li&gt;I will not pay out a bounty after the expiration date.&lt;&#x2F;li&gt;
&lt;li&gt;I may choose to extend the expiration date.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;How can I trust you’ll pay me?&lt;&#x2F;em&gt; I like to think that I’m a trustworthy
person. However, if you would like to discuss a partial payment prior
to starting work please get in touch.&lt;&#x2F;li&gt;
&lt;li&gt;You have to be the primary contributor to claim the bounty. If someone else
does all the work and you just nudge it over the line the other person is
the intended recipient.&lt;&#x2F;li&gt;
&lt;li&gt;If in doubt contact me.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;cairo-user-font-with-colour-bitmap-always-comes-out-black&quot;&gt;Cairo user-font With Colour Bitmap Always Comes Out Black&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.gnome.org&#x2F;Apps&#x2F;Evince&quot;&gt;Evince&lt;&#x2F;a&gt; PDF viewer uses &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;poppler&#x2F;poppler&quot;&gt;Poppler&lt;&#x2F;a&gt; to render PDFs, which in turn uses
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.cairographics.org&#x2F;&quot;&gt;Cairo&lt;&#x2F;a&gt;.  Emoji embedded in a PDF using a PDF Type 3 font always come out as a
black silhouette instead of the colour image when viewed in Evince do to a
limitation in Cairo’s user-font functionality.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Issues:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;cairo&#x2F;cairo&#x2F;-&#x2F;issues&#x2F;389&quot;&gt;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;cairo&#x2F;cairo&#x2F;-&#x2F;issues&#x2F;389&lt;&#x2F;a&gt;,&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;poppler&#x2F;poppler&#x2F;-&#x2F;issues&#x2F;729&quot;&gt;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;poppler&#x2F;poppler&#x2F;-&#x2F;issues&#x2F;729&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Criteria:&lt;&#x2F;strong&gt; Implement support for colour user-fonts in Cairo, resulting in
the Cairo issue being closed as completed.&lt;br &#x2F;&gt;
&lt;strong&gt;Language:&lt;&#x2F;strong&gt; C&lt;br &#x2F;&gt;
&lt;strong&gt;Amount:&lt;&#x2F;strong&gt; AU$500&lt;br &#x2F;&gt;
&lt;strong&gt;Expires:&lt;&#x2F;strong&gt; 2021-01-01T00:00:00Z&lt;&#x2F;p&gt;
&lt;h3 id=&quot;emoji-reactions-in-fractal&quot;&gt;Emoji Reactions in Fractal&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.gnome.org&#x2F;GNOME&#x2F;fractal&quot;&gt;Fractal&lt;&#x2F;a&gt; is a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;&quot;&gt;Matrix&lt;&#x2F;a&gt; client written in Rust using GTK.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Issue:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.gnome.org&#x2F;GNOME&#x2F;fractal&#x2F;-&#x2F;issues&#x2F;530&quot;&gt;https:&#x2F;&#x2F;gitlab.gnome.org&#x2F;GNOME&#x2F;fractal&#x2F;-&#x2F;issues&#x2F;530&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
&lt;strong&gt;Criteria:&lt;&#x2F;strong&gt; Implement emoji reactions in Fractal to the satisfaction of the
maintainers, resulting in the issue being closed as completed.&lt;br &#x2F;&gt;
&lt;strong&gt;Language:&lt;&#x2F;strong&gt; Rust&lt;br &#x2F;&gt;
&lt;strong&gt;Amount:&lt;&#x2F;strong&gt; AU$500&lt;br &#x2F;&gt;
&lt;strong&gt;Expires:&lt;&#x2F;strong&gt; 2021-01-01T00:00:00Z&lt;&#x2F;p&gt;
&lt;h3 id=&quot;update-mattermost-server-to-support-emoji-added-after-unicode-9-0&quot;&gt;Update Mattermost Server to Support Emoji Added After Unicode 9.0&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mattermost.com&#x2F;&quot;&gt;Mattermost’s&lt;&#x2F;a&gt; emoji picker is stuck on emoji from Unicode 9. We’re
now up to Unicode 13 and many emoji added in the last few years are missing.
This bounty pertains only to the work required in the Mattermost server, not
the desktop and mobile apps.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Issue:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mattermost.atlassian.net&#x2F;browse&#x2F;MM-13676&quot;&gt;https:&#x2F;&#x2F;mattermost.atlassian.net&#x2F;browse&#x2F;MM-13676&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
&lt;strong&gt;Criteria:&lt;&#x2F;strong&gt; Update the list of emoji in the Mattermost server to Unicode
13.0, resulting in the issue being closed as completed.&lt;br &#x2F;&gt;
&lt;strong&gt;Language:&lt;&#x2F;strong&gt; Go&lt;br &#x2F;&gt;
&lt;strong&gt;Amount:&lt;&#x2F;strong&gt; AU$200&lt;br &#x2F;&gt;
&lt;strong&gt;Expires:&lt;&#x2F;strong&gt; 2021-01-01T00:00:00Z&lt;&#x2F;p&gt;
</description>
    </item>
    
    <item>
      <title>Comparing Alternatives to top Written in Rust</title>
      <pubDate>Sat, 21 Mar 2020 10:45:00 +1100</pubDate>
      <atom:published>2020-03-21T10:45:00+11:00</atom:published>
      <atom:updated>2020-03-27T21:53:53+11:00</atom:updated>
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;rust-top-alternatives&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;rust-top-alternatives&#x2F;</guid>
      <description>&lt;p&gt;Recently I aliased &lt;code&gt;top&lt;&#x2F;code&gt; to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cjbassi&#x2F;ytop&quot;&gt;ytop&lt;&#x2F;a&gt;. Then I became aware of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ClementTsang&#x2F;bottom&quot;&gt;bottom&lt;&#x2F;a&gt;, and
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bvaisvil&#x2F;zenith&quot;&gt;zenith&lt;&#x2F;a&gt;. These are all terminal based system monitoring tools that you might
use instead of &lt;code&gt;top&lt;&#x2F;code&gt;. In this post I set out to compare them.&lt;&#x2F;p&gt;




&lt;figure class=&quot;text-center&quot;&gt;
  
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2020&amp;#x2F;rust-top-alternatives&amp;#x2F;ytop-btm-zenith-screenshot.png&quot;&gt;
  
  
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;www.wezm.net&amp;#x2F;v2&#x2F;posts&amp;#x2F;2020&amp;#x2F;rust-top-alternatives&amp;#x2F;ytop-btm-zenith-screenshot.png&quot; alt=&quot;Screenshot of ytop, bottom, and zenith while building some Rust code&quot; &#x2F;&gt;
  
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;Left to right: ytop, bottom, and zenith.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;aside class=&quot;float-right&quot;&gt;
  &lt;div class=&quot;emoji text-center&quot;&gt;💡&lt;&#x2F;div&gt;
  &lt;strong&gt;Why Rust?&lt;&#x2F;strong&gt;

  &lt;p&gt;The Rust programming language helps people write efficient, reliable software.
I like the language and the tools people are
building with it.&lt;&#x2F;p&gt;
&lt;p&gt;If you’re interested in more Rust CLI tools check out my
post: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;technical&#x2F;2019&#x2F;10&#x2F;useful-command-line-tools&#x2F;&quot;&gt;An Illustrated Guide to Some Useful Command Line
Tools&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;As the title states all three tools are written in Rust. They show similar
information and are all open source under the MIT license. I tested each one
on: Arch Linux, FreeBSD 12.1, macOS Mojave, and Windows 10. At the time of
testing, all three failed to build on FreeBSD and Windows. Figures below are
from the Arch Linux system, which is a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bitcannon.net&#x2F;page&#x2F;ryzen9-pc&#x2F;&quot;&gt;12 core AMD Ryzen desktop PC&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;ytop&lt;&#x2F;code&gt; and &lt;code&gt;bottom&lt;&#x2F;code&gt; use a layout that appears to be inspired by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cjbassi&#x2F;gotop&quot;&gt;gotop&lt;&#x2F;a&gt;. In
fact, &lt;code&gt;ytop&lt;&#x2F;code&gt; is written by the same person as &lt;code&gt;gotop&lt;&#x2F;code&gt;. &lt;code&gt;zenith&lt;&#x2F;code&gt; uses a layout
that’s a bit more like traditional &lt;code&gt;top&lt;&#x2F;code&gt; with histograms above the process
list.&lt;&#x2F;p&gt;
&lt;p&gt;I typically use &lt;code&gt;top&lt;&#x2F;code&gt; to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Check on overall system load and free memory.&lt;&#x2F;li&gt;
&lt;li&gt;Find specific processes that are using a lot of CPU or memory.&lt;&#x2F;li&gt;
&lt;li&gt;Observe CPU and memory use over time.&lt;&#x2F;li&gt;
&lt;li&gt;Occasionally kill processes.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I find the &lt;code&gt;zenith&lt;&#x2F;code&gt; layout more information dense, with less space taken up
with graphs. I also like the header row with info and help. The main feature
that it is missing compared to the others is temperatures — but that’s in the
list of planned features. &lt;del&gt;There is one issue with &lt;code&gt;zenith&lt;&#x2F;code&gt;: it doesn’t show my
ZFS pool. My system has an NVMe system disk and a ZFS pool of 3 SSDs that is
mounted as &lt;code&gt;&#x2F;home&lt;&#x2F;code&gt;, which is absent in the disk summary. I’ve raised &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bvaisvil&#x2F;zenith&#x2F;issues&#x2F;9&quot;&gt;an issue
on GitHub&lt;&#x2F;a&gt;.&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update 27 March 2020:&lt;&#x2F;strong&gt; Zenith 0.7.7 now shows ZFS pools.&lt;&#x2F;p&gt;
&lt;p&gt;The individual lines for each CPU in &lt;code&gt;bottom&lt;&#x2F;code&gt; makes the display quite noisy. I
prefer the aggregated line that &lt;code&gt;ytop&lt;&#x2F;code&gt; shows. &lt;code&gt;ytop&lt;&#x2F;code&gt; has a handy &lt;code&gt;-m&lt;&#x2F;code&gt; option
to only show CPU, memory, and process list.&lt;&#x2F;p&gt;
&lt;p&gt;So, after reviewing them all I’m going to start using &lt;code&gt;zenith&lt;&#x2F;code&gt;. It might not
be quite as pretty in screenshots but it’s the best for doing this things
I want to do with a &lt;code&gt;top&lt;&#x2F;code&gt; like tool.&lt;&#x2F;p&gt;
&lt;p&gt;Read on for further information about each tool, including an additional
honorable mention, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;epilys&#x2F;bb&quot;&gt;bb&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bottom&quot;&gt;bottom&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;rust-top-alternatives&#x2F;bottom.png&quot; alt=&quot;Screnshot of bottom&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ClementTsang&#x2F;bottom&quot;&gt;Repository&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Version tested:&lt;&#x2F;strong&gt; 0.2.2&lt;br &#x2F;&gt;
&lt;strong&gt;Runtime dependencies:&lt;&#x2F;strong&gt; None&lt;br &#x2F;&gt;
&lt;strong&gt;Lines of code:&lt;&#x2F;strong&gt; 4894&lt;br &#x2F;&gt;
&lt;strong&gt;Cargo dependencies:&lt;&#x2F;strong&gt; 109&lt;br &#x2F;&gt;
&lt;strong&gt;Stripped binary size:&lt;&#x2F;strong&gt; 3.4MiB&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;bottom&lt;&#x2F;code&gt; has &lt;code&gt;vi&lt;&#x2F;code&gt; style key bindings for navigating the process list. The
selection is not stable across updates but there is a key binding, &lt;code&gt;f&lt;&#x2F;code&gt; to
freeze the display that allows you navigate the list without it changing. It
supports killing processes with &lt;code&gt;dd&lt;&#x2F;code&gt; but does not prompt for the signal to
send. It shows a confirmation before killing the process.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Usage:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;bottom 0.2.2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Clement Tsang &amp;lt;cjhtsang@uwaterloo.ca&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;A cross-platform graphical process&#x2F;system monitor with a customizable interface and a multitude of features. Supports&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Linux, macOS, and Windows.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;USAGE:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    btm [FLAGS] [OPTIONS]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;FLAGS:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -a, --avg_cpu                Enables showing the average CPU usage.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -S, --case_sensitive         Match case when searching by default.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -c, --celsius                Sets the temperature type to Celsius.  This is the default option.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        --cpu_default            Selects the CPU widget to be selected by default.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        --disk_default           Selects the disk widget to be selected by default.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -m, --dot_marker             Use a dot marker instead of the default braille marker.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -f, --fahrenheit             Sets the temperature type to Fahrenheit.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -g, --group                  Groups processes with the same name together on launch.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -k, --kelvin                 Sets the temperature type to Kelvin.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -l, --left_legend            Puts external chart legends on the left side rather than the default right side.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        --memory_default         Selects the memory widget to be selected by default.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        --network_default        Selects the network widget to be selected by default.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        --process_default        Selects the process widget to be selected by default.  This is the default if nothing&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                 is set.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -R, --regex                  Use regex in searching by default.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -s, --show_disabled_data     Show disabled data entries.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        --temperature_default    Selects the temp widget to be selected by default.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -u, --current_usage          Within Linux, sets a process&amp;#39; CPU usage to be based on the total current CPU usage,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                 rather than assuming 100% usage.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -W, --whole_word             Match whole word when searching by default.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -h, --help                   Prints help information&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -V, --version                Prints version information&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;OPTIONS:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -C, --config &amp;lt;CONFIG_LOCATION&amp;gt;    Sets the location of the config file.  Expects a config file in the TOML format.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -r, --rate &amp;lt;RATE_MILLIS&amp;gt;          Sets a refresh rate in milliseconds; the minimum is 250ms, defaults to 1000ms.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                      Smaller values may take more resources.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;ytop&quot;&gt;ytop&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;rust-top-alternatives&#x2F;ytop.png&quot; alt=&quot;Screnshot of ytop&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cjbassi&#x2F;ytop&quot;&gt;Repository&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Version tested:&lt;&#x2F;strong&gt; 0.5.1&lt;br &#x2F;&gt;
&lt;strong&gt;Runtime dependencies:&lt;&#x2F;strong&gt; None&lt;br &#x2F;&gt;
&lt;strong&gt;Lines of code:&lt;&#x2F;strong&gt; 1903&lt;br &#x2F;&gt;
&lt;strong&gt;Cargo dependencies:&lt;&#x2F;strong&gt; 89&lt;br &#x2F;&gt;
&lt;strong&gt;Stripped binary size:&lt;&#x2F;strong&gt; 2.1MiB&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;ytop&lt;&#x2F;code&gt; has &lt;code&gt;vi&lt;&#x2F;code&gt; style key bindings for navigating the process list. The
selection remains on the same process across updates. It supports killing
processes with &lt;code&gt;dd&lt;&#x2F;code&gt; but does not prompt for the signal to send. There is
no confirmation when typing &lt;code&gt;dd&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Usage:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ytop 0.5.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;USAGE:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ytop [FLAGS] [OPTIONS]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;FLAGS:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -a, --average-cpu    Show average CPU in the CPU widget&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -b, --battery        Show Battery widget (overridden by &amp;#39;minimal&amp;#39; flag)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -f, --fahrenheit     Show temperatures in fahrenheit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -h, --help           Prints help information&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -m, --minimal        Only show the CPU, Mem, and Process widgets&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -p, --per-cpu        Show each CPU in the CPU widget&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -s, --statusbar      Show a statusbar with the time&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -V, --version        Prints version information&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;OPTIONS:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -c, --colorscheme &amp;lt;colorscheme&amp;gt;    Set a colorscheme [default: default]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -i, --interface &amp;lt;interface&amp;gt;        The name of the network interface to show in the Net widget. &amp;#39;all&amp;#39; shows all&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                       interfaces [default: all]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -I, --interval &amp;lt;interval&amp;gt;          Interval in seconds between updates of the CPU and Mem widgets. Can specify&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                       either a whole number or a fraction with a numerator of 1 [default: 1]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;zenith&quot;&gt;zenith&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;rust-top-alternatives&#x2F;zenith.png&quot; alt=&quot;Screnshot of zenith&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bvaisvil&#x2F;zenith&quot;&gt;Repository&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Version tested:&lt;&#x2F;strong&gt; 0.7.5&lt;br &#x2F;&gt;
&lt;strong&gt;Runtime dependencies:&lt;&#x2F;strong&gt; None&lt;br &#x2F;&gt;
&lt;strong&gt;Lines of code:&lt;&#x2F;strong&gt; 2006&lt;br &#x2F;&gt;
&lt;strong&gt;Cargo dependencies:&lt;&#x2F;strong&gt; 105&lt;br &#x2F;&gt;
&lt;strong&gt;Stripped binary size:&lt;&#x2F;strong&gt; 2.6MiB&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;zenith&lt;&#x2F;code&gt; has a small number of key bindings for changing the panes. You can
navigate the process list with the arrow keys. The selection is not stable
across updates but the default update frequency is 2 seconds. Pressing Enter on
process shows expanded information about it and allows you to send it various
signals:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;rust-top-alternatives&#x2F;zenith-process.png&quot; alt=&quot;zenith process view&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Usage:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;zenith 0.7.5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Benjamin Vaisvil &amp;lt;ben@neuon.com&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Zenith, sort of like top but with histograms.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Up&#x2F;down arrow keys move around the process table. Return (enter) will focus on a process.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Tab switches the active section. Active sections can be expanded (e) and minimized (m).&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Using this you can create the layout you want.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;USAGE:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    zenith [FLAGS] [OPTIONS]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;FLAGS:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        --disable-history    Disables history when flag is present&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -h, --help               Prints help information&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -V, --version            Prints version information&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;OPTIONS:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -c, --cpu-height &amp;lt;INT&amp;gt;        Height of CPU&#x2F;Memory visualization. [default: 10]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        --db &amp;lt;STRING&amp;gt;             Database to use, if any. [default: &#x2F;home&#x2F;wmoore&#x2F;.zenith]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -d, --disk-height &amp;lt;INT&amp;gt;       Height of Disk visualization. [default: 10]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -n, --net-height &amp;lt;INT&amp;gt;        Height of Network visualization. [default: 10]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -p, --process-height &amp;lt;INT&amp;gt;    Min Height of Process Table. [default: 8]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -r, --refresh-rate &amp;lt;INT&amp;gt;      Refresh rate in milliseconds. [default: 2000]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;bb&quot;&gt;bb&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;rust-top-alternatives&#x2F;bb.png&quot; alt=&quot;Screnshot of bb&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;epilys&#x2F;bb&quot;&gt;Repository&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Version tested:&lt;&#x2F;strong&gt; git 35c3017&lt;br &#x2F;&gt;
&lt;strong&gt;Runtime dependencies:&lt;&#x2F;strong&gt; None&lt;br &#x2F;&gt;
&lt;strong&gt;Lines of code:&lt;&#x2F;strong&gt; 8450&lt;br &#x2F;&gt;
&lt;strong&gt;Cargo dependencies:&lt;&#x2F;strong&gt; 27&lt;br &#x2F;&gt;
&lt;strong&gt;Stripped binary size:&lt;&#x2F;strong&gt; 534KiB&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;bb&lt;&#x2F;code&gt; is closer to regular &lt;code&gt;top&lt;&#x2F;code&gt; than the other tools. It shows a CPU histograms
and a process list.  It has the best process view though, allowing sending all
named signals, filtering the list by name or pid, toggleable tree view and
following a process group. It also has the fewest crate dependencies and
smallest binary. The drawback is the author describes it as, “a “weekend”
side-project made for fun“, and it hasn’t seen any updates since Nov 2019.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Usage:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;bb&lt;&#x2F;code&gt; does not have any command line arguments.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;test-notes&quot;&gt;Test Notes&lt;&#x2F;h3&gt;
&lt;p&gt;The dependency count was calculated using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sfackler&#x2F;cargo-tree&quot;&gt;cargo-tree&lt;&#x2F;a&gt; as follows:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cargo tree --no-dev-dependencies --no-indent -q | sed &amp;#39;s&#x2F; (\*)$&#x2F;&#x2F;&amp;#39; | sort -u | wc -l&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The lines of code values were calculated using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;XAMPPRocky&#x2F;tokei&quot;&gt;tokei&lt;&#x2F;a&gt;. The value of the Code
column in the Total row from the output of &lt;code&gt;tokei src&lt;&#x2F;code&gt; was used. E.g. 2372 in
the output below:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-------------------------------------------------------------------------------&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; Language            Files        Lines         Code     Comments       Blanks&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-------------------------------------------------------------------------------&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; JSON                    5          105          105            0            0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; Markdown                2          231          231            0            0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; Rust                   18         2404         2001           87          316&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; TOML                    2           39           35            2            2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-------------------------------------------------------------------------------&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; Total                  27         2779         2372           89          318&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-------------------------------------------------------------------------------&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</description>
    </item>
    
    <item>
      <title>New Design 2020</title>
      <pubDate>Mon, 27 Jan 2020 15:42:29 +1100</pubDate>
      <atom:published>2020-01-27T15:42:29+11:00</atom:published>
      
      <author>wes@wezm.net (Wesley Moore)</author>
      <link>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;new-design&#x2F;</link>
      <guid>https:&#x2F;&#x2F;www.wezm.net&#x2F;v2&#x2F;posts&#x2F;2020&#x2F;new-design&#x2F;</guid>
      <description>&lt;p&gt;It’s been more than 10 years since I started working on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.wezm.net&#x2F;technical&#x2F;2010&#x2F;07&#x2F;new-design&#x2F;&quot;&gt;the previous design&lt;&#x2F;a&gt;
for this website 😅. This feels like a good point to come up with a new one!&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;The previous design served me well. The uncluttered design focussed on text was
fast and responsive. It saw the introduction of new devices like iPad and a
gradual increase in mobile display size without needing updating. The new
design aims to retain these features while giving it a lighter, more modern
feel.&lt;&#x2F;p&gt;
&lt;p&gt;Inspired by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;v4.chriskrycho.com&#x2F;2019&#x2F;my-final-round-of-url-rewrites-ever.html&quot;&gt;Chris Krycho&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20160422175043&#x2F;http:&#x2F;&#x2F;shauninman.com&#x2F;archive&#x2F;2006&#x2F;12&#x2F;04&#x2F;the_original_heap&quot;&gt;Shaun Inman&lt;&#x2F;a&gt; I’ve taken to versioning the
website instead of attempting to port all the existing content over to the new
technology. This makes the redesign more of a clean slate and leaves old posts
appearing how the did when originally posted. The new site is hosted under the
&lt;code&gt;&#x2F;v2&#x2F;&lt;&#x2F;code&gt; prefix.  This allows all existing pages to stay where they are and
retains the &lt;code&gt;www.wezm.net&lt;&#x2F;code&gt; domain. Compared to using a sub-domain it doesn’t
mess with DNS or search ranking. I have put redirects in place to direct the
RSS feeds from the previous version to the new feed.&lt;&#x2F;p&gt;
&lt;p&gt;The new design uses the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;manropefont.com&#x2F;&quot;&gt;Manrope&lt;&#x2F;a&gt; variable font for all text. Variable fonts
are a fairly recent addition to the web platform but they have good support
from fairly recent versions of all modern browsers and operating systems. On
older browsers&#x2F;operating systems the layout will fall back to a sans-serif font.
Webfonts generally come with a non-trivial download cost. However, Manrope is
108kB and being a variable font that includes all weights between 200 and 800,
as well as italic!&lt;&#x2F;p&gt;
&lt;p&gt;Technology wise, the previous site was built with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nanoc.ws&#x2F;&quot;&gt;Nanoc&lt;&#x2F;a&gt;, a Ruby static site
compiler. I’ve been very happy with Nanoc over the years but as my programming
interests have shifted away from Ruby to Rust I’ve wanted to try a Rust static
site compiler.  I’m now using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt;. Zola is perhaps not quite as flexible as
Nanoc but I’ve been able to achieve everything I wanted to with it.  It’s super
fast and has nice conveniences like live-reload when editing content. Being a
single file native binary also makes installation a breeze — no need to juggle
Ruby versions or install gems.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, I’ve now made &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wezm&#x2F;wezm.net&quot;&gt;the repository&lt;&#x2F;a&gt; that the site is generated from
public. This is to allow others to see how the site is built and permit
corrections&#x2F;fixes via issue or pull request.&lt;&#x2F;p&gt;
</description>
    </item>
    
  </channel>
</rss>
