<rss version="2.0">
  <channel>
    <title>Long Form Posts on Leon Mika</title>
    <link>https://lmika.org/categories/long-form-posts/</link>
    <description></description>
    
    <language>en</language>
    
    <lastBuildDate>Tue, 05 May 2026 20:58:38 +1000</lastBuildDate>
    
    <item>
      <title>Release: Dequoter 0.1.9</title>
      <link>https://lmika.org/2026/05/05/release-dequoter.html</link>
      <pubDate>Tue, 05 May 2026 20:58:38 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/05/05/release-dequoter.html</guid>
      <description>&lt;p&gt;I figured it was time to start releasing some of the small apps I make to assist me. I&amp;rsquo;ll start with Dequoter, one of the smaller ones. Until today, it was not in a fit enough state to release: the code wasn&amp;rsquo;t signed, there was no app icon, and certain niceties like dark mode were not working. Those have been addressed, and while I&amp;rsquo;m not claiming this app to be a marvel of software engineering, I&amp;rsquo;m releasing it anyway on the off chance that someone else would find it useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Download:&lt;/strong&gt; &lt;a href=&#34;https://lmika-public-files.s3.ap-southeast-2.amazonaws.com/Apps/Dequoter/0.1.9/Dequoter-0.1.9.app.zip&#34;&gt;Dequoter 0.1.9&lt;/a&gt; for MacOS&lt;/p&gt;
&lt;h2 id=&#34;about&#34;&gt;About&lt;/h2&gt;
&lt;p&gt;So what is Dequoter? It&amp;rsquo;s a single buffer text processor, where you can write or paste in text, perform some operation on it — like down-case — and then use that text elsewhere. It&amp;rsquo;s somewhat similar to Boop if you&amp;rsquo;re familiar with that. I built it mainly to convert quoted JSON strings into formatted JSON text that I could read (that&amp;rsquo;s where the name came from), and over time I added a few other processors that I found useful. It&amp;rsquo;s not as full featured as Boop, and realistically I probably could&amp;rsquo;ve just implemented what I needed as a Boop extension. But I had a few weekends free, and I figured it would be a small thing I could start and just improve over time. Plus it has a few niceties that Boop doesn&amp;rsquo;t offer, like multi-cursor support.&lt;/p&gt;
&lt;h2 id=&#34;usage&#34;&gt;Usage&lt;/h2&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/out-20260505-205025.png&#34; width=&#34;600&#34; height=&#34;478&#34; alt=&#34;Auto-generated description: A software interface displays a dropdown menu with options for converting and formatting text, alongside a text area containing placeholder text and a JSON snippet.&#34;&gt;
&lt;p&gt;Launching the app will bring you into a text buffer. Write or paste in text from anywhere. To perform an operation, press &lt;kbd&gt;Cmd&lt;/kbd&gt;+&lt;kbd&gt;P&lt;/kbd&gt;. This will bring up a picker of processors you can invoke. Start typing to filter and press &lt;kbd&gt;Enter&lt;/kbd&gt; to invoke the one that appears on the top. The processor will apply to the contents of the buffer, or if any text is selected, it will only apply to the selected text.&lt;/p&gt;
&lt;p&gt;The processor name gives a hint as to how the processor handles the text, or &amp;ldquo;input&amp;rdquo;. For example, processors beginning with &lt;code&gt;Lines&lt;/code&gt; will operate on a per-line basis, where-as &lt;code&gt;String&lt;/code&gt; will operate on the input as a whole. Some, like &lt;code&gt;Lines: Count&lt;/code&gt; will actually calculate a value, which will appear in the status bar. Others, like &lt;code&gt;Generate: Lorem Ipsum&lt;/code&gt; won&amp;rsquo;t take any input at all, and will instead produce text at the caret. Processors that have a trailing &lt;code&gt;…&lt;/code&gt; will require an additional argument, that you&amp;rsquo;ll be prompted to provide when selecting.&lt;/p&gt;
&lt;p&gt;Undo is supported, so you can back out of any transformations by pressing &lt;kbd&gt;Cmd&lt;/kbd&gt;+&lt;kbd&gt;Z&lt;/kbd&gt;. To repeat the last invoked processor, press &lt;kbd&gt;Shift&lt;/kbd&gt;+&lt;kbd&gt;Cmd&lt;/kbd&gt;+&lt;kbd&gt;P&lt;/kbd&gt;.&lt;/p&gt;
&lt;p&gt;The rest is just the regular text editing features you know and love. The app uses Coding Mirror so if you know the key bindings for that, you&amp;rsquo;ll be right at home. Multi-caret support is enabled, and to spawn a new caret, press either &lt;kbd&gt;Opt&lt;/kbd&gt;+&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;Up&lt;/kbd&gt; or &lt;kbd&gt;Opt&lt;/kbd&gt;+&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;Down&lt;/kbd&gt;.&lt;/p&gt;
&lt;h2 id=&#34;tech-stack&#34;&gt;Tech Stack&lt;/h2&gt;
&lt;p&gt;If it isn&amp;rsquo;t obvious, this is not a Mac-arsed Mac app. This was built using &lt;a href=&#34;https://wails.io&#34;&gt;Wails&lt;/a&gt;, which could be described as Electron for Go. However, the apps built with Wails used the built-in HTML renderer, rather than bundle Chromium, so they end up being a fair bit smaller. The text editor used is &lt;a href=&#34;https://codemirror.net/&#34;&gt;Code Mirror&lt;/a&gt;. Much of the core is hand rolled, although I&amp;rsquo;ve started using coding agents for the more recent changes, especially the processors. The logo was made using Affinity Designer.&lt;/p&gt;
&lt;p&gt;Anyway, if you&amp;rsquo;re looking for a simple text processor, give this a try. I hope you find it useful.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Could Swift&#39;s Guard Statement Work in Go?</title>
      <link>https://lmika.org/2026/04/30/could-swifts-guard-statement-work.html</link>
      <pubDate>Thu, 30 Apr 2026 22:26:05 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/04/30/could-swifts-guard-statement-work.html</guid>
      <description>&lt;p&gt;While writing yet another &lt;code&gt;if&lt;/code&gt; block to check whether a map had a value, I asked myself a question: could Swift&amp;rsquo;s  &lt;code&gt;guard&lt;/code&gt; statement work in Go?&lt;/p&gt;
&lt;p&gt;Go is such an &lt;code&gt;if&lt;/code&gt; heavy language. You&amp;rsquo;re adding &lt;code&gt;if&lt;/code&gt; statements pretty much everywhere: after you receive an error, after you do a type assertion, when you want to verify that a map has a value. All of these come to you in the form of tuples, with the last one being the value to switch on:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;thing&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;doSomething&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Having a way to switch like this within an expression would be amazing. It could be an operator that would strip out the last value and pass it through to an &lt;code&gt;else&lt;/code&gt; block when it&amp;rsquo;s either non-nil (for errors) or false (for bools). Otherwise, it would simply return everything else:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;thing&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;guard&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;doSomething&lt;/span&gt;() } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; { &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I believe this is achievable in Swift using &lt;code&gt;guard&lt;/code&gt; or &lt;code&gt;guard let&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;guard&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; thing = doSomething() &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; { &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But thinking about it for more than a few minutes, I can&amp;rsquo;t really see this workable in Go. It&amp;rsquo;s easy enough for Swift to have this, with it&amp;rsquo;s nullability baked into the type system. If a value is either something or nothing, then simply returning that something as the result of an expression is a natural thing to do.&lt;/p&gt;
&lt;p&gt;And because there&amp;rsquo;s only one variable, there&amp;rsquo;s no need to do things like declare that &lt;code&gt;err&lt;/code&gt; is the variable to receive the last parameter. That&amp;rsquo;s right, there&amp;rsquo;s no variable declaration of &lt;code&gt;err&lt;/code&gt; in that &lt;code&gt;else&lt;/code&gt; branch in that first example. Could you assume that the variable name would be &lt;code&gt;err&lt;/code&gt; if the last tuple value is of type &lt;code&gt;error&lt;/code&gt; (likewise, for &lt;code&gt;bool&lt;/code&gt; types, the implicit name would be &lt;code&gt;ok&lt;/code&gt;)? Well, you could, but it&amp;rsquo;d be pretty ugly. You&amp;rsquo;re now relying on knowledge not evident in the code, a property that goes against Go&amp;rsquo;s effort to be clear.&lt;/p&gt;
&lt;p&gt;Another problem is the use of blocks over function calls. In Go, the &lt;code&gt;defer&lt;/code&gt; and &lt;code&gt;go&lt;/code&gt; statements actually takes a function call. The semantics of a regular function call are the same, it&amp;rsquo;s just when the function is dispatched that differs. One could argue that in order to keep the design consistent, a &lt;code&gt;guard&lt;/code&gt; statement would need similar semantics: a function which returns at least two results. This is probably not a deal breaker, but it would be a little limiting, and would break the ability to nest these in a single expression without making lambdas.&lt;/p&gt;
&lt;p&gt;The closest plausible solution I could come up with is a new statement that uses this property, and combines it with an assignment and &lt;code&gt;else&lt;/code&gt; block. Something along the lines of:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;guard&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;thing&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;doSomething&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; { &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While this looks like it includes a regular assignment, its presence will actually be baked into the syntax, and would be used to declare the variables that would accept the function result. Once called, and the tuple returned, the statement will assign it to the declared variables, and branch to &lt;code&gt;else&lt;/code&gt; based on the last value&amp;rsquo;s type. If it&amp;rsquo;s &lt;code&gt;error&lt;/code&gt;, it will branch when it&amp;rsquo;s non-nil; if it&amp;rsquo;s &lt;code&gt;bool&lt;/code&gt;, it&amp;rsquo;ll branch when it&amp;rsquo;s false (anything else will be a type error). If the branch isn&amp;rsquo;t taken, the other variables will be available to anything beyond it (this is one of the key differences with the existing assignment based &lt;code&gt;if&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Because the assignment is required to declare the necessary variables, this has to be a statement, and cannot be nested. Such limitation feels critical to the usefulness of this, and if proposed, its worth in the language would probably be questioned. That&amp;rsquo;s a lot of new syntax for something that&amp;rsquo;s marginally better than an &lt;code&gt;if&lt;/code&gt; block.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;m doubtful something like this will see its presence in Go anytime soon. But it&amp;rsquo;s been an interesting exercise.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Imagen</title>
      <link>https://lmika.org/2026/04/26/devlog-imagen.html</link>
      <pubDate>Sun, 26 Apr 2026 11:18:46 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/04/26/devlog-imagen.html</guid>
      <description>&lt;p&gt;I won&amp;rsquo;t bore you with any justification on why I actually built this thing. It&amp;rsquo;s really nothing more than wanting a harness to play with Google&amp;rsquo;s Nano Banana. Up until now, I&amp;rsquo;ve been using Blogging Tools for that, but it lacked any ability to request changes to images in a chat-like interface (so called &amp;ldquo;multi-step flows&amp;rdquo;), where the context is preserved. So I made Imagen as a replacement.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/cleanshot-2026-04-26-at-11.00.58.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The landing page, showing current chat sessions and &amp;#39;assets&amp;#39; (uploaded and generated images).
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/cleanshot-2026-04-26-at-11.01.22.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  An example of a chat session, showing the meta-commands, plus messages from the user and model.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/cleanshot-2026-04-26-at-11.01.29.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Clicking &amp;#39;New Chat&amp;#39; will show you this: a prompt window along with a way to upload images.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/cleanshot-2026-04-26-at-11.01.40.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Chat management section, allowing you to archive or delete any chats.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/cleanshot-2026-04-26-at-11.01.49.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Clicking the gear icon brings you to the admin section, allowing you to revoke passkeys and end your session.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;This is a pretty typical chat-based AI model harness. You start a chat requesting an image, maybe uploading one you want modified, click &amp;ldquo;Send&amp;rdquo;, and wait for the model to respond.  There also exist a bunch of meta commands, entered using the &lt;code&gt;/&lt;/code&gt; prefix, that allow you change the model, and retry or undo the last request.&lt;/p&gt;
&lt;p&gt;Chats and assets are stored as regular files (assets as regular image files, chats as a Gob-serialised history of the chat) and metadata is managed by a Sqlite3 database. In terms of the tech stack, I&amp;rsquo;ve settled on a mix that works well for me. A Go backend, using &lt;a href=&#34;https://gofiber.io&#34;&gt;Fiber&lt;/a&gt; as the web middleware, and Go templates for server-side rendering. I eschewed Stimulus.js for this one though, deciding to try out &lt;a href=&#34;https://htmx.org&#34;&gt;HTMX&lt;/a&gt;. And it&amp;rsquo;s a unique way of working. I learnt that to really get the most out of HTMX, you really need to find ways not to use JavaScript to do something.&lt;/p&gt;
&lt;p&gt;The chat interface is a good example. While a more traditional frontend would post a message as JSON and rerender the page with a JSON response, posting a chat message here would return a server-side HTML render of the chat interface, which HTMX will swap out. This also include things like the message form itself, which would be rendered disabled from the server template, rather than being controlled by JavaScript. The good thing about that is that if you were to hard-refresh the page, it will appear disabled. No need to run JavaScript on load to detect the state and disable the form: the HTML is the state. As for getting updates while waiting for a message, well that&amp;rsquo;s just a poll. Nothing too sophisicated here.&lt;/p&gt;
&lt;p&gt;As you can probably guess, much of this was hand-rolled. I did use coding agents to assist with making the passkey authentication flow, the meta commands, and the OpenAI API integration. While I probably could&amp;rsquo;ve done the last two myself, if there was any justification for using coding agents for anything it was probably adding passkeys. The complexity involved with the passkey integration probably justified the use . And this is probably one area of AI-generated code that I&amp;rsquo;m more likely to trust the model over my own shoddy code.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Other Project Updates</title>
      <link>https://lmika.org/2026/03/29/devlog-other-project-updates.html</link>
      <pubDate>Sun, 29 Mar 2026 10:17:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/29/devlog-other-project-updates.html</guid>
      <description>&lt;p&gt;A few updates of some other projects I worked on recently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Webtools&lt;/strong&gt; saw some love as I needed some tooling made to make the icon easy to include in the &lt;a href=&#34;https://devlog.lmika.org/categories/well-read/&#34;&gt;Well Read&lt;/a&gt; Flutter project itself. Android expects the logo of a specific size, so I &amp;ldquo;commissioned&amp;rdquo; an &lt;a href=&#34;https://tools.lmika.app/android-icons/&#34;&gt;Android Icon Resizer&lt;/a&gt;, which will take one or more PNG files, resize them to what Android expects, and prepare them in a ZIP that could be extracted at the route of the &lt;code&gt;res/mipmap&lt;/code&gt; directory. It will also produce a small preview of the icon, rendering it in a circle so you can see how it looks on the device. It&amp;rsquo;s background savvy, layering the icon over the PNG with &amp;ldquo;background&amp;rdquo; in the filename.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/s0jaggyk13u39x7e.png&#34; width=&#34;600&#34; height=&#34;501&#34; alt=&#34;Auto-generated description: An Android Icon Resizer tool interface is displayed, allowing users to choose PNG files and preview a circular crop of selected images.&#34;&gt;
&lt;figcaption&gt;The Icon Resizer tool.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The background for Well Read is a gradient, so I also added a simple &lt;a href=&#34;https://tools.lmika.app/gradient-image/&#34;&gt;gradient maker&lt;/a&gt; to Webtools too. Finally, there was a need to resize the logo without resizing the image canvas size itself so that it looked good in the cut-out circle. This required the commission of the &lt;a href=&#34;https://tools.lmika.app/image-inner-resize/&#34;&gt;image inner resize&lt;/a&gt; tool.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/kukrpoflwwgn4or-.png&#34; width=&#34;600&#34; height=&#34;473&#34; alt=&#34;Auto-generated description: A gradient generation web page features a gradient preview, color selectors, gradient type options, rotation settings, image size dropdown, and a blue download button.&#34;&gt;
&lt;figcaption&gt;The gradient tool.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It&amp;rsquo;s true I could&amp;rsquo;ve used any image editor to do this, but none of them were running and Claude was, so I figured it was easier doing it this way at the time.&lt;/p&gt;
&lt;p&gt;I did get my hands a little more dirty with &lt;strong&gt;UCL&lt;/strong&gt;. I was making some changes to Dequoter to allowed users to perform UCL transformations, and I needed a way for the user to reference the current line. UCL has pseudo-variables, so &lt;code&gt;@line&lt;/code&gt; was possible here. But I was finishing of a Go template transformation, and much like those templates, I was wondering if a simple dot could work: &lt;code&gt;add . 2&lt;/code&gt;, for example. This was a little larger than expected, as it did require changes to the AST, but it was simple enough to knock out in an hour. The host sets the value of dot via the pseudo-variable &lt;code&gt;.&lt;/code&gt;, and it even supports direct sub-indexing at the syntax level: &lt;code&gt;add .item.(0) 2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Oh, and &lt;strong&gt;Dequoter&lt;/strong&gt; now allows actions to specify input values. Here it is being used for a new UCL transform action, which will map each line of the input to the result of a UCL expression.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/a-axir-c6rw50apr.png&#34; width=&#34;600&#34; height=&#34;474&#34; alt=&#34;Auto-generated description: A software interface displays a command line entry with a suggestion tooltip that reads add .2.&#34;&gt;
&lt;figcaption&gt;The action input prompt.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;So, quite a busy week few weeks. Not sure what I&amp;rsquo;ll work on next. I do have an inkling of going back to that Godot project that&amp;rsquo;s been on the back-burner for about a month.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: gRPC Client - A Vibe-coded Client for Testing REST/gRPC Endpoints</title>
      <link>https://lmika.org/2026/03/29/devlog-grpc-client-a-vibecoded.html</link>
      <pubDate>Sun, 29 Mar 2026 09:56:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/29/devlog-grpc-client-a-vibecoded.html</guid>
      <description>&lt;p&gt;My current craving of vibe-coding various tools I need to do my job continues, with an attempt to build a REST/gRPC test client.&lt;/p&gt;
&lt;p&gt;This is motivated by my distaste with all the other clients I&amp;rsquo;ve tried. There&amp;rsquo;ve been a few, and I&amp;rsquo;ve been unhappy with each one. For one thing, they seem more heavyweight than my needs. I don&amp;rsquo;t know if this is just how they&amp;rsquo;re implemented, or it&amp;rsquo;s because the realm of HTTP request testing is complicated (It&amp;rsquo;s probably a bit of both).&lt;/p&gt;
&lt;p&gt;Another thing is that they&amp;rsquo;re tend to add features targeting testing teams that manage a central repository of test cases. I get the feeling that&amp;rsquo;s how most of them try to make money: offer the tool for free to get the foot in the door, then upsell them with a hosted test repository. This leaves half the interface either useless, or just bait for throwing up requests to setup a service account and pay them money.&lt;/p&gt;
&lt;p&gt;Finally, some are just awkward to use. The UI is inconsistent and buggy, and just gets in the way. I did wonder if the typical UI for these REST clients is partly to blame for this, but as you will see, it&amp;rsquo;s not like this project deviates from the mainstream, so I think it&amp;rsquo;s just comes down to implementation.&lt;/p&gt;
&lt;p&gt;Anyway, these frustrations, along with wanting to see what these coding agents are capable of, motivated me to  requested Claude Code to make me one. And it did a pretty decent job.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/wcshwyvqxu1ilkhp.png&#34; width=&#34;600&#34; height=&#34;400&#34; alt=&#34;Auto-generated description: A screenshot of a gRPC client interface showing request and response details, including headers and response data.&#34;&gt;
&lt;figcaption&gt;Main client area of gRPC Client.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It&amp;rsquo;s called gRPC Client — stellar naming, I know — and it&amp;rsquo;s pretty typical of most other REST/gRPC test clients out there. Request details on the left, the latest response on the right. A list of saved requests, along with a history, in the sidebar, that can be recalled and resent. There&amp;rsquo;s a reflection browser for gRPC endpoints, and a command for producing an example request for service methods, a feature of Insomnia that I really like.&lt;/p&gt;
&lt;p&gt;The right sidebar has three parts, of which the first is the ability to define environment values, which can be included in the request properties via Go templates (this is a Wails app with a Go backend and HTML+JS frontend).&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/6ae4qncxgixszmcz.png&#34; width=&#34;600&#34; height=&#34;400&#34; alt=&#34;Auto-generated description: A user interface for a gRPC client is displayed, showing API request and environment settings with options to edit user ID information.&#34;&gt;
&lt;figcaption&gt;Editing environment values. This modal is only really useful for large values, like JWT tokens, but values can also be modified from the sidebar itself.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;One feature of this client is the ability to override HTTP headers at the global level, the second thing in the right sidebar. When testing APIs, I usually have a collection of pre-made requests for endpoints I&amp;rsquo;m familiar with. Yet I&amp;rsquo;m always editing the headers when the user or auth token changes. The motivation with this feature was to make the change once, so that I don&amp;rsquo;t need to do so when I switch saved requests. I originally requested a way to do this for the hostname and port too, but I found this wasn&amp;rsquo;t as useful for me.&lt;/p&gt;
&lt;p&gt;In practice, I ended up simply using environment values and request scripts, so-called &amp;ldquo;Hooks&amp;rdquo;, which is the third thing in the right sidebar. This is some JavaScript that executes before each requests, allowing the user to modify the headers and body using the values from the environment. What I would do is set the username as an environment value, and a hook would set the HTTP header, and generate the payload for a JWT token, saving me from getting this from the browser (the endpoints I need to test just need the payload, not the signature, so this simply involvers marshalling a JSON object to Base64).&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/llsonbzzizbjy-tl.png&#34; width=&#34;600&#34; height=&#34;400&#34; alt=&#34;Auto-generated description: A user interface displays a gRPC client with a pop-up window for editing a hook script.&#34;&gt;
&lt;figcaption&gt;Editing a hook. The placeholder gives a good example of what is supported.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;These are all features of most REST clients out there, but they&amp;rsquo;re always hidden away and difficult to modify, which means I&amp;rsquo;m never using them. Placing them in the main client area makes them front and centre, so to speak, and so much easier to change.&lt;/p&gt;
&lt;p&gt;One other thing I requested was the use of &lt;a href=&#34;https://github.com/tailscale/hujson&#34;&gt;HuJSON&lt;/a&gt; for the request body. It&amp;rsquo;s a little crazy to think that all these REST/gRPC clients, designed for use by humans, require their users to write perfect JSON for the body, especially when I&amp;rsquo;m left wanting to comment out parts of the body as I test something. This gets translated to JSON when the request is being sent.&lt;/p&gt;
&lt;p&gt;So all in all, it&amp;rsquo;s a pretty decent client, and despite it being like many other clients out there, it&amp;rsquo;s pleasant to use. And I can&amp;rsquo;t really put my finger on why. Might be that it&amp;rsquo;s just simpler than all the other ones out there. It doesn&amp;rsquo;t have tabs, for example, which does making testing multiple endpoints a little annoying, and I&amp;rsquo;m wondering if it&amp;rsquo;s worth asking Claude to add them. Yet at the same time, the frustration I had with other clients came about with their implementation of tabs. Likewise for saved requests. This just has a single list of saved requests that are either REST or gRPC requests. No need to worry about folders, or request types I don&amp;rsquo;t use, like GraphQL.&lt;/p&gt;
&lt;p&gt;I guess if you&amp;rsquo;re vibe coding something for your own use, less is indeed more.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Weiro - Categories, Pages, and Upload Editing</title>
      <link>https://lmika.org/2026/03/29/devlog-weiro-categories-pages-and.html</link>
      <pubDate>Sun, 29 Mar 2026 08:53:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/29/devlog-weiro-categories-pages-and.html</guid>
      <description>&lt;p&gt;Some more work on Weiro. Much of it is pretty mundane, mainly to get it to feature parity with other CMS&amp;rsquo;s out there. Yes, I know the existence of those other CMS&amp;rsquo;s make the entire project pointless. Doubly so when you consider that much of what I&amp;rsquo;m going to talk about was largely done by coding agents. It made me wonder whether it was worth writing this update at all. Well, it&amp;rsquo;s drafted up already so I may as well finish it off. At least one thing will get finished.Okay, enough wallowing in my self-doubt. What was added? Well, categories are now a thing. These can be defined in a new categories section and consist of a label, a slug, and a description. Going to &lt;code&gt;/categories/&amp;lt;slug&amp;gt;&lt;/code&gt; will list all the posts with that category. Posts can be in zero or more categories, which can be selected from the edit post screen. Pretty simple stuff.&lt;figure&gt;&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/m7rczzbd0bdam8jj.png&#34; width=&#34;600&#34; height=&#34;417&#34; alt=&#34;Auto-generated description: A webpage displays a list of categories with their names, slugs, and post counts, along with navigation options like New Category and settings.&#34;&gt;&lt;figcaption&gt;The Categories section, where the user can manage categories.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/cslhwkomkhk8rycc.png&#34; width=&#34;600&#34; height=&#34;417&#34; alt=&#34;Auto-generated description: An online interface is displayed, showing fields to edit a category, including Name, Slug, and Description, with Save and Delete buttons.&#34;&gt;&lt;figcaption&gt;Editing a category.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/62aswp-h8po2jaa1.png&#34; width=&#34;600&#34; height=&#34;417&#34; alt=&#34;Auto-generated description: A blog post interface is displayed with a text editor on the left featuring a draft update, and categories on the right labeled Well Read.&#34;&gt;&lt;figcaption&gt;Selecting categories from within the edit post screen.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/nuoc2szdzv-3qalp.png&#34; width=&#34;600&#34; height=&#34;417&#34; alt=&#34;Auto-generated description: A webpage titled Devlog displays categories with links to Weiro and Well Read, indicating it&#39;s under construction.&#34;&gt;&lt;figcaption&gt;The categories page as it appears on the published site.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/nnljqeqf1-gd7lav.png&#34; width=&#34;600&#34; height=&#34;417&#34; alt=&#34;Auto-generated description: A webpage titled Devlog features a blog post about Weiro, a blogging CMS for hosting via Netlify, with an update dated 6th March 2026.&#34;&gt;&lt;figcaption&gt;The single category page.&lt;/figcaption&gt;&lt;/figure&gt;Another small thing added was pages. This allows the user to define &amp;ldquo;slash&amp;rdquo; pages, with the option of appearing in the nav bar, and can also be used to replace the home page, by setting the slug to &lt;code&gt;/&lt;/code&gt; (the posts are still available at &lt;code&gt;/posts&lt;/code&gt;). I do need to spend some more time figuring out how to organise the nav bar as I would also like to include things like redirects. I thinking of making that an extension of the pages model, but I&amp;rsquo;m not sure.&lt;figure&gt;&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/oe-1ncpvxjkhlomh.png&#34; width=&#34;600&#34; height=&#34;417&#34; alt=&#34;Auto-generated description: A webpage is displayed showing a simple layout with options for creating a new page and a table with columns labeled Title, Slug, and Nav.&#34;&gt;&lt;figcaption&gt;List of pages of a site.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/tbd-bczthswmloip.png&#34; width=&#34;600&#34; height=&#34;417&#34; alt=&#34;Auto-generated description: A webpage editor is displaying text input fields for creating an About page with options for page settings and navigation.&#34;&gt;&lt;figcaption&gt;Editing a page. There&amp;rsquo;s only a single page type at the moment.&lt;/figcaption&gt;&lt;/figure&gt;Those two features were mainly done with the help of Claude Code, but I did build some stuff manually. The largest addition was the ability to do some edits on uploaded images. I&amp;rsquo;ve never been a fan of how Apple produces the shadows around windows: the margins are just too large. So I added a way to do this within Weiro itself.&lt;figure&gt;&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/mkmzi73t1xlfyv7j.png&#34; width=&#34;600&#34; height=&#34;439&#34; alt=&#34;Auto-generated description: A web interface is displaying settings for editing a category in a blogging CMS.&#34;&gt;&lt;figcaption&gt;The edit upload feature.&lt;/figcaption&gt;&lt;/figure&gt;It&amp;rsquo;s pretty simple. Just imagine the filter section from any image editor, then remove all the other features of that editor. Yeah, it&amp;rsquo;s &lt;em&gt;that&lt;/em&gt; simple. There&amp;rsquo;s no cropping, rotating, or anything else of that nature: just a bunch of &amp;ldquo;processors&amp;rdquo; that you can add to the image, with the sole one being a drop shadow.This is actually done server side using a simple file-based approach. When opening an image upload to edit, you spawn a session. Each session has a JSON file maintaining the processors, plus a series of cached image files of all the intermediate steps along the processing chain. When a new processor is added, a hash is computed with the processor&amp;rsquo;s properties, and if they change, the cached image will be regenerated. The processor includes the hash of the previous step too, so that if processors further up the chain are modified or remove, they will force a recompute of subsequent images. The user is then served the last image in the chain.
This differs from the image processor in Blogging Tools, which performed the transformations within the browser itself. The motivation there was to avoid the need of a slow upload of the image, but it came with the performance cost associated with doing the processing within the browser itself. WASM might be fast, but it&amp;rsquo;s not &lt;em&gt;that&lt;/em&gt; fast[^fast]. Since the upload in Weiro is already uploaded, I figured it would be quicker to just do the image processing server side. My hope is that the computing power the server has access to would offset the time it takes to download the image. So far this seems to be the case.Finally, I did some minor work around the UI, adding a site chooser and a much needed way to open the published site from the admin section.So this project is still coming along, surprisingly. It&amp;rsquo;s probably the furthest I&amp;rsquo;ve got in a blogging CMS that I actually want to use. I do have a large list of things I want to add to it, and I certainly need to do something with the design of the actual site. It&amp;rsquo;s all a question of whether I&amp;rsquo;m interested in spending time on it.[^fast]: Although to be fair, I think the slow down comes from encoding the processed image as a data URI and setting it as the source of an &lt;code&gt;img&lt;/code&gt; tag.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Replacing Ear Cups on my Sennheniser HD 280 Pro Headphones</title>
      <link>https://lmika.org/2026/03/28/replacing-ear-cups-on-my.html</link>
      <pubDate>Sat, 28 Mar 2026 10:39:19 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/28/replacing-ear-cups-on-my.html</guid>
      <description>&lt;p&gt;The padding of one of my ear cups of my Sennheniser HD 280 Pro headphones was starting to wear out. I can&amp;rsquo;t remember how long I&amp;rsquo;ve had these headphones for but it must have been close to a decade, so to have it last as long as it did is pretty good. I use these headphones pretty much every day, and I have no desire of replacing it. But the old padding had to go, so last week I bought a new set and this morning I set about trying to get it on.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/pxl-20260327-220857995.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: Headphones with worn and frayed ear cushions are resting on a textured surface.&#34;&gt;
&lt;figcaption&gt;The &#39;before&#39; shot, with the damaged ear padding.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It was not easy. Unlike the Bluetooth headphones I had, the replacement ear pads didn&amp;rsquo;t have adhesive strips that you could just stick on. Instead, there&amp;rsquo;s a vinal lip that you have to thread into the gap that rings the ear cup. &lt;a href=&#34;https://www.youtube.com/watch?v=g79BBFDdXTs&#34;&gt;This YouTube video&lt;/a&gt; gives a good explanation of what that entails.&lt;/p&gt;
&lt;p&gt;But first you had to get the old ear cup off. This was simply a matter of tearing it off, but just be sure to tear the whole thing off, right down until you get to the actual speaker, which is a flat surface with two screws. If you don&amp;rsquo;t, you won&amp;rsquo;t be able to get the replacement padding on. There is almost zero slack with the padding, and if any part of the old padding is still there, it won&amp;rsquo;t fit. I spent about an hour trying to thread the damn thing into the gap before I realised I had more to remove.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/pxl-20260327-230539357.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A disassembled set of over-ear headphones lies on a table with screws and a screwdriver nearby.&#34;&gt;
&lt;figcaption&gt;Successfully getting the replacement padding on before realising that I couldn&#39;t access the screws to reattach the speaker.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;One thing making it difficult was that the entire headphone was getting in my way. I had the genius plan of unscrewing the speaker from the rest of the body first, just so I could move it around easier. And yeah, it was easier, and I managed to get the padding on, but then I realised that I couldn&amp;rsquo;t access the screws to attach the speaker back once I did. I wasted about 30 minutes all up from that, but it was probably a good idea after all, as I found a little more of the old padding under the lip that I had to remove.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/pxl-20260327-233039992.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A pair of black headphones with one ear cup detached and its cover removed sits on a light-colored surface.&#34;&gt;
&lt;figcaption&gt;With the last bit of the old padding removed, threading the replacement was significantly easier.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When I put the speaker back, I made sure to make the lip a little larger than before. With that little more space, along with a lot of effort trying to get the lip of the padding into the gap (using a technique of rolling the lip up so you could get it over the side), I finally managed to get the replacement padding on the cup.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/pxl-20260327-234536257.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A pair of black over-ear headphones is resting on a light gray surface.&#34;&gt;
&lt;figcaption&gt;Job done. The replacement pad is on the right. The left one is still good so I&#39;m leaving it alone for now.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;With the new padding on, and the speaker undamaged from my tampering, the headphones are good as new. There is also padding for the overhead bar which I need to replace, but I think I&amp;rsquo;ll leave that for later.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>TUIs Are Underrated</title>
      <link>https://lmika.org/2026/03/21/tuis-are-underrated.html</link>
      <pubDate>Sat, 21 Mar 2026 08:57:20 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/21/tuis-are-underrated.html</guid>
      <description>&lt;p&gt;I won&amp;rsquo;t bury the lead here: I think TUIs (Terminal User Interfaces) are underrated as a UI paradigm.&lt;/p&gt;
&lt;p&gt;Now I&amp;rsquo;m someone who likes a good TUI. I like using them. I like &lt;a href=&#34;https://dynamo-browse.lmika.dev&#34;&gt;building them&lt;/a&gt;. So I may have a slight bias here. But I&amp;rsquo;m wondering if TUIs are starting to see a bit of a resurgence, at least in software development. The modern example is Claude Code and it&amp;rsquo;s ilk, but it&amp;rsquo;s also seeing people using Vim and Emacs for their coding tasks. There are more than a few that are doing that where I work.&lt;/p&gt;
&lt;p&gt;And why not? I think there&amp;rsquo;s quite a lot to like about the humble TUI. By the act of being something that is invokable within the terminal, it has some pretty nice advantages over some other UIs other there:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It&amp;rsquo;s more approachable than a command based application, while still being cheaper to build than GUIs or web-based UIs. I&amp;rsquo;ve built a few tools at work that operate using a REPL, and after handing it over to others, I&amp;rsquo;ve heard complaints on how difficult they are to use. I&amp;rsquo;ve stared introducing TUIs after that, and they&amp;rsquo;ve told me they appreciate the simpler interaction model. They did profess their wish for a full-blown GUI which, for what these tools are, is something I can&amp;rsquo;t really justify spending the effort. But a TUI is rich enough for these tools made &amp;ldquo;on the side&amp;rdquo; without needing to spin up a full-on formal project.&lt;/li&gt;
&lt;li&gt;You can use it over SSH. This is probably only to those that work on Internet-based services, but if you&amp;rsquo;ve managing computers running somewhere else, you rarely have a GUI at your disposal. It&amp;rsquo;s either SSH, an ephemeral Kubernetes pod, or something else that only has a character-based UI.&lt;/li&gt;
&lt;li&gt;You get context built in when you invoke it from the terminal. Current directory, environment variables, files supplied by arguments. All that is available to the TUI command up front, since you as the user know where you invoked it.&lt;/li&gt;
&lt;li&gt;You get terminal features like tabs for free. The TUI may have something akin to tabs or buffers in it&amp;rsquo;s UI, but if not, no worries. Just open another tab in your terminal and invoke another instance.&lt;/li&gt;
&lt;li&gt;They can be faster than using a mouse. I say &amp;ldquo;can&amp;rdquo;, because this is usually only true if you know the TUI front-to-back.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, obviously this is not the best approach for everything. Graphics, music, visual design; in fact, pretty much anything that doesn&amp;rsquo;t deal heavily in text will not work with a TUI. And there&amp;rsquo;s usually a learning curve involved with any of these tools, and that&amp;rsquo;s usually only worth the investment if you know you&amp;rsquo;ll be using it often. I don&amp;rsquo;t subscribe to the notion that every app needs to be approachable to newcomers at the expense of expert users: some things in life, you just gotta put in the effort to learn it. But not everyone wants to do that for everything, and I can respect that. Hell, even I have a very surface understanding of Vim and probably wouldn&amp;rsquo;t turn to it for my coding needs like my colleagues have.&lt;/p&gt;
&lt;p&gt;So a TUI is not useful for every problem out there, but don&amp;rsquo;t rule them out completely. There&amp;rsquo;s still room in the problem spaces out there for apps with UIs made of line characters.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Well Read - One Week Later</title>
      <link>https://lmika.org/2026/03/17/devlog-well-read-one-week.html</link>
      <pubDate>Tue, 17 Mar 2026 20:34:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/17/devlog-well-read-one-week.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s been a week since I learnt about Inkwell&amp;rsquo;s API and got an agent to start work on an RSS reader. Since then, Well Read has been in a state of flux, as I ask for agents to make changes to the interaction and layout. I think I&amp;rsquo;ve got it in a pretty good state now, certainly in a state that works well for me.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/rggwaorys0kcw-bm.png&#34; width=&#34;600&#34; height=&#34;402&#34; alt=&#34;Auto-generated description: Three phone screens display different interfaces, including email and note-taking applications with visible text and icons.&#34;&gt;
&lt;figcaption&gt;The earliest screenshots of Well Read, taken on 11 March 2026&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The Today and Recent tabs still retain their original behaviour, although the idea of using 6:00 pm of the previous evening as the cutoff for &amp;ldquo;todays&amp;rsquo; posts&amp;rdquo; was a good one. Being where I&amp;rsquo;m located in the world, many people I follow publish posts while I&amp;rsquo;m asleep. I recently added a tab for bookmarks which uses Micro.blog&amp;rsquo;s bookmarking feature. Any post can be added as a bookmark, and since there&amp;rsquo;s no real way to get to a post once it drops out of the Recent tab (well, apart from opening it up in Inkwell proper), I have in mind this feature to act as a place to stash posts for later. So I tried making bookmarks as easy to add as possible. Sliding aside the item in the feed list will reveal a bookmark action, as will the overflow menu in the feed viewer.&lt;/p&gt;
&lt;p&gt;The app now has an icon too, which was made with the help of ChatGPT. I haven&amp;rsquo;t consulted the agents about a better name though.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/pyge70epfdvskrrd.png&#34; width=&#34;148&#34; height=&#34;148&#34; alt=&#34;Auto-generated description: An open book with an ink bottle and a Wi-Fi signal icon on its pages is depicted against a gradient background.&#34;&gt;
&lt;figcaption&gt;App logo of Well Read&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I did get my hands a little dirty to try and speed up the Webviewer. I think I made some success with removing the &lt;code&gt;max-resizing&lt;/code&gt; from the &lt;code&gt;viewport&lt;/code&gt; meta tag aded to the HTML the Webviewer opens. It could be psychosomatic, though. It feels a little different after I added it, but only if I&amp;rsquo;m using the actual phone. I see no performance issues in the simulator. I even tried comparing two screen recordings I made before an after the change, but I couldn&amp;rsquo;t see a different. Since then, the scrolling has become choppy again. Sigh. That&amp;rsquo;s one downside with choosing Flutter for this: you&amp;rsquo;re bound by the speed of the language runtime. Maybe if I got the coding agent to port it to plain Android… 🤔&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/bvycgp7ect8jftfx.png&#34; width=&#34;600&#34; height=&#34;402&#34; alt=&#34;Auto-generated description: Three screenshots showcasing different layouts of an email application, each displaying a list of emails, email details, or related interactions.&#34;&gt;
&lt;figcaption&gt;Screenshots of Well Read taken this evening&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;But the proof of any app is in the pudding: am I actually using it to read my RSS feeds? And the answer is yes, I am. It doesn&amp;rsquo;t do everything a full-featured RSS app does, but what it does it does reasonably well — jankiness Webviewer scrolling aside — and I find myself turning to it at lunch or during my commutes. And even though there aren&amp;rsquo;t any unread counts anywhere, it&amp;rsquo;s slightly ironic to find that this river-based pattern has turned me into a bit of a completeness, wanting to get through today&amp;rsquo;s feed before they drop over to recents. Oh I still save the odd post by keeping it marked as unread (the &amp;ldquo;Mark as Unread&amp;rdquo; action is optimised for this, in that it actually kicks you back to the post list when you use it), but not a frequently as I have been.&lt;/p&gt;
&lt;p&gt;Anyway, my need for changes for this app might settle a little as I just go about simply using it for a while. But with the use of these agents, it&amp;rsquo;s pretty amazing what they can be accomplish in a week.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Well Read - An Inkwell Client for Android</title>
      <link>https://lmika.org/2026/03/12/devlog-well-read-an-inkwell.html</link>
      <pubDate>Thu, 12 Mar 2026 06:22:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/12/devlog-well-read-an-inkwell.html</guid>
      <description>&lt;p&gt;When Manton mentioned that &lt;a href=&#34;https://help.micro.blog/t/feeds-syncing-with-inkwell/4255&#34;&gt;Inkwell has an API&lt;/a&gt;, I… um… may have vibe-coded an Android client.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/gdyts0xjbn7-uheb.png&#34; width=&#34;600&#34; height=&#34;639&#34; alt=&#34;Auto-generated description: Two smartphone screenshots display a reading list with articles and a detailed view of a specific article quoting John Carmack.&#34;&gt;
&lt;p&gt;It&amp;rsquo;s called &amp;ldquo;Well Read&amp;rdquo;, which is not a great name but better than &amp;ldquo;Inkwell Client&amp;rdquo; which was the working title. Much like Inkwell, it follows the river approach to RSS. There&amp;rsquo;s a Today tab and Recent tab, each showing a portion of the entries in reverse chronological order. Today shows all the posts from today, plus the last 6 hours of yesterday. The motivation here is that this will be the tab you&amp;rsquo;ll be spending your time, with posts aging out to recent over time. There&amp;rsquo;s no Fading tab: all posts older than about half a week fall out of the app. And I&amp;rsquo;m not sure if I&amp;rsquo;m going to add one. There&amp;rsquo;s only a few feeds that I want to catch up on if I miss their post, and since I&amp;rsquo;m using NetNewsWire and Inkwell, I&amp;rsquo;m pretty sure I&amp;rsquo;ll catch them later.&lt;/p&gt;
&lt;p&gt;At the moment this is pretty bare bones. There&amp;rsquo;s no bookmarks (yet), you can&amp;rsquo;t add feeds, there&amp;rsquo;s no search, and only a few actions you can perform on a post: copy the link, mark it as unread, and open it in the external browser. The webview itself is a little janky too, which is not great, and I need to do something about that as it does impact the reading experience.&lt;/p&gt;
&lt;p&gt;But so far it&amp;rsquo;s been pretty good. Feels the time when all I have is a phone to read stuff, like during lunch or while commuting.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Weiro - Update 6th March 2026</title>
      <link>https://lmika.org/2026/03/09/devlog-weiro-update-th-march.html</link>
      <pubDate>Mon, 09 Mar 2026 09:48:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/09/devlog-weiro-update-th-march.html</guid>
      <description>&lt;p&gt;A small update on Weiro. I&amp;rsquo;ve been working on it over the past week, trying to get it in a state that is pleasant to use. I&amp;rsquo;m been trying to get something halfway usable before doubt scuppers my motivation and this project appears on the growing list of aborted attempts at making a CMS. There&amp;rsquo;ve been one or two close calls, but it hasn&amp;rsquo;t caused me to stop yet.&lt;/p&gt;
&lt;p&gt;A large part of that was a feature I knew I wanted but was daunting to implement: uploads. The thought of writing the logic to manage large files, make sure EXIF data is stripped, and serve and manage them always seems like a pain. It&amp;rsquo;s the reason why I&amp;rsquo;ve abandoned CMS projects in the past. And I want something that support uploads: I&amp;rsquo;ve tried CMSes that didn&amp;rsquo;t have them and I never stayed long.&lt;/p&gt;
&lt;p&gt;But this time, I rolled up my sleeves, cracked open Claude, and told it to… no, I&amp;rsquo;m kidding. Much of the feature was hand rolled, although Claude helped with some of the plumbing of getting uploaded files. But it&amp;rsquo;s done: uploads are now supported. Which means screenshots:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/wrdzcj60vx1nkyui.png&#34; width=&#34;600&#34; height=&#34;423&#34; alt=&#34;Auto-generated description: A screenshot of a website interface showing a blog post titled Weiro - Update 6th March 2026 with a draft status indicator.&#34;&gt;
&lt;figcaption&gt;Main post screen, with this post shown as draft. The &#39;rebuild site&#39; is temporary at the moment.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/kavv7u9ugrazfpzk.png&#34; width=&#34;600&#34; height=&#34;423&#34; alt=&#34;Auto-generated description: A webpage is displayed featuring a blog post about a CSV Editor, including links, text, and a screenshot of a CSV tool.&#34;&gt;
&lt;figcaption&gt;The post editor. Pretty basic at the moment.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/ibatq9w6yc38hmor.png&#34; width=&#34;600&#34; height=&#34;423&#34; alt=&#34;Auto-generated description: A browser window displays an online platform featuring in-progress upload thumbnails and an Upload button.&#34;&gt;
&lt;figcaption&gt;The uploads list.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/29hzeboaipdylaou.png&#34; width=&#34;600&#34; height=&#34;423&#34; alt=&#34;Auto-generated description: A computer screen displays a web browser with a table showing various individuals&#39; names, emails, phones, and other personal details.&#34;&gt;
&lt;figcaption&gt;A single upload, allowing the user to copy the HTML to paste in the post.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Along with this are some updates to the published site. This I&amp;rsquo;m hand-rolling too, rather than relying on an existing SSG. Probably a mistake in the long run, but it does keep things flexible. There&amp;rsquo;s nothing flashy about the HTML version of the site right now. I&amp;rsquo;m using &lt;a href=&#34;https://simplecss.org&#34;&gt;Kev Quirk&amp;rsquo;s awesome Simple.css&lt;/a&gt;, that I use as a base style for most things nowadays. But I have added an RSS and JSON feed. Two, in fact. One is a standard RSS feed for the site, and another is designed from crossposting to Micro.blog. The main difference between the two is that the crossposted version will have &amp;ldquo;Devlog&amp;rdquo; prepended to each of the post titles, as they&amp;rsquo;ll be imported as full posts in a blog that has other content.&lt;/p&gt;
&lt;p&gt;This is all hard-coded at the moment but I&amp;rsquo;m hoping to extend this to support other sorts of feeds, allowing the author to modify, include, and exclude posts based on their desires. The motivation here is to make it easier to integrate with services that cross-post, while recognising that not all of these &amp;ldquo;views&amp;rdquo; of a post would be the same. Cross-post to a service that supports at-mentions and maybe you&amp;rsquo;d like to ensure their handle is properly written out. But display it on the site, and maybe you&amp;rsquo;d prefer that the handle is a link to their homepage. These should all derived from the post source, and expanded in the various feeds based on who&amp;rsquo;s consuming it.&lt;/p&gt;
&lt;p&gt;Anyway, that&amp;rsquo;s the idea. We&amp;rsquo;ll see if any of this comes about.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: CSVTool - A Vibe-coded CSV Editor</title>
      <link>https://lmika.org/2026/03/08/devlog-csvtool-a-vibecoded-csv.html</link>
      <pubDate>Sun, 08 Mar 2026 06:11:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/08/devlog-csvtool-a-vibecoded-csv.html</guid>
      <description>&lt;p&gt;One of the fun aspects of these new code agents is seeing what they&amp;rsquo;re capable of producing just form the prompt, so called &amp;ldquo;vibe-coding.&amp;rdquo; There are some that are definitely all in on the concept: I&amp;rsquo;m thinking of Steve Yeggie and his Gas Town work. As for myself, I still prefer to be a bit more hands on. But it&amp;rsquo;s still amusing to see what these agents are capable of just from the prompt.&lt;/p&gt;
&lt;p&gt;I got inspired by &lt;a href=&#34;http://scripting.com/2026/03/03.html#a145455&#34;&gt;Dave Winder&amp;rsquo;s post&lt;/a&gt; about how he asked Claude Code to make a spreadsheet app for him. I use a TUI app I made for myself called &lt;a href=&#34;https://lmika.dev/cmd/ted&#34;&gt;Ted&lt;/a&gt; that I use to edit CSV files, but seeing how good Claude was in making the spreadsheet, I thought I&amp;rsquo;d ask Claude to make me a GUI version. This is what it came up with:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/egpa1ybj7ftpzmez.png&#34; width=&#34;600&#34; height=&#34;386&#34; alt=&#34;Auto-generated description: A table displays a list of names, contact details, and job information for various individuals, formatted like a spreadsheet or CSV file.&#34;&gt;
&lt;p&gt;The results were pretty decent, at least on the surface. I haven&amp;rsquo;t put it quite through it&amp;rsquo;s paces for editing large CSV files, but what it managed to do out of the box was pretty impressive. Not that it&amp;rsquo;s groundbreaking in any technical sense: think spreadsheet without the ability to define expressions. Most of the supportable commands are available via a command palette, invokable using Cmd+P. There are a few copy-and-paste options for getting table data out into usable formats, something I find quite frustrating with the spreadsheets I use. You can paste table data as CSV, a Markdown table, and Jira markup. There&amp;rsquo;s the usual load, save, and commands to insert rows; all pretty standard.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/t6eu6lo8pfu-7avl.png&#34; width=&#34;600&#34; height=&#34;386&#34; alt=&#34;Auto-generated description: A spreadsheet-like interface displays a list of names, emails, and other information, with a context menu open showing options related to row and column operations.&#34;&gt;
&lt;p&gt;It isn&amp;rsquo;t perfect though. Despite two attempts to instruct it to make the header row fixed such that it won&amp;rsquo;t scroll off the top, Claude was unable to achieve this. The techniques of making such a header are pretty hacky: the one I&amp;rsquo;m aware of is making a second table with just the header, placing it above the first, setting the position to absolute, then adding some JavaScript to keep the column widths in sync. Granted, this worked a good 18 years ago, and for tables that didn&amp;rsquo;t scroll horizontally. Maybe there&amp;rsquo;s a better way of doing this nowadays? Oh and speaking of the columns, despite providing affordances for resizing the columns, Claude didn&amp;rsquo;t implement the code to actually resize them. This makes me wonder if HTML tables are probably not the best approach for such a control. I guess I probably should&amp;rsquo;ve told Claude that.&lt;/p&gt;
&lt;p&gt;In any case, I&amp;rsquo;m not sure I&amp;rsquo;ll put this to any real use. One thing I like about Ted is that it&amp;rsquo;s modal, like Vim. Being able to move around using the keyboard (I, J, K, and L are mapped to up, left, down, and right, respectively) is something I&amp;rsquo;d miss. So I think Ted will still be useful for me. I may revisit this in the future though, and see if Claude could make a GUI version of Ted. That could be interesting.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll finish up by talking about some of the other features I asked to be added. Some of them were pretty ill conceived, as they came while I had a flurry of needs for CSV data. I ended up using to tool more frequently than I expected.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sorting:&lt;/strong&gt; Sort the table (the actual model, not just the view) either alphanumeric ascending, or alphanumeric descending based on the values of the current cell. There was also a &amp;ldquo;Sort Advanced&amp;rdquo; that prompted the user to enter the columns to sort in priority order (although not the direction, it&amp;rsquo;s always alphanumeric ascending).&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/rddp-wu2pcj1j8ij.png&#34; width=&#34;600&#34; height=&#34;386&#34; alt=&#34;Auto-generated description: A computer screen displays a CSV editing tool with a dataset of names, contact information, and occupations, featuring a Sort Advanced pop-up for organizing columns.&#34;&gt;
&lt;p&gt;&lt;strong&gt;Match Cell:&lt;/strong&gt; Select cells that had a value matching the current cell. This was an attempt of selecting rows to delete, but it never really worked well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Set Where:&lt;/strong&gt; Prompts the user for a source column, a set of match values, and a target value. Whenever the source column has one of the match values, it will set the current column to the target value. This acts like a poor man&amp;rsquo;s inner join, and was made as I needed to interleave values I had from a shell command in the table I was working on.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Gallery: Newport Workshops Open Day</title>
      <link>https://lmika.org/2026/03/07/gallery-newport-workshops-open-day.html</link>
      <pubDate>Sat, 07 Mar 2026 15:05:49 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/07/gallery-newport-workshops-open-day.html</guid>
      <description>&lt;p&gt;Streamrail Victoria, the organisation that preserves old railway stock, had an open day at Newport Workshops, where they do their restoration work. They only have this open day occasionally, and seeing that they organised one this year, I decided to go. Part of it was a nostalgia trip for me, but it also was a chance to see locomotives and rolling stock I&amp;rsquo;ve not seen in person. Plus, it was a good excuse to get out of the house.&lt;/p&gt;
&lt;p&gt;And I took photos; quite a few photos. Not great photos, mind you; and I won&amp;rsquo;t be publishing all of them — I&amp;rsquo;m sure you&amp;rsquo;ll find better ones from other gunzels online. But here are few from me.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260306-233421087.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Kicking off the day with the traditional form of rail travel round here: getting on a train-replacement bus. 😛
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-002641124.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260306-235230644.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Inside a Taitset.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260306-235550241.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  A P-class diesel in the orange V-line livery. I remember when these were in service. Their power to noise ratio was not great.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-000815206.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-000720277.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  K-class coal fired stream locomotive. After checking my photos, this is one of the locomotives they use for the Wattle Festival at Hurstbridge.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-001826027.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-002312286.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Buying some swag: a photo book on the Hitachi trains.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-003015557.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  A Y-class diesel locomotive. These were to replace the J and K class steam locomotives. Used the power bogies of scrapped Taitsets, which was interesting.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-002924648.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Looking inside the Y-class.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-003431078.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Model trains certainly made an appearance here. This is of Sunbury station.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-004353363.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Old train tickets. I can&amp;#39;t believe we once used tickets that worked by scratching off the date.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-004628700.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Old station signs.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-010957563.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Front of a Taitset.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-011730539.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The old administration building, itself being restored.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-012359569.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  One the reasons why I wanted to visit: seeing the old Greasing Train, made from converted Harris EMUs. Unfortunately, it looks like it&amp;#39;s still awaiting restoration, so no way to look inside. Maybe next time.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;It was quite crowded, more crowded than I expected. A lot of families, but also just a lot of train enthusiast. But the exhibitions were great. Many of them you can go in and look around. They also ran stream trains on runs of track beside the workshop; and I hope you like steam whistles, as there was quite a chorus of them.&lt;/p&gt;
&lt;p&gt;I got on the bus, expecting it to take me back to Newport, but it stopped at the Railway Museum, which was also open (and was included in the ticket I purchased). So I thought I&amp;rsquo;d take a look around that too.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-014207318.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  A pretty ordinary photo of the oldest steam locomotive still intact in Victoria. This ran suburban services from 1880 to 1961.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-014737070.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Another tank engine that ran suburban services.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-014852024.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  A swing-door &amp;#39;doggy&amp;#39; carriage, the first set of electric suburban trains. Started life as steam-hauled carriage, it was converted to electric in 1923, 103 years ago.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-015013533.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Cab of the swing-door carriage.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-015025014.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-015133973.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  A wigwag level crossing warning signal. I&amp;#39;ve not seen a real-life version of this, but apparently a level crossing close by use to have these, way back.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-015559278.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  X36 heavy freight, apparently the most powerful mainline freight stream locomotive made for Victorian railways. This was a beast. Notice how high the stairs are to get to the cab.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-021419742.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  A silver Hitachi in front of a blue Harris set.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-020711741.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  A refurbished Harris set, the so called &amp;#39;grey ghosts&amp;#39;. These were before my time, although the livery was typical of suburban trans back in the 80&amp;#39;s and 90&amp;#39;s.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-020810528.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Inside the grey ghost.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-021145405.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Inside a Hitachi, taken from where I used to stand while these were in service. Forgotten how uncomfortable leaning back was. But man were they fun trains to ride.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2026/pxl-20260307-021843486.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  An old Metcard machine. This must have been added to the museum&amp;#39;s collection when they were taken out of service, as the annual ticket and Zone 3 were blanked out.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;All in all, a good way to spend a day.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>PostgreSQL Docker and &#34;No space left on device&#34;</title>
      <link>https://lmika.org/2026/03/06/was-trying-to-launch-a.html</link>
      <pubDate>Fri, 06 Mar 2026 13:57:43 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/06/was-trying-to-launch-a.html</guid>
      <description>&lt;p&gt;I was trying to launch a PostgreSQL Docker container and was seeing it fail with the following error:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;initdb: error: could not create directory &amp;#34;/var/lib/postgresql/data/pg_wal&amp;#34;: No space left on device
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I checked my disk usage and I had plenty of storage left, almost half. Did an online search and discovered that Docker caps the amount of storage set aside for volumes. And that was set criminally low, about 119.21 GB or so, which is 6% of what&amp;rsquo;s available:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/out-20260306-145512.png&#34; width=&#34;600&#34; height=&#34;546&#34; alt=&#34;Auto-generated description: It shows a graphical interface with adjustable sliders for setting limits on CPU, memory, swap, and disk usage.&#34;&gt;
&lt;p&gt;Raising the storage to 1 TB seemed to resolve the problem.&lt;/p&gt;
&lt;p&gt;Kind of figured we&amp;rsquo;ve learnt from the issues with capping resources like this. Anyone who remembers the bad old days of adjusting the heap size of Java would surely get PTSD from dealing with this sort of rubbish. I know I did. I understand that for a shared instances or production setups, fair enough: cap the storage so it won&amp;rsquo;t go out of bounds. But for a standard laptop setup, just use all resources that&amp;rsquo;s available to you.&lt;/p&gt;
&lt;p&gt;Anyway, if anyone else using PostgreSQL in Docker encounters this, try the method above. You can find it by opening Docker Desktop, then going to Settings → Resources.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: 3rd March 2026</title>
      <link>https://lmika.org/2026/03/03/devlog-rd-march.html</link>
      <pubDate>Tue, 03 Mar 2026 06:15:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/03/devlog-rd-march.html</guid>
      <description>&lt;p&gt;Oof! Everyone&amp;rsquo;s building blogging CMS&amp;rsquo;s now, apparently.&lt;/p&gt;
&lt;p&gt;Since starting work on this project, I saw one other announce their own CMS that was vibe-coded with Claude. No shame in that: making something that works for you is part of the joy of participating in the Indie-web. I did take a brief look at it, and dismissed it because it was written in PHP. Yes, I am a snooty developer that looks down on those using PHP (it&amp;rsquo;s just so annoying to deploy; although credit to this person, they did prepare a Docker container).&lt;/p&gt;
&lt;p&gt;Weiro is not vibe coded. Much of it is written by hand. Not all of it, mind you: I am using agents to help with the more mundane stuff. But the models, DB schema, and much of the UI is hand-rolled. And I&amp;rsquo;m conflicted as to whether that&amp;rsquo;s the right balance. Progress is slower: these vibe-coders are whipping up CMS&amp;rsquo;s in the same time it takes me to add a single feature to it. And there are probably better things I could be doing other than adding one more CMS into the world (although when my mind whispers that doubt, it usually suggests watching TV or doomscrolling as an alternative, so there are certainly worse things I could be doing).&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t stopped working on it yet, so here&amp;rsquo;s a brief update. I&amp;rsquo;ve got a version of it up and running in Coolify. It currently supports posts at the moment: both draft and published via Netlify. Much of the work was just making the writing experience feel natural, given that working on posts is probably the core feature of any CMS. So there&amp;rsquo;s a very large window for the post body (maybe a little too large), and there&amp;rsquo;s a Cmd+S keyboard shortcut for saving updates. I would like to add an auto-save feature, but I&amp;rsquo;m not entirely sure how best to do that server side, so I may settle for something that&amp;rsquo;s browser only, just to save work for when the browser crashes or has no network connection. I&amp;rsquo;m also working on uploads, so there shouldn&amp;rsquo;t be too much time before I can start sharing screenshots.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Hello</title>
      <link>https://lmika.org/2026/02/28/devlog-hello.html</link>
      <pubDate>Sat, 28 Feb 2026 10:28:24 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/02/28/devlog-hello.html</guid>
      <description>&lt;p&gt;This is the inaugural post of Devlog, where I&#39;m planning to write on what I&#39;m working on. This post was created using Weiro, a new blogging CMS I&#39;ve been working on. This post is little more than a test to see if the deployed version of Weiro is working.  I&#39;ll post more about Weiro in later posts. For now, I just want to make sure this is being published correctly.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Colour of Production</title>
      <link>https://lmika.org/2026/02/27/the-colour-of-production.html</link>
      <pubDate>Fri, 27 Feb 2026 14:50:46 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/02/27/the-colour-of-production.html</guid>
      <description>&lt;p&gt;Most places I&amp;rsquo;ve worked at allocate red as the &amp;ldquo;colour of production,&amp;rdquo; i.e. the colour use for banners, badges, shell prompts, etc. Pretty suitable when viewed from a Western culture&amp;rsquo;s perspective: red traditionally means stop or danger, and one should always conduct their tasks in production with care.&lt;/p&gt;
&lt;p&gt;But what about the other environments? I&amp;rsquo;ve not seen such a strong convention here. Maybe you could say that staging/pre-production is typically amber or orange, and dev is usually something like green; but this is not universal, and I&amp;rsquo;ve worked places that deviate quite a bit from this.&lt;/p&gt;
&lt;p&gt;Whenever I have the choice, tend to follow this convention for these environments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Production is red:&lt;/strong&gt; naturally. Caution, stop-to-think, etc. All sorts of things that come to mind when I see red.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pre-production/staging is yellow:&lt;/strong&gt; you&amp;rsquo;re not dealing with live systems like you would be in production, but you do want to be careful. Ideally you should treat staging as if it were production, for no other reason than to rehearse what you&amp;rsquo;re about to do in production. And yellow&amp;rsquo;s a good colour for this.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Development is green:&lt;/strong&gt; green as in &amp;ldquo;go nuts.&amp;rdquo; Do what you need to do to get your work done. You&amp;rsquo;re sort of expecting things to break here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testing is blue:&lt;/strong&gt; since red, yellow, and green are used, I tend to allocate blue for the environment set aside for QA. It&amp;rsquo;s expected to be somewhat stable, so you&amp;rsquo;re not as free to do things here as you would in Dev. Yet you are expected to make changes here for QA to test, and not all changes will succeed. That doesn&amp;rsquo;t necessarily shout &amp;ldquo;blue&amp;rdquo; but it works.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other colours, like purple or cyan, are reserved for the more auxiliary environments I may have to deal with. There&amp;rsquo;s no real semantic meaning to any of these, other than to tell them apart. I tend not to use the neutral colours like black, white, and grey, as they tend to blend into the UI and aren&amp;rsquo;t distinctive enough.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Dialogue: LLM Auto-complete Is…</title>
      <link>https://lmika.org/2026/02/20/dialogue-llm-autocomplete-is.html</link>
      <pubDate>Fri, 20 Feb 2026 11:57:22 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/02/20/dialogue-llm-autocomplete-is.html</guid>
      <description>&lt;style&gt;
.dialogue.dialogue-2026-02-the-frustrations-of {
    --dialogue-m1-color: #4C6C8C;
    --dialogue-m1-background: no-repeat center / 105% url(https://avatars.micro.blog/avatars/2024/15/55331.jpg);
    --dialogue-m2-color: #625309;
    --dialogue-m2-background: white no-repeat center / 75% url(https://lmika.org/uploads/2025/rubber-duck.jpg);
}
&lt;/style&gt;
&lt;div class=&#34;dialogue dialogue-2026-02-the-frustrations-of&#34;&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; You writing code?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Author:&lt;/b&gt; Documentation.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; And you&#39;re using an…&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Author:&lt;/b&gt; IDE? Yes, I am.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Do you have that…&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Author:&lt;/b&gt; LLM-powered auto complete? I do, yes. And it&#39;s fine, although it does seem like the IDE&#39;s…&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Always trying to finish your sentences?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Author:&lt;/b&gt; Yes. It can be quite…&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Bothersome? Yeah, I can&#39;t imagine how that must feel. 😛&lt;/p&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>What to Talk About When You&#39;re Talking About Your Side-Business</title>
      <link>https://lmika.org/2026/02/16/what-to-talk-about-when.html</link>
      <pubDate>Mon, 16 Feb 2026 07:14:29 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/02/16/what-to-talk-about-when.html</guid>
      <description>&lt;p&gt;Dear side-business owners,&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m sure talking about your side-business — one setup to take in a bit of income on the side in addition to your main job — is not as interesting as talking about tech or whatever you do that excites you. The topic is always a little sensitive, and probably quite boring.&lt;/p&gt;
&lt;p&gt;But as someone who doesn&amp;rsquo;t own a side-business, I do find these posts very interesting to read, and I&amp;rsquo;m grateful that you write them at all, and try to be as transparent as you do. Obviously it would be too much to ask about exact figures, but whatever information you do provide I tend to find quite useful.&lt;/p&gt;
&lt;p&gt;If I can make a suggestion on what to write about, a general breakdown of where your income comes from is not nearly as interesting as knowing what you did to form the business in the first place, and why. If you were to ask me what I&amp;rsquo;d want to know, it would be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Why did you choose to form a legal entity rather than take money in your own name?&lt;/li&gt;
&lt;li&gt;How did you form the business? As in, what are the actual steps you did to go from no legal entity to something capable of taking in money. Obviously this will be different based on your location, but knowing, in general, what is involved would be useful (and being specific would help me map the steps to my own jurisdiction).&lt;/li&gt;
&lt;li&gt;What did you tell your current employer, and when did you tell them?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&amp;rsquo;m not sure what I&amp;rsquo;d do with answer to these questions. Probably nothing, other than feed my dreams of starting something of my own one day. But it&amp;rsquo;ll still make for interesting content, if nothing else. I hear time and time again from others that they don&amp;rsquo;t want to talk about how the sausage is made. But hearing about how the sausage is made tends to be the most interesting part. Just think about those docos that going into how something is built or operated; or heck, the director&amp;rsquo;s commentary in DVDs and video games. People like learning about these things.&lt;/p&gt;
&lt;p&gt;Anyway, that&amp;rsquo;s all I have to say on the subject. I hope this letter finds you well, if it finds you at all.&lt;/p&gt;
&lt;p&gt;Regards,&lt;/p&gt;
&lt;p&gt;lmika&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>80% Done 80% Well</title>
      <link>https://lmika.org/2026/02/14/done-well.html</link>
      <pubDate>Sat, 14 Feb 2026 15:08:24 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/02/14/done-well.html</guid>
      <description>&lt;p&gt;No doubt you&amp;rsquo;ve seen &lt;a href=&#34;https://shumer.dev/something-big-is-happening&#34;&gt;that blog post&lt;/a&gt; making the rounds this week. I read it this morning, or I should say I &amp;ldquo;read&amp;rdquo; it, in that I read down to the the action items, then sort of skimmed it from there. I didn&amp;rsquo;t need to hear someone say I should learn to use coding agents. I already use coding agents, usually when I need them to do something tedious.&lt;/p&gt;
&lt;p&gt;But the original poster&amp;rsquo;s breathless description about how effective these new coding agents released a week ago are made me curious. I don&amp;rsquo;t think I&amp;rsquo;ve tried having a coding agent build something from scratch, completely unsupervised. So I fired up Claude Code and asked it to build me two things: an RSS reader, and a replacement for My Tracks, both in Flutter targeting Android.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll spare you the details (although I may write more about it in a later post) but my impression on the capabilities of these models is… well, I will say that it&amp;rsquo;s ability to build an entire project from scratch is pretty impressive. What&amp;rsquo;s less impressive is that I found it needed further intervention from me to get it working and up to spec. The RSS reader didn&amp;rsquo;t load the feeds I tried it, and once I fixed that, it got itself into an infinite loop. The My Tracks replacement was better, and I think it works (I&amp;rsquo;m always skeptical of location tracking without excessive testing), but there were some questionable visual decisions that require fixing.&lt;/p&gt;
&lt;p&gt;So after using these models to get 80% of what I wanted 80% done well, I tend to side towards the takes made by &lt;a href=&#34;https://om.co/2026/02/12/living-in-the-petri-dish-of-the-future/&#34;&gt;others&lt;/a&gt; &lt;a href=&#34;https://spyglass.org/ai-future-work/&#34;&gt;writers&lt;/a&gt; regarding this post. It&amp;rsquo;d be foolish for me to say that AI won&amp;rsquo;t change things: I feel pretty confident that that&amp;rsquo;s going to happen. But going to a world where unsupervised AI agents will be making apps all the apps for all of us? There I remain skeptical.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Post Timestamps</title>
      <link>https://lmika.org/2026/02/02/on-post-timestamps.html</link>
      <pubDate>Mon, 02 Feb 2026 06:58:17 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/02/02/on-post-timestamps.html</guid>
      <description>&lt;p&gt;Just saw the timestamp of a couple of photo posts that I thought were off by a few hours. I went to change them, thinking it was something like a timezone bug, but then remembered that I made the post a few hours after I took the photo. I wonder if having the option to set the post timestamp to the photo would be a good idea, something akin to how Day One works. Of course, the issue would be that the post would be a few hours old when published, making it unlikely to appear in chronological timelines.&lt;/p&gt;
&lt;p&gt;I sometimes wonder if it&amp;rsquo;s better that blog posts have two timestamps: one for when the post was published, and one for when the event occurred. That way, the post will still appear in chronological feeds while being about events that happened hours, days, or even years in the past. Maybe they can show up in chronological order within the category feed.&lt;/p&gt;
&lt;p&gt;But that might complicate things a little. Much of our existing blogging infrastructure assumes a single timestamp, and while it would be nice to occasionally encode a separate date for a specific event, it&amp;rsquo;s probably not going to be needed that often. When would the event date for this post be, for example? When I started writing the post?&lt;/p&gt;
&lt;p&gt;Maybe a nice compromise would be to allow the change in timestamp, but only if the image is recent, say 4 hours. That way, it&amp;rsquo;ll still be sort of realtime in the various chronological feeds, yet the date of the post would be closer to the event after that. Once the post drops off the feed, the published time looses it&amp;rsquo;s relevance and knowing when the event actually occurred would be more important for posterity reasons.&lt;/p&gt;
&lt;p&gt;Or maybe I&amp;rsquo;m just over thinking the whole thing.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Micro apps - Some Score Cards</title>
      <link>https://lmika.org/2026/02/01/devlog-microapps-some-score-cards.html</link>
      <pubDate>Sun, 01 Feb 2026 09:05:02 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/02/01/devlog-microapps-some-score-cards.html</guid>
      <description>&lt;p&gt;In the spirit of maintaining a document of what I&amp;rsquo;ve been working on, and being somewhat inspired by &lt;a href=&#34;https://birchtree.me/tag/micro-apps/&#34;&gt;Matt Birchler&amp;rsquo;s posts about his micro apps&lt;/a&gt;, I&amp;rsquo;d thought I&amp;rsquo;d document on some of the small apps I&amp;rsquo;ve worked on recently.&lt;/p&gt;
&lt;p&gt;The fact of the matter is that I&amp;rsquo;ve been building quite a few of these apps over the course of the summer, primarily in response to a specific need I have at the time. I haven&amp;rsquo;t written about them before, mainly because there didn&amp;rsquo;t feel like there was much to say. Some were vibe coded, and saying that I &amp;ldquo;made&amp;rdquo; them didn&amp;rsquo;t feel correct. Others were made by hand, but they were super simple and there was no real challenge in making them at all. In either case, being able to say &amp;ldquo;I made it&amp;rdquo; is difficult as the amount of effort spent in making it is quite low, and that doesn&amp;rsquo;t make for interesting blog posts. Would a post saying that &amp;ldquo;I went to work today&amp;rdquo; be worth reading if that&amp;rsquo;s what I do every weekday?&lt;/p&gt;
&lt;p&gt;But regardless of what my feelings are, the scales were tipped ever so slightly into wanting to write about them. So that&amp;rsquo;s what I&amp;rsquo;ll do. And there is some leeway in writing about the small things if I were to put them in the Devlog category, which could be seen as my public workbook.&lt;/p&gt;
&lt;p&gt;So, let&amp;rsquo;s set expectations by talking about a series of score-keeping apps. These are among the simplest micro-apps I have, yet they do have their uses. You&amp;rsquo;re always left high and dry with a need to keep score of some ad-hoc game made in the moment (or, at least, I am). Notebooks and spreadsheets could work here, but I don&amp;rsquo;t carry a paper notebook, and use a digital notebook for this is always a little cumbersome. And a spreadsheet just feel a little overkill. Hence, the want for having a simple web-app for keeping scores:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/out-20260201-100150.png&#34; width=&#34;600&#34; height=&#34;409&#34; alt=&#34;Auto-generated description: A scorecard interface displays scores for four players over nine rounds, with options to undo or reset.&#34;&gt;
&lt;p&gt;Using it couldn&amp;rsquo;t be simpler: enter the score under the specific player, and it will be recorded in a table, alongside a running total. If you make a mistake, tap &amp;ldquo;Undo&amp;rdquo;. If you want to clear the scorecard, tap &amp;ldquo;Reset&amp;rdquo;. The scores are stored in local-storage so there&amp;rsquo;s nothing server side that&amp;rsquo;s running (although it does mean they&amp;rsquo;re tied to your browser). Focus automatically moves to the player to the right, but any entry could be entered at any time.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a &lt;a href=&#34;https://tools.lmika.app/scorecard-2p/&#34;&gt;two player&lt;/a&gt; and &lt;a href=&#34;https://tools.lmika.app/scorecard-4p/&#34;&gt;four player&lt;/a&gt; variant. I was considering having a &amp;ldquo;player entry&amp;rdquo; screen to select the number of players, but that felt like adding too much complexity for something that&amp;rsquo;s designed to be quick to implement.&lt;/p&gt;
&lt;p&gt;In fact, much of this was already implemented in the form of a &lt;a href=&#34;https://tools.lmika.app/finska/&#34;&gt;Finska scorecard&lt;/a&gt;.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/out-20260201-100202.png&#34; width=&#34;600&#34; height=&#34;409&#34; alt=&#34;Auto-generated description: A Finska scorecard displaying scores for Team A and Team B, with Team A at 12 points and Team B at 25 points.&#34;&gt;
&lt;p&gt;This is not a new project, I just moved it from a dedicated domain name and changed the styling. It&amp;rsquo;s been a while since I needed it for a game of Finska: I was more often than not using it for generic scorekeeping. But there were some aspects of scoring Finska which made it useful for that, and less useful for anything generic. For those who never played Finska, the goal is to get to exactly 50 points. If you go over 50, you go back to 25 points. There&amp;rsquo;s also a mode where all the zero point entries are highlighted to indicate a foul (3 fouls means you loose the game).&lt;/p&gt;
&lt;p&gt;And, that&amp;rsquo;s it! Really, there&amp;rsquo;s nothing more to say about these apps. I had a need and some free time, so I put these together. There is one other scoring app that I&amp;rsquo;ve made, but that&amp;rsquo;s a little more complicated and warrants it&amp;rsquo;s own post.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>HV #old Submission</title>
      <link>https://lmika.org/2026/01/23/hv-old-submission.html</link>
      <pubDate>Fri, 23 Jan 2026 10:53:34 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/01/23/hv-old-submission.html</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s my submission for &lt;a href=&#34;https://pca.st/8j6v8atq?t=38m30s&#34;&gt;Hemispheric View&amp;rsquo;s #155&lt;/a&gt; request for fun and interesting computer peripherals (#old). In the abstract, this one&amp;rsquo;s neither fun nor interesting, yet it&amp;rsquo;s one I remember fondly. It&amp;rsquo;s the HP DeskJet 690c colour printer:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/slika11276259870.jpg&#34; width=&#34;600&#34; height=&#34;450&#34; alt=&#34;Auto-generated description: A white HP DeskJet printer is positioned on a wooden floor with its paper tray extended.&#34;&gt;
&lt;figcaption&gt;Source: &lt;a href=&#34;https://darudar.org/gift/3422112/&#34;&gt;darudar.org&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This was the first printer I used for school work growing up. It replaced the first printer I actually had experience with. We had an old line printer which worked with &lt;a href=&#34;https://en.wikipedia.org/wiki/Continuous_stationery&#34;&gt;continuous form paper&lt;/a&gt; (the one that has the holes down the sides). Quick aside: it&amp;rsquo;s great seeing these printers still in use today. I always get a hit of nostalgia when I see and hear these line printers print out passenger manifests at the gate at airports.&lt;/p&gt;
&lt;p&gt;Anyway, this printer was slow, monochrome, and could really only handle text. And this became a problem when I entered school, and the need for printed out &amp;ldquo;project&amp;rdquo; work became I thing. So my Dad eventually replaced it with this thing. And wow, to see a print out of one of your creations in colour back then! Hardly revolutionary in hindsight, but the rest of the printed world was in colour, and it was quite something to point to one of your colour printouts and say, &amp;ldquo;I made that.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Also great was being able to use regular sized paper. We had some success with continuous form paper with perforated feed holes that you could tear them off before stapling. But the sheets were never a great size for booklets. They were either too big, or the aspect ratio was wrong. So it was nice moving to a machine that could produce A4 (or A5) booklets. The print software even supported double-sided printing, although not without manipulating the paper yourself.&lt;/p&gt;
&lt;p&gt;And the sounds it made while it was printing… super interesting to hear. I still remember them.&lt;/p&gt;
&lt;p&gt;Eventually it died and got replaced with a printer-scanner machine that was actually pretty decent, but not nearly as good. That  got replaced by something that was lesser still, and so goes the downward quality of home printers. Since then my need for home printing was dwindling, as access to printers at school became available to me, followed by the lack of need to use printers at all. I don&amp;rsquo;t even own a home printer now, and I can&amp;rsquo;t say that I miss them. If I do need to print something, once every couple of years, I just use the one at work or at the library.&lt;/p&gt;
&lt;p&gt;So for me, I think home printing peaked with this machine. And that&amp;rsquo;s what makes it my entry for HV&amp;rsquo;s fun and interesting computer peripherals.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Game For Niece — More Art and Cropping Ebitengine Images</title>
      <link>https://lmika.org/2026/01/20/devlog-game-for-niece-more.html</link>
      <pubDate>Tue, 20 Jan 2026 22:22:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/01/20/devlog-game-for-niece-more.html</guid>
      <description>&lt;p&gt;Drew some more artwork and started integrating it into the game. Replaced the previous bus image I got from an image search with one I created myself using Affinity Designer:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/out-20260120-223806.png&#34; width=&#34;600&#34; height=&#34;359&#34; alt=&#34;Auto-generated description: A cartoon-style bus is depicted in an illustration with the word BUS in green text above it.&#34;&gt;
&lt;p&gt;It&amp;rsquo;s orange, just like the busses round here, although this one has a plainer livery.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve not integrated the car just yet: I&amp;rsquo;m hoping to finish off the animations for the bus first. But I&amp;rsquo;m so glad I embraced Affinity Designer for this. I&amp;rsquo;m surprise I haven&amp;rsquo;t considered it sooner. The whole image is designed as a whole, then I export each component — body, door, wheels — as a separate PNG and recompose it in the code, giving me the means of animating each component. I&amp;rsquo;d imagine this is a pretty typical workflow: and I&amp;rsquo;m surprise I haven&amp;rsquo;t considered this sooner too.&lt;/p&gt;
&lt;p&gt;And yes, I will admit I did look at AI image generation for this. But I wasn&amp;rsquo;t happy with what it produced, and I wanted each image to have a consistent style. Turns out an &amp;ldquo;elementary vector artist&amp;rdquo; style works perfectly here.&lt;/p&gt;
&lt;p&gt;Anyway, took a bit of time working out how to draw a cropped image in Ebitengine. Spent a little bit of time seeing what was possible with masks. But after looking at the &lt;a href=&#34;https://ebitengine.org/en/examples/tiles.html&#34;&gt;examples&lt;/a&gt;, it looks like using &lt;a href=&#34;https://pkg.go.dev/github.com/hajimehoshi/ebiten/v2#Image.SubImage&#34;&gt;SubImage&lt;/a&gt; for this is viable:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;busDoor&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Image&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;getSpriteImage&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bus_door.png&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;render&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;screen&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Image&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;doorDX&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;bp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;sprBusDoor&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Bounds&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;Dx&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;doorDY&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;bp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;sprBusDoor&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Bounds&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;Dy&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;pos&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GeoM&lt;/span&gt;{}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;pos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Translate&lt;/span&gt;(float64(&lt;span style=&#34;color:#a6e22e&#34;&gt;doorDX&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;), &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;screen&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DrawImage&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;busDoor&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SubImage&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;image&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Rect&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;doorDX&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;doorDY&lt;/span&gt;)).(&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Image&lt;/span&gt;), 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DrawImageOptions&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;GeoM&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;pos&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As for the other scenes I need to add, I plan to work through vehicles and objects first as they seem easier to draw. Hopefully by doing so I gain some practice before I tackle more organic objects like trees and animals. Tackling those feel a little daunting to me: more curves involved.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Apple&#39;s Icons for Their Creator Studio Bundle</title>
      <link>https://lmika.org/2026/01/17/on-apples-icons-for-their.html</link>
      <pubDate>Sat, 17 Jan 2026 10:00:39 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/01/17/on-apples-icons-for-their.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s been said before, but the &amp;ldquo;icons&amp;rdquo; of Apple&amp;rsquo;s Creator Studio apps are not doing their job as icons. By &lt;a href=&#34;https://developer.apple.com/design/human-interface-guidelines/app-icons&#34;&gt;Apple&amp;rsquo;s own definition&lt;/a&gt;, an app icon is meant to express the purpose or personality of an app, and help people &amp;ldquo;recognize it at a glance.&amp;rdquo; But I&amp;rsquo;ve been starting at these new icons for several minutes now and I&amp;rsquo;m struggling to work out what each one is.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2026/out-20260117-102911.png&#34; width=&#34;600&#34; height=&#34;310&#34; alt=&#34;Auto-generated description: A set of eight colorful, stylized app icons with dark backgrounds, featuring various abstract designs.&#34;&gt;
&lt;figcaption&gt;Source: &lt;a href=&#34;https://digg.com/apple/pVowWKV/can-we-talk-about-these-icons&#34;&gt;Digg user Austen&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I&amp;rsquo;ll be referring to the icons via their coordinates as if they were placed on a spreadsheet. So rows are numbered 1 (top) and 2 (bottom), and the columns go from A (left) to D (right).&lt;/p&gt;
&lt;p&gt;Some I can infer pretty easily, such as the ones for Keynote (2D), Final Cut (1B), and Pages (2A). But what about the others? What is 1A? Based on what look like the handles that appear when you&amp;rsquo;re drawing splines, it may have something to do with graphics. But the rest of icon alludes to something involving an SDK, since it shares the same shape that Apple uses for its various &amp;ldquo;kits.&amp;rdquo; So is it a graphics program or an app development program? Is this Shortcuts? Pixelmator Pro? Or is Pixelmator Pro 2C? Or is 2C Freeform?&lt;/p&gt;
&lt;p&gt;Okay, forget graphics: let&amp;rsquo;s consider audio. I think Logic Pro is 1C, but 1D looks an awful lot like a fader on a mixing board. If 1D has nothing to do with music, does it have something else to do with audio that I&amp;rsquo;m not aware of? Or is 1D actually Numbers? I don&amp;rsquo;t see a better one for that app, and it does share the same greenish hue as the old Numbers logo. As for 2B? Well… err, um. Okay, I give up. I have no idea.&lt;/p&gt;
&lt;p&gt;Anyway, I hope my point is clear: you pretty much need to have experience with every app that&amp;rsquo;s included in this bundle to know what these icons mean. For everything else, you&amp;rsquo;re left having to read Apple&amp;rsquo;s copy about the bundle, and no-one reads copy (I certainly didn&amp;rsquo;t). And to be fair, it&amp;rsquo;s not like Google does any better: I&amp;rsquo;m always struggling to find the app I want from the sea of quad-colour abstract shapes that is Google&amp;rsquo;s icon design motif. But my expectations were higher with Apple&amp;rsquo;s iconography here. They were the leaders in this space. It&amp;rsquo;s just so striking to see how far they&amp;rsquo;ve fallen.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some More Thoughts on Apple&#39;s Ability to Make Vision Pro Content</title>
      <link>https://lmika.org/2026/01/13/some-more-thoughts-on-apples.html</link>
      <pubDate>Tue, 13 Jan 2026 06:58:37 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/01/13/some-more-thoughts-on-apples.html</guid>
      <description>&lt;p&gt;Thinking about Ben Thompson&amp;rsquo;s article some more, I&amp;rsquo;m now wondering if Apple is incapable of producing the type of Vision Pro content that he&amp;rsquo;s hoping for. Not because they don&amp;rsquo;t have the money or talent, but because they do. You employ a video production team, given them a huge budget, and send them to work with the expectation that they produce a return on investment, of course they&amp;rsquo;re going to make overly produced immersive video. They&amp;rsquo;re going to feel like they need to.&lt;/p&gt;
&lt;p&gt;This might be unfair, but from the outside looking in, Apple doesn&amp;rsquo;t strike me as the type of place for the rank and file to have much leeway in deciding the direction of a product. Direction comes form the top, and the executive team need to justify why Apple is spending so much money on video production. And the easiest way to do so would be to produce content that uses all those cameras, video switchers, editors, etc. that they have.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; The thought of saying &amp;ldquo;we&amp;rsquo;re not going to use any of that, we&amp;rsquo;re just going to stand a camera up&amp;rdquo; could, in the mind of anyone listening, have them wonder &amp;ldquo;okay, so why are we setting aside millions of dollars for you again?&amp;rdquo; The executive team can probably just tell the video producers to use that single camera, but I&amp;rsquo;m sure they&amp;rsquo;ll get asked the same question from Apple&amp;rsquo;s investors.&lt;/p&gt;
&lt;p&gt;Then again, maybe it&amp;rsquo;s more simply that this department has access to all this money and equipment, and they want to make use of it, or they&amp;rsquo;re more comfortable using it because &amp;ldquo;that&amp;rsquo;s how it&amp;rsquo;s always done.&amp;rdquo; Such it would be if you explicitly hire people with those skills for roles that require it.&lt;/p&gt;
&lt;p&gt;In any case, I don&amp;rsquo;t think the type of content Ben is asking for will come from Apple. Creativity requires constraints, and Apple just doesn&amp;rsquo;t have those anymore.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Not sure if this is worthy of a disclaimer, but I should mention that I work at a place that builds and sells this videography equipment. I hope it&amp;rsquo;s clear that I don&amp;rsquo;t speak for my employer, and that these opinions are my own.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>I Heart M Down</title>
      <link>https://lmika.org/2026/01/10/i-heart-m-down.html</link>
      <pubDate>Sat, 10 Jan 2026 09:16:50 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/01/10/i-heart-m-down.html</guid>
      <description>&lt;p&gt;Anil Dash wrote a wonderful post extolling the &lt;a href=&#34;https://www.anildash.com/2026/01/09/how-markdown-took-over-the-world/&#34;&gt;success and virtues of Markdown&lt;/a&gt;:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Nearly every bit of the high-tech world, from the most cutting-edge AI systems at the biggest companies, to the casual scraps of code cobbled together by college students, is annotated and described by the same, simple plain text format. Whether you’re trying to give complex instructions to ChatGPT, or you want to be able to exchange a grocery list in Apple Notes or copy someone’s homework in Google Docs, that same format will do the trick. The wild part is, the format wasn’t created by a conglomerate of tech tycoons, it was created by a curmudgeonly guy with a kind heart who right this minute is probably rewatching a Kubrick film while cheering for an absolutely indefensible sports team.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;You&amp;rsquo;re always encountering the work of others within the realm of software. That language, package, or protocol that you&amp;rsquo;re using was invented or designed by someone, those &lt;a href=&#34;https://www.anildash.com/2026/01/09/how-markdown-took-over-the-world/#:~:text=generosity%20and%20genius%20of%20regular%20people&#34;&gt;regular people&lt;/a&gt; that Anil mentions in his essay. And usually you don&amp;rsquo;t think twice about how you came across the technology, or the people behind it. You see something useful, and you use it.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s something different about Markdown. I&amp;rsquo;ve not encountered a technology quite like it that I remember having such an effect on my dealings with computers and software. For instance, did you know it was Markdown that led me here, typing this post into Micro.blog?&lt;/p&gt;
&lt;p&gt;I was building something for myself — I can&amp;rsquo;t remember what but it was probably some Ruby on Rails web-app for taking notes — and I wanted a format that would allow me to add some basic styling without having to import a HTML-based rich-text area. This was in the late 2000s and such editors were &lt;em&gt;horrible&lt;/em&gt;. So I was searching for a nice text based format, and I ran across &lt;a href=&#34;https://daringfireball.net/projects/markdown/&#34;&gt;John Gruber&amp;rsquo;s Markdown page&lt;/a&gt;. I didn&amp;rsquo;t immediately warm to the format: the idea of using one asterisk for italics and two for bold didn&amp;rsquo;t appeal to me (those key presses require energy). But what sold me was that there was a parser already written in Ruby that I could use then and there. Also nice was that it had the PHP Markdown extensions with support for tables and definition lists.&lt;/p&gt;
&lt;p&gt;From there I started following Daring Fireball. I wasn&amp;rsquo;t an Apple person at the time but I figured that someone making a format as good as Markdown was worth listening to. One day Gruber wrote about Manton, and I started following him and his work on Micro.blog.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; Eventually, on a particularly lonely day during the pandemic, I took a closer look at what Micro.blog actually was. And what sealed the deal for me was that I could write posts in Markdown.&lt;/p&gt;
&lt;p&gt;As for my work (such as it is), if ever I needed to add a remarks field that took free text and presented it as HTML, I would find a way to add Markdown support to it. And I mean everything. There&amp;rsquo;s an admin console for a system that is deemed critical Australian infrastructure that has Markdown because I saw an opportunity to do so. It&amp;rsquo;s wonderful to be able to enable basic formatting without relying on anything more sophisticated than a simple text field. And I think this is what drove some of its success. It was sort of &amp;ldquo;pollinated&amp;rdquo; across the landscape, with developers liking the format, hating the need to add crappy WYSIWYG editors, and finding Markdown parsers that are easy to integrate.&lt;/p&gt;
&lt;p&gt;So, along with Anil Dash, I celebrate the success of Markdown. Kudos to John for making a really nice format.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Podcasts played a role too, but &lt;a href=&#34;https://lmika.org/2022/09/19/really-specific-stories.html&#34;&gt;that&amp;rsquo;s a different story&lt;/a&gt;.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Too Much HTML</title>
      <link>https://lmika.org/2026/01/09/too-much-html.html</link>
      <pubDate>Fri, 09 Jan 2026 09:14:45 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/01/09/too-much-html.html</guid>
      <description>&lt;p&gt;Being a backend developer, it&amp;rsquo;s sometimes nice to be given the option to do something different. Right now I need to make changes to a Vue frontend project to support some work I&amp;rsquo;m doing in the backend. And while working on the HTML template of a new component, a funny feeling came to me: &amp;ldquo;wait, is this too much HTML? Should I be abstracting this out into another component?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The want to build reusable components before you know you need them is a seductive one. You&amp;rsquo;re not just writing HTML, your &amp;ldquo;engineering&amp;rdquo; something: building the bricks that, when assembled, will be the application. It&amp;rsquo;s not just in frontend thing: Java development was much like this, with Spring containers wiring up individual classes that all have abstract APIs, so if you want to trace execution within the editor, you&amp;rsquo;re traversing through a bunch of frameworks you have no knowledge about just to reach the code you need.&lt;/p&gt;
&lt;p&gt;Now this could be a little unfair. I have very little experience with Vue and the various other frontend tech this project it using. So this all could come from a place of ignorance and maybe there&amp;rsquo;s a really good reason for composing things like this. There&amp;rsquo;s a reason this form of development became popular after all. But I do wonder if some of the trade-offs are being discounted. This form of decomposition has a price, in the form of readability and simplicity, and I somethings wonder if the benefit of reusability you&amp;rsquo;re paying that price for is oversold. When changes come through, I usually see people just rewrite the components.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t know. This is all probably because I&amp;rsquo;m paying that price myself now. But when you&amp;rsquo;re exploring a project you&amp;rsquo;re unfamiliar with, you tend to pick up patterns from the code that&amp;rsquo;s already there. And I found it interesting that I felt that my component had too much HTML.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>About The Journey</title>
      <link>https://lmika.org/2026/01/05/about-the-journey.html</link>
      <pubDate>Mon, 05 Jan 2026 11:25:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/01/05/about-the-journey.html</guid>
      <description>&lt;p&gt;I was catching up on my RSS posts today (while procrastinating on my first day back at work) and I saw a few posts about people using coding agents to spin up projects in less than an hour. I spent a fair bit of my leave working on projects, and I can probably count on one hand the number of times I actually used an agent. And I don&amp;rsquo;t fully know why I haven&amp;rsquo;t embraced them as much as others have.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not because I shun them deliberately, or see no value in using them. I think it&amp;rsquo;s probably because the finished product is only part of the reason of why I work on something. It&amp;rsquo;s probably even less than half. Knowing that any particular project would only be used by myself (if I use it at all), I don&amp;rsquo;t get the value that comes from having others appreciate something I finished. I need another source of motivation for working on something. &lt;a href=&#34;https://werd.io/2025-the-year-in-llms/&#34;&gt;Ben Werdmuller&lt;/a&gt; probably puts it best:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;I also think we’re going to see a real split in the tech industry (and everywhere code is written) between people who are outcome-driven and are excited to get to the part where they can test their work with users faster, and people who are process-driven and get their meaning from the engineering itself and are upset about having that taken away.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;Clearly I fall squarely in that second group who find the engineering more interesting than the finish product. That probably explains a lot about how I approach projects: always starting new ones, never polishing them up once the &amp;ldquo;interesting&amp;rdquo; bit of the development work has been solved, never releasing them to anyone else.&lt;/p&gt;
&lt;p&gt;Of course there are exceptions to these feelings, and maybe I&amp;rsquo;m more likely to use agents then. It probably also explains why I turn to the agents when there&amp;rsquo;s something &amp;ldquo;boring&amp;rdquo; that needs doing: there&amp;rsquo;s no fun in coding up DB marshalling logic for the eighth time.&lt;/p&gt;
&lt;p&gt;But using a coding agent for an entire project without getting my hands dirty? Not sure I see myself doing that anytime soon.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>2025 Retro</title>
      <link>https://lmika.org/2026/01/01/retro.html</link>
      <pubDate>Thu, 01 Jan 2026 16:51:49 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/01/01/retro.html</guid>
      <description>&lt;p&gt;No, not the good retro: the &amp;ldquo;agile&amp;rdquo; one. I felt that I didn&amp;rsquo;t have enough material for a comprehensive &amp;ldquo;year wrapped&amp;rdquo; post, but I did want to be a little reflective on what was . So it seemed fitting to use an approach favoured by software teams, where the points are brief, and the action items are never followed up.&lt;/p&gt;
&lt;h2 id=&#34;keep-doing&#34;&gt;Keep Doing&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Public goal keeping:&lt;/strong&gt; Documenting my goals, and when I achieve them, on this blog was a massive success. It wasn&amp;rsquo;t easy, but I did manage to get out more, and I have a new standing event that I didn&amp;rsquo;t at the start of the year. An I attribute that to being public about what I want to achieve. It turns out that being public about your goals tends to keeps them in mind more often. You&amp;rsquo;re more likely to follow up on them as a result. So definitely more of this in 2026.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Getting out more:&lt;/strong&gt; I would like to continue this in 2026, or at least keep it up. Going to boardgames every second Wednesday is a good start, but I think more of this would be good for me.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;do-more&#34;&gt;Do More&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;More reading:&lt;/strong&gt; Seriously, way more reading this year. I didn&amp;rsquo;t finish anything new last year, and the only books I did pick-up were ones I read already. I may need to be a little stricter about when I ought to have finished something.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;More building in public:&lt;/strong&gt; In sort, making more of my work available to others. I tend to be afraid of being on the hook for supporting such work, but the support load of the projects that I did release last year (a couple of Micro.blog plugins) wasn&amp;rsquo;t so bad, and I think I&amp;rsquo;d like it more if I know others are enjoying my work. Doesn&amp;rsquo;t have to be everything, but just something to keep in mind.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Travel:&lt;/strong&gt; This is probably more of a wish rather than a goal, but it would be nice to get out into the world. I barely did any of that last year.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;do-less&#34;&gt;Do Less&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Unhealthy vices:&lt;/strong&gt; More healthy eating, less YouTube, basically. This is probably more of a wish rather than an actual goal, but it is something that&amp;rsquo;s been on my mind, and I do need to improve here. I probably should track some metrics here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Going with the flow in my career:&lt;/strong&gt; The career rut continues and I am wondering whether I should move on. But I&amp;rsquo;m really not sure what I&amp;rsquo;d like to do. I still enjoy working with the technology (or, more generally, on the product development side) so the idea of going into a more managerial position doesn&amp;rsquo;t appeal to me. But moving up the corporate ladder is difficult for one that doesn&amp;rsquo;t want to take on such a role. So would a sideways shift work? Contracting? A new job? All these questions I really need to get a handle on, and not let these career moves just &amp;ldquo;happen.&amp;rdquo; That won&amp;rsquo;t lead to a career I want to lead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;achievements&#34;&gt;Achievements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;1,073 blog posts.
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2025/03/18/dont-be-afraid-of-types.html&#34;&gt;1 blog post&lt;/a&gt; that somewhat went viral, and is still getting hits from Hacker News.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;3 project releases:
&lt;ul&gt;
&lt;li&gt;Two Micro.blog plugins, plus a &lt;a href=&#34;https://isknow.lmika.app&#34;&gt;silly quiz&lt;/a&gt; about ISO standards.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;18 domain names, that&amp;rsquo;s 5 less than this time last year.&lt;/li&gt;
&lt;li&gt;40th rotation around the sun.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are a few other things I&amp;rsquo;d like to do this year that I&amp;rsquo;ll keep private, but I think this is a good start.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Gallery: Rail Infrastructure Around Taradale</title>
      <link>https://lmika.org/2025/12/31/gallery-rail-infrastructure-around-taradale.html</link>
      <pubDate>Wed, 31 Dec 2025 16:33:46 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/12/31/gallery-rail-infrastructure-around-taradale.html</guid>
      <description>&lt;p&gt;Since I was in the area, I figured I&amp;rsquo;d take a quick detour to Taradale to check out some of the local rail infrastructure: the closed station and the viaduct. Here are some photos of both.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251231-021750913.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The main Taradale Station building. Opened 1862, and closed 1976.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251231-021813528.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The bluestone goods shed. The nearby plaque boasts that this is of a &amp;#39;unique design with finely crafted detail.&amp;#39; It is quite a nice building.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251231-022647413.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  A view of the Up platform. I wanted to get closer but the station building looked occupied so I didn&amp;#39;t know if I would be trespassing.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251231-022647413-2.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Close-up of a light fixture on the up platform.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251231-022702857.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Looking up the line towards Melbourne. No trains coming.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251231-022835021.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  On the way out I saw this interesting weather vane at the top of this old semaphore signal in a nearby garden.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251231-023218904.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Unlike the station, the Taradale Viaduct, built 1862, is very much still in operation.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251231-023416170.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  If my eyes don&amp;#39;t deceive me, I believe this design in the railing is a VR. Not sure what I means. My guess: either Victoria Regina, or Victoria Railways.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251231-023537185.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The iron trestles were added in 1933 to reduce stress on the girders from heavier trains crossing the viaduct.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251231-024628616.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Distant view of the viaduct from the highway.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: An API For a Keyframe Animation Package</title>
      <link>https://lmika.org/2025/12/30/devlog-an-api-for-a.html</link>
      <pubDate>Tue, 30 Dec 2025 16:42:35 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/12/30/devlog-an-api-for-a.html</guid>
      <description>&lt;p&gt;Spent the day building a key-frame animator for an Ebitengine project. All coded in Go, which should make it usable for some other projects in theory. I don&amp;rsquo;t have a lot of experience with animation frameworks — other than as an end-user of apps that consist of a timeline where I place keyframes — so I&amp;rsquo;m not aware of what constitutes best practice for a programatic API. So I set out to build one with an API that made sense to me. After one major iteration, this is the approach I came up with.&lt;/p&gt;
&lt;p&gt;The core model type is the Var, which represents an animatable value. These are essentially handles describing the type of value the (package) user wants to animate. At the moment there is only a single type, which is a float:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;xPos&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewFloatVar&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;yPos&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewFloatVar&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To animate the value, the user will need to create an Animation and place any created Vars on a track. Tracks consist of key-frames, which is essentially a time offset, and one or more values a Var should have at that time. These values can be created from the Var itself, by calling &lt;code&gt;Set()&lt;/code&gt;.   This returns a type holding what that Var should be set to at that key-frame (&amp;ldquo;set&amp;rdquo; is probably not the best term for this):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;moveDown&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewAnimation&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;posTrack&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;moveDown&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewTrack&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;posTrack&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;KeyFrame&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.0&lt;/span&gt;, []&lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FloatSet&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;xPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.0&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;yPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.0&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;posTrack&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;KeyFrame&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;3.0&lt;/span&gt;, []&lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FloatSet&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;xPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;100.0&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;yPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;100.0&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Not all Vars need to be included on a particular keyframe, but prior to animating, each of the Vars known to the track will need to be tweened if there isn&amp;rsquo;t a &amp;ldquo;set&amp;rdquo; for them on the key-frame, so it&amp;rsquo;s generally good practice to set all the Vars for each track. Different tracks can have a different of Vars, so if different keyframes are needed, it probably should exist on a separate track.&lt;/p&gt;
&lt;p&gt;Animations are designed to be reusable and simply encode what the Animation is. The motivation here is that all the expensive work should be done once, when the Animation is created. To actually animate the Vars, the user will need to create a timeline and set it to the Animation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewTimeline&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetAnimation&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;moveDown&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will initialise all the Var values to what they would be at frame 0 (at the moment, frame 0 must be a time 0, but down the line I&amp;rsquo;d like to remove this to allow for blending between Animations). The value of each Var can now be retrieved, as Vars only have a value within the scope of a Timeline.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;yPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// x and y == 0, as that is the value they were set to in the first frame&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;drawRect&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Timeline clock is advanced by calling the &lt;code&gt;Advance()&lt;/code&gt; method, which takes a delta in fractions of a second. This will tween all the Vars based on the current action, and the new value can be retrieved by calling &lt;code&gt;Value()&lt;/code&gt; again. Thus the core use of this package is advancing the clock and querying the values for each tweened frame.&lt;/p&gt;
&lt;p&gt;Within an Ebitengine project, this is done by calling &lt;code&gt;Advance&lt;/code&gt; within &lt;code&gt;Update&lt;/code&gt; function and sampling the values within &lt;code&gt;Draw&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Timeline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;xPos&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FloatVar&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;yPos&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FloatVar&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Update&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;dt&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; float64(&lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TPS&lt;/span&gt;())  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Advance&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;dt&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Draw&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;screen&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Image&lt;/span&gt;) {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;vector&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FillRect&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;screen&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       float32(&lt;span style=&#34;color:#a6e22e&#34;&gt;xPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt;)), float32(&lt;span style=&#34;color:#a6e22e&#34;&gt;yPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt;)),  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;col&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s an test animation:&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://cdn.uploads.micro.mov/25293/2025/cleanshot-2025-12-30-at-17.14.42/playlist.m3u8&#34; poster=&#34;https://cdn.uploads.micro.blog/25293/2025/frames/1647920-0-36a228.jpg&#34; width=&#34;1232&#34; height=&#34;720&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m quite happy with how this turns out. I like how little &amp;ldquo;magic&amp;rdquo; is involved in the API: values and clocks are not changed from underneath you: you need to explicitly advance the clock yourself, and read the values when you need them.&lt;/p&gt;
&lt;p&gt;Obviously there&amp;rsquo;s room for improvement. Tracks and keyframes cannot be changed; only linear interpolation of float values are supported; and there&amp;rsquo;s no looping, bouncing, or reversing of animations. I&amp;rsquo;d hope to add all this in due time, probably based on the needs of the project I&amp;rsquo;m working on. There are also a few things I&amp;rsquo;m a little wary of, such as a global atomic int responsible for allocating an ID for each newly created Var. And the Timeline holds Var values in a map which I hope is fast enough for rendering on a screen.&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s nice to have something like this in my toolbox. It&amp;rsquo;s been something I&amp;rsquo;ve been wishing for a while.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Rendering Outlined Text in Ebitengine</title>
      <link>https://lmika.org/2025/12/28/rendering-outlined-text-in-ebitengine.html</link>
      <pubDate>Sun, 28 Dec 2025 09:34:09 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/12/28/rendering-outlined-text-in-ebitengine.html</guid>
      <description>&lt;p&gt;For anyone else using the &lt;a href=&#34;https://ebitengine.org&#34;&gt;Ebitengine&lt;/a&gt; that wants to render text as an outline, I&amp;rsquo;ve had had some success using the &lt;a href=&#34;https://github.com/erparts/go-shapes&#34;&gt;shapes&lt;/a&gt; package. The approach that worked for me was to render the text to a separate image, then call &lt;a href=&#34;https://pkg.go.dev/github.com/erparts/go-shapes#Renderer.ApplyOutline&#34;&gt;ApplyOutline&lt;/a&gt; using the screen as the target.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s some very unoptimised code demonstrating this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;State&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;Draw&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;screen&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Image&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// The image holding the rendered text&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;img&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewImage&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;screen&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Bounds&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;Dx&lt;/span&gt;(), &lt;span style=&#34;color:#a6e22e&#34;&gt;screen&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Bounds&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;Dy&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// The filled in text colour. Here it&amp;#39;s set to red.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// As far as I can tell, this has no effect on the rendered outline.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;col&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ColorScale&lt;/span&gt;{}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;col&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scale&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;pos&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GeoM&lt;/span&gt;{}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;text&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Draw&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;img&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Outline&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;face&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;text&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DrawOptions&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;DrawImageOptions&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DrawImageOptions&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;GeoM&lt;/span&gt;:       &lt;span style=&#34;color:#a6e22e&#34;&gt;pos&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;ColorScale&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;col&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Filter&lt;/span&gt;:     &lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FilterNearest&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewRenderer&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// Sets the outline colour to green.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetColorF32&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0.0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// Applies the outline. The numerical arguments are the &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// x and y offsets, and the outline width.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ApplyOutline&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;screen&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;img&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251228-102957.png&#34; width=&#34;600&#34; height=&#34;359&#34; alt=&#34;Auto-generated description: A computer window displays the word OUTLINE in large, green, outlined capital letters on a black background.&#34;&gt;
&lt;p&gt;If you want to render the filled in font alongside the outline, simply draw &lt;code&gt;img&lt;/code&gt; to the screen before applying the outline:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;screen&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DrawImage&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;img&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DrawImageOptions&lt;/span&gt;{})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewRenderer&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetColorF32&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0.0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ApplyOutline&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;screen&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;img&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251228-103014.png&#34; alt=&#34;Auto-generated description: Large text reading FILLED IN &amp; OUTLINED is displayed in bright green with a red outline against a black background.&#34;&gt;
</description>
    </item>
    
    <item>
      <title>2025 Song of the Year</title>
      <link>https://lmika.org/2025/12/25/song-of-the-year.html</link>
      <pubDate>Thu, 25 Dec 2025 06:24:01 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/12/25/song-of-the-year.html</guid>
      <description>&lt;p&gt;Once again, a need to play something during Christmas Eve Mass has come around, so it&amp;rsquo;s time to elect 2025&amp;rsquo;s Song of the Year.&lt;/p&gt;
&lt;p&gt;No obvious song stood out this year, unlike &lt;a href=&#34;https://lmika.org/2023/12/13/song-of-the.html&#34;&gt;2023&lt;/a&gt; and &lt;a href=&#34;https://lmika.org/2024/12/24/song-of-the-year.html&#34;&gt;2024&lt;/a&gt;. Looking back on what new music I listened to, it was mainly a lot of &lt;a href=&#34;https://en.wikipedia.org/wiki/Dark_Sky_Island&#34;&gt;Enya&lt;/a&gt; and &lt;a href=&#34;https://albumwhale.com/albums/27234&#34;&gt;Lee Rosevere&lt;/a&gt;, with a bit of &lt;a href=&#34;https://eoxstudios.bandcamp.com/track/06-wing-dopamine-extended-neuro-remix&#34;&gt;Anders Enger Jensen&lt;/a&gt;. Near the end of the year I also started listening to &lt;a href=&#34;https://albumwhale.com/albums/13910&#34;&gt;Jon Hopkins&lt;/a&gt;, in addition to one or two other artists featured in the &amp;ldquo;medleys&amp;rdquo; published on &lt;a href=&#34;https://musicforprogramming.net/latest/&#34;&gt;Music For Programming&lt;/a&gt;. Apparently the way I find new music — YouTube videos or finding something I can put on the background for a commute or project work — remains unchanged.&lt;/p&gt;
&lt;p&gt;So, which one get&amp;rsquo;s Song of the Year for 2025? I&amp;rsquo;ll spare you the tension: it&amp;rsquo;s &lt;a href=&#34;https://albumwhale.com/albums/22907&#34;&gt;Dark Sky Island&lt;/a&gt;, by Enya.&lt;/p&gt;
&lt;p&gt;And despite the reason being mainly that this seemed like the appropriate song to play at the time, it&amp;rsquo;s also ties in with the other theme of the year, in that it&amp;rsquo;s one song I remember playing while driving to the train station &lt;a href=&#34;https://lmika.org/2025/10/27/if-one-can-sum-up.html&#34;&gt;thanks to bus replacements&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So bravo, Enya. You&amp;rsquo;ve still got it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Dialogue: Good Banter Not Found</title>
      <link>https://lmika.org/2025/12/19/dialogue-good-banter-not-found.html</link>
      <pubDate>Fri, 19 Dec 2025 13:52:51 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/12/19/dialogue-good-banter-not-found.html</guid>
      <description>&lt;style&gt;
.dialogue.dialogue-2025-12-404-errors {
    --dialogue-m1-color: #4C6C8C;
    --dialogue-m1-background: no-repeat center / 105% url(https://avatars.micro.blog/avatars/2024/15/55331.jpg);
    --dialogue-m2-color: #625309;
    --dialogue-m2-background: white no-repeat center / 75% url(https://lmika.org/uploads/2025/rubber-duck.jpg);
    --dialogue-m3-color: #325f04;
    --dialogue-m3-background: white no-repeat center / 75% url(https://lmika.org/uploads/2025/dialogue-avatar-turtle.jpg);
}
&lt;/style&gt;
&lt;div class=&#34;dialogue dialogue-2025-12-404-errors&#34;&gt;
&lt;p class=&#34;member direction&#34;&gt;&lt;i&gt;Duck is typing on a laptop when Tortoise arrives.&lt;/i&gt;&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; Good day, Duck.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Good day, Tortoise. What a splendid day it is. I see that you are alone?&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; Indeed. I tried reaching the author but he couldn&#39;t be found.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Ah, a regrettable scenario. But not one that is worthy of panic.&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; Curious that you were to say that, as I have been thinking of REST API design recently.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Oh, you too. For you see, I am working on such an API myself. Would you like to see it?&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; Not right now, although I did show the author mine, as I wanted his opinion on something.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Oh, do tell.&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; Well, this API deals with users, you see. And some users pay for the service, and some don&#39;t. And I&#39;ve been tasked with coding an endpoint that&#39;ll answer which is which.&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;To do this, the service is to receive a user ID, and if the user is paying, it is to return the billing information along with a 200 OK.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Okay, that&#39;s pretty straightforward.&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; But what if the user is not a customer? What should the API return then?&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Well, I&#39;d imagine a 404 Not Found would do.&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; That&#39;s what the author suggested, but when I proposed this to the team, they were unhappy.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Unhappy, how?&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; They told me 404 Not Found should be reserved for errors. They said that they&#39;ve got monitoring in place to catch these errors, and if I were to use it to signal a non-paying user, it&#39;ll throw off all these alarms.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Ah, yes. The &#34;only 200 OK is okay&#34; approach to monitoring. Understandable.&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; Oh, so you agree with their concerns?&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Well, I said I understand them. For there are a number of 4xx error codes that are worthy of knowing about sooner rather than later. A significant jump in 401 Unauthorized may be of interest to these operators.&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; I guess. But isn&#39;t that what the 404 is for? Just because it can&#39;t be found doesn&#39;t mean that it&#39;s an error.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Yes, but at the same time, just because it may not be an error, doesn&#39;t necessarily mean it never is. Should a deployment go bad, and a URL is miss-configured, wouldn&#39;t you want to know straight away?&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; That&#39;s the argument they made, and I don&#39;t have a good counter. Using 404 Not Found feels right to me. It&#39;s REST-y. But I also understand their concerns about the monitoring. Oh, Mr D. I&#39;m at a loss of what to do.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Well, some practical thinking may be worth considering. I assume you are using JSON, right?&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; Yes, that&#39;s correct.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; So could you, for example, return an empty JSON object if the billing information is not found? Or maybe a boolean saying whether billing information exists or not, along with the information is available? Maybe a pattern along the same lines if multiple users are required, and if none of them are paying, you can simply return an empty array. Each could be done with a 200 OK response.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;Of course, it may not be true to the idea of REST. But such is the limitations of using error codes for both business logic, such as saying a user is not paying; and control logic, such as when the endpoint is misconfigured.&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; I see. Well this has been enlightening. Thank you, Mr. D. By the way, was this the same problem you were seeing?&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Oh, no. For you see, I took the bold step of shaking off REST in favour of gRPC.&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; Really? gRPC?&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Yes. It eliminates the problem entirely, as it separates the absence of something (NOT_FOUND) from the absence of the service itself (UNIMPLEMENTED, INTERNAL).&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; Oh, how cleaver of you! I wonder if I can convince my team to switch. What an enviable position to be in.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Well, the envy may be all mine. For I am having a devil of a time trying to get the code generator to work. And when you are tasked with debugging an endpoint that is producing an incorrect response, and all you see is unreadable binary data; well, you may be singing a different tune.&lt;/p&gt;
&lt;p class=&#34;member member-m3&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Tortoise:&lt;/b&gt; Well, I will sure be singing a different tune as I head off home and ponder the suggestions you gave me. I will leave you to your errant tool chain. Good day, Duck.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;Duck:&lt;/b&gt; Good day, Tortoise.&lt;/p&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Some Caution on Using ROWIDs for Primary Keys in Sqlite 3</title>
      <link>https://lmika.org/2025/12/18/some-caution-on-using-rowids.html</link>
      <pubDate>Thu, 18 Dec 2025 15:24:21 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/12/18/some-caution-on-using-rowids.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve noticed that relying on ROWIDs for primary keys in Sqlite 3 tables can result in IDs being reused. If you insert a row that is given a ROWID of 1, delete it, then insert another row, that second row will also be given a ROWID of 1. At first I thought it was just the &lt;a href=&#34;https://pkg.go.dev/modernc.org/sqlite&#34;&gt;Go port I was using&lt;/a&gt;, but I also tried a &lt;a href=&#34;https://github.com/mattn/go-sqlite3&#34;&gt;package that uses the C library&lt;/a&gt; and I observed the same thing. It&amp;rsquo;s reproducable using the &lt;code&gt;sqlite3&lt;/code&gt; CLI tool:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251218-161954.png&#34; width=&#34;600&#34; height=&#34;369&#34; alt=&#34;Auto-generated description: A terminal window displays SQLite commands and outputs, including creating and modifying a table with text values.&#34;&gt;
&lt;p&gt;This shouldn&amp;rsquo;t be an issue normally, but I have been trying to enable &lt;a href=&#34;https://sqlite.org/pragma.html#pragma_foreign_keys&#34;&gt;foreign keys&lt;/a&gt; with cascade deletes in a program that&amp;rsquo;s using Sqlite 3. And maybe I&amp;rsquo;m not enabling them properly, but I&amp;rsquo;ve been finding rows that should&amp;rsquo;ve been deleted via this cascade were showing up in joins with new rows created on the foreign table. I don&amp;rsquo;t know why this is not working, but even so, having IDs that are never reused might be helpful here.&lt;/p&gt;
&lt;p&gt;To avoid this, you can add &lt;code&gt;autoincrement&lt;/code&gt; to the ID field to ensure that IDs are always monotonically increasing:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251218-162010.png&#34; width=&#34;600&#34; height=&#34;369&#34; alt=&#34;Auto-generated description: A terminal window displays commands and output illustrating the creation and modification of a test table in SQLite, including inserting and deleting rows.&#34;&gt;
&lt;p&gt;Apparently, this is &lt;a href=&#34;https://sqlite.org/autoinc.html#:~:text=The%20AUTOINCREMENT%20keyword%20imposes%20extra%20CPU%2C%20memory%2C%20disk%20space%2C%20and%20disk%20I/O%20overhead%20and%20should%20be%20avoided%20if%20not%20strictly%20needed.%20It%20is%20usually%20not%20needed.&#34;&gt;not encouraged&lt;/a&gt; as it adds a bit of overhead to inserting rows. But if the price of unique IDs is a little more memory and CPU usage, for my teeny-tiny database,  it&amp;rsquo;s worth it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Using Pagefind in Micro.blog to Only Return Posts in Search Results</title>
      <link>https://lmika.org/2025/12/14/using-pagefind-in-microblog-to.html</link>
      <pubDate>Sun, 14 Dec 2025 10:44:52 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/12/14/using-pagefind-in-microblog-to.html</guid>
      <description>&lt;p&gt;When I tried out Pagefind for the first time on this site, I was finding that the results were including more than just the posts, such as the post lists, the stats page, the archive page, and a bunch of other indexed pages. These were crowding out the posts themselves, and I expressed my wish for a way to control the indexing in some way.&lt;/p&gt;
&lt;p&gt;Turns out there&amp;rsquo;s no need to modify the indexing, you can do this using &lt;a href=&#34;https://pagefind.app/docs/filtering/&#34;&gt;Pagefind filters&lt;/a&gt;. This enables faceted search in that the end user can configure which pages to show in the results, based on arbitrary key-value pairs you as the site author specify in the HTML (I&amp;rsquo;m not doing a great job explaining this, please click through to the documentation to see how it works).&lt;/p&gt;
&lt;p&gt;The way I used this to filter the results down to only post entries is by defining a &amp;ldquo;pagetype&amp;rdquo; filter. This is done by including a HTML element in the Hugo template used for posts. For those using the Card theme, this involves adding a &lt;code&gt;span[data-pagefind-filter]&lt;/code&gt; element to the &amp;ldquo;post/single.html&amp;rdquo; template:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- layouts/post/single.html --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ define &amp;#34;main&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;span&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data-pagefind-filter&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pagetype&amp;#34;&lt;/span&gt;&amp;gt;post&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;span&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;main&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;main&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Doing this will enable a filter sidebar in the default search UI, which I didn&amp;rsquo;t want. So I hid that, along with the filter span itself, with a bit of CSS:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;data-pagefind-filter&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pagefind-ui__filter-panel&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;display&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;none&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;!important&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But there still needed to be a way to enable to filter to apply it to the results. Fortunately, Pagefind has a way to &lt;a href=&#34;https://pagefind.app/docs/ui-usage/#programmatically-controlling-the-pagefind-ui&#34;&gt;control the default search UI&lt;/a&gt; to some degree. And the way to enable this new &lt;code&gt;pagetype&lt;/code&gt; filter is by calling the &lt;code&gt;triggerFilters&lt;/code&gt; methods in the bit of JavaScript initialising the UI:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- /search page --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;search&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    window.&lt;span style=&#34;color:#a6e22e&#34;&gt;addEventListener&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;DOMContentLoaded&amp;#39;&lt;/span&gt;, (&lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;search&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PagefindUI&lt;/span&gt;({ 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;element&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#search&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;showSubResults&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;pageSize&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;excerptLength&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;50&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;autofocus&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#a6e22e&#34;&gt;search&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;triggerFilters&lt;/span&gt;({ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pagetype&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt; ] });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After that, rebuild the site. Pagefind should run and index all the posts with the new filter, and the new filter should be applied by default, resulting in a search that only return the posts itself.&lt;/p&gt;
&lt;p&gt;I hid the filter sidebar for now as I didn&amp;rsquo;t want it cluttering up the UI, but in time, I may bring it back. The filtering mechanism looks pretty powerful, and could be useful for things like category filtering down the line. It would also be nice to find out how to use the filter within the query string itself: I still like the idea of using the hash to filter based on category. But as it stands now, I&amp;rsquo;m very happy with the result.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EDIT:&lt;/strong&gt; Turns out there may be an even easier way to choose what is indexed. Perusing through the documents, you can &lt;a href=&#34;https://pagefind.app/docs/indexing/&#34;&gt;use an annotation&lt;/a&gt; to tell Pagefind what to index. It looks like if you make use of this annotation on a page, anything outside of it won&amp;rsquo;t be indexed (the filters and metadata still will). So the use of filters like this may not be necessary at all.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Gallery: Riding The Metro Tunnel</title>
      <link>https://lmika.org/2025/12/13/gallery-riding-the-metro-tunnel.html</link>
      <pubDate>Sat, 13 Dec 2025 16:15:34 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/12/13/gallery-riding-the-metro-tunnel.html</guid>
      <description>&lt;p&gt;Today&amp;rsquo;s the day! I finally had an opportunity to try out the new Metro Tunnel. And it&amp;rsquo;s stunning. The construction team did an amazing job. Given that the last significant rail project around CBD was the City Loop, which opened in the 1980&amp;rsquo;s, it&amp;rsquo;s certainly nice to have something resembling a modern metro system, complete with features like curtain doors and carriage density indicators. But the new stations had a timeless design aesthetic to them: I want to say Art Deco, but I&amp;rsquo;m too much of a layman to be certain. In either case, I love it. To say that it celebrates commuter rail travel may be a stretch, but it&amp;rsquo;s clear they put a lot of effort into the design language for these stations, and it absolutely shows.&lt;/p&gt;
&lt;p&gt;Anyway, here&amp;rsquo;s a gallery of the photos I took of my ride:&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-224902327.jpg&#34;
     
        alt=&#34;Auto-generated description: Parkville Station features a modern architectural design with a glass entrance and surrounding office buildings.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Outside the entrance to Parkville along Gratten St, on the south side of Melbourne University.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-224930227.jpg&#34;
     
        alt=&#34;Auto-generated description: A wide hallway in what appears to be a modern train station features escalators, platforms, and people walking, with colorful wall panels and overhead lights.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Heading down to the concourse. Note the double-M Melbourne Metro logo lockup on the light fixtures.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-225016034.jpg&#34;
     
        alt=&#34;Auto-generated description: A spacious, modern train station interior features an escalator, visible signage, and a KFC restaurant along one side.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  It was a little early so the natural light coming through the skylights was subtle. In the afternoon the concourse was flooded with sunlight.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-225114849.jpg&#34;
     
        alt=&#34;Auto-generated description: A storefront display features a black-and-white illustration of a bustling city street scene with the People&amp;amp;#39;s Coffee brand prominently showcased.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  This was the second weekend of operation so many of the independent businesses were not quite opened yet.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-225354047.jpg&#34;
     
        alt=&#34;Auto-generated description: A modern train station entrance features ticket barriers, digital information displays, and a few people walking.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Waiting for barriers to open to let us through to the platforms. This can be described as a soft opening, in that the tunnels were only opened during the day outside of peak hours.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-230139717.jpg&#34;
     
        alt=&#34;Auto-generated description: People move through the turnstiles inside a modern train station with directional signage and digital displays.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Ten o&amp;amp;#39;clock. Time to let us through.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-230223198.jpg&#34;
     
        alt=&#34;Auto-generated description: A modern, empty subway platform features clean lines, automated platform doors, and directional signage.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Down on the platforms, looking south towards the CBD.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-230306192.jpg&#34;
     
        alt=&#34;Auto-generated description: A modern, well-lit, and empty subway station platform features benches, digital screens, and vending machines.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Looking north now. The platforms can accomodate 10 carriage trains, although only 7 carriage trans are operating now.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-230325361.jpg&#34;
     
        alt=&#34;Auto-generated description: A mosaic of a stylized, elongated hand design is embedded in a tiled floor.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The indigenous designs that dot the platform are a nice touch.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-230447937.jpg&#34;
     
        alt=&#34;Auto-generated description: A modern underground metro station with tracks, signage, and emergency equipment along the platform.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  At the end of the platform looking down the south tunnel. Notice the block indicator for the in-cab signalling.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-230712600.jpg&#34;
     
        alt=&#34;Auto-generated description: A large ceiling fan with a yellow and silver central hub is mounted above perforated metal surfaces.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  A close-up of the double-M logo lockup. Many of the light fixtures have this design mark.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-231056246.jpg&#34;
     
        alt=&#34;Auto-generated description: A modern, glass-walled subway platform features an arriving train and visible reflections of commuters.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Train arriving to take us south to Anzac station.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-231125322.jpg&#34;
     
        alt=&#34;Auto-generated description: Passengers are seated inside a modern train carriage with yellow poles and overhead LED displays.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  My first time on a HCMT actually, despite them being in operation for a couple of years now.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-231710131.jpg&#34;
     
        alt=&#34;Auto-generated description: A train door is open on a subway platform, revealing safety signs and reflections in the glass.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Still some teething issues, such as aligning the train doors to the platform curtain doors. Once the train had to creep forward a little before the doors could open.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251212-232530540.jpg&#34;
     
        alt=&#34;Auto-generated description: A modern entrance to Anzac Station is shown, featuring a sign and a few people nearby, with a city tram and buildings in the background.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Anzac station, on the south end of the tunnel. This, I gather, is going to be a busy mode transfer between the tunnel and the Swanson St. trams.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251213-015757529.jpg&#34;
     
        alt=&#34;Auto-generated description: A modern architectural space with a high ceiling, green beams, and a staircase leading to an upper level with glass panels.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Under the wooden canopy of the Anzac station concourse.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251213-023516853.jpg&#34;
     
        alt=&#34;Auto-generated description: A long escalator descends in a modern building with people on both sides and screens along the walls.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  After a quick lunch at Melbourne Central, it was time to take a look at State Library. This was probably the deepest of the stations, as it intersected with the underground City Loop.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251213-023644224.jpg&#34;
     
        alt=&#34;Auto-generated description: A modern, spacious train station features orange arches, overhead signage, and people walking along the platform.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The platforms of State Library. The use of the arched ceiling and the chandeliers  was quite different to the other stations I saw today.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251213-023808822.jpg&#34;
     
        alt=&#34;Auto-generated description: A modern, empty subway station features a curved ceiling with warm lighting and a clean, spacious platform.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  State Library is probably the best example of how strong the design language is: the orange colour scheme and Art Deco (maybe) style of the light fixtures.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251213-030014321.jpg&#34;
     
        alt=&#34;Auto-generated description: A modern, multi-level indoor space features an elevator, concrete walls, glass railings, and several people walking or waiting.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  One more stop today, Arden Station, near the north east. Another station that relies a lot on natural light.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251213-030217511.jpg&#34;
     
        alt=&#34;Auto-generated description: A large building features a mural of two outstretched hands on its facade, with an arched entrance and people walking nearby.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The exterior of Arden Station. This is out of my way so I&amp;amp;#39;m not likely to visit this station often, so I&amp;amp;#39;m glad I had a chance to see it today.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251213-030356429.jpg&#34;
     
        alt=&#34;Auto-generated description: A series of curved architectural arches with overhead lighting is visible above the escalators.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Under the brick arch, which was the selling point that got me to take a look.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251213-030444019.jpg&#34;
     
        alt=&#34;Auto-generated description: A stone engraved with C&amp;amp;amp;M R 1857 is displayed behind glass, with reflections of people and surroundings visible on the surface.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  A sandstone plaque for the Gellong and Melbourne Railways from 1857, designed to commemorate the opening of Werribee Station, which was set on the wall of one of the workshops where Arden Station is.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251213-031141700.jpg&#34;
     
        alt=&#34;Auto-generated description: A modern indoor setting with escalators, overhead lights, and a Way out sign.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Back at Parkville and heading back out.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;I didn&amp;rsquo;t get a chance to see Town Hall, but it was a lot of fun checking out the new stations, and I was by far the only one. It being the end of the second week of operations, and all public transport being free over the weekend, I&amp;rsquo;d imagine most were simply taking a look at this spanking new bit of infrastructure. But I&amp;rsquo;m already hearing from people I know that this would help a lot with getting to and around the city, so I think this tunnel will be a success. I too have my plans for integrating this into my own commute.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Trying Out Pagefind in Micro.blog</title>
      <link>https://lmika.org/2025/12/09/trying-out-pagefind-in-microblog.html</link>
      <pubDate>Tue, 09 Dec 2025 07:21:24 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/12/09/trying-out-pagefind-in-microblog.html</guid>
      <description>&lt;p&gt;Trying out &lt;a href=&#34;https://www.manton.org/2025/12/08/pagefind-and-microblog-actions.html&#34;&gt;Pagefind&lt;/a&gt; that was just released for Micro.blog. I find myself using the search on this blog surprisingly often, and while the search plugin has served me well, I&amp;rsquo;m always up for trying something new.&lt;/p&gt;
&lt;p&gt;It took me a bit of time to work out how to get the actual search page. What I did was uninstall the search plugin, create a new &amp;ldquo;Search&amp;rdquo; page, and copied-pasted the &lt;a href=&#34;https://pagefind.app/docs/ui-usage/&#34;&gt;sample UI&lt;/a&gt; on Pagefind&amp;rsquo;s docs. That worked without a hitch. I explored changing some of the &lt;a href=&#34;https://pagefind.app/docs/ui/&#34;&gt;display options&lt;/a&gt;, such as raising the number of results and the size of the snippets. Interestingly, a smaller number of results actually feels nicer. I started with 25 but seeing all those results felt overwhelming (who knew there was so much nonsense written here). I think I&amp;rsquo;ll give 10 a go.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251209-080903.png&#34; width=&#34;600&#34; height=&#34;403&#34; alt=&#34;Auto-generated description: A code snippet featuring a search function and options to include the page in navigation, set page size, and excerpt length is displayed.&#34;&gt;
&lt;p&gt;If I can provide some feedback to Manton, it would be to choose which pages to index. I did a search for &amp;ldquo;Devlog&amp;rdquo; and found that the stats page, category list page, and various pages of the blog list were showing up in the results. It would be nice to configure the index to just the blog posts, maybe also the custom pages, and leave the entry lists out. I am curious to explore the capabilities of Pagefind some more, particularly around faceted search. One thing I&amp;rsquo;ve been thinking of for a while is the ability to use &lt;code&gt;#&lt;/code&gt; to filter based on cataegories, such as &lt;code&gt;#photos birds&lt;/code&gt; to return only the results with the &amp;ldquo;Photos&amp;rdquo; category. I did consider hacking this into the search plugin, but maybe I won&amp;rsquo;t have to.&lt;/p&gt;
&lt;p&gt;All in all, I think I like it. I like how fast the results come through, and although I do prefer the UI of the search plugin, I think that&amp;rsquo;s solvable by tuning this one to my desires (or, just more likely, I&amp;rsquo;ll simply get use to it). I think this is a great addition to Micro.blog.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Godot Project - Level 3-2 And Bobbing Water</title>
      <link>https://lmika.org/2025/12/08/devlog-godot-project-level-and.html</link>
      <pubDate>Mon, 08 Dec 2025 21:32:32 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/12/08/devlog-godot-project-level-and.html</guid>
      <description>&lt;p&gt;I spent a lot of time on distractions recently, so I was a little surprised to find myself wanting to get back to working on that Godot game. The level under design is going okay: it took a few attempts to find the right way to start. Turns out writing down how you want the level to progress helps, like having three &amp;ldquo;acts&amp;rdquo; where the first act consists of some jumping puzzles, then introducing one of the gimmick, than a variant of said gimmick, and so on.&lt;/p&gt;
&lt;p&gt;Anyway, I&amp;rsquo;m working on the second &amp;ldquo;act&amp;rdquo; which will feature a low-power mechanic: there&amp;rsquo;s only so many units of power to energise the various zones, and the player needs to juggle it all to get through the act. Because power is involved, I wanted to have a generator sprite to provide some decoration. This is what I came up with:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251203-221842.png&#34; width=&#34;600&#34; height=&#34;327&#34; alt=&#34;Auto-generated description: A pixelated knight stands next to a large, metal generator in a block-brick environment with blue water below.&#34;&gt;
&lt;p&gt;It&amp;rsquo;s… fine. I did try to get some inspiration by uploaded the tile-set to Google Gemini and asked it to produce something that resembled a generator:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Using the tile set images uploaded, please provide some suggested designs for a tile-set representing an electric generator. The suggestion must match the style and colour scheme of the supplied image.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;This helped, although what I have here is a fair bit different to what was generated. I may adjust the colours a bit (the simple colours are by design as I want to maintain the palette used by the existing tile-set) and I may need to add some wire decorations and maybe a sign that says &amp;ldquo;Generator&amp;rdquo; to make it clear that&amp;rsquo;s what it is, and that it&amp;rsquo;s not a cannon, as the alt-text generation suggests. We&amp;rsquo;ll see how it goes, I guess.&lt;/p&gt;
&lt;p&gt;One other thing I found myself wanting to add is water that can be raised and lowered when activated. I came up with something from first principals that looks a little like this (the tile layer is hidden to show the &lt;code&gt;Area2D&lt;/code&gt; shapes):&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251208-221530.png&#34; width=&#34;600&#34; height=&#34;226&#34; alt=&#34;Auto-generated description: A 2D platformer game editor displays a character, crates, water sections, and terrain blocks.&#34;&gt;
&lt;p&gt;It consists of is a top-level &lt;code&gt;Node2D&lt;/code&gt; which will raise and lower when triggered. This is done in code, just to allow me to configure the speed and displacement on a case-by-base basis. A child of that is a &lt;code&gt;TileMapLayer&lt;/code&gt; containing the water tiles. It&amp;rsquo;s at Z-index 6, which is just in front of the player so that it looks like it engulfs them if they fall in. Four units below the top is the kill-plane — water is a hazard — and four units below that is the &amp;ldquo;bobbing&amp;rdquo; plane. This is a &lt;code&gt;Node2D&lt;/code&gt; that simply oscillates up and down by 2 units every second. A child of that is an &lt;code&gt;Area2D&lt;/code&gt; solid with allows the player to move through, but will collide with a new crate sprite, which also collides with everything else.&lt;/p&gt;
&lt;p&gt;This has the effect of a water plane in which creates would appear buoyant yet slightly submerged, giving the player somewhere to stand. Activating the water layer will allow it to &amp;ldquo;drain,&amp;rdquo; bringing the crates down to rest on any solids. Activate it again and the water level will rise, catching any crates which will begin floating:&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://cdn.uploads.micro.mov/25293/2025/screencast-water-bobbing/playlist.m3u8&#34; poster=&#34;https://cdn.uploads.micro.blog/25293/2025/frames/1633294-2-a7753f.jpg&#34; width=&#34;1158&#34; height=&#34;720&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s probably better ways to do this, and there are some drawbacks. It can&amp;rsquo;t be bundled into a dedicated scene, so each instance will need to be built manually. And if more than one crate is in the scene, they will bob in unison (although that could probably be alleviated with splitting the bobbing plane into areas with distinct offsets). But for something devised from first principals, I&amp;rsquo;m quite happy with it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Blogging Tools - Image Collections Triage App</title>
      <link>https://lmika.org/2025/11/29/devlog-blogging-tools-image-collections.html</link>
      <pubDate>Sat, 29 Nov 2025 09:54:02 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/11/29/devlog-blogging-tools-image-collections.html</guid>
      <description>&lt;p&gt;Up until now, I&amp;rsquo;ve been using Blogging Tools&amp;rsquo; in-app notification system to sort images to collections. There were quite a few limitations in doing so: only notifications for the first for images were raised, galleries weren&amp;rsquo;t handled, and you could only select one category per image. I also wanted a way to automatically change the header image in this blog. This was done using the image category notification, but it was pretty basic in that the image was chosen as the header if any category was selected.&lt;/p&gt;
&lt;p&gt;I had ideas of improving all of this but I realised quickly that I needed something more sophisticated to do so. So I added another app to Blogging Tools to do this. I also thought to try out using Antigravity to do this. I was curious to know how well Google Gemini would work with a project that already had an established architecture and coding patterns. I was also curious to know how Antigravity felt when I held a tighter grip of the reigns: I wanted to drive the development work and only turning to the agent when I need something done.&lt;/p&gt;
&lt;p&gt;I started simply enough: I built a basic HTML template for this new app and asked Gemini to prepare a stub handler:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Please create a new handler that services the template &amp;ldquo;imageposts/show.html&amp;rdquo;. Implement it much like the other handlers found in the &amp;ldquo;handlers&amp;rdquo; directory.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I forgot to mention to the agent that this was to be a stub, and it ended up using the existing provider for the gallery app. But it was pretty close to what I wanted. I then asked it to suggest a good HTML entity for opening a page in an external window.&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;What&amp;rsquo;s a good HTML entity for indicating that a link will open up into an external window/tab?&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;It suggested &lt;code&gt;↗&lt;/code&gt;, also a good answer. I then went on to build the Go models and wanted to work on the CSS styling next, so I asked Gemini to produce some example data using these models:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Please generate 3 stubbed values for this item slice. Use real example image URLs for the source.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;It struggled a little on this one, suggesting a URL that didn&amp;rsquo;t exist. But after pointing this out, it got there in the end. It was at this point that I needed to do some refactoring to some of the existing services, needing to extract out a type that used only the values from another type. Gemini did quite well here too:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Note the select call. At the moment this is returning a micropub.Item model. I want to make a new model called &amp;ldquo;ImageCollection&amp;rdquo; that uses only the values of this micropub.Item that the code is currently using now. I don&amp;rsquo;t want the micropub.Item changed, but any code that is using this for the purpose of the selected call should include a map to this new model.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I guess this is not a novel experience to anyone using Cursor, but what I really liked about this interaction is that I was free to work on other things or move away from the keyboard entirely while the agent was working. This is one annoyance that I have with Claude Code: the exchange pauses and waits for me to approve every change before it proceeds. Having Gemini plan the changes first, then have me approve them once I&amp;rsquo;m back is a much nicer way of working.&lt;/p&gt;
&lt;p&gt;I then turned to the database schema, defining the table schema and SQL queries myself, and asking Gemini to produce the Go code that marshals the data: the so called &amp;ldquo;database providers&amp;rdquo;.&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Implement the Go DB provider for DB tables for the queries in &amp;ldquo;imageposts.sql&amp;rdquo;. Do it in a similar style to the other providers found in &amp;ldquo;providers/db&amp;rdquo;.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;This is where I get the most use out of using coding agents. This code is not hard, but it&amp;rsquo;s super annoying to write. I think it also helps that there&amp;rsquo;s an established code base for the agent to work on. I was a little unhappy with what the agent produced last week, probably because it was required to produce it from scratch and in a way that wasn&amp;rsquo;t how I would write it. But here, probably because the agent had existing patterns to work on, it&amp;rsquo;s producing something that looks a lot better, to my eyes at least.&lt;/p&gt;
&lt;p&gt;It did struggle with the providers a little, choosing to merge an update to two models in a single method. I did try to correct it, but I just had to go in and fix it manually.&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;We can probably separate SaveImagePost into one that saves the ImagePost, and another that saves ImagePostItems.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;One other thing I discovered is that getting Gemini to write unit tests is important, as it provides a way for the agent to verify it&amp;rsquo;s code. I approved a change Gemini produced without looking too closely, and if I were to test it myself, it would&amp;rsquo;ve failed. But only after I asked it to produce a unit test did I see it fix the errors introduced in the last interaction. Something to think about in the future.&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Might be a good idea to implement a unit test to verify that the DB methods in imageposts.go work. Do this by creating a new db.Provider instance with a temporary file, and verifying that the provider methods of imageposts.go work as expected. Please use testify.Assert to do the asserts.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;It was now time to build the service layer. This is something I wanted to drive myself, occasionally relying on auto-suggestions. Antigravity fell down (ha ha) here a little, producing suggestions that were pretty far from what I wanted. I guess it was lacking the context to know what I actually wanted, which is somewhat understandable as I wasn&amp;rsquo;t giving that context. It also seems to fight with IntelliSence from VS Code sometimes. I occasionally press tab in response to a type suggestion from the index, only to have it produce a completion for some unknown type. I do wish the suggestions use the code index more in general. There&amp;rsquo;ve been more than a few times where the agent suggested types and methods that just didn&amp;rsquo;t exist. Might be something that can be tuned.&lt;/p&gt;
&lt;p&gt;One quality of life improvement I did make was increase the timeout for when suggestions would appear. The default was something like 50 milliseconds, which is pretty short. Bumping it to 1 second helps, but even so they still get in the way. I may bump it a little higher.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pasted-image-20251129100529.png&#34; width=&#34;600&#34; height=&#34;449&#34; alt=&#34;Auto-generated description: A web interface for blogging tools features options to categorize or set an image of a scenic landscape as a header.&#34;&gt;
&lt;figcaption&gt;First cut of the collection triage screen at the end of day one.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I worked on this feature over the next couple of evenings, occasionally changing tack which required a change in the database schema. One thing I did like while working on this was getting to the point where I could simply ask the agent to update the provider code whenever I did so, and it knew what I meant:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;The ImagePost model and table schema now has a PostDate field/column. Please update the providers.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I do get the feeling that the user interaction in Antigravity needs to be refined a little. For someone who is willing to do most of the driving, it would be nice if the editor was a little less &amp;ldquo;needy&amp;rdquo;. This is probably more to do with VS Code being the underlying editor: lots of annoying popups and mouse overs, all shouting for my attention: LOOK AT THIS, LOOK AT THIS. But the AI suggestions don&amp;rsquo;t help. Instead of displaying the generated code, disrupting my sense of where everything is, maybe a more subtler approach is warranted: a small indicator beside the cursor or in the margin indicating that a suggestion is present. I may miss it, but who cares? Remember, I&amp;rsquo;m the one driving here.&lt;/p&gt;
&lt;p&gt;Today, I got the feature finished. The way it works is that it will poll the RSS feed of this blog, and for every post that has an image, it will add an entry for me to triage. Each entry consists of a brief summary, plus the images found within the post:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pasted-image-20251129100017.png&#34; width=&#34;600&#34; height=&#34;449&#34; alt=&#34;Auto-generated description: A menu interface displaying various blogging tools and options for managing images, videos, and text.&#34;&gt;
&lt;figcaption&gt;A new &#39;Sort Image Posts&#39; app, with a badge indicating the number of images to triage.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pasted-image-20251129100346.png&#34; width=&#34;600&#34; height=&#34;449&#34; alt=&#34;Auto-generated description: Blogging interface featuring a post titled Under the big, grey sky-rail with a small image and options to edit or delete.&#34;&gt;
&lt;figcaption&gt;List of posts with images that need triaging.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Clicking through to the post would list the collections, and I would select the ones I&amp;rsquo;d like the image to be filed in. And this would list all the images of the post, not just the first few. It is all or nothing, unfortunately. There are ways to get the images of the collection, but I couldn&amp;rsquo;t see a way for me to get the collections of an image. I don&amp;rsquo;t think this would be a problem, as Blogging Tools is the only way I actually set the collections of an image.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pasted-image-20251129100432.png&#34; width=&#34;600&#34; height=&#34;449&#34; alt=&#34;Auto-generated description: A screen displays a blogging tool interface with an image of an elevated roadway and options for categorizing an image post.&#34;&gt;
&lt;figcaption&gt;Selecting a post will list the images found within that post, with the collections they should be added to.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Saving the categories will queue the changes, which will be applied every 5 minutes. A first cut of this feature applied the changes immediately, but the way I imagine using this is that I&amp;rsquo;d go in, triage the images in one go, then leave. Applying all the changes then felt inefficient.&lt;/p&gt;
&lt;p&gt;When it comes to setting the header image, I figured I&amp;rsquo;d add a dedicated collection for that. Every hour a cronjob will query the local database for images that is within this category, and write the URLs to a manifest file in the assets Git repository. A Forgejo Action on that repository runs every night, and will check the date-stamps for the next image to display on the header. If one is found, it will fetch the image, save it as a JPEG with reduced quality and upload to Netlify. Since Netlify flushes the CDN whenever a deployment happens, nothing more needs to be done: no MD5 sum hashes, no adjustment to CSS files. Just update &lt;code&gt;header.jpg&lt;/code&gt; and go:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Example of the manifest file.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;images&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;date&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-11-22T13:00:00Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://lmika.org/uploads/2025/pxl-20251120-083552448.jpg&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;crop&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bottom&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;date&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-11-26T13:00:00Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://lmika.org/uploads/2025/a3c74bfebd.jpg&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Deployed all these changes to prod and naturally some rework that&amp;rsquo;s needed. First, I completely forgot about galleries. They do come through as image tags in the generated HTML, but I&amp;rsquo;m operating on the source post, and they appear as Hugo short-codes. Fortunately the way I write galleries is pretty standardised now, so I think a regular expression would work here.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;galleryRegexp&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;regexp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MustCompile&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`[{]{2}[&amp;lt;][ ]?glightbox.*src=&amp;#34;([^&amp;#34;]+)&amp;#34;.*[&amp;gt;][}]{2}`&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Oh, I also want to fix the layout of the collection checkboxes and hide some options, such as those that for past years. And finally, will turn off the existing image category notification logic.&lt;/p&gt;
&lt;p&gt;Deployed again, and now I just need to wait for the next time I post a photo. I suspect some bug fixes or usability adjustments will come from this, but more on that when that happens.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some Thoughts on the State of Python Package Documentation</title>
      <link>https://lmika.org/2025/11/27/some-thoughts-on-the-state.html</link>
      <pubDate>Thu, 27 Nov 2025 11:09:28 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/11/27/some-thoughts-on-the-state.html</guid>
      <description>&lt;p&gt;Wow, the state of package documentation in Python is awful!&lt;/p&gt;
&lt;p&gt;Maybe I&amp;rsquo;m just spoiled using languages like Java and Go that have excellent tooling for browsing package documentation of the standard library AND all available third-party packages. Their conventions pretty much boil down to this: it&amp;rsquo;s this way or the highway.&lt;/p&gt;
&lt;p&gt;As far as I can tell, there&amp;rsquo;s nothing like this in the Python space. There are several browsers for the standard library docs, but I want to browse the docs of packages I install via Pip, and I&amp;rsquo;ve not found a canonical way to do so. There doesn&amp;rsquo;t seem to be a unified way in which package documentation is published. At one end you have vendors using the same doc generators as the standard library, which is fine. But at the other end, you&amp;rsquo;re left looking at &lt;a href=&#34;https://github.com/kubernetes-client/python/blob/master/kubernetes/README.md&#34;&gt;massive Markdown files&lt;/a&gt; trying to find the package a particular symbol belongs (fix this please, Google).&lt;/p&gt;
&lt;p&gt;This really is one area where Python shows it&amp;rsquo;s age. For a language that is popular and forward looking as it is, it feels like it missed that crucial time when the language designers took on the responsibility of standardising the way package documentation is published. Hope this is something that can be fixed. They&amp;rsquo;ve laid the foundation for this, by adding &lt;a href=&#34;https://peps.python.org/pep-0257/&#34;&gt;docstrings&lt;/a&gt; for example. They just need to make it easy to browse it (and no, &lt;code&gt;pydoc&lt;/code&gt; does not do this. I&amp;rsquo;ve tried it).&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Gallery: Revisiting Preston Market</title>
      <link>https://lmika.org/2025/11/26/gallery-revisiting-preston-market.html</link>
      <pubDate>Wed, 26 Nov 2025 20:22:15 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/11/26/gallery-revisiting-preston-market.html</guid>
      <description>&lt;p&gt;I was rendezvousing with a few people for dinner around Preston this evening. I was early so I&amp;rsquo;d though I&amp;rsquo;d walk around the closed market. I used to come here all the time when I was a kid, and it was nice walking the laneways again, even if everything was closed for the night. As with most places you revisit when you&amp;rsquo;re older, it&amp;rsquo;s smaller than I remembered.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251126-062001208.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  What could be described as the main laneway. Turn around and you would see Preston station. The tent-like roofs were not original, and the centre of each laneway was open to the elements.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251126-062026242.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The meat stalls. Back in the day you would have rails suspended from the ceiling where you could move hooked carcasses around.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251126-062049563.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  I find the complex itself a bit of a warren, and as a kid, it was easy to get turned about.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251126-062127400.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  I believe this area is where you&amp;#39;d find greengrocers.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251126-062349994.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251126-062544703.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  This area used to have coffee and pastries, particularly cannoli.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251126-062637347.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Featured sections in the middle of the laneways would change from time to time. You&amp;#39;d occasionally find people trying to sell rooster and duck chicks. Today, apparently, it&amp;#39;s flowers.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251126-062651896.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251126-062732749.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>First Impressions Of Google Antigravity</title>
      <link>https://lmika.org/2025/11/22/first-impressions-of-google-antigravity.html</link>
      <pubDate>Sat, 22 Nov 2025 13:03:33 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/11/22/first-impressions-of-google-antigravity.html</guid>
      <description>&lt;p&gt;Well, as predicted, I had a go at using Google Antigravity.&lt;/p&gt;
&lt;p&gt;I downloaded last Wednesday to try out the editor and agent. I didn&amp;rsquo;t have a good idea for it so I simply asked it to produce &lt;a href=&#34;https://tools.lmika.app/timestamps/&#34;&gt;an additional tool&lt;/a&gt; for my suite of online tools: one for dealing with timestamps and timezone conversions. I had plans for this one for a while, although they were relatively vague, it seemed like a good test of Gemini as a coding agent.&lt;/p&gt;
&lt;p&gt;I started with a bit of vibe-coding to see how much it could produce on it&amp;rsquo;s own. Here the prompt I used:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Note the tools located in &amp;ldquo;site&amp;rdquo;. I would like another one made that will process timestamps.&lt;/p&gt;
&lt;p&gt;The tool will consist of two text boxes side-by-side. The text box to the left will be user input: the user will use this to enter timestamps of various types, with one timestamp per line. On the right will be the output. It is not designed to be editable, but the user should be able to scroll, copy, and otherwise get the text from this pane.&lt;/p&gt;
&lt;p&gt;Above the two should be a select picker of operations. Beside that are two radio buttons: &amp;ldquo;UTC&amp;rdquo; and &amp;ldquo;Local&amp;rdquo;. This is for selecting the given timezone, where Local is the browser&amp;rsquo;s local timezone.&lt;/p&gt;
&lt;p&gt;The way this works is that the user will enter a line of input in the text area in the left. When changed, as long as the line of input is valid, the tool will apply the operation and display the result on the same line as the input in the text area on the right. The operation to apply will depend on the picker. Blank lines and any line beginning with &amp;ldquo;#&amp;rdquo; are valid and are to be copied as is. Timestamps are to be displayed in ISO 8601 within the selected timezone.&lt;/p&gt;
&lt;p&gt;The picker should have the following options, which do the following operations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;Convert from Unix&amp;rdquo;: converts the input in seconds from the Unix epoch into a timestamp on the right.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Convert from Unix Micro&amp;rdquo;: converts the input in milliseconds from the Unix epoch into a timestamp on the right.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;To UTC&amp;rdquo;: converts the input in ISO 8601 in the selected timezone into UTC, regardless of what timezone is chosen. &amp;ldquo;From UTC&amp;rdquo;: converts the input in ISO 8601, which is assumed to be in UTC, into the selected timezone.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This should be implemented as a static HTML page with vanilla JavaScript.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;The agent did crash a few times: probably launch day, go-live teething problems. At one point it stopped halfway, and I had to ask it to &amp;ldquo;continue with the implementation plan.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;In the end it did manage to produce a decent-looking HTML UI, plus a working JavaScript implementation of what I described. The quality of the generated code was… fine. The JavaScript worked, but it wasn&amp;rsquo;t as neat as something produced by Claude Code, and definitely not as neat as something I would&amp;rsquo;ve written myself (the HTML was fine).&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251119-224038.png&#34; width=&#34;600&#34; height=&#34;361&#34; alt=&#34;Auto-generated description: A timestamp converter interface displays UNIX time conversions for specified dates and the current time.&#34;&gt;
&lt;p&gt;I had ideas of going further, so I asked the agent to port the JavaScript to Go and to produce a WSDL target, like many of the other tools.  The agent had no issues doing so.&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Please port the main logic defined in &amp;ldquo;main.js&amp;rdquo; to Go targetting WASM, in a new directory located in &amp;ldquo;cmds&amp;rdquo;. You can use the other WASM files found in &amp;ldquo;cmds&amp;rdquo; as a guide.&lt;/p&gt;
&lt;p&gt;In it&amp;rsquo;s place, please change &amp;ldquo;site/timestamps/main.js&amp;rdquo; to be a WASM launcher, much like ther other launchers defined in &amp;ldquo;site/clocks&amp;rdquo; and &amp;ldquo;site/templates&amp;rdquo;. Add the new Go target to the Makefile&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I then &amp;ldquo;started driving&amp;rdquo; by extending the Go code by adding a parser for a potential mini-language. And from this I got to experience Antigravity as a code editor. And it&amp;rsquo;s essentially VS Code, which honestly is not my favourite IDE. In fact, with all the LLM-powered code completion, it&amp;rsquo;s a little closer to Cursor, which I only tried once before. I was turned off by Cursor due to all the suggestions getting in the way, and Antigravity is a bit like that too. I kind of wish there&amp;rsquo;s more prioritising of suggestions: those that fix an compile error, or can be inferred by a refactor should have more weight than those that lack the context to be more than just mere guesses. The latter is rarely what I want, and I usually have to leave the home row to reach for the Escape, or I&amp;rsquo;ve accidentally pressed tab and have to back-out of the unwanted insert. Maybe that&amp;rsquo;s something that I can adjust: I haven&amp;rsquo;t looked, but I would prefer it if it just did nothing if it couldn&amp;rsquo;t be sure of what I want.&lt;/p&gt;
&lt;p&gt;I also tried the testing agent, where it spun up Chrome and drove it to test the UI. The agent did have some issues launching the service — probably because I had the dev server already running — but it did successfully test the UI out. The tests were unsuccessful, and I had to go in and debug it, but hey, that&amp;rsquo;s something, I guess.&lt;/p&gt;
&lt;p&gt;So, would I use Antigravity again? I mean, it&amp;rsquo;s a decent IDE (or maybe it&amp;rsquo;s better to say that it&amp;rsquo;s built on a decent IDE), and I think the agents work quite well. But I probably wouldn&amp;rsquo;t use it again, mainly because it&amp;rsquo;s now how I like to interact with agents. I&amp;rsquo;ve grown accustom to how Claude Code works, where the agent takes instruction from you on your terms. Otherwise it stays out of the way. And to be fair, that&amp;rsquo;s sort of how the agent in Antigravity works too. If they just toned down the suggestions in the editor itself, it may be something for me in the future. We&amp;rsquo;ll see.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Gallery: Walking the Darebin Creek Trail</title>
      <link>https://lmika.org/2025/11/20/gallery-walking-the-darebin-creek.html</link>
      <pubDate>Thu, 20 Nov 2025 20:09:47 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/11/20/gallery-walking-the-darebin-creek.html</guid>
      <description>&lt;p&gt;Had to do some stuff today that weighed on me a little, so it was nice to be given the opportunity to walk the Darebin Creek trail this evening. Walked as far as the Main Yarra Trail, beside the Eastern Freeway. It was a really lovely evening, and it called for some photos to be taken.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251120-083552448.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Crossing the Yarra.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251120-083858000.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251120-084243678.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Freeway overpass beside the Main Yarra Trail. It&amp;#39;s been a while since I&amp;#39;ve been at this part of the trail, so I completely forgot about it.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251120-090249613.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251120-090422625.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Lots of families of ducks out this evening.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251120-090641185.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20251120-092133313.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Github FOMO</title>
      <link>https://lmika.org/2025/11/19/github-fomo.html</link>
      <pubDate>Wed, 19 Nov 2025 07:29:49 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/11/19/github-fomo.html</guid>
      <description>&lt;p&gt;We&amp;rsquo;re coming up to about a year-and-a-half of moving my code to a &lt;a href=&#34;https://lmika.org/2024/07/09/a-tour-of.html&#34;&gt;self-hosted Forgejo instance&lt;/a&gt;. So far it&amp;rsquo;s been great, but I will admit that I am suffering from a bit of Github FOMO. So much of the technical world assumes your code lives on Github. Many tools assume you&amp;rsquo;re either pushing code, or distributing binaries via Github. None of the AI coding assistances work with anything other than Github.&lt;/p&gt;
&lt;p&gt;Even when you&amp;rsquo;re considering whether you should advertise where the source code of your open-source projects, it feels a little funny posting a link to an SCM that is not Github. Open-source development is a social activity, and I doubt it helps the appeal of your project in the eyes of potential contributors if your code is not where they tend to be. Doubly so when when you consider that they&amp;rsquo;ll need to create an account on your SCM service to contribute.&lt;/p&gt;
&lt;p&gt;There are other costs that I haven even considered, like the stability of external services your open-source SCM relies on. I found I wasn&amp;rsquo;t able to run any CI/CD builds last night because Codeberg was down and the pipelines were unable to fetch the Git checkout action. The idea of relying on services hosted by volunteers is a nice one, but I can imagine Microsoft&amp;rsquo;s SLAs being a lot stricter when it comes to keeping Github online.&lt;/p&gt;
&lt;p&gt;So yeah, it&amp;rsquo;s cold and lonely out here. I&amp;rsquo;ll keep at it though: I think the benefits that come from having my own place for my code is worth it. But I do wonder if I should move some projects back, at least some of the &amp;ldquo;big&amp;rdquo; ones. Not that any of the projects I&amp;rsquo;m working on need contributors, but just to be where others are. There&amp;rsquo;s still value in that.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Blogging Tools - Chunking File Uploader</title>
      <link>https://lmika.org/2025/11/16/devlog-blogging-tools-chunking-file.html</link>
      <pubDate>Sun, 16 Nov 2025 19:40:43 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/11/16/devlog-blogging-tools-chunking-file.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m hoping to use Micro.blog more for video hosting, meaning that I would like a fast way to upload them. Because my internet connection is not fast, I&amp;rsquo;m looking at adding some fancy uploading techniques to Blogging Tools. Blogging Tools does have a simple file repository, and I&amp;rsquo;ve already built a reusable upload component, which uploads the contents of a file with a single HTTP request. My goal is to improve the performance of this by using a few of the techniques &lt;a href=&#34;https://dev.to/leapcell/how-to-handle-large-file-uploads-without-losing-your-mind-3dck&#34;&gt;found on this dev.to page&lt;/a&gt;, such as chunking and concurrency.&lt;/p&gt;
&lt;p&gt;But first, I start with some baseline tests. I will simulate a slow upload by throttling the browser speeds at &amp;ldquo;Fast 4G&amp;rdquo;, and test the time it takes to upload a 45.4 MB file.&lt;/p&gt;
&lt;p&gt;Test done. It took 269.174 seconds, about 4.49 minutes.&lt;/p&gt;
&lt;p&gt;So my first goal is add some chunking and concurrency uploads, similar to what was suggested in the linked article. The way I&amp;rsquo;m thinking of doing this is as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Modify the &amp;ldquo;new upload&amp;rdquo; request handler to accept a maximum file size, a chunk size, and the expected number of chunks. The server will respond by creating a empty file of that size, ready to be written to.&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;ll then make multiple fetches requests, each uploading a single chunk, identified by the chunk&amp;rsquo;s starting offset.&lt;/li&gt;
&lt;li&gt;Then, once all chunks are uploaded, I&amp;rsquo;ll send a finalise request with a file hash. The server will also calculate the hash and say whether or not the upload was successful.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&amp;rsquo;ll ignore the progress indicator for now, but that will need to be updated to reflect the actual upload progress.&lt;/p&gt;
&lt;p&gt;First pass is to simply make sure the chunking works and doesn&amp;rsquo;t corrupt the uploaded file. I&amp;rsquo;ll begin on the server side by creating an empty file of size N. It was difficult to find how to do this in Go, but it turns out the &lt;a href=&#34;https://pkg.go.dev/os#Truncate&#34;&gt;os.Truncate&lt;/a&gt; method can be used to set the file size, once the file has been touched:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// This creates the file and sets it&amp;#39;s length  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fileName&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cfs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fsProvider&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FilePath&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SymID&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Create&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fileName&lt;/span&gt;); &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Close&lt;/span&gt;()  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Truncate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fileName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Size&lt;/span&gt;); &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cfs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fsProvider&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OpenFileFlags&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SymID&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_RDWR&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;cfs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fileHandles&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ID&lt;/span&gt;] = &lt;span style=&#34;color:#a6e22e&#34;&gt;uploadSessions&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;mutex&lt;/span&gt;:      &lt;span style=&#34;color:#a6e22e&#34;&gt;sync&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Mutex&lt;/span&gt;{},  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fileHandle&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt;:       &lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Size&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;startTime&lt;/span&gt;:  &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Now&lt;/span&gt;(),  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The rest of the Go code is pretty straightforward: requests to upload a file chunk will simply be passed down to the file handle and written at the requested position using &lt;code&gt;WriteAt&lt;/code&gt;. The finalise call will seek to the start of the file, compute the SHA-1, and compare it with the SHA-1 computed by the browser. MD5 would probably have been my preferred hash because of it&amp;rsquo;s speed, but SHA-1 seems to be the one &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest&#34;&gt;supported natively in browsers&lt;/a&gt;. The open files are kept in an in-memory &amp;ldquo;upload session&amp;rdquo; data structure. Finalising will also close the file handle and remove the session, but I do need to add a &amp;ldquo;session reaper&amp;rdquo; to deal with any sessions that died or terminated early.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the first cut of the JavaScript which does the chunking upload:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fileMetadata&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;window.&lt;span style=&#34;color:#a6e22e&#34;&gt;bgtData&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;resPrefix&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/files/new`&lt;/span&gt;, { 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;JSON&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stringify&lt;/span&gt;({  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mime_type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;mimeType&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;size&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})).&lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;();  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;chunkSize&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1024&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1024&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;chunks&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;ceil&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;chunkSize&lt;/span&gt;);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;chunks&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// TODO: retries  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;didUpload&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_uploadFile&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;chunkSize&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fileMetadata&lt;/span&gt;);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Finalise the upload  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_finalizeUpload&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fileMetadata&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Doing a quick test to see how this performs, although I&amp;rsquo;m not expecting any improvements. And indeed, there weren&amp;rsquo;t. In fact, it&amp;rsquo;s slower, at 270.301 seconds (4.51 minutes).&lt;/p&gt;
&lt;p&gt;But this is where the performance boost comes in, in theory: replacing the loop with a call to &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all&#34;&gt;Promise.all&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;chunkSize&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1024&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1024&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;chunks&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;ceil&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;chunkSize&lt;/span&gt;);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Prepare promises for each upload chunk, then dispatch them all at once  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;uploadPromises&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Array(&lt;span style=&#34;color:#a6e22e&#34;&gt;chunks&lt;/span&gt;);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;chunks&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;uploadPromises&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_uploadFile&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;chunkSize&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fileMetadata&lt;/span&gt;);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; Promise.&lt;span style=&#34;color:#a6e22e&#34;&gt;all&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;uploadPromises&lt;/span&gt;);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Finalise the upload  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_finalizeUpload&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fileMetadata&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Testing this approach. As expected, the progress bar is jumping around as each upload fights to update it. But we can deal with that later. We&amp;rsquo;re only interested in the upload time.&lt;/p&gt;
&lt;p&gt;And the results are in. And sadly, they&amp;rsquo;re not much better: 269.597 seconds, about 4.49 minutes.&lt;/p&gt;
&lt;p&gt;I am curious if this has something to do with the browser limiting the number of parallel uploads per domain. I hacked a version of the JavaScript which would send each chunk to a separate subdomain, so chunk 0 will go to &lt;code&gt;u0.localhost:3000&lt;/code&gt;, chunk 1 will go to &lt;code&gt;u1.localhost:3000&lt;/code&gt; and so on. This does mean adding &lt;a href=&#34;https://docs.gofiber.io/api/middleware/cors/&#34;&gt;CORS middleware&lt;/a&gt; to Blogging Tools, as this would be considered cross-origin requests. But alas, this didn&amp;rsquo;t help matter: the upload time was still around 269 seconds.&lt;/p&gt;
&lt;p&gt;So the bottleneck must be the connection itself. I was afraid of that, especially when you consider that the browser and OS would be using all the available network bandwidth to do the upload. I can&amp;rsquo;t think of any reason why it would be throttled. Another issue might simply be distance. Blogging Tools is currently hosed in Germany, since Hetzner doesn&amp;rsquo;t offer any hosting locations in Australia. But there&amp;rsquo;s also Singapore, which is quite close. Maybe it&amp;rsquo;s worth moving Blogging Tools there.&lt;/p&gt;
&lt;p&gt;Anyway, it may still be useful to keep the chunking uploader around, if for no other reason just to avoid timeouts due to long running connections. So I undid all that multi-domain work and finishing the feature off with a stalled upload reaper which will close file handles that haven&amp;rsquo;t been written to in the last 5 minutes. I also found that I wasn&amp;rsquo;t doing anything to throttle the fetch requests: a hundred or so were dispatched at once, and I think the stalled upload reaper was having an effect on all the requests fighting for bandwidth: I found uploads failing because  no single request managed to finish after 5 minutes, and the reaper was killing the upload.&lt;/p&gt;
&lt;p&gt;So made a few small changes to only dispatch batches of 5 at a time, each with a 2 MB chunk, and bumped the reaper to wait for 30 minutes. Gave it a test to see how long it took to upload a 469.4 MB file. It was not quick: 7,112 seconds, or around 118.5 minutes, or 1.98 hours. It did work, and I managed to get the file to Blogging Tools. Forwarding it on to Micro.blog failed though: turns out the file was too big anyway. So yeah, may need to do something about that.&lt;/p&gt;
&lt;p&gt;Now, this may have been wasted effort, but I think it&amp;rsquo;s worth keeping. There are some benefits from uploading files this way, such as not having connections die due to timeouts. You also have the opportunity to retry particular chunks that individually fail, without causing the entire upload to be wasted. And I do think they are being uploaded in parallel: seeing the 5 requests in the console end around the same time suggests that they&amp;rsquo;re not entirely sequential. But I think the limiting factor here is that my upload speeds are terrible. It&amp;rsquo;d be probably easier upgrading the connection first before embarking on any more fancy upload techniques.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On TOON And Human Readable Data Formats </title>
      <link>https://lmika.org/2025/11/12/on-toon-and-human-readable.html</link>
      <pubDate>Wed, 12 Nov 2025 11:42:32 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/11/12/on-toon-and-human-readable.html</guid>
      <description>&lt;p&gt;Today I learnt about a format call &lt;a href=&#34;https://dev.to/abhilaksharora/toon-token-oriented-object-notation-the-smarter-lighter-json-for-llms-2f05&#34;&gt;TOON: Token-Oriented Object Notation&lt;/a&gt;, which is a data interchange format like JSON, that is geared towards being more efficient for use with LLMs. How does it do so? By stripping back the syntax of a JSON like structure to one that is almost a CSV file, with a type signature describing how the data is structured. It claims that doing so produces a format that is ingestible by LLMs while still being (somewhat) human readable.&lt;/p&gt;
&lt;p&gt;Well, that is all well and good, but some syntax affordance for making it easy to work with would be appreciated. This was the huge issue with JSON: making a format that prioritises the machine over the human, yet claiming that it&amp;rsquo;s still human readable because it&amp;rsquo;s all &amp;ldquo;plain text.&amp;rdquo; Such designers forget that if the format is human readable, then it&amp;rsquo;s also going to be human writable. And I really wish the people designing JSON actually considered this when they were deciding to strip out comments or trying to get it working with Microsoft&amp;rsquo;s crappy JavaScript parser that shipped with IE. Sure, it made it easier to build the parsers, but now you&amp;rsquo;ve got an annoying data format that freaks out whenever an extra comma is missing.&lt;/p&gt;
&lt;p&gt;Did nobody learn anything from the days of WSDL and XSD? Do not assume that your human readable interchange format will always have a frontend that is user friendly. If people can read it, they will modify it. The law of least resistance applies to technologists just as well as it does to travelling electrons. I can sort of admire Protobuf here: it&amp;rsquo;s makes no apologies for not being human readable. Binary or nada.&lt;/p&gt;
&lt;p&gt;Anyway, I&amp;rsquo;ve not had any experience with TOON so I can&amp;rsquo;t say how easy it is to work with. But please, no more text-based data structure formats that assume humans will read but won&amp;rsquo;t write. At the very least, always include comments.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Laying The Groundwork For Dynamic Header Images</title>
      <link>https://lmika.org/2025/11/08/devlog-laying-the-groundwork-for.html</link>
      <pubDate>Sat, 08 Nov 2025 11:11:26 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/11/08/devlog-laying-the-groundwork-for.html</guid>
      <description>&lt;p&gt;Making some changes to the Card theme I&amp;rsquo;m using for this blog. First think I&amp;rsquo;m considering is a banner image, similar to the one in Scripting News. And like Scripting News, I&amp;rsquo;m hoping for the image to change occasionally. I&amp;rsquo;d like the change to happen when the blog is being built, and in order to do this, I need a way to configure this value. I&amp;rsquo;m hoping to use Blogging Tools to do this, but to actually make use of these values, I&amp;rsquo;m hoping to use &lt;a href=&#34;https://gohugo.io/methods/resource/data/#article&#34;&gt;Hugo&amp;rsquo;s resource data&lt;/a&gt; methods.&lt;/p&gt;
&lt;p&gt;The idea is to setup an assets project which will be hosted on Netlify. When I want to change anything in it, Blogging Tools will push new changes via Git, which will run a pipeline that will deploy the updates to Netlify. Then, next time I add a blog post, Hugo will pick up these data fields and use it to build the site, probably by setting an attribute that will itself be picked by CSS&amp;rsquo;s newly improved &lt;a href=&#34;**https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/attr&#34;&gt;attr()&lt;/a&gt; function to set the banner background.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/assets-lmika-org.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;a9d076a666de56db96237c1af78f6cc3&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/assets-lmika-org.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;assets-lmika-org.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;This will start off as simple data values, stored in JSON, which will include things like the current banner header. One of the reasons why I&amp;rsquo;m keeping the actual data separate is that I don&amp;rsquo;t want any issues with Blogging Tools from impacting my ability to publish blog posts, and I have more confidence in Netlify&amp;rsquo;s ability to keep a service up than I do, especially as a company that is getting paid to do so.&lt;/p&gt;
&lt;p&gt;Being a &amp;ldquo;backend guy&amp;rdquo;, I&amp;rsquo;ll start on the Netlify and Blogging Tools side of things first. Actually, I&amp;rsquo;ll start on the asset repo first, just so I can verify that Micro.blog&amp;rsquo;s Hugo instance can pull data like this.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve added the new Git repo and got it publishing to Netlify on commit to main. At the moment there&amp;rsquo;s a single JSON file, &lt;code&gt;/site/data.json&lt;/code&gt; that has a test message. The goal is now to get that onto my test blog and included in the Hugo template on build. Naturally, because Hugo loves changing their public API so often, I need to see how this is done in Hugo 0.117 as it seems to be completely different in Hugo 0.141, the current version as of this writing.&lt;/p&gt;
&lt;p&gt;So, checking out the Hugo source code and doing a &lt;code&gt;find&lt;/code&gt;/&lt;code&gt;grep&lt;/code&gt; search to find the relevant technique. Turn&amp;rsquo;s out the way to do this in Hugo 0.117.0 is to use the &lt;code&gt;getJSON&lt;/code&gt; function. &lt;a href=&#34;https://github.com/gohugoio/hugo/blob/release-0.117.0/docs/content/en/templates/data-templates.md&#34;&gt;Here&amp;rsquo;s a link to the docs&lt;/a&gt; for anyone else who&amp;rsquo;s interested.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {{ $siteData := getJSON &amp;#34;https://assets.lmika.org/data/site.json&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;My data is {{$siteData.data}}&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Okay, that seams to work:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251108-102005.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;a9d076a666de56db96237c1af78f6cc3&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251108-102005.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;out-20251108-102005.png&#34; 
        
  /&gt;
&lt;/a&gt;


So, the next thing to do is to add a banner image. I&amp;rsquo;m thinking of a slightly washed out version of the image, with a subtle gradient to the body housing the cards.  I had to adjust the header template a little to move the &lt;code&gt;site-header&lt;/code&gt; to a wrapping &lt;code&gt;div&lt;/code&gt;. This allowed me to add new CSS to allow the header to span the entire width of the page. It&amp;rsquo;s really fortunate that the Card theme styles the CSS classes, and not the HTML elements themselves. It also allowed me to add a new HTML element with a &lt;code&gt;header-end&lt;/code&gt; class to act as the gradient blend from the backing image to the regular background colour:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- Template: layouts/partials/header.html --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;site-header&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- The original content of &amp;#39;header&amp;#39; --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;header-end&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With that I could restyle the header:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/** Header */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;/* The body had a 10px padding which I had to disable to allow
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     the header to blend all the way to the client sides. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;padding&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;page-content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;footer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;site-footer&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;/* Disabling the 10px padding on the body meant I had to move
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     the padding here.*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;padding&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;/* Using &amp;#39;background-blend-mode&amp;#39; to mix the header image with the background
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     colour. We&amp;#39;re using &amp;#39;color-mix&amp;#39; to make the background color slighty
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     transparent to do this. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background-color&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;color-mix&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in srgb, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;body&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;background&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rgba(&lt;span style=&#34;color:#ae81ff&#34;&gt;255&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;255&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;255&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0.75&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background-image&lt;/span&gt;: url(&lt;span style=&#34;color:#e6db74&#34;&gt;https://lmika.org/uploads/2025/c1745b14d3.jpg&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background-blend-mode&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;screen&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background-size&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;cover&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;50&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;50&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;margin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;header-end&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;/* A small gradient from transparent to a fully opaque background colour
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     allows a smooth transition from the header to the body. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background&lt;/span&gt;: linear-gradient(&lt;span style=&#34;color:#ae81ff&#34;&gt;180&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;deg&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rgba(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;%&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;body&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;background&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;) &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;%&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Various links to MDN documentation covering all these changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/background-blend-mode&#34;&gt;background-blend-mode&lt;/a&gt; to blend the header image with the background colour. This is to wash it out some so that the menu items are not illegible.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/color_value/color-mix&#34;&gt;color-mix&lt;/a&gt; to set the transparency on the background colour.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/gradient/linear-gradient&#34;&gt;linear-gradient&lt;/a&gt; to generate a linear gradient.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251108-110434.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;a9d076a666de56db96237c1af78f6cc3&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251108-110434.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;out-20251108-110434.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Oh, and I naturally forgot dark mode, so added a new style to darken the image in that mode:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@&lt;span style=&#34;color:#66d9ef&#34;&gt;media&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;prefers-color-scheme&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;dark&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;background-color&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;color-mix&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      in srgb, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;body&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;background&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      rgba(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;background-blend-mode&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;darken&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251108-111245.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;a9d076a666de56db96237c1af78f6cc3&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251108-111245.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;out-20251108-111245.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Okay, the next thing is to make the image dynamic. I want to try and use CSS &lt;code&gt;attr()&lt;/code&gt;  to do this. Basically the idea is that when the template is built, the image URL is pulled from the data and sent to the CSS via a HTML attribute. Here&amp;rsquo;s the new header template&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- Template: layouts/partials/header.html --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ $siteData := getJSON &amp;#34;https://assets.lmika.org/data/site.json&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data-background-url&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{ $siteData.header.imageUrl }}&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the updated CSS:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background-image&lt;/span&gt;: url(&lt;span style=&#34;color:#e6db74&#34;&gt;attr(data-background-url raw-string&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ah, that didn&amp;rsquo;t work. Apparently there are &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/attr#limitations_and_security&#34;&gt;some restrictions on attr&lt;/a&gt; which could potentially result in security issues. So, plan B is to simply set the &lt;code&gt;background-image&lt;/code&gt; in a &lt;code&gt;style&lt;/code&gt; a style attribute:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- Template: layouts/partials/header.html --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ $siteData := getJSON &amp;#34;https://assets.lmika.org/data/site.json&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;style&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;background-image: url({{ $siteData.header.imageUrl }})&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Okay, that&amp;rsquo;s working. Now the final test: can I make it dynamic? I&amp;rsquo;ll change the URL to another image:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;header&amp;#34;&lt;/span&gt;:{&lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;imageUrl&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://lmika.org/uploads/2025/pxl-20251105-200617867.jpg&amp;#34;&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And publish a new post to rebuild the template. Let&amp;rsquo;s have a look:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251108-114024.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;a9d076a666de56db96237c1af78f6cc3&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/out-20251108-114024.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;out-20251108-114024.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Awesome, that&amp;rsquo;s working. Now to apply it to the actual blog. I did make one last change: the &lt;code&gt;header&lt;/code&gt; attribute is used for the header of posts too, so I had to add a class — &lt;code&gt;site-header-wrapper&lt;/code&gt; — to only select the site header. But yeah, it looks good. I like it. For reference, here&amp;rsquo;s the final template changes I&amp;rsquo;ve made:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- Template: layouts/partials/header.html --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ $siteData := getJSON &amp;#34;https://assets.lmika.org/data/site.json&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;site-header-wrapper&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;style&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;background-image: url({{ $siteData.header.imageUrl }})&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;site-header&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- The original content of &amp;#39;header&amp;#39; --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;header-end&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the final CSS changes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/** Header */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;padding&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;page-content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;footer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;site-footer&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;padding&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;site-header-wrapper&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background-color&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;color-mix&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in srgb, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;body&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;background&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rgba(&lt;span style=&#34;color:#ae81ff&#34;&gt;255&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;255&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;255&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0.75&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background-blend-mode&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;screen&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background-size&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;cover&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;50&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;50&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;margin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@&lt;span style=&#34;color:#66d9ef&#34;&gt;media&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;prefers-color-scheme&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;dark&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;site-header-wrapper&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;background-color&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;color-mix&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      in srgb, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;body&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;background&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      rgba(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;background-blend-mode&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;darken&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;header-end&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;/* A small gradient from transparent to a fully opaque background colour
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     allows a smooth transition from the header to the body. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background&lt;/span&gt;: linear-gradient(&lt;span style=&#34;color:#ae81ff&#34;&gt;180&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;deg&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rgba(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;%&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;body&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;background&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;) &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;%&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I didn&amp;rsquo;t have time to integrate this with Blogging Tools, but the groundwork has now been laid for that now.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Dynamo Browse - Item View Annotations and Asynchronous Tasks</title>
      <link>https://lmika.org/2025/11/04/devlog-dynamo-browse-item-view.html</link>
      <pubDate>Tue, 04 Nov 2025 20:42:40 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/11/04/devlog-dynamo-browse-item-view.html</guid>
      <description>&lt;p&gt;The laundry list of things to do in Dynamo Browse has grown over the last week, as I find myself wanting using it and wanting more from it. I&amp;rsquo;ve knocked off many of the small ones: fixing bugs, making it easier to get the first item from a result set. The time has come to tackle some of the larger ones.&lt;/p&gt;
&lt;p&gt;The first is the ability to annotate fields in the item view, as in add additional information to the right of the value. I would personally find that useful for describing the value of an other table using it&amp;rsquo;s ID. But the point is that it can be anything. I&amp;rsquo;d like users to add these annotations via extensions, using UCL. The way I&amp;rsquo;m thinking of doing this is by &amp;ldquo;installing&amp;rdquo; a field annotator, something along the lines as follows:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ui:add-item-annotator { |rs attr_path| 
    return &amp;#34;&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will add an annotator which will take a result set, and a path to an attribute. It will return the annotated value. We&amp;rsquo;ll see how well this will work, but first I&amp;rsquo;ll need to build the Go types. Rendering the item view is done using an &lt;code&gt;ItemRenderer&lt;/code&gt; which basically walks through the attributes of an item and renders it as a table. I think I&amp;rsquo;ll change this to take an item annotation. I also realised I need a type to represent the attribute path. I think a simple linked list would work here. Here&amp;rsquo;s what I got:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AttrPathNode&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Index&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;IsIndex&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Parent&lt;/span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;AttrPathNode&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The trouble is representing both string attribute names and numerical indicies in the same type. Go doesn&amp;rsquo;t have a great way of doing this, so I&amp;rsquo;ve gone with something simple and added boolean which would be true if the node is actually an integer index to a list or set.&lt;/p&gt;
&lt;p&gt;Actually, that may not be necessary. Poking around the renderer code, I found this type used to represent the sub items of an item:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;SubItem&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Renderer&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So I can simply use strings for the key. Good to know. Okay, added a test annotator and gave it a quick test:
&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-11-03-at-20.37.46.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;8765091e77c1a5bd3340e2f3fa6a373e&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-11-03-at-20.37.46.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-11-03 at 20.37.46.png&#34; 
        
  /&gt;
&lt;/a&gt;


Not bad, although found some glaring issues. It would be nice if the annotation was closer to the value. And laying out the annotations in a dedicated column will cause the entire column to shift as I move through the items. So instead of making it a separate column, I&amp;rsquo;ll try simply concatenating it to the end of the value. I&amp;rsquo;ll also apply the meta-info styling to dim the text a little:
&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-11-03-at-20.39.24.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;8765091e77c1a5bd3340e2f3fa6a373e&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-11-03-at-20.39.24.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-11-03 at 20.39.24.png&#34; 
        
  /&gt;
&lt;/a&gt;


That&amp;rsquo;s much better. I&amp;rsquo;m banking on only a few attributes having annotations, so I&amp;rsquo;m hoping it won&amp;rsquo;t be as busy as it looks here.&lt;/p&gt;
&lt;p&gt;Now to consider the UCL integration. Hmm, how am I going to connect the two? As expected, injecting the annotation directly into &lt;code&gt;ItemRenderer&lt;/code&gt; would introduce a dependency loop. What I could do instead is add a setter allowing the caller of the &lt;code&gt;ItemRenderer&lt;/code&gt; to change the annotation on the fly, and inject the &lt;code&gt;ItemRenderer&lt;/code&gt; as a dependency of the command controller.&lt;/p&gt;
&lt;p&gt;Implemented this, and it was actually easier than I thought.  Now to test this. I settled on the following UCL command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ui:set-item-annotator { |rs item path| 
    &amp;#34;annotation&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rs&lt;/code&gt; is the result set&lt;/li&gt;
&lt;li&gt;&lt;code&gt;item&lt;/code&gt; is the item being rendered in the view&lt;/li&gt;
&lt;li&gt;&lt;code&gt;path&lt;/code&gt; is the attribute path, represented as a list.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The attribute path is a new proxy, just to reduce the amount of memory copying between Go and UCL. I kept the same list index semantics, where &amp;gt; 0 starts from the left, and &amp;lt; 0 starts from the right, but it was a little mind bending to reverse this for a linked list.&lt;/p&gt;
&lt;p&gt;A simple annotator which returns the currently displayed item value as an annotation can be implemented as follows:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ui:set-item-annotator { |rs item path|
    $item.($path.(-1))
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here it is in action:
&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-11-03-at-20.41.25.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;8765091e77c1a5bd3340e2f3fa6a373e&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-11-03-at-20.41.25.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-11-03 at 20.41.25.png&#34; 
        
  /&gt;
&lt;/a&gt;


Brilliant, it&amp;rsquo;s working! Naturally as commands are simple UCL statements, this could be entered on the command line.&lt;/p&gt;
&lt;p&gt;One thing to consider is that unlike normal commands, the UCL block rendering the annotation is running on the UI thread. Not sure I like this, but the amount of effort required to change this would be significant. I guess I&amp;rsquo;ll just tell everyone to keep annotation rendering fast. And as it&amp;rsquo;s currently implement, it is reasonably fast. Or more accurately, it&amp;rsquo;s not noticeably slow, which is good enough.&lt;/p&gt;
&lt;p&gt;The next feature to add to Dynamo Browse is a way to asynchronously schedule blocks. I think it may be worth tapping into the existing command looper in some way.&lt;/p&gt;
&lt;p&gt;Commands in Dynamo Browse are invoked on a dedicated goroutine. Calling &lt;code&gt;execute()&lt;/code&gt; will attempt to send a command via a channel. If that fails, &lt;code&gt;execute()&lt;/code&gt; will return an error indicating that a command is currently running. This keeps running UCL code off the UI thread, and it also makes it possible to implement commands like &lt;code&gt;ui:prompt&lt;/code&gt; synchronously from the UCL code&amp;rsquo;s perspective, when in reality it pauses this goroutine and sends a message to the UI with a callback to resume the thread:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;uiModule&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;uiPrompt&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ucl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CallArgs&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;) (&lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prompt&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Bind&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;prompt&lt;/span&gt;); &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;resChan&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;cancelChan&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt;{})  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;commandctrl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PostMsg&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;events&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PromptForInputMsg&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Prompt&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;prompt&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;OnDone&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;tea&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Msg&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;resChan&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;OnCancel&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() &lt;span style=&#34;color:#a6e22e&#34;&gt;tea&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Msg&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;cancelChan&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt;{}{}  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        })  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }()  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;resChan&lt;/span&gt;:  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;cancelChan&lt;/span&gt;:  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Done&lt;/span&gt;():  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Err&lt;/span&gt;()  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So tapping into this event loop would be nice. But how does one do so?&lt;/p&gt;
&lt;p&gt;I think, probably the simplest way to do so is with buffered channels. These are bounded, meaning that there will be an upper limit to the number of pending tasks, but maybe that&amp;rsquo;s not a bad thing. Having too many pending tasks crowding out the user&amp;rsquo;s ability to run commands will probably make for a poor user experience. So let&amp;rsquo;s set the limit to something quite generous, say 50, and integrate it into the event loop:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cmdChan&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;cmdChan&lt;/span&gt;:  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;res&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ExecuteAndWait&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;cmdChan&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;cmd&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;postMessage&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;events&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Error&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;))  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;res&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;postMessage&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;events&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;StatusMsg&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprint&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;res&lt;/span&gt;)))  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;execCtx&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;requestRefresh&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;postMessage&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;events&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ResultSetUpdated&lt;/span&gt;{})  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// New code here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;task&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pendingTaskChan&lt;/span&gt;:  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;task&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;task&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;); &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;postMessage&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;events&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Error&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;))  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now for the UCL API. I have an idea of adding a new &lt;code&gt;async&lt;/code&gt; package for this, which will provide commands for running things asynchronously. It&amp;rsquo;s simplest command would be &lt;code&gt;async:do&lt;/code&gt;, which will schedule a block when the command loop is free. So invoking the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ui:command testasync {  
    echo &amp;#34;This&amp;#34;
    async:do {
        echo &amp;#34;Other&amp;#34;
        async:do { echo &amp;#34;Sierra&amp;#34; }
    }
    async:do {
        echo &amp;#34;Romeo&amp;#34;
    }
    echo &amp;#34;That&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Should display:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;This
That
Other
Romeo
Sierra
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the logs. Trying it out and here&amp;rsquo;s how it looks:
&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-11-03-at-21.31.42.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;8765091e77c1a5bd3340e2f3fa6a373e&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-11-03-at-21.31.42.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-11-03 at 21.31.42.png&#34; 
        
  /&gt;
&lt;/a&gt;


Okay, that&amp;rsquo;s pretty good. Now to build on this. The next thing to add is a command that will schedule tasks in the future, similar to &lt;code&gt;window.setTimeout()&lt;/code&gt;. For this, I plan to use the &lt;a href=&#34;https://github.com/go-co-op/gocron&#34;&gt;gocron&lt;/a&gt; package. Lots there, but for my purpose, I plan to use the &lt;a href=&#34;https://pkg.go.dev/github.com/go-co-op/gocron/v2@v2.17.0#OneTimeJob&#34;&gt;OneTimeJob&lt;/a&gt; type. It does feel like bringing in a crane to lift an empty wood pallet, but the alternative is building my own scheduler (or getting AI to vibe-code one) using a heap. Maybe something for later, but I think this is fine for now.&lt;/p&gt;
&lt;p&gt;To make use of this, I&amp;rsquo;ll add &lt;code&gt;async:in&lt;/code&gt;, which takes a timeout in seconds, and a block to run. To test this, I&amp;rsquo;ll do the classic &amp;ldquo;count down&amp;rdquo; tests:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;proc countdown { |from|
    if (le $from 0) {
        echo &amp;#34;Blast off!!&amp;#34;
        return
    }
    echo &amp;#34;$from&amp;#34;
    async:in 1 { countdown (sub $from 1) }
}

ui:testasync {
    countdown 10
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here it is in action:&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-11-03-at-22.12.51.mp4&#34; poster=&#34;https://lmika.org/uploads/2025/72bad33388.png&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;One last thing to add to async, the ability to run a query in the background and execute a block once the results are available, keeping long running queries off the UCL goroutine. I think for this I will add another thread-pool which will execute up to two queries in the background, then schedule a task to run once the results are ready.&lt;/p&gt;
&lt;p&gt;The API for running queries is pretty well established, to the point that I&amp;rsquo;ve actually got a helper functions which deal with the UCL side of things, so it&amp;rsquo;s just a matter of implementing an asynchronous version of this. I&amp;rsquo;m thinking of an interface along the following lines:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;async:query &amp;lt;query&amp;gt; { |rs|
    # do something with the result-set $rs
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It should also support keyword arguments too. Unfortunately, UCL requires keyword arguments to be placed after the positional arguments, so they would need to be placed after the block, which is a little yucky.&lt;/p&gt;
&lt;p&gt;Implemented the code, now for the test. This one&amp;rsquo;s a little contrived. What it does is asynchronously run a query on startup, counting the number of opened and closed offices in the &lt;code&gt;business-addresses&lt;/code&gt; table. Once those results are available, it will install a new column annotation, which will display the count to the right of the &lt;code&gt;officeOpened&lt;/code&gt; fields.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;proc _prep_officeCount {
    openedOffices = 0
    closedOffices = 0

    async:query &amp;#39;officeOpened=true&amp;#39; { |rs|
        openedOffices = len $rs
        async:query &amp;#39;officeOpened=false&amp;#39; { |rs|
            closedOffices = len $rs

            ui:set-item-annotator { |rs item path|
                if (eq $path.(-1) &amp;#34;officeOpened&amp;#34;) {
                    if $item.officeOpened {
                        &amp;#34;Count = ${openedOffices}&amp;#34;
                    } else {
                        &amp;#34;Count = ${closedOffices}&amp;#34;
                    }
                } else {
                    &amp;#34;&amp;#34;
                }
            }
        } -table business-addresses
    } -table business-addresses
}

_prep_officeCount
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It is contrived, but I&amp;rsquo;m hoping to use this in a real setting where I annotate ID fields with a value I retrieve from another table, so a use case like this is pretty close to why I built this feature. Because the queries are running in the background, the goal is to avoid blocking the UI thread, but since this runs on startup before the user selects a table, the table needs to be specified as part of the call. I may have to have a think of how I am to fix that.&lt;/p&gt;
&lt;p&gt;But here it is working. Note the &lt;code&gt;Count = 244&lt;/code&gt; next to the &lt;code&gt;officeOpened&lt;/code&gt; field:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-11-04-at-13.49.19.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;8765091e77c1a5bd3340e2f3fa6a373e&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-11-04-at-13.49.19.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-11-04 at 13.49.19.png&#34; 
        
  /&gt;
&lt;/a&gt;


Excellent. The next thing to do is try this out in a real setting. I finished off making a proper goroutine pool for the task that actually runs the query — which I&amp;rsquo;m calling the &amp;ldquo;aux task pool&amp;rdquo; — and a few other changes, like making the meta information a little easier to see.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Delta of the Defaults 2025</title>
      <link>https://lmika.org/2025/11/04/delta-of-the-defaults.html</link>
      <pubDate>Tue, 04 Nov 2025 07:55:28 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/11/04/delta-of-the-defaults.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s been two years since I published the default apps I use after listening to &lt;a href=&#34;https://listen.hemisphericviews.com/097&#34;&gt;Hemispheric Views #97 - Duel of the Defaults&lt;/a&gt;. A year later, I &lt;a href=&#34;https://lmika.org/2024/11/26/its-a-little.html&#34;&gt;published a delta&lt;/a&gt; listing the changes I&amp;rsquo;ve made since that original list. Now that Cup Day is here, it&amp;rsquo;s time for the update no-one asked for.&lt;/p&gt;
&lt;p&gt;Like last year, I&amp;rsquo;ll simply list the changes. You can see the &lt;a href=&#34;https://lmika.org/2023/11/04/defaults.html&#34;&gt;original list of defaults here&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Notes&lt;/strong&gt;: The perennial category, apparently. 😀 I&amp;rsquo;m all in on Obsidian now, for all notes for work and my personal life, opting to pay for the 10 vault plan, which will become relevant for later categories. Only exception is Google Keep for my shopping lists.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;To-do&lt;/strong&gt;: Obsidian too.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bookmarks&lt;/strong&gt;: I still have that Linkding instance running on Pikapods, but I haven&amp;rsquo;t been using that as often as I had been. I tend to use Micro.blog&amp;rsquo;s Bookmarks feature for this now.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Presentations:&lt;/strong&gt; It&amp;rsquo;s two years and counting since I had to make a presentation. But it&amp;rsquo;s still iA Presenter, as I&amp;rsquo;m still paying for it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Social Clients:&lt;/strong&gt; I&amp;rsquo;ve pulled away from the fediverse a little but I still use Micro.blog to follow Mastodon accounts. I have been spending more time perusing BlueSky, but I don&amp;rsquo;t want to get into the habit of spending too much time on the socials, so I simply use the browser for that.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Music:&lt;/strong&gt; Alto, my own music app, is still my primary music player. I&amp;rsquo;m no longer using Spotify, electing for YouTube music when I want to stream something. But lately, I&amp;rsquo;ve been buying most of my music on &lt;a href=&#34;https://www.qobuz.com/&#34;&gt;Qobuz&lt;/a&gt; and Bandcamp.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Weather&lt;/strong&gt;: Still using the Bureau of Meterology website, but wow, their recent redesign is a shocker. Fortunately the domain for &lt;a href=&#34;https://reg.bom.gov.au/&#34;&gt;their registered users&lt;/a&gt;, which has all the public pages, has been left untouched for now.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Journalling App&lt;/strong&gt;: Obsidian. This is where that 10 vault plan comes in handy.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Godot Game - Level 3-2 and a Rotating Platform</title>
      <link>https://lmika.org/2025/10/28/devlog-godot-game-level-and.html</link>
      <pubDate>Tue, 28 Oct 2025 21:19:28 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/10/28/devlog-godot-game-level-and.html</guid>
      <description>&lt;p&gt;Okay, time to start level 3-2. This, like level 3-1, is in the mountainous regions. Except this time, the player will be a little higher. So that means jagged platforms, more verticality, lots of gaps: a real sense that care of where one steps must be taken.&lt;/p&gt;
&lt;p&gt;Starting with some platforming to give this sense: a few narrow safe areas, separated by pits, leading into an area with platforms.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-22-at-21.23.18.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;8eeed4ab2dca5262f0cb22b195a74537&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-22-at-21.23.18.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-22 at 21.23.18.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Now, I have two choices of mechanics here: I can have the rotating platform, or I can have an area of the tile map fall away when the player lands on it. I think I prefer the rotating platform for the sole reason that adding the falling away platform may be a little overwhelming for the player.&lt;/p&gt;
&lt;p&gt;Actually, no. Danger and instability is the feeling I want to go for here. So I&amp;rsquo;ll add the falling platform, followed by a large safe area where the player can see the rotating player. Also, since this is a new mechanic, it&amp;rsquo;s only right that the player should play with it in a safe area first.&lt;/p&gt;
&lt;p&gt;Okay, I&amp;rsquo;ve added the falling platform. Well to be honest, I designated an area of the field where the falling platform will go. This leads into a large safe area which would be an ideal place to introduce the rotating platforms I have in mind.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m thinking of a scene with an &lt;code&gt;AnimationPlayer&lt;/code&gt; that will do the actual rotation animation: as in rotate the sprites from 0° to 90°. The question is, do I want the entire sequence timed using the &lt;code&gt;AnimationPlayer&lt;/code&gt;? As in, have a single animation that will rotate the platform all 4 times, with gaps between each stage? Or should I stick with the &lt;code&gt;AnimationPlayer&lt;/code&gt; simply animating a 90° rotation, and having another thing timing the wait time between stages?&lt;/p&gt;
&lt;p&gt;I think I prefer the latter. That way, I have more control over the pause between stage rotations. I can do things like disable it, or making it configurable. All desirable features.&lt;/p&gt;
&lt;p&gt;I guess the next question then is whether I want the &lt;code&gt;AnimationPlayer&lt;/code&gt; at all. Wouldn&amp;rsquo;t it be more flexible to do the animation in code? Probably, but I&amp;rsquo;m not sure I want that flexibility, at least not yet. Maybe in time it would be needed, but I think for now, I&amp;rsquo;ll just stick with the &lt;code&gt;AnimationPlayer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, let&amp;rsquo;s start building out the animation. I think I&amp;rsquo;ll start with four animations, each one rotate the platform from θ to θ+90°, just from different starting positions. I&amp;rsquo;ll use names that include the start and end angles, like &lt;code&gt;rotate_0_to_90&lt;/code&gt;, to allow for bidirectional rotation, should I decided to add that. Angle 0° will be up, and rotation will be clockwise. I think each rotation will be 0.5 seconds at speed 1, just so I have a consistent speed control. I can adjust the speed of the overall playback on the &lt;code&gt;AnimationPlayer&lt;/code&gt; itself.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-25-at-16.22.23.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;8eeed4ab2dca5262f0cb22b195a74537&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-25-at-16.22.23.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-25 at 16.22.23.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Okay, animations made. Now to think of collision. I don&amp;rsquo;t like the idea of a collision box rotating along with the platforms. I&amp;rsquo;m not too certain about how well this plays with the physics engine, and even if there were no issues, non-orthogonal geometry is just not a thing in this game. So what I&amp;rsquo;m thinking of doing is adding two collision boxes, one horizontal and one vertical, and just toggling between the two as the platform moves into position. While the platform is rotating, both boxes will be turned off.&lt;/p&gt;
&lt;p&gt;Ooh, I didn&amp;rsquo;t know the collision layer could be added as a animatable track. That&amp;rsquo;s pretty cool.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-25-at-16.33.27.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;8eeed4ab2dca5262f0cb22b195a74537&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-25-at-16.33.27.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-25 at 16.33.27.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Now to add a &lt;code&gt;Timer&lt;/code&gt; which will actually dictate the cadence. The included code is nice and neat: basically take the current rotation of the sprites in degrees, add 90 to it, and produce a string which will become the next animation to play:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-gdscript&#34; data-lang=&#34;gdscript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_on_timer_timeout&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; next &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_next_animation&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	animation_player&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;play&lt;/span&gt;(next)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_next_animation&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; rot &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;(sprites&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;rotation &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;180&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;PI&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;360&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rotate_&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;_to_&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; [rot, rot &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;90&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Added it to a test level, and I had to speed up the &lt;code&gt;AnimationPlayer&lt;/code&gt; to 3x normal speed, just so that the movement is snappy enough to justify the loss of collision during the rotation. It may need further adjusting a little later, but so far works pretty well. Although I do think I need to telegraph when the platform is about to move.&lt;/p&gt;
&lt;p&gt;Oh, I remember how I&amp;rsquo;m going to telegraph to the player when the platform is about to rotate! It&amp;rsquo;s to be an indicator panel mounted at the front that will rotate slowly on it&amp;rsquo;s own. When it reaches a new orientation it will pause, then the platform behind it will rotate.&lt;/p&gt;
&lt;p&gt;Okay, new sprite. How the heck did I choose a canvas size with non-even dimensions and still end up with a sprite that doesn&amp;rsquo;t have a definitive centre line? 🤦&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-28-at-21.21.15.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;8eeed4ab2dca5262f0cb22b195a74537&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-28-at-21.21.15.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-28 at 21.21.15.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Turns out it was how I was removing the upper and lower circles. I was using the eraser for that, and I didn&amp;rsquo;t position it properly.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-28-at-21.24.05.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;8eeed4ab2dca5262f0cb22b195a74537&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-28-at-21.24.05.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-28 at 21.24.05.png&#34; 
        
  /&gt;
&lt;/a&gt;


Okay, here&amp;rsquo;s the sprite:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-28-at-21.29.07.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;8eeed4ab2dca5262f0cb22b195a74537&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-28-at-21.29.07.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-28 at 21.29.07.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Now to think about animating it. I&amp;rsquo;m wondering if it would be possible to simply add it to the same animation player as the platform. The thing is, I would like them to be somewhat independent. The indicator should move on it&amp;rsquo;s own accord, even when the platform sprite is rotating to a new orientation. I would also like the indicator to take on the platform rotation cadence, sending it a signal when it reaches a stop (this will remove the need for the &lt;code&gt;Timer&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Can I do that all in a single scene though? Maybe with two animation players? I&amp;rsquo;ll try that and see how viable it is.&lt;/p&gt;
&lt;p&gt;Okay, just tried that, and yes, using two &lt;code&gt;AnimationPlayers&lt;/code&gt; is viable. Now to prepare the animation. The current rotation period is 3 seconds so I&amp;rsquo;ll start with a 6 second animation that would just rotate the indicator over 180° with a slight pause halfway to trigger the platform. One good thing about the indicator&amp;rsquo;s symmetry is that one animation can cover the full rotation. Although be sure to change the loop wrap mode to &amp;ldquo;Clamp&amp;rdquo;, otherwise you&amp;rsquo;ll see the sprite spin round to 0° when the animation repeats.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-28-at-21.58.34.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;8eeed4ab2dca5262f0cb22b195a74537&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-28-at-21.58.34.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-28 at 21.58.34.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the full thing in action. And yeah, adding the indicator was pretty crucial in the end. The platform wouldn&amp;rsquo;t have been fair otherwise.&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-28-at-21.56.12.mp4&#34; poster=&#34;https://lmika.org/uploads/2025/12f0aa11e2.png&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: UCL - Adding Some Missing Library Functions</title>
      <link>https://lmika.org/2025/10/27/devlog-ucl-adding-some-missing.html</link>
      <pubDate>Mon, 27 Oct 2025 21:26:14 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/10/27/devlog-ucl-adding-some-missing.html</guid>
      <description>&lt;p&gt;Working on UCL, adding some missing builtins I&amp;rsquo;ve been finding myself wanting. Nothing too interesting. Just functions like &lt;code&gt;strs:has-prefix&lt;/code&gt;, &lt;code&gt;strs:trim-suffix&lt;/code&gt;, and other functions involving strings and lists that are missing. That sort of thing.&lt;/p&gt;
&lt;p&gt;I am facing some decisions around &lt;code&gt;strs:substr&lt;/code&gt;. See, the usual way these builtins are validating arguments is that if there are more arguments than are necessary, they are generally just ignored. One could describe this as the &amp;ldquo;JavaScript&amp;rdquo; approach to argument validation: open Node and evaluate &lt;code&gt;&amp;quot;hello&amp;quot;.substr(1,2,3,4,5,6,7,8,9)&lt;/code&gt; and one will get the same result as if they simply typed in &lt;code&gt;&amp;quot;hello&amp;quot;.substr(1,2)&lt;/code&gt;. But now I&amp;rsquo;m wondering if it&amp;rsquo;s better to assert that the arguments are either one or two positions, along with the string. What if I wanted to add more positional arguments in the future?&lt;/p&gt;
&lt;p&gt;Although in practice, why would I want to do that? Well, okay, I do have an idea of for taking multiple substrings in a single call. So could be useful? Ah, probably doesn&amp;rsquo;t matter: it&amp;rsquo;ll just be me using this for now.&lt;/p&gt;
&lt;p&gt;Anyway, &lt;code&gt;strs:substr&lt;/code&gt;. One deviation from JavaScript I think I would like to do is that if a single number is used, that would be treated as the end position of the substring; as oppose to JavaScript which treats it as the start position. So running &lt;code&gt;strings:sub &amp;quot;hello, world&amp;quot; 5&lt;/code&gt; will return &lt;code&gt;&amp;quot;hello&amp;quot;&lt;/code&gt;, rather than &lt;code&gt;&amp;quot;, world&amp;quot;&lt;/code&gt;. To start from the right, a negative position can be used: &lt;code&gt;strings:sub &amp;quot;hello, world&amp;quot; -5&lt;/code&gt; will return &lt;code&gt;&amp;quot;world&amp;quot;&lt;/code&gt;. Two numbers will be treated as a start (inclusive) and end (exclusive) positions, like most other languages; although both can be negative which will set the position from the right side.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strs:substr &amp;#34;hello, world&amp;#34; 5   
--&amp;gt; &amp;#34;hello&amp;#34;

strs:substr &amp;#34;hello, world&amp;#34; -5  
--&amp;gt; &amp;#34;world&amp;#34;

strs:substr &amp;#34;hello, world&amp;#34; 3 10
--&amp;gt; &amp;#34;lo, wor&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Okay, that&amp;rsquo;s working for strings. Need to do the same thing for lists. I could almost use the same function.&lt;/p&gt;
&lt;p&gt;Oh, I keep forgetting that I need to worry about nils. How should &lt;code&gt;sublists&lt;/code&gt; work if the passed in list is nil? Maybe it&amp;rsquo;s better to just return a nil. Less hassle than requiring the user to deal with nil values. I&amp;rsquo;ll start with that approach, but I may change that the future.  Should I do the same for &lt;code&gt;strs:substr&lt;/code&gt;? Hmm, no. I think I&amp;rsquo;ll keep the default binding rules for nil to strings, which is basically convert it into an empty string. That saves me from changing the binding logic. Maybe I should lock that decision in with a test.&lt;/p&gt;
&lt;p&gt;Hmm, also, many of the other list builtins support iterators. If I were going to add iterator support to &lt;code&gt;sublist&lt;/code&gt; , then how would I handle the negative numbers? Iterators, by definition, can be boundless. Maybe leave iterator support out for now.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;lists:sublist [a b c] 1
--&amp;gt; [a]

lists:sublist [a b c] -1
--&amp;gt; [c]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It also looks like the argument binder has made my mind up about nils for me. I added the test which testing for nils, and I got an &lt;code&gt;expected listable&lt;/code&gt; error.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;lists:sublist () 1
--&amp;gt; error: expected listable
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I think that&amp;rsquo;s fine. Might be better to fail on nils actually.&lt;/p&gt;
&lt;p&gt;Getting back to the argument question raised earlier, I did run into an issue where I was using &lt;code&gt;strs:split&lt;/code&gt; without a second argument. When one does that, UCL will do something similar to Go, and return a list of each individual rune. I think that&amp;rsquo;s a useful feature, but I think I&amp;rsquo;d prefer it if that option was explicit, and if a second argument were not set, raise an error.&lt;/p&gt;
&lt;p&gt;Okay, a few more items on my todo list. I need a string replace function. I generally prefer such a function to replace all the instances of a string, rather than just the first; but that might be pushing my preference a little too far. So I&amp;rsquo;ll settle with replacing the first instance of a substring and add an &lt;code&gt;-all&lt;/code&gt; flag to replace all instances.&lt;/p&gt;
&lt;p&gt;Actually, no. Go has support for specifying a count, so I&amp;rsquo;ll use that by adding an &lt;code&gt;-n COUNT&lt;/code&gt; option. If N is unset, then all the substrings are replaced.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strs:replace &amp;#34;hello hello hello&amp;#34; &amp;#34;hello&amp;#34; &amp;#34;world&amp;#34;
--&amp;gt; world world world

strs:replace &amp;#34;hello hello hello&amp;#34; &amp;#34;hello&amp;#34; &amp;#34;world&amp;#34; -n 1
--&amp;gt; world hello hello
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Also, given that this is using Go&amp;rsquo;s builtin &lt;code&gt;strings.Replace&lt;/code&gt; function, setting &amp;ldquo;match&amp;rdquo; to the empty string will cause &lt;code&gt;replace&lt;/code&gt; to match between each rune, plus the start and end of the
string.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strs:replace &amp;#34;each one&amp;#34; &amp;#34;&amp;#34; &amp;#34;|&amp;#34;
--&amp;gt; |e|a|c|h| |o|n|e|
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Could be useful, so I&amp;rsquo;ll keep this feature in.&lt;/p&gt;
&lt;p&gt;Nearing the end now: adding an &lt;code&gt;-in&lt;/code&gt; switch to &lt;code&gt;os:exec&lt;/code&gt; to set stdin for an command. The original idea was to name the switch &lt;code&gt;-stdin&lt;/code&gt;, and I also considered &lt;code&gt;-input&lt;/code&gt;. But when writing the tests, I naturally settled on &lt;code&gt;-in&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;os:exec tr &amp;#39;[a-z]&amp;#39; &amp;#39;[A-Z]&amp;#39; -in &amp;#34;hello&amp;#34;
--&amp;gt; HELLO
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It would&amp;rsquo;ve been nice to accept stdin from a pipe. Sadly, that&amp;rsquo;s not possible with how pipes actually work.&lt;/p&gt;
&lt;p&gt;And the last one: adding a &lt;code&gt;nil?&lt;/code&gt; builtin, which will return true if the given item is nil.  I guess in the grand scheme of things, it&amp;rsquo;s probably not strictly necessary, as anything that&amp;rsquo;s not zero, including nil, will be true. And one could always use &lt;code&gt;eq $thing ()&lt;/code&gt; to test if &lt;code&gt;$thing&lt;/code&gt; is nil. So I may leave this one on the backlog.&lt;/p&gt;
&lt;p&gt;Okay, finished. I wanted to add some stuff to Dynamo Browse, but I ran out of time. I will update the version of UCL used by Dynamo Browse, but all that other stuff will need to wait for tomorrow.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Godot Game - Working On Backdrops</title>
      <link>https://lmika.org/2025/10/20/devlog-godot-game-working-on.html</link>
      <pubDate>Mon, 20 Oct 2025 21:01:17 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/10/20/devlog-godot-game-working-on.html</guid>
      <description>&lt;p&gt;Time to focus on backdrops. &lt;em&gt;Aaaaargh!&lt;/em&gt; What it&amp;rsquo;s time for is to face my inability to make artwork. I&amp;rsquo;ve been able to do small things: lifts, signs, even some pickups. But for the larger stuff, I don&amp;rsquo;t know where to begin. And regretfully it fills me with shame that I&amp;rsquo;m even looking at asset packs: despite most of the assets in this game &lt;a href=&#34;https://brackeysgames.itch.io/brackeys-platformer-bundle&#34;&gt;already originating from an asset pack&lt;/a&gt;. But the alternative is drawing it myself. And I&amp;rsquo;m not confident that I&amp;rsquo;ll be able to do a good job here.&lt;/p&gt;
&lt;p&gt;But let&amp;rsquo;s try. If it doesn&amp;rsquo;t work, I&amp;rsquo;ll use the asset pack.&lt;/p&gt;
&lt;p&gt;The first idea is to get some inspiration. Well there are plenty of those in my photo collection. Here&amp;rsquo;s one that seems like a good start:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20230624-143911534.jpg&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;6dad51e49e4b55baeeaca0ad769758db&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20230624-143911534.jpg&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;PXL_20230624_143911534.jpg&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;In short, foothills are slightly jagged yet mainly consist of sweeping curves at roughly the same vertical height. Colour and shadow provide the sense of distance. The hills in the photo are largely covered in vegetation, which could potentially be simulated by a vertical spray-brush: favouring light and dark green with some light brown on a largely mid-level green colour.&lt;/p&gt;
&lt;p&gt;Also I need to remember a principal of illustrations: go from the foreground to the background. I guess one saving grace with modern art tools is that I&amp;rsquo;ve got layers to play around.&lt;/p&gt;
&lt;p&gt;Okay, new sprite. Let&amp;rsquo;s start with the foothills. Taking inspiration from both the photo and the asset pack. Tracing around the foothills and adding some colour to give us depth yields the following:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-18-at-10.23.02.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;6dad51e49e4b55baeeaca0ad769758db&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-18-at-10.23.02.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-18 at 10.23.02.png&#34; 
        
  /&gt;
&lt;/a&gt;


Okay, not bad. A little plain but a good first start.&lt;/p&gt;
&lt;p&gt;Trying out some ridge-lines.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-18-at-10.27.33.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;6dad51e49e4b55baeeaca0ad769758db&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-18-at-10.27.33.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-18 at 10.27.33.png&#34; 
        
  /&gt;
&lt;/a&gt;


Ugh, that looks awful! Not convincing at all. Okay, forget about the ridge-lines. I also tried out adding a spray to simulate vegetation but that didn&amp;rsquo;t look convincing either. I think given the distance, my skill level, and the fact that this is a backdrop, less might be more here.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s try it out in the game. I&amp;rsquo;ll use the existing blue sky image I have for the desert world. Let&amp;rsquo;s also remove the existing backdrop.&lt;/p&gt;
&lt;p&gt;Hmm, the issue is trying to get the hills to look convincing, and given that the player starts quite high, and given that the camera is position to track the player near the centre of the screen, they won&amp;rsquo;t see this background layer. But this might be okay as I also need to make a distant mountain layer too.&lt;/p&gt;
&lt;p&gt;Okay, after a lot of trial and error, I&amp;rsquo;ve got the foothills positioned in a way that works well:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-19-at-09.11.36.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;6dad51e49e4b55baeeaca0ad769758db&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-19-at-09.11.36.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-19 at 09.11.36.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;In order to do this I had to do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scale them down by 50% so that they don&amp;rsquo;t dominate the screen or have too large pixel edges.&lt;/li&gt;
&lt;li&gt;Use a horizontal scroll offset of 0.7 and a vertical scroll offset of 0.2 so they look convincing in the background. I did try a vertical scroll offset of 0.0 to keep them anchored at the bottom of the screen. But because of how the camera follows the player vertically as they jump, that just looked disorientating.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A few things I&amp;rsquo;ve learnt about using &lt;code&gt;Parallax2D&lt;/code&gt;: you want the &amp;ldquo;repeat size&amp;rdquo; to be the size of the sprite (after transforms) so that they&amp;rsquo;re laid out seamlessly across the screen. If the scaled size doesn&amp;rsquo;t make it all away across, bump the &amp;ldquo;repeat times&amp;rdquo; up a few times. You will get the automatic repeat, which you can verify during play through by overriding the camera, zooming all the way out, and turning on 2D controls:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-19-at-09.19.24.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;6dad51e49e4b55baeeaca0ad769758db&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-19-at-09.19.24.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-19 at 09.19.24.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Now it&amp;rsquo;s time for the mountains. For this I&amp;rsquo;m going to use &lt;a href=&#34;https://bearbreath.itch.io/parallax-mountain-background&#34;&gt;this asset pack&lt;/a&gt; for inspiration. Obviously much taller than the foothills, with jagged peaks. Less sweeping curves, and more like upside-down Vs, yet with flat tops.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s attempt one:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-19-at-09.39.32.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;6dad51e49e4b55baeeaca0ad769758db&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-19-at-09.39.32.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-19 at 09.39.32.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;No, don&amp;rsquo;t like it at all. For one thing the canvas is too wide in the vertical direction and too short in the horizontal direction. For another, the mountains don&amp;rsquo;t look convincing. The peaks are too pointy. So what I&amp;rsquo;ll do is cut the canvas in half and try again with a steadier brush.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s attempt two:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-19-at-09.50.03.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;6dad51e49e4b55baeeaca0ad769758db&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-19-at-09.50.03.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-19 at 09.50.03.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;A little better. The general shapes look okay. Might look better after colouring it in.&lt;/p&gt;
&lt;p&gt;Okay, here&amp;rsquo;s the mountain layer with the colouring:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-20-at-21.00.18.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;6dad51e49e4b55baeeaca0ad769758db&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-20-at-21.00.18.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-20 at 21.00.18.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s almost convincing. Let&amp;rsquo;s try it out in the scene.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-20-at-21.25.56.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;6dad51e49e4b55baeeaca0ad769758db&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-20-at-21.25.56.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-20 at 21.25.56.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-20-at-21.26.56.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;6dad51e49e4b55baeeaca0ad769758db&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-20-at-21.26.56.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-20 at 21.26.56.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Hmm, I&amp;rsquo;m not sure I like it. It looks a little cartoon-ie, what with it&amp;rsquo;s large, sweeping areas filled with a single colour. I also don&amp;rsquo;t like the outlines: they&amp;rsquo;re a little too bold. It may help if I made the mountain layer a little smaller and reduce the motion a little.&lt;/p&gt;
&lt;p&gt;Okay, after doing that, I like it a little more. The outlines are less pronounced and you see the more of the mountain plate throughout the level. I added back the outline in the foothill plate. I think you can either go with outlines, or you go without: mixing the two looks bad.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-20-at-21.38.12.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;6dad51e49e4b55baeeaca0ad769758db&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-20-at-21.38.12.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-20 at 21.38.12.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll try this for a while. I may play around with the scroll settings a bit but it is difficult coming up with a combination that will show the mountains for more of the level while also hiding the seams between the layers. I may need to adjust the contrast a little, just so that the background doesn&amp;rsquo;t clash with the foreground: even in the screenshot about it&amp;rsquo;s a little muddy. I also need to put in a cloud layer too. Not quite sure how I&amp;rsquo;ll make convincing looking clouds, but I&amp;rsquo;ll think of something.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Ten Pointless Facts About Me</title>
      <link>https://lmika.org/2025/10/17/ten-pointless-facts-about-me.html</link>
      <pubDate>Fri, 17 Oct 2025 07:13:58 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/10/17/ten-pointless-facts-about-me.html</guid>
      <description>&lt;p&gt;Jumping on the bandwagon of another blogging trend. I saw &lt;a href=&#34;https://kevquirk.com/blog/ten-pointless-facts-about-me/&#34;&gt;Kev post answers to these ten questions&lt;/a&gt; and I thought I&amp;rsquo;d do the same. So without further ado: here are ten pointless facts about me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do you floss your teeth?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;No, no I don&amp;rsquo;t. I know that I should, and my dentist and family all say that I should. And I&amp;rsquo;ve tried a few times (although reluctantly). But I couldn&amp;rsquo;t do it. I don&amp;rsquo;t like the sensation of a bit of thread against my teeth and gums.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tea, coffee, or water?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;All three. I&amp;rsquo;m quite a heavy coffee drinker in the morning (maybe a little too heavy). Then over the course of the morning I start throwing in some tea into the mix: both caffeinated and herbal. Then it&amp;rsquo;s one more coffee around lunchtime before switching to herbal tea exclusively after mid-afternoon. There are exceptions, such as having a coffee after a meal out, but I try not to have too much caffeine after three as it throws my sleep out. Style of water: still when I&amp;rsquo;m working, and sparkling during my down time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Footwear preference?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I tend to prefer runners, despite not doing any activities that require running of me. They&amp;rsquo;re just a nice comfortable shoe to wear. Although they never last long, and I&amp;rsquo;m trying to mix up the shoes I wear just so I&amp;rsquo;m not buying new pairs every 6 months.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Favourite dessert?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Probably tiramisu. One of those deserts where you grew up loving the version prepared by your grandparents.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The first thing you do when you wake up?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Straight to the bathroom for the usual routines, then get dressed, then straight out the door to work. Go to whoa in around 15 minutes. If it&amp;rsquo;s a weekend I will do a casual read on my iPad after doing all that, but I try to keep much of that for the cafe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Age you’d like to stick at?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Probably early thirties, where you&amp;rsquo;re still young enough to do things yet still have enough money that you&amp;rsquo;re not scrimping too often.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How many hats do you own?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;How many hats do I own? Ooh… probably 7: 3 beanies and 4 wide-brimmed hats, but I do tend to stick to one or two of each. Given the time of year, I&amp;rsquo;m usually wearing a hat when I&amp;rsquo;m outside, and right now that&amp;rsquo;s a blue wide-brimmed hat I wear for sun protection. No caps thought: they don&amp;rsquo;t keep the sun off.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Describe the last photo you took?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is a little embarrassing. It was this photo, of a sign in a carpark warning people that thieves were targeting GPS units:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20251015-193545743.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A sign warning about thieves targeting GPS units is displayed in a parking lot with several parked cars.&#34;&gt;
&lt;p&gt;I took it with the intention of posting it here, with a quip along the lines of, &amp;ldquo;Oh, GPS units? Nobody uses those anymore. The thieves can have it&amp;rdquo; (although only after I spent some time workshopping it). 😛&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Worst TV show?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This one&amp;rsquo;s a hard one as I don&amp;rsquo;t have a lot of patience for shows I don&amp;rsquo;t like. There are a few shows I tried that others rave about but just didn&amp;rsquo;t work for me. &lt;em&gt;Mad Men&lt;/em&gt; is one such show, but I wouldn&amp;rsquo;t call it &amp;ldquo;the worst&amp;rdquo;. Probably the most recent one I tried that I had the most contempt for was &lt;a href=&#34;https://lmika.org/2024/04/28/sugar-season.html&#34;&gt;Sugar&lt;/a&gt;. It just seemed vapid: that they had an idea of a show set in a particular time and place, and the remaining attributes — plot, characters, etc. — were little more than cardboard cutouts designed to fit the space. But I only watched the first episode so this may be an unfair characterisation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;As a child, what was your aspiration for adulthood?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Being a huge fan of trains, I was telling others that I&amp;rsquo;d would like to be involved in the railways in some capacity: either a signalman or a driver. I did have a passing fancy of becoming a priest: having gone to a catholic school they had an air of leadership that was amenable amongst us kids, plus the robes were cool. But after encountering a computer and learning how to program, I think my fate was pretty much sealed.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in participating, &lt;a href=&#34;https://forkingmad.blog/ten-pointless-facts-about-me/&#34;&gt;David&lt;/a&gt; has the questions listed that you can copy and paste, plus a link to a few others who&amp;rsquo;ve posted answers.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>State of the Feed Reader</title>
      <link>https://lmika.org/2025/10/16/state-of-the-feed-reader.html</link>
      <pubDate>Thu, 16 Oct 2025 15:14:45 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/10/16/state-of-the-feed-reader.html</guid>
      <description>&lt;p&gt;After posting about how I zeroed out my feed reader (which, I will conceded, was largely a result of making things that didn&amp;rsquo;t interest me as &amp;ldquo;read&amp;rdquo;) I was asked how many feeds I actually subscribe to:&lt;/p&gt;
&lt;blockquote class=&#34;quoteback&#34; data-author=&#34;Sebastian&#34; data-avatar=&#34;https://micro.blog/bstn@social.lol/avatar.jpg&#34; cite=&#34;https://social.lol/users/bstn/statuses/115378559011322178&#34;&gt;&lt;p&gt;&lt;span class=&#34;h-card&#34;&gt;&lt;a href=&#34;https://micro.blog/lmika&#34; class=&#34;u-url mention&#34;&gt;@&lt;span&gt;lmika&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; Ok. Crucial questions are: how many source do you have on RSS reader and how many news did you have to see on it. 😅&lt;/p&gt;&lt;footer&gt;Sebastian &lt;cite&gt;&lt;a href=&#34;https://social.lol/users/bstn/statuses/115378559011322178&#34; class=&#34;u-in-reply-to&#34;&gt;https://social.lol/users/bstn/statuses/115378559011322178&lt;/a&gt;&lt;/cite&gt;&lt;/footer&gt;&lt;/blockquote&gt;&lt;script src=&#34;https://cdn.micro.blog/quoteback.js&#34;&gt;&lt;/script&gt;
&lt;p&gt;Ah, a good opportunity to do a quick audit over my current feeds. An activity that&amp;rsquo;s long overdue if I may add: I saw others do similar things a little while ago, and I started to do this myself, but I got distracted, and eventually deleted the draft I was working on. If you&amp;rsquo;re reading this, it means I&amp;rsquo;ve actually followed through this time.&lt;/p&gt;
&lt;p&gt;So, total number of feeds I subscribed to. On the surface, a relatively easy thing to work out: just export OPML and count the number of nodes. But I thought of going a little deeper. I have been noticing my feed reader being relatively quiet recently, and I was curious as to how often certain feeds are pushing new items. While I do have some news-based subscriptions — mainly around technology — most of the feeds I follow are personal blogs, or projects thereof, and are largely not governed by the need to publish daily. That said, I do expect some pattern of posting schedules to emerge, and I was curious as to whether things have just been a little quiet, or whether it&amp;rsquo;s just that I don&amp;rsquo;t follow feeds that are updated often.&lt;/p&gt;
&lt;p&gt;So along with doing a general count, I thought I&amp;rsquo;d consult Feedbin&amp;rsquo;s frequency analysis and break the count down into how &amp;ldquo;busy&amp;rdquo; the feeds are.  The way I did this was simple enough:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to the Subscriptions tab in the Feedbin Settings&lt;/li&gt;
&lt;li&gt;Filter out feeds to my own sites&lt;/li&gt;
&lt;li&gt;Filter out feeds that aren&amp;rsquo;t to personal blogs or publications. Examples of these might be Github activity feeds, or feeds to blog rolls that post links without commentary.&lt;/li&gt;
&lt;li&gt;Note the feeds average posting frequency. If it&amp;rsquo;s more than 1 a month, place that feed in the &amp;ldquo;frequently updated&amp;rdquo; bucket.&lt;/li&gt;
&lt;li&gt;If the average is zero, note when Feedbin has last pulled an item. If it&amp;rsquo;s less than a year ago, place that feed in the &amp;ldquo;infrequently updated&amp;rdquo; bucket.&lt;/li&gt;
&lt;li&gt;Otherwise, consider that feed abandoned.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&amp;rsquo;s the result:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Bucket&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Feeds&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Frequently updated&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;45&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Infrequently updated&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;19&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Dead/abandoned&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;8&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Filtered out&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;9&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Total&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;81&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Here&amp;rsquo;s a breakdown of the &amp;ldquo;frequently updated&amp;rdquo; feeds, based on the average calculated by Feedbin. The cutoff points are a little arbitrary, which is why the weekly and fortnightly post counts are much lower compared to the daily or monthly:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Frequency&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Feeds&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Daily (≥ 30 items /month)&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;13&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Multiple times a week (≥ 20 items /month)&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;4&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Fortnightly (≥ 10 items /month)&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;7&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Weekly (≥ 5 items /month)&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;6&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Monthly (≥ 1 items /month)&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;15&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;And here&amp;rsquo;s a breakdown of the &amp;ldquo;infrequently updated&amp;rdquo; feeds, based on when their last post was received:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Frequency&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Feeds&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Last 3 months&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;10&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Last 6 months&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;5&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Last year&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;4&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;So from the data, it does look like I subscribe to a number of feeds that are updated quite frequently. As to why things have been a little quiet: well, I suspect it&amp;rsquo;s because people are posting less. There&amp;rsquo;s a lot going on so I can understand that, although I have noticed a few people rediscovering social media, and I&amp;rsquo;m wondering if they&amp;rsquo;re moving away from their blogs.&lt;/p&gt;
&lt;p&gt;Another reason might be that I&amp;rsquo;m just not reading as much recently. My reading patterns vary based on my interests and moods, and it&amp;rsquo;s not unheard of to just mark an entire feed as read if I feel like skipping a day. Also of note that the more frequent feeds generally (although not always) have the smaller posts, so it&amp;rsquo;s takes no time to read through them all. I&amp;rsquo;m more likely to go through a few of these while I wait for a build of something, for example. Whereas the longer feeds require a bit more attention, and are likely to be marked as read if they hang about for more than a day or so, or if the weight of seeing that feed unread is too much.&lt;/p&gt;
&lt;p&gt;But this is pure speculation: none of this is scientific in any way.&lt;/p&gt;
&lt;p&gt;As for who I follow specifically, I keep a &lt;a href=&#34;https://lmika.org/blogroll/&#34;&gt;blog-roll which you can find here&lt;/a&gt;. It&amp;rsquo;s not exhaustive, and probably needs to be updated. But it&amp;rsquo;s a good indication of who I tend to following.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Godot Game - A Trigger That Reveals Secrets</title>
      <link>https://lmika.org/2025/10/12/devlog-godot-game-a-trigger.html</link>
      <pubDate>Sun, 12 Oct 2025 10:09:40 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/10/12/devlog-godot-game-a-trigger.html</guid>
      <description>&lt;p&gt;Okay, I think I need something to hide secret areas from the player and reveal it to them when they approach it. I&amp;rsquo;ve lived without this mechanic for a while, placing secret areas a fair distance from the main game area, so that they&amp;rsquo;re not visible. But such distances make it difficult to place time-limited things within those secret areas, like the invulnerability power-up, since travelling back to the main area to use it would eat up into the player&amp;rsquo;s power-up time. I could add ways for the player to return to the main play area quickly, like doors, but that complicates the level geometry and I need to place them out of the way, like in the sky.&lt;/p&gt;
&lt;p&gt;So, I&amp;rsquo;ll add a new element to do this. It will be an &lt;code&gt;Area2D&lt;/code&gt; node with an associated tile layer as a child. When the player enters the &lt;code&gt;Area2D&lt;/code&gt;, the tile layer is hidden away and the player can see the revealed secret. The player leaving the &lt;code&gt;Area2D&lt;/code&gt; will make the tile layer visible again.&lt;/p&gt;
&lt;p&gt;Okay, the new &lt;code&gt;SecretReveal&lt;/code&gt; scene has been built. The root node is an &lt;code&gt;Area2D&lt;/code&gt; which takes, as children when added to the main scene, a &lt;code&gt;CollisionShape2D&lt;/code&gt; to act as the trigger space, and a tile layer to act as the tiles to hide/show. The tile layer needs to be in the foreground, which is Z Index 10 and with collision turned off, so that it&amp;rsquo;ll reveal the mid and background layers. The script simply tracks entering bodies (it monitors layer 2, which is dedicated to the player).&lt;/p&gt;
&lt;p&gt;Now, the next question is how to fade the tile map in and out. I could just toggle the visibility but that&amp;rsquo;s way too jarring. What I hope to achieve is to add an animation player to &lt;code&gt;SecretReveal&lt;/code&gt; which will play a fade transition as the player enters or exits the &lt;code&gt;Area2D&lt;/code&gt;. But how to do that without using scripting? I rather use the primitives offered to me as I&amp;rsquo;m guessing they&amp;rsquo;ll be more efficient than throwing events around in GDSscript.&lt;/p&gt;
&lt;p&gt;One possibility is the &lt;code&gt;Modulate&lt;/code&gt; option in the &lt;code&gt;CanvasItem&lt;/code&gt; properties. After quickly playing around with this in the editor, it looks like it&amp;rsquo;s possible to adjust the alpha channel on this property, and that would adjust the visibility of the tile layer:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-12-at-10.02.17.gif&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;057d625dbf85066aaf4426135761271e&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-12-at-10.02.17.gif&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-12 at 10.02.17.gif&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;So this looks viable. I&amp;rsquo;m actually glad that &lt;code&gt;Area2D&lt;/code&gt; has this property, despite not actually rendering itself in any way (outside of the editor) Let&amp;rsquo;s give it a try.&lt;/p&gt;
&lt;p&gt;Okay, that works. Hooking it up to an animation player playing an animation that fades out and fades in the tiles when the player enters and leaves the trigger area works. It needs some improvement though. If the player were to enter and leave the target area quickly, the visibility would quickly &amp;ldquo;jump&amp;rdquo; from partially visible to fully visible. I suspect the reason for this is that the animation is always starting from the start.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-12-at-10.10.53.gif&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;057d625dbf85066aaf4426135761271e&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-12-at-10.10.53.gif&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-12 at 10.10.53.gif&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;One technique I used in the past is to preserve the current timestamp of the animation when I change it. But the animations here are using non-linear easing as it makes for a better transition.&lt;/p&gt;
&lt;p&gt;So what I&amp;rsquo;ll try is a single animation that will be played forwards and backwards. When there&amp;rsquo;s a need to play the animation, the logic will detect whether the animation player is currently playing, and will resume playback from it&amp;rsquo;s current position in the desired direction.&lt;/p&gt;
&lt;p&gt;Okay, so this is what I settled with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-gdscript&#34; data-lang=&#34;gdscript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_on_body_entered&lt;/span&gt;(body: &lt;span style=&#34;color:#a6e22e&#34;&gt;Node2D&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	candidate_body &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; body
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; animation_player&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;is_playing&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		animation_player&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;play_section&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;reveal_secret&amp;#34;&lt;/span&gt;, \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		    animation_player&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;current_animation_position)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		animation_player&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;play&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;reveal_secret&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_on_body_exited&lt;/span&gt;(body: &lt;span style=&#34;color:#a6e22e&#34;&gt;Node2D&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; body &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; candidate_body:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		candidate_body &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; animation_player&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;is_playing&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			animation_player&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;play_section&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;reveal_secret&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0.0&lt;/span&gt;, \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			    animation_player&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;current_animation_position, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			animation_player&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;play_backwards&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;reveal_secret&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What I&amp;rsquo;m doing is when the player enters the trigger space, I&amp;rsquo;ll play the &amp;ldquo;reveal secret&amp;rdquo; animation, either from the start or from the current position, depending if the animation player is playing. If the player leaves the trigger space, and the animation player is not playing, I&amp;rsquo;ll play the &amp;ldquo;reveal secret&amp;rdquo; in reverse from the end. When it is playing, I do something similar but I need to set the end position to the current animation position, rather than the start position. I&amp;rsquo;m guessing the animation player will take the initial position as the end when it&amp;rsquo;s playing in reverse, which makes sense.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how the transition looks now:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-12-at-10.27.25.gif&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;057d625dbf85066aaf4426135761271e&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-12-at-10.27.25.gif&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-12 at 10.27.25.gif&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Much smoother.&lt;/p&gt;
&lt;p&gt;Now to try it out in level 3-1. And yeah, this is a much better way to do secrets. I can build them close to the play area and the player no longer needs to navigate them blindly, or walk a million miles just to get to the bonus. I won&amp;rsquo;t overdo it though. I&amp;rsquo;m going to keep some of the large secrets that are naturally out of the way as they were. The player will need to react fast to enter them so I want to make sure they have the opportunity to see them.&lt;/p&gt;
&lt;p&gt;I am wondering whether to merge this with the &amp;ldquo;You found a secret&amp;rdquo; trigger, but I think I&amp;rsquo;ll keep them separate for now.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Godot Game - More on Level 3-1</title>
      <link>https://lmika.org/2025/10/11/devlog-godot-game-more-on.html</link>
      <pubDate>Sat, 11 Oct 2025 20:10:10 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/10/11/devlog-godot-game-more-on.html</guid>
      <description>&lt;p&gt;Starting on the Godot game again. Level 3-1 is nearing its finish. I&amp;rsquo;m building the corridor/cave that&amp;rsquo;ll lead to the lift that the player will travel up to the exit. To add some interest, I&amp;rsquo;m terminating the corridor at a spike section which should be impossible for the player to navigate. But now I&amp;rsquo;m wondering: what if the player managed to navigate this with invulnerability? Maybe a secret where the player picks up a vile given them this power that would allow them to travel through this section and pick up something nice?&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll come back to that. I&amp;rsquo;ll build the lift first and give it a try to see how it feels.&lt;/p&gt;
&lt;p&gt;Need to remember how these lifts work. Okay, yeah, it&amp;rsquo;s all driven by the doors. Added the lift and gave the level a test from the midpoint. Feels good. I do like the lift. I jumped around the spike section a little and yeah I think something like a secret allowing the player to travel through that section for a bonus would be nice.&lt;/p&gt;
&lt;p&gt;First implementation of the invulnerability power-up: simple enough. The current implementation taps into the existing death handler, in which an Area2D trigger will signal to the player to die when they enter a trigger area. Note that this happens when the player &lt;em&gt;enters&lt;/em&gt; the area. If the player is already in the area, nothing happens. This means that if the player enters an area, and stays there when the invulnerability power-up times out, they&amp;rsquo;re still invulnerable until they exit that area. A way around this is to constantly signal to the player to die while they&amp;rsquo;re inside, but I think I&amp;rsquo;ll keep it this way for now. I like the idea of giving the player just a little more time than they have to be invulnerable (although when they leave and enter a new kill zone, they will die).&lt;/p&gt;
&lt;p&gt;Okay, implemented the invulnerability power-up and gave it a quick test in the spiky area. Yeah, it works. I haven&amp;rsquo;t added the kill zones yet so the need for invulnerability is completely unnecessary, but the progress bar has been implemented, and based on how quickly that runs down, there&amp;rsquo;s ample time for the player to get the pickups and get back before the bar runs out.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-11-at-10.17.02.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;86fd78ccac1ad324ea845862fdf12816&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-11-at-10.17.02.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-11 at 10.17.02.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Place the exit and now just playing through the level. It needs checkpoints, and there are some tricky jumps that I&amp;rsquo;m tweaking. But I think it plays rather well. I&amp;rsquo;ll start dressing up the level now.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Godot Game - More On Level 3-1</title>
      <link>https://lmika.org/2025/10/09/devlog-godot-game-more-on.html</link>
      <pubDate>Thu, 09 Oct 2025 21:19:54 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/10/09/devlog-godot-game-more-on.html</guid>
      <description>&lt;p&gt;Working on the Godot game again. Finishing off the top layer of zone 3 in 3-1. Made a floating balloon with a smaller drop-payload trigger area, just to avoid accidental triggers if the player gets too close.&lt;/p&gt;
&lt;p&gt;One thing I didn&amp;rsquo;t appreciate is that if the player misses a jump, it takes a long time for them to fall and recover from that. I&amp;rsquo;m wondering if I need to add some kill zones between each horizontal level of the cross-back, along some checkpoints so that the player doesn&amp;rsquo;t have to spend so much time waiting to fall and redoing large chunks of the zone.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll try this will some barbed-wire graphics. See how I feel about it. Here&amp;rsquo;s how they looks:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-09-at-21.45.49.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;487a0b931c939eca484b5e4434df7187&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-09-at-21.45.49.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-09 at 21.45.49.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Doesn&amp;rsquo;t look inviting, although that&amp;rsquo;s kind of the point. But this may work as a kill-zone between hight levels. Although I&amp;rsquo;m uncertain as to how I&amp;rsquo;m going to introduce them to the player. Maybe they won&amp;rsquo;t need any introduction: some ugly, spiky coils might be deterrent enough.&lt;/p&gt;
&lt;p&gt;Anyway, the elements of the actual jumping part has been filled in. I think adding this was a good idea: feels interesting again. Now it&amp;rsquo;s just a matter of adding the final lift, and then the exit.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Godot Game - More On Level 3-1</title>
      <link>https://lmika.org/2025/10/08/devlog-godot-game-more-on.html</link>
      <pubDate>Wed, 08 Oct 2025 21:07:17 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/10/08/devlog-godot-game-more-on.html</guid>
      <description>&lt;p&gt;Continuing the build out of the zone 3 in level 3-1. I&amp;rsquo;ll start with a platform that is self rising when the player jumps on it. Always a classic; and honestly, I think the start of the level can use that. Lift does feel a little slow. I boosted the speed from 70 to 90. Also started added floating balloons with mines around this part of the level, since those elements were introduced in zone 2.&lt;/p&gt;
&lt;p&gt;Hmm, I wonder if I should make a variant of the red balloon with mine. The mines will kill you, but the player can safely land on the top of the red balloon. I&amp;rsquo;m wondering if a variant that is unsafe for the player could be useful. Maybe a  &amp;ldquo;spiky balloon&amp;rdquo; type. It can be attached to the grey mine I made for level 2-2.&lt;/p&gt;
&lt;p&gt;Alternatively, I can make it a different colour and just make it such that landing on it will pop the balloon. Took a brief look at a colour blindness test and it looks like red and green is the way to go for two distinguished colours.&lt;/p&gt;
&lt;p&gt;Hit a roadblock: I need to import a type, the &lt;code&gt;TextureVariant&lt;/code&gt; enum, from one script (the static mine) into another one (balloon_with_static_mine). &lt;a href=&#34;https://forum.godotengine.org/t/how-do-i-include-a-script-into-another-script/19217&#34;&gt;Quick web search&lt;/a&gt; and it looks like the way to do this is by &amp;ldquo;preloading&amp;rdquo; a script. This gives you access to the script&amp;rsquo;s symbols:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-gdscript&#34; data-lang=&#34;gdscript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; StaticMine &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; preload(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;res://scripts/static_mine.gd&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; mine_texture_variant: StaticMine&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;TextureVariant
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To support popping when the player lands on it, the balloon is an &lt;code&gt;AnimatableBody2D&lt;/code&gt; which can detect collisions as it bobs up and down. I wonder if I can actually test collision that way:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-gdscript&#34; data-lang=&#34;gdscript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; coll &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; animatable_body_2d&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;move_and_collide&lt;/span&gt;(velocity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; coll &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; coll&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;get_collider&lt;/span&gt;()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Player&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bang!&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No, turns out I can&amp;rsquo;t. Tried this and even played around with layers a little, and it didn&amp;rsquo;t detect when the player landed on it. So moving to plan B, which is adding an Area2D set to layer 2, the same layer as the player. When the player lands in that, it will pop the balloon… eventually. At the moment it just makes the balloon disappear. I&amp;rsquo;ll need to add the pop animation and sound effects. It will also give the player a jump at at about the same distance they would travel in a normal jump.&lt;/p&gt;
&lt;p&gt;Okay, new variant implemented. Here&amp;rsquo;s how it looks:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-08-at-21.51.39.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;0d8f284ff5fe12ea5c49f3cc0c6878b6&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-08-at-21.51.39.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-08 at 21.51.39.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;I was going for something a little deadlier, but I think having the extra jump could make for some interesting mechanics. I guess we&amp;rsquo;ll see. Anyway, I&amp;rsquo;ll leave it there this evening. I&amp;rsquo;ve managed to get 2/3rds of new section built out.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Godot Game - The Back Third of Level 3-1</title>
      <link>https://lmika.org/2025/10/07/devlog-godot-game-the-back.html</link>
      <pubDate>Tue, 07 Oct 2025 21:15:22 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/10/07/devlog-godot-game-the-back.html</guid>
      <description>&lt;p&gt;Working on Blogging Tool was a nice little break but I&amp;rsquo;m back on my Godot game. I&amp;rsquo;m trying to get the layout of level 3-1 finished. I&amp;rsquo;m actually not very happy with the back 60% of this. For you see, world 3 is meant to be a mountainous world, with more pits and more vertical levels. Much of this is to be seen in 3-2, with 3-1 being merely the foothills of the range the player is expected to climb. And part of this is that this level was originally the second one, but some players I shared this with said the difficulty curve ramped up a little too much. So I&amp;rsquo;ve moved it in world 3. making it the 5th level of the game.&lt;/p&gt;
&lt;p&gt;So the first part of the level sort of double backs on itself as the player is to negotiate some pretty careful jumping with moving platforms and enemies:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-07-at-21.10.27.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;831d29443fb026cb0e199d85e4c8bdd3&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-07-at-21.10.27.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-07 at 21.10.27.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;This is largely untouched from when this was the second level. It&amp;rsquo;s a little clunky — mainly due to some of the platforms being mistimed — but it works. This then extends to the second part, which is more linear and traditional:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-07-at-21.12.12.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;831d29443fb026cb0e199d85e4c8bdd3&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-07-at-21.12.12.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-07 at 21.12.12.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s this part I&amp;rsquo;m not happy with. For one thing, it&amp;rsquo;s not challenging enough. Not that it has to be super challenging, but the pacing just sort of peters out here. It&amp;rsquo;s also on the smaller size, especially compare to world 2 and their significantly longer levels.&lt;/p&gt;
&lt;p&gt;But I think I have an idea. The relatively flat section, featuring a bridge and a bit of dodging of balloons, I think I&amp;rsquo;ll keep. But for the third part, I think I&amp;rsquo;ll bring back the double backing jumping challenge, and finish it off with a lift ride. My theory is that the flat section will act as a nice palate cleanser to break up the other two &amp;ldquo;acts&amp;rdquo; of the level, along with reintroducing the floating balloons with mines. Act three will mirror act one, in the precision jumping while giving the sense of ascending difficult terrain. The lift at the end will also add to this which also giving a sense of accomplishment.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s a lot of words I&amp;rsquo;ve picked up from YouTube videos about game design. Let&amp;rsquo;s see if I can execute on this. I&amp;rsquo;ll start with a pit, designed to to be an allusion to the pit at the start. I wish I can draw convincing organic terrain: it never looks right. Although the pit at the start has straight walls, so maybe this one should too.&lt;/p&gt;
&lt;p&gt;Okay, so tried to draw some floating land masses that the player will need to navigate. Gave it a quick test, and nothing disastrous so far. The player should be able to see where they need to go while they&amp;rsquo;re traversing the horizontal parts. Verticals are another story, but the path should be easy to follow once platforms are added.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-07-at-21.52.12.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;831d29443fb026cb0e199d85e4c8bdd3&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-07-at-21.52.12.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-07 at 21.52.12.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Have jumped around this area a few times and I think this may work. It makes for a more interesting area, and I can see where hazards and platforms would go. Tried it from the start and yeah, I like it. I think I&amp;rsquo;ll go with this.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Blogging Tools - Podcast Clip Favourites</title>
      <link>https://lmika.org/2025/10/06/devlog-blogging-tools-podcast-clip.html</link>
      <pubDate>Mon, 06 Oct 2025 21:29:36 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/10/06/devlog-blogging-tools-podcast-clip.html</guid>
      <description>&lt;p&gt;Back on Blogging Tools. Gave up on trying to set a remote ref: all the ones I&amp;rsquo;ve tried were not working. Looking at the examples it looks like I can call &lt;code&gt;Push&lt;/code&gt; with defaults (apart from authentication which looks like I&amp;rsquo;ll require with my repo). Gave it a try and that looks like it worked. I peeked in the source code and the default ref that work was the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;DefaultPushRefSpec&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;refs/heads/*:refs/heads/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Turns out by adding &lt;code&gt;refs/remote/origin/…&lt;/code&gt; I was using the default pull ref. I suppose I should learn more about how Git works in this respect. But later, it&amp;rsquo;s working now. That&amp;rsquo;s all I need. Finishing off the Blogging Tool side of things by moving all the hard coded values to the config, and I think this is ready to go.&lt;/p&gt;
&lt;p&gt;Turning to the Hugo site. I&amp;rsquo;ve started a new site from scratch, which does little than just push to Netlify. Same template and same changes I&amp;rsquo;ve made for the test site. I don&amp;rsquo;t love the template, but I think it&amp;rsquo;ll work for now.&lt;/p&gt;
&lt;p&gt;Created a new site and installed the theme. Set the menu and cleared the footer as per this Hugo config:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;baseURL&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://example.org/&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;languageCode&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;en-us&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Podcast Favourites&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;theme&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;hugo-flex&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;params&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;footer&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[[&lt;span style=&#34;color:#a6e22e&#34;&gt;menu&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Clips&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;pageRef&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/about&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;weight&lt;/span&gt; = &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;markup&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;goldmark&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;renderer&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt; = &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Produces this nice clean home page:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-06-21.25.37-localhost-48f16a06173b.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;cd2795880879f7ce0b88795de840a206&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-06-21.25.37-localhost-48f16a06173b.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;2025-10-06 21.25.37 localhost 48f16a06173b.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Added CI/CD to build the site and deploy it to Netlify. All seems to work.&lt;/p&gt;
&lt;p&gt;Merge changes in Blogging Tools, pushed to Forgjo, and redeployed in Coolify. Status is degraded. Reason is that the temp directory, which is where the workspace for the Hugo site&amp;rsquo;s Git repo, doesn&amp;rsquo;t exist:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-06-at-21.48.17.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;cd2795880879f7ce0b88795de840a206&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-06-at-21.48.17.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-06 at 21.48.17.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;I forget that this is deployed in a &lt;code&gt;scratch&lt;/code&gt; container, which has no guest OS. So creating the &lt;code&gt;/tmp&lt;/code&gt; directory is something I&amp;rsquo;ll need to do. Will add a quick bit of logic to Blogging Tools to do this prior to creating the Git provider.&lt;/p&gt;
&lt;p&gt;Actually, no. Change of pace. I&amp;rsquo;ll do it in the Docker file. What I&amp;rsquo;ll do is touch a file in &lt;code&gt;/tmp&lt;/code&gt; during the build phase and just copy it over to scratch (I can&amp;rsquo;t do this in scratch as it doesn&amp;rsquo;t come with executables). This should force the directory creation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;RUN&lt;/span&gt; touch /tmp/t&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ---------  &lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;scratch&lt;/span&gt;  &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;COPY&lt;/span&gt; --from&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;builder /tmp/t /tmp&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Redeployed and… what?&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-06-at-21.58.54.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;cd2795880879f7ce0b88795de840a206&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-06-at-21.58.54.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;CleanShot 2025-10-06 at 21.58.54.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Oh, that&amp;rsquo;s meant to be:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;COPY&lt;/span&gt; --from&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;builder /tmp/t /tmp/t&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Copying directly to &lt;code&gt;/tmp&lt;/code&gt; makes a new file with the filename &lt;code&gt;tmp&lt;/code&gt; in &lt;code&gt;/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Okay, Blogging Tools is working again. Let&amp;rsquo;s try it out.&lt;/p&gt;
&lt;p&gt;Ah, error: &lt;code&gt;author field is required&lt;/code&gt;. I need to set this for the commit call. That may be why the commit author was me and not the &lt;code&gt;bloggingtools&lt;/code&gt; user when I was testing this on my own machine.&lt;/p&gt;
&lt;p&gt;Trying again. Hey, success! Blogging Tools added the clip to the repository and pushed the changes as the &lt;code&gt;bloggingtools&lt;/code&gt; user. This kicked off the CI/CD to rebuild the Hugo site and pushes it to Netlify. Did forget to fix the base URL add the Hugo template for making the audio playable, but fixed that and now the clip is playable.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/pasted-image-20251006222338.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;cd2795880879f7ce0b88795de840a206&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/pasted-image-20251006222338.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;Pasted image 20251006222338.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Nice! Calling this feature done. I will need to style the Hugo site, but I&amp;rsquo;ll do that over time. It&amp;rsquo;s getting a bit late and I want to wrap this up.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Blogging Tools - Podcast Clip Favourites</title>
      <link>https://lmika.org/2025/10/05/devlog-blogging-tools-podcast-clip.html</link>
      <pubDate>Sun, 05 Oct 2025 16:52:30 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/10/05/devlog-blogging-tools-podcast-clip.html</guid>
      <description>&lt;p&gt;Inspired by the way &lt;a href=&#34;https://ronjeffries.com/articles/-w025/y/t/&#34;&gt;Ron Jeffries writes&lt;/a&gt; about his work, as well as by &lt;a href=&#34;https://blog.martin-haehnel.de/2025/10/03/whiledo/&#34;&gt;Martin Hähnel&lt;/a&gt; attempt at this, I thought for this instalment of Devlog I&amp;rsquo;ll try a more &amp;ldquo;lab notes&amp;rdquo; approach. This means a potentially more mundane and less satisfactory description of project work today: less showing of what was accomplished, and more of a running commentary. I wanted to see if I liked this style of writing, and if it helped me or slowed me down. Preliminary results were mixed: it did slow me down, but I found it enjoyable. I&amp;rsquo;ll try this a few more times to see how I feel about it.&lt;/p&gt;
&lt;p&gt;Today&amp;rsquo;s coding session is to continue the work of adding saved favourite podcast clips from Blogging Tools. Previously this was a standalone application connected to a database, but along with the tech stack used to build this falling out of active support, along with the feeds no longer working for reasons I rather not look into, I want to replace this with a static site that takes nothing to maintain. One other thing is that I want to move away from referencing links to the podcast clips and saving actual audio. Since I&amp;rsquo;m dealing with copyrighted material, some of it that is for pay, this site will be kept private.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m choosing to use Blogging Tools for this as it has a relatively decent podcast clipper that I use for clips I publish to my site. For the static site itself, I&amp;rsquo;m choosing to look at Hugo, given that I&amp;rsquo;m comfortable with this approach too. Although I do have some concerns about the long term viability of this for sites I don&amp;rsquo;t touch that often. An initial attempt at this used another site I use to track audio that used a template that fell out of maintenance and broke when I moved to Hugo 0.151. The Hugo maintainers really need to clean up their versioning act.&lt;/p&gt;
&lt;p&gt;Anyway, the current status of this is a temporary Hugo site with a new template. At this stage, I&amp;rsquo;ve got Blogging Tools running a new job type that will take the clipped audio and image thumbnail, save them as static data to Hugo, and publish a new post.&lt;/p&gt;
&lt;p&gt;At the moment, the template isn&amp;rsquo;t including the thumbnail or audio HTML. This is now a point of committing to this site and template: moving it out of the temp directory and into a proper workspace. So let&amp;rsquo;s start that now. I&amp;rsquo;m using the &lt;a href=&#34;https://github.com/ldeso/hugo-flex&#34;&gt;Hugo Flex&lt;/a&gt; theme as it provides a nice clean canvas to start with, and it seems to be in active maintenance (something to watch out for when dealing with Hugo themes). Added the git module, and setting Goldmark unsafe to true (once again) and now the new Hugo site is ready:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-09.35.03-localhost-fd2731f7bcc3.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;9ea5d8956f0e4d3138d476569919f9c7&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-09.35.03-localhost-fd2731f7bcc3.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;2025-10-05 09.35.03 localhost fd2731f7bcc3.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Now testing this with Blogging Tools. I&amp;rsquo;ve already got an ATP clip ready to go. And after remembering to change the target directory from the temp Hugo site to the real one, I am ready to test the extract. All that involves is clicking &amp;ldquo;Save&amp;rdquo;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-09.36.41-localhost-bdf14f391c73.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;9ea5d8956f0e4d3138d476569919f9c7&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-09.36.41-localhost-bdf14f391c73.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;2025-10-05 09.36.41 localhost bdf14f391c73.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Okay, that didn&amp;rsquo;t work for a very stupid reason. I set the target directory to Blogging Tools repo, not the Hugo site. Made the change (Blogging Tools is being run using &lt;a href=&#34;https://github.com/air-verse/air&#34;&gt;Air&lt;/a&gt; which gives me file watching capabilities one would see in the frontend world) and can now see that the new post was written to the new Hugo site.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-09.43.17-localhost-42cb85cfbe26.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;9ea5d8956f0e4d3138d476569919f9c7&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-09.43.17-localhost-42cb85cfbe26.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;2025-10-05 09.43.17 localhost 42cb85cfbe26.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Now to adjust the template a little, just to add the audio and thumbnail. The Markdown files produced by Blogging Tools have all the clip information in the front matter:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;title: &amp;#34;449:  An Unclean Mouse&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;episode:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  guid: some-random-id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  url: &amp;#34;https://atp.fm/449&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  title: &amp;#34;449:  An Unclean Mouse&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  date: 2021-09-23 12:27:49 +1000 AEST
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  image_src: &amp;#34;https://cdn.atp.fm/artwork-bootleg&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  audio_src: &amp;#34;https://example.com/file.mp3&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;show:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  title: &amp;#34;Accidental Tech Podcast: Unedited Live Stream&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;clip:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  start: 0s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  duration: 60ms
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;date: 2021-09-23 12:27:49 +1000 AEST
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;thumbnail: images/DQkt_Lk5QRjXVHE0rdR09.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;audio: audio/5fFX8m0BqqFqTwlVAtb2Y.mp3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Test thing
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Oh, before I do that. Let me fix the dates should that they&amp;rsquo;re in UTC and formatted as ISO 8601. This is a quick change to the template used by Blogging Tools:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;date: {{.Clip.EpisodeDate.UTC.Format &amp;#34;2006-01-02T15:04:05Z07:00&amp;#34;}}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Another quick test… perfect. The dates are coming through again. Oh, and a quick aside: because the front matter is all included in one Go template, I had to make sure to properly quote the string values, lest they contain colons and other constructs YAML doesn&amp;rsquo;t like. Easy way to do that is pipe them through &lt;code&gt;printf &amp;quot;%q&amp;quot;&lt;/code&gt;, which quotes them as if they&amp;rsquo;re Go strings. May not be perfect but it&amp;rsquo;s good enough for now:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;title: {{.Clip.EpisodeTitle | printf &amp;#34;%q&amp;#34;}}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So, lets fix the template. I&amp;rsquo;ll start by creating a new template in &lt;code&gt;layouts/clips/singles.html&lt;/code&gt; which is a copy-and-paste of the existing theme post. I&amp;rsquo;ll then add details of the front matter, such as the thumbnail and audio player. This I can get using the &lt;a href=&#34;https://gohugo.io/configuration/params/&#34;&gt;Params&lt;/a&gt; template construct. I&amp;rsquo;ll start by just printing it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-gotemplate&#34; data-lang=&#34;gotemplate&#34;&gt;{{ define &amp;#34;main&amp;#34; }}

  {{ $context := dict &amp;#34;page&amp;#34; . &amp;#34;level&amp;#34; 1 &amp;#34;isdateshown&amp;#34; true }}
  {{ partial &amp;#34;heading.html&amp;#34; $context }}
  
  Audio = {{.Params.audio}}, image = {{.Params.thumbnail}}
  
  {{ .Content }}
  {{ partial &amp;#34;tags.html&amp;#34; . }}
  {{ partial &amp;#34;comments.html&amp;#34; . }}

{{ end }}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-09.54.52-localhost-c59d83c214d9-1.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;9ea5d8956f0e4d3138d476569919f9c7&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-09.54.52-localhost-c59d83c214d9-1.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;2025-10-05 09.54.52 localhost c59d83c214d9 1.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;I had to restart Hugo, but that&amp;rsquo;s coming through. I need to convert these into proper URLs though. I&amp;rsquo;ll start by trying the &lt;a href=&#34;https://gohugo.io/functions/urls/absurl/&#34;&gt;absurl&lt;/a&gt; template function:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-gotemplate&#34; data-lang=&#34;gotemplate&#34;&gt;Audio = {{ absURL .Params.audio}}, image = {{ absURL .Params.thumbnail}}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-09.57.47-localhost-392aecb7bb5f-1.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;9ea5d8956f0e4d3138d476569919f9c7&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-09.57.47-localhost-392aecb7bb5f-1.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;2025-10-05 09.57.47 localhost 392aecb7bb5f 1.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Okay, getting better. Let&amp;rsquo;s try and turn them into proper HTML elements:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;  &amp;lt;img src=&amp;#34;{{ absURL .Params.thumbnail}}&amp;#34;&amp;gt;
  &amp;lt;audio src=&amp;#34;{{ absURL .Params.audio}}&amp;#34; controls&amp;gt;&amp;lt;/audio&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-09.59.35-localhost-56693aeddfc3-1.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;9ea5d8956f0e4d3138d476569919f9c7&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-09.59.35-localhost-56693aeddfc3-1.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;2025-10-05 09.59.35 localhost 56693aeddfc3 1.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Good, the image and audio is coming through. Image looks a little large but I&amp;rsquo;ll fix that at little later. Oh, and it looks like the clips are showing up in the home page already. That&amp;rsquo;s a nice touch. I do want to change the clip paths though. At the moment, they&amp;rsquo;re saved using the auto-incrementing clip ID, which is not ideal. Clip metadata is stored in an Sqlite database and the library I&amp;rsquo;m using — which is &lt;a href=&#34;modernc.org/sqlite&#34;&gt;a Go port of Sqlite3 that&lt;/a&gt; doesn&amp;rsquo;t need CGO — has a nasty habit of reusing row IDs. I really should replace this with an auto-generated primary key. But that&amp;rsquo;s for later.&lt;/p&gt;
&lt;p&gt;Right now, I think I&amp;rsquo;ll use a hash of the episode Guid for the path. ATP seems to have randomly generated hashes but I know that some RSS feeds choose to use URLs, which won&amp;rsquo;t do for a path. Why the Guid? Well, I want to be able to regenerate a clip if need be, replacing the existing one.&lt;/p&gt;
&lt;p&gt;Actually, no, that won&amp;rsquo;t do. I want to be able to create multiple clips from the same episode. So maybe I&amp;rsquo;ll use a hash that includes the Guid and clip timestamp. So let&amp;rsquo;s try that.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;clipPathRaw&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%v:%v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;clip&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;EpisodeGUID&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;clip&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ClipStart&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;clipPathHash&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;md5&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sum&lt;/span&gt;([]byte(&lt;span style=&#34;color:#a6e22e&#34;&gt;clipPathRaw&lt;/span&gt;))  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;clipPath&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hex&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;EncodeToString&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;clipPathHash&lt;/span&gt;[:])  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;postFilename&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filepath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;postDir&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%v.md&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;clipPath&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;MD5 should be fine: these don&amp;rsquo;t need to be cryptographically safe. Also, &lt;code&gt;md5.Sum&lt;/code&gt; returns an &lt;code&gt;[16]byte&lt;/code&gt; array, and I keep forgetting how to return this as a &lt;code&gt;[]byte&lt;/code&gt; slice. Apparently it&amp;rsquo;s &lt;code&gt;array[:]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another quick test. Yeah, it works. The clip paths now look like this: &lt;code&gt;http://localhost:1313/clips/210da5b665c38c809eb1ea481b1b22de/&lt;/code&gt; Not great, but I tend to browse this visually so I think I can live with this.&lt;/p&gt;
&lt;p&gt;Okay, next thing is having Blogging Tools generate the clips and commit them to Git. The goal is to have the Hugo site stored in a Git repository and built to a private, undisclosed website. I did this for Nano Journal, which synchronised journal entries to a Git repo after saving it locally. Looking at the code, though, I have no idea how I setup the credentials. There&amp;rsquo;s nothing in the config hinting at a certificate so I may have done it manually.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also wondering if I can use &lt;a href=&#34;https://github.com/go-git/go-git&#34;&gt;Go Git&lt;/a&gt; for this. I didn&amp;rsquo;t use this for Nano Journal, as I wanted to use LFS; which Go Git doesn&amp;rsquo;t support (instead I just shelled out to &lt;code&gt;git&lt;/code&gt;). But I&amp;rsquo;m wondering if I can live without LFS for this.&lt;/p&gt;
&lt;p&gt;Okay, let&amp;rsquo;s try this. I&amp;rsquo;ll push the current Hugo site to a test repo on my Forgejo instance and see whether I can check it out from Blogging Tools. This will be done as a user that I&amp;rsquo;ll configure Blogging Tools to act as.&lt;/p&gt;
&lt;p&gt;Then I added Go Git to blogging tool and created a new provider for Git. Added a method to either pull or clone a remote repository, checkout a branch, and pull updates from the repo. I&amp;rsquo;ve chosen to use the file system for keeping the local workspace so that Blogging Tools doesn&amp;rsquo;t need to keep cloning the repo whenever there&amp;rsquo;s a need to add a clip. This I&amp;rsquo;m just going to keep in ephemeral storage so that when the Docker container gets cycled and the workspace lost, it&amp;rsquo;ll just recreate it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Provider&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;CloneOrPull&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; (  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;repo&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Repository&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; !&lt;span style=&#34;color:#a6e22e&#34;&gt;isDir&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;workspace&lt;/span&gt;) {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#75715e&#34;&gt;// Workspace doesn&amp;#39;t exist. Pull from repo.  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;repo&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PlainClone&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;workspace&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CloneOptions&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;:               &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;repoURL&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;RecurseSubmodules&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DefaultSubmoduleRecursionDepth&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;Auth&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;http&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;BasicAuth&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#a6e22e&#34;&gt;Username&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;username&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#a6e22e&#34;&gt;Password&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;password&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          },  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       })  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;repo&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PlainOpen&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;workspace&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;worktree&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;repo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Worktree&lt;/span&gt;()  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;worktree&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pull&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PullOptions&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;RemoteURL&lt;/span&gt;:         &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;repoURL&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;RecurseSubmodules&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DefaultSubmoduleRecursionDepth&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;ReferenceName&lt;/span&gt;:     &lt;span style=&#34;color:#a6e22e&#34;&gt;plumbing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ReferenceName&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;branch&lt;/span&gt;),  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;Auth&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;http&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;BasicAuth&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;Username&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;username&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;Password&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;password&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       },  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }); &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;worktree&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Checkout&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CheckoutOptions&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;Branch&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;plumbing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ReferenceName&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;branch&lt;/span&gt;),  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }); &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Quick test of this, and I&amp;rsquo;m seeing a &amp;ldquo;repository doesn&amp;rsquo;t exist&amp;rdquo; error.&lt;/p&gt;
&lt;p&gt;Actually, no. That was because of an earlier use of the repository URL when I actually wanted the workspace. But now I&amp;rsquo;m seeing a &amp;ldquo;reference not found&amp;rdquo; error when I try to pull from the origin:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-11.24.51-localhost-87f4f1d9b693.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;9ea5d8956f0e4d3138d476569919f9c7&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-11.24.51-localhost-87f4f1d9b693.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;2025-10-05 11.24.51 localhost 87f4f1d9b693.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Ah, okay. I should&amp;rsquo;ve used &lt;code&gt;plumbing.NewBranchReferenceName&lt;/code&gt; here. That&amp;rsquo;s fix that. Now it&amp;rsquo;s an &lt;code&gt;already up-to-date&lt;/code&gt; error. That&amp;rsquo;s fine, I&amp;rsquo;ll just add an ignore case here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;worktree&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pull&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PullOptions&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;RemoteURL&lt;/span&gt;:         &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;repoURL&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;RecurseSubmodules&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DefaultSubmoduleRecursionDepth&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ReferenceName&lt;/span&gt;:     &lt;span style=&#34;color:#a6e22e&#34;&gt;plumbing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewBranchReferenceName&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;branch&lt;/span&gt;),  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Auth&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;http&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;BasicAuth&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;Username&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;username&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;Password&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;password&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}); &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; !&lt;span style=&#34;color:#a6e22e&#34;&gt;errors&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Is&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NoErrAlreadyUpToDate&lt;/span&gt;) {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another test:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-11.28.44-localhost-055ec2134222.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;9ea5d8956f0e4d3138d476569919f9c7&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/2025-10-05-11.28.44-localhost-055ec2134222.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;2025-10-05 11.28.44 localhost 055ec2134222.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;And that worked. I managed to pull changes from remote.&lt;/p&gt;
&lt;p&gt;Deleting the workspace and trying a clone. Hmm, that&amp;rsquo;s strange. I&amp;rsquo;m seeing an &lt;code&gt;authentication required&lt;/code&gt; error, yet it looks like the repository was cloned successfully. Let me try and ignore that error.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;repo&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;repo&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;warn: error received alongside repo: %v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Okay, that worked. It&amp;rsquo;s not pretty, but I think it&amp;rsquo;s fine for now.&lt;/p&gt;
&lt;p&gt;Now to commit the new files to the repository. Although partial failures from staging multiple files — one file is stage but the second one fails for some reason — may introduce some problems where the state is not completely settled, so I may also need a method of ensuring the workspace is pristine: no files should exist after cloning or pulling that is not staged. So need to add that first. I don&amp;rsquo;t see a method to do this on the Workspace type, so basically what I&amp;rsquo;ll do is reset hard, and then iterate over the untracked files and remove them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;w&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Reset&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ResetOptions&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Mode&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;HardReset&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}); &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;w&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Status&lt;/span&gt;()  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Worktree&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Untracked&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Remove&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Well, that&amp;rsquo;s the plan eventually. I&amp;rsquo;ll just log them for now.&lt;/p&gt;
&lt;p&gt;Then it&amp;rsquo;s just a matter of adding files and commit the changes. First attempt at this failed with an &lt;code&gt;entry not found&lt;/code&gt; error. That was just a bad path. Adding files now working, along with committing, and now we&amp;rsquo;re failing at the push. Reason is that unlike all the other Go Git methods, which were pretty high level, this one leaks a bit of the mechanics of Git, requiring the use of a &lt;a href=&#34;https://git-scm.com/book/en/v2/Git-Internals-The-Refspec&#34;&gt;Ref Spec&lt;/a&gt;. I&amp;rsquo;ve just hard coded it to a version which will push the local main branch to the origin main branch.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;repo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Push&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PushOptions&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;RemoteURL&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;repoURL&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Auth&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;http&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;BasicAuth&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;Username&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;username&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;Password&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;password&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;RefSpecs&lt;/span&gt;: []&lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RefSpec&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RefSpec&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;+refs/heads/%v:refs/remotes/origin/%v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;branch&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;branch&lt;/span&gt;)),  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}); &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Oh, before that, I finding a &lt;code&gt;cannot create empty commit: clean working tree&lt;/code&gt;. I&amp;rsquo;m opening the work tree between staging the files and committing. Is that the problem?&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-05-at-21.57.36.png&#34; 
   class=&#34;glightbox&#34;
   data-gallery=&#34;9ea5d8956f0e4d3138d476569919f9c7&#34;
   
&gt;
  &lt;img src =&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-10-05-at-21.57.36.png&#34; 
       loading=&#34;lazy&#34;
       decoding=&#34;async&#34;
       style=&#34;border-radius: 5px; max-width: 100%&#34;
       alt=&#34;cleanshot-2025-10-05-at-21.57.36.png&#34; 
        
  /&gt;
&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Hmm, no. It might be that I&amp;rsquo;m adding the full path of the file to the index. Running &lt;code&gt;git status&lt;/code&gt; in that repository yielded full path entries, which is very uncharacteristic. Furthermore, trying to unstage them did nothing. I&amp;rsquo;ll remove the repo directory and change the logic to get the relative path of the file before they&amp;rsquo;re added:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;files&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;relPath&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filepath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Rel&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;workspace&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;w&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;relPath&lt;/span&gt;); &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;w&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Commit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;msg&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;git&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CommitOptions&lt;/span&gt;{}); &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fault&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Oh, it was because the files I was trying to add were already added to the repository from earlier in the day, and they weren&amp;rsquo;t being changed by the job. Let me remove them from the repository and try again.&lt;/p&gt;
&lt;p&gt;Okay, it was that. Commits are now working, although pushes don&amp;rsquo;t seem to be synchronising the commits. When I look at recent changes in Forgejo, it only shows my commits, not the ones created by the service user I made for Blogging Tools. Will try adjusting the refspec to push all heads:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;RefSpecs&lt;/span&gt;: []&lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RefSpec&lt;/span&gt;{  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;+refs/heads/*:refs/remotes/origin/*&amp;#34;&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nah, that didn&amp;rsquo;t help. The method is returning no error, yet it doesn&amp;rsquo;t seem to be synchronising the branches. Will need to learn more about how this all works. But I think that&amp;rsquo;s something for later.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Hosting&#39;s Not the Problem With Distributed Video</title>
      <link>https://lmika.org/2025/09/24/hostings-not-the-problem-with.html</link>
      <pubDate>Wed, 24 Sep 2025 08:03:43 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/09/24/hostings-not-the-problem-with.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://daringfireball.net/linked/2025/09/23/goddard-kimmel&#34;&gt;John Gruber writes about the pitfalls of centralised video&lt;/a&gt; in the wake of Jimmy Kimmel’s suspension:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;The big problem is YouTube. With YouTube, Google has a centralized chokehold on video. We need a way that’s as easy and scalable to host video content, independently, as it is for written content. I don’t know what the answer to that is, technically, but we ought to start working on it with urgency.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;m not sure this is the whole problem. Sure hosting video is one part of the picture, but given the decreasing price of storage and CDNs, I think that&amp;rsquo;s relatively easy problem to solve.&lt;/p&gt;
&lt;p&gt;I think the biggest hurdle is the client. Consider podcasts. In the early days it was difficult to host audio on the web, but certainly possible. But podcasts remained a niche activity as there wasn&amp;rsquo;t no easy way to subscribe to a feed and just start playing an episode. You&amp;rsquo;d have to connect your iPod to your machine, run a pod-catcher, wait for it to sync, etc. etc. It&amp;rsquo;s an activity that one goes out of their way to do. And people did it, back in the day, because they were interests in shows that weren&amp;rsquo;t being served by the broader media landscape at the time.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s no way podcasts would be as big as they are now if people still had to do this. There are just have too many other options for entertainment, each which requires minimal effort to get started. I believe the introduction of mobile podcast players, with better discovery and &lt;em&gt;much&lt;/em&gt; better playback, led to the open podcasting renaissance that we&amp;rsquo;re enjoying today. The technologies are distributed and open, but the user experiences it like an integrated experience.&lt;/p&gt;
&lt;p&gt;Open-web distributed video needs to be much like this. It&amp;rsquo;s nice to think that at the end of the day, people will open an webpage of their favourite creator, and start playing video in the browser&amp;rsquo;s — dare I say — bare-bones video player. But that&amp;rsquo;s never going to happen in a world of YouTube. They&amp;rsquo;re just going to do what they do every day: which is open YouTube on their mobile or TV, and start viewing video. Sure they may care about certain YouTuber creators, but not enough to change their habits.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s what open-web distributed video is up against. Solve the user&amp;rsquo;s drive to get to video as quick as they possibly can, and I think the rest will sort itself out.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Don&#39;t Choose To Reuse (Yet)</title>
      <link>https://lmika.org/2025/09/22/dont-choose-to-reuse-yet.html</link>
      <pubDate>Mon, 22 Sep 2025 19:56:54 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/09/22/dont-choose-to-reuse-yet.html</guid>
      <description>&lt;p&gt;I think the industry&amp;rsquo;s push to design software packages as if they&amp;rsquo;re going to be reused was a mistake. This was a major selling point with technologies back in the day, such as Java. Build a software component once, then reuse it for other projects. I don&amp;rsquo;t really know what the original motivation was. Probably because people wanted to cut the cost of developers reimplementing the same thing over and over again (how many C developers does it take to implement a linked list? Well, how many C developers are currently employed?).&lt;/p&gt;
&lt;p&gt;But this rhetoric has resulted in a high degree of complexity coming from pre-optimisation. Someone starts coding something and thinks to themselves that &amp;ldquo;hey, I may need this for something in the future. I don&amp;rsquo;t know what that thing is, but I better make sure this code is ready to support it if and when it comes.&amp;rdquo; So they generalise. They spin out interfaces and customisations to which they only have a single concrete implementation for. They start peppered their code with the logical equivalence of caveats: &lt;em&gt;do this only if this implements this interface, but if it implements that interface, then do this other thing, etc&lt;/em&gt;. They blow out the amount of code one needs to read to understand it, with the critical path hidden, all covered by tests cases that need to be maintain to cover cases that never happen in real life.&lt;/p&gt;
&lt;p&gt;All this present-day debt is taken out to offset the costs of an imagined future in which this  component is needed and can be reused. Of course, the vast majority of the time, that future never comes, and you&amp;rsquo;re left trying to understand and support code you needlessly made more complicated by &amp;ldquo;generalising&amp;rdquo; it.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Keep it simple, stupid&amp;rdquo; and &amp;ldquo;you ain&amp;rsquo;t gonna need it&amp;rdquo; is hard advice for a developer to follow. In a field where it&amp;rsquo;s not hard to run into someone with ego (look no further than the person writing this, dear reader) it&amp;rsquo;s can be hard for a developer to admit that the data structure or algorithm they&amp;rsquo;re working on won&amp;rsquo;t be needed for anything else. That it&amp;rsquo;s purpose is this project and this project alone. And maybe it&amp;rsquo;s premature to assume that it&amp;rsquo;s worthy of it&amp;rsquo;s own library that people will start looking at, and giving you praise for, and getting incorporated into large open-source projects that you yourself used, and being discussed by podcasters you follow, and being considered a vector for supply-chain attack by state-backed hackers because it&amp;rsquo;s an awesome library that everyone&amp;rsquo;s using.&lt;/p&gt;
&lt;p&gt;No, put those thoughts of reuse away. Embrace the phrase &amp;ldquo;fit for purpose.&amp;rdquo; Build it specifically for the use case you need it for right now. Worry about reuse when you find that you actually do need to reuse it. Jeff Atwood in Coding Horror has this great principal known as &lt;a href=&#34;https://blog.codinghorror.com/rule-of-three/&#34;&gt;The Rule of Three&lt;/a&gt; which broadly states that one shouldn&amp;rsquo;t attempt to put any effort into making something reusable until the third instance they need it. Then, and only then, can you generalise it, using the knowledge of what you need in the moment to do so well.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>New Theme, Who&#39;s Dis</title>
      <link>https://lmika.org/2025/09/20/new-theme-whos-dis.html</link>
      <pubDate>Sat, 20 Sep 2025 16:45:51 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/09/20/new-theme-whos-dis.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been using Tiny Theme, by Matt Langford, on lmika.org for &lt;a href=&#34;https://lmika.org/2023/10/26/happy-new-theme.html&#34;&gt;about 23 months&lt;/a&gt;. And it&amp;rsquo;s been an excellent theme. A good looking, versatile design. But I felt it was time for a refresh, so I&amp;rsquo;m moving this blog over to Matt&amp;rsquo;s latest theme, &lt;a href=&#34;https://mythos.micro.blog&#34;&gt;Mythos&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before completely abandoning Tiny Theme, I&amp;rsquo;d thought to record some screenshots, as recommended by more experienced bloggers whenever they do a theme refresh. So here&amp;rsquo;s a gallery of what lmika.org looked before I made the switch — complete with it&amp;rsquo;s &amp;ldquo;summer&amp;rdquo; colour scheme that I neglect to updated when autumn came around (if I kept it for two more months, it would&amp;rsquo;ve been relevant again):&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250920-160327.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  This blog in Tiny Theme, with a custom &amp;#39;summer brown&amp;#39; colour scheme.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250920-160353.png&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250920-160407.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  I tried adjusting the background colour of these dialogues, but I just couldn&amp;#39;t find a colour that I liked.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250920-160420.png&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Simply applying the theme made this site look pretty good, but I also took this opportunity to try a few things out. For one thing, the index page is no longer a simple reverse-chronological list of posts. I&amp;rsquo;ve modelled it after Scripting News, where posts are grouped into days. The days themselves are reverse chronological but the posts within those days are arrange in reverse-reverse chronological (some might choose to call this simply &amp;ldquo;chronological&amp;rdquo;) with the large posts with titles now appearing below the shorter ones.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a style that I&amp;rsquo;ve found myself wanting to try after adopting it for my journal in Obsidian. I&amp;rsquo;ve settled on an approach where each day gets a single daily note and nothing more. For too long I&amp;rsquo;ve been holding on to the need to know the exact second any given thought was recorded, which was keeping me from having a system that works. Days would have three or four notes that would make the sidebar look untidy. After letting go of this need, I found that I didn&amp;rsquo;t really care about this detail. The exact second of the day when a thought occurred doesn&amp;rsquo;t seem that important in a body of work that&amp;rsquo;d meant to span multiple years. So what better time to try it here, with this redesign.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250920-160446.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The new home page, demonstrating the new layout grouping posts by day.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250920-160524.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  A better demonstration of a grouped day with a few micro-posts.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250920-160600.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The restyled &amp;#39;More&amp;#39; page. Now with more emoji (I do like the one I chose for my Twitter Archive).
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250920-161613.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  A new &amp;#39;Evergreen&amp;#39; post list, showing posts that are meant to be revisited occasionally.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;rsquo;ve made a few other changes, such as turning off visitor analytics. I don&amp;rsquo;t need those metrics, and they were starting to becoming unreliable anyway, as this site is starting to appear on the lists of AI indexers. I am also considering some more journal-ey posts: more things about works in progress or thoughts that are half-developed. I&amp;rsquo;ve gotten into the habit of recording them in Obsidian, but some of them I do want to publish, and I think the redesigned home page will make it easier for me to do that.&lt;/p&gt;
&lt;p&gt;So, that&amp;rsquo;s the reason for the redesign. Thanks again to &lt;a href=&#34;https://mattlangford.com/&#34;&gt;Matt Langford&lt;/a&gt; for making an awesome theme.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Learning To Like Sentinel Errors In Go</title>
      <link>https://lmika.org/2025/09/18/learning-to-like-sentinel-errors.html</link>
      <pubDate>Thu, 18 Sep 2025 09:24:50 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/09/18/learning-to-like-sentinel-errors.html</guid>
      <description>&lt;p&gt;I use to be someone who returned a nil value along with a nil error whenever I needed to indicate that a value was missing, like a database record. But recently, I&amp;rsquo;ve come around to the idea of returning either a result, or an error: never returning neither.&lt;/p&gt;
&lt;p&gt;It may not be technically correct. If something doesn&amp;rsquo;t exist in a database, one can make the argument that a function implemented in a way that, when said aloud, would effectively be &amp;ldquo;well, I didn&amp;rsquo;t have any errors trying to find the thing; but I couldn&amp;rsquo;t find the thing either, so I got nothing successfully.&amp;rdquo; And I know that sentinel errors is &lt;a href=&#34;https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully#:~:text=So%2C%20my%20advice%20is%20to%20avoid%20using%20sentinel%20error%20values%20in%20the%20code%20you%20write.%20There%20are%20a%20few%20cases%20where%20they%20are%20used%20in%20the%20standard%20library%2C%20but%20this%20is%20not%20a%20pattern%20that%20you%20should%20emulate.&#34;&gt;generally frowned upon by certain Go language designers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But I think it makes for more readable code; dare I say, more empathetic code. If much of what you write in Go is stuff like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;res&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getThingFromDB&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then I think it&amp;rsquo;s safe to assume that the reader will think that &lt;code&gt;res&lt;/code&gt; will have a value if &lt;code&gt;err&lt;/code&gt; is nil. I know it&amp;rsquo;s generally not correct to assume that, just like it&amp;rsquo;s not correct to assume that a pointer value is never nil. But us coders are still human (at least for now), and doing the obvious thing is still better than being technically correct.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Trying OpenAI Codex to Produce Freelens Logo Creator</title>
      <link>https://lmika.org/2025/09/13/devlog-trying-openai-codex-to.html</link>
      <pubDate>Sat, 13 Sep 2025 09:05:30 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/09/13/devlog-trying-openai-codex-to.html</guid>
      <description>&lt;p&gt;Tried a bit of vibe coding using OpenAI Codex yesterday.&lt;/p&gt;
&lt;p&gt;I needed something to produce nice logos for &lt;a href=&#34;https://freelensapp.github.io&#34;&gt;Freelens&lt;/a&gt; so I can tell each cluster apart. The default avatar for Freelens is an abbreviation of the cluster name set atop a random background colour, which doesn&amp;rsquo;t do me a lot of good as the cluster names I work with are all similar, and would produce the same abbreviations. Furthermore, Freelens doesn&amp;rsquo;t have a way to change these default avatars. But it does have a way to upload new avatars as an image.&lt;/p&gt;
&lt;p&gt;This was a perfect candidate for trying out OpenAI Codex. A use-case that I needed but was not important nor interesting enough for me to build manually. I mean, knowing me, I probably would&amp;rsquo;ve built it, but it wouldn&amp;rsquo;t have been good use of my time. So I created a new Github repo, connected it to Codex, and gave it this prompt:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;I would like a static web-page, consisting of one HTML file, and one JavaScript file. This is to be vanilla JavaScript that does not require NPM or any external dependencies.&lt;/p&gt;
&lt;p&gt;This page is to have a form which accepts the following input:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A text input with the name &amp;ldquo;Upper&amp;rdquo; which accepts a string of any kind.&lt;/li&gt;
&lt;li&gt;A text input with the name &amp;ldquo;Lower&amp;rdquo; which accepts a string of any kind.&lt;/li&gt;
&lt;li&gt;A selection for background colour. Instead of a colour picker, please allow the user to select one of 8 distinct colour choices. Each one should be distinct enough such that the user is able to distinguish them from each other. They should be of medium darkness, such that if white text were to be display atop of it, the text should be legible; yet not too dark as to be hard to distinguish.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The page is also to have a canvas element, of about 80 x 80 points, which will take input of the form above and do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fill the background with the chosen background colour.&lt;/li&gt;
&lt;li&gt;Draw a thin dividing line — no greater than 6 px wide — in white across the width of the canvas.&lt;/li&gt;
&lt;li&gt;Print the form value of &amp;ldquo;Upper&amp;rdquo; in uppercase, in a san serif font, centred and above the dividing line in white. The printed text should be large enough to fill most of&lt;/li&gt;
&lt;li&gt;Print the form value of &amp;ldquo;Lower&amp;rdquo; in uppercase, in a san serif font, centred and below the dividing line in white.&lt;/li&gt;
&lt;li&gt;Ensure that a suitable margin of 15 pixels padding around the border of the canvas content, and that the printed text and dividing line is not drawn beyond the margin.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The canvas element is to be updated when the form values are changed.&lt;/p&gt;
&lt;p&gt;Below the canvas should be a button which should allow the user to download the canvas image as a PNG.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;And here&amp;rsquo;s what it produced:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250913-090254.png&#34; width=&#34;600&#34; height=&#34;447&#34; alt=&#34;Auto-generated description: A browser window showcases a logo generator tool with a blue logo on the left labeled TEST AP-1 and options for customization above.&#34;&gt;
&lt;p&gt;It pretty much did what it said on the tin: when I changed the value of upper or lower, or changed the background colour, the canvas would redraw showing a different preview. Clicking &amp;ldquo;Download PNG&amp;rdquo; would allow me to save it as an image.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250913-090258.png&#34; width=&#34;600&#34; height=&#34;447&#34; alt=&#34;Auto-generated description: A logo design interface with fields for upper and lower text, a background color menu, and a Download PNG button is displayed in a browser window.&#34;&gt;
&lt;p&gt;On the whole, what I received was pretty good. It&amp;rsquo;s not perfect — there&amp;rsquo;s no styling; orange is a bit too close to amber, as is green and teal; and the alignment of the bottom line is slightly off. But it&amp;rsquo;s a pretty decent start.&lt;/p&gt;
&lt;p&gt;There were some things I wanted to change, so I went in myself. I found a nice looking stylesheet called &lt;a href=&#34;https://picocss.com&#34;&gt;Pico CSS&lt;/a&gt;, which I&amp;rsquo;ve applied. Normally I would&amp;rsquo;ve turned to &lt;a href=&#34;https://simplecss.org&#34;&gt;simple.css&lt;/a&gt;, but I&amp;rsquo;ve been using that a lot and I wanted to try a nice, fresh look. I also adjusted the vertical positioning of the lower row in the icon, added support for single line icons, and adjusted the colours a little.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250913-090305.png&#34; width=&#34;600&#34; height=&#34;435&#34; alt=&#34;Auto-generated description: A logo creation interface is displayed with options to input text, select background color, and download a PNG file.&#34;&gt;
&lt;p&gt;The whole thing looks quite nice now, and I&amp;rsquo;m quite happy with the icons it produces:&lt;/p&gt;
&lt;img class=&#34;block-center&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250912-220408.png&#34; width=&#34;212&#34; height=&#34;212&#34; alt=&#34;Auto-generated description: Four colored squares labeled Dev, Test, Preprod, and Prod represent different stages of a deployment pipeline.&#34;&gt;
&lt;p&gt;I&amp;rsquo;m still working out how these AI coding agents will fit into my life. I&amp;rsquo;ve been using Claude Code occasionally, and that&amp;rsquo;s been a good fit for projects where I&amp;rsquo;d prefer to be the driver. But there are occasional things that I&amp;rsquo;d like to see exist but are a little too trivial to spend the time on. In those cases, I may not have built it at all; or I would&amp;rsquo;ve built it and then immediately regret spending the time doing so it. And that might be where Codex could be useful: it provides a good starting point, which I can then refine.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in trying this tool out, &lt;a href=&#34;https://tools.lmika.app/freelens-logo/&#34;&gt;you can find a link to it here&lt;/a&gt;. The source &lt;a href=&#34;https://github.com/lmika/webtools&#34;&gt;you can find on GitHub&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Dequoter — Something Different Today</title>
      <link>https://lmika.org/2025/09/06/devlog-dequoter-something-different-today.html</link>
      <pubDate>Sat, 06 Sep 2025 16:10:04 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/09/06/devlog-dequoter-something-different-today.html</guid>
      <description>&lt;p&gt;When it comes to hobby projects, I&amp;rsquo;m trying to reduce the number of things I build for work. But I&amp;rsquo;ve decided to revisit one that I&amp;rsquo;ve abandoned, given the frequent need for this. The issue is simple: I have a JSON data structure that is quoted within a string, and I want to &amp;ldquo;unquote&amp;rdquo; it and filter the JSON so that I can read it.&lt;/p&gt;
&lt;p&gt;So I thought I&amp;rsquo;d spend today restarting the Dequoter project. The best way to describe it is &lt;a href=&#34;https://github.com/IvanMathy/Boop&#34;&gt;a Boop clone&lt;/a&gt;. I like Boop, I use Boop. And I know Boop is extendable, so I could implement any processor I need using JavaScript. But I&amp;rsquo;m not a huge fan of JavaScript, and Go has the function I need &lt;a href=&#34;https://pkg.go.dev/strconv@go1.25.1#Unquote&#34;&gt;right there in the standard library&lt;/a&gt;. And with most things I encounter, I want to build my own. I know I shouldn&amp;rsquo;t, and I&amp;rsquo;ve been better at resisting. But I&amp;rsquo;d figured this could be a nice little distraction. And I thought I could get much of what I need done in a morning.&lt;/p&gt;
&lt;p&gt;So I started a new &lt;a href=&#34;https://wails.io&#34;&gt;Wales project&lt;/a&gt; and after about 3 hours, I had a scratchpad with an invokable command palette that I can use to dequote a string and format a JSON object:&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-09-06-at-15.57.26.mp4&#34; poster=&#34;https://lmika.org/uploads/2025/ecf16f805a.png&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Granted, it&amp;rsquo;s not much for 3 hours. While I did use Claude Code a little, much of this was hand rolled. And I spent a lot of time learning the ins and outs of &lt;a href=&#34;https://codemirror.net&#34;&gt;Code Mirror&lt;/a&gt;. This is why I wanted to use Wales this time around. The &lt;a href=&#34;https://lmika.org/2024/07/20/continuing-my-exploration.html&#34;&gt;previous run at this&lt;/a&gt; used &lt;a href=&#34;https://fyne.io/&#34;&gt;Fyne&lt;/a&gt;, which was really nice to use, but the available widgets were quite limited. The nice thing about Wales is that I can build the UI in HTML and it uses the built-in HTML renderer from the OS: no need to bundle Chrome. The frontend is mainly vanilla HTML and JavaScript, although I am using &lt;a href=&#34;https://stimulus.hotwired.dev&#34;&gt;Stimulus&lt;/a&gt; to help with some of the controls. The backend is, of course, Go.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-09-06-at-15.46.43.png&#34; width=&#34;400&#34; height=&#34;572&#34; alt=&#34;Auto-generated description: Window displaying file information for an application named dequoter, including details like size, location, and version.&#34;&gt;
&lt;figcaption&gt;Final build size is 8.4 MB.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;By the end of this morning&amp;rsquo;s session, what I had was not pretty, but it&amp;rsquo;s functional and it already does what I need it to do. I do have some plans for integrating this with UCL, and using that to define processors. That should hopefully leave all the heavy core stuff to be built near the beginning, and that could sit pretty while the extensions are added in a nicer language. Anyway, more on this if this goes anywhere.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Godot Project — Bricks in Level 2-3 Laid</title>
      <link>https://lmika.org/2025/09/01/devlog-godot-project-bricks-in.html</link>
      <pubDate>Mon, 01 Sep 2025 21:31:56 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/09/01/devlog-godot-project-bricks-in.html</guid>
      <description>&lt;p&gt;Just a quick update today. I&amp;rsquo;ve finished all the brickwork in level 2-3. And it didn&amp;rsquo;t go too badly. Made one significant mistake which would&amp;rsquo;ve involved a lot of rework, that I patched up with some single tiles:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250901-212752.png&#34; width=&#34;600&#34; height=&#34;722&#34; alt=&#34;Auto-generated description: Two sections of a pixelated game map featuring stone walls, an arched door, and orange blocks resembling platforms.&#34;&gt;
&lt;figcaption&gt;Top: the mistake. Bottom: the fix.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Doing the rest of it was pretty dreary work. Godot does have some tools to make this easier, but there was no getting around the level of care needed to place the bricks correctly. But it&amp;rsquo;s all pretty much done now. And just for comparison to the &lt;a href=&#34;https://lmika.org/2025/07/26/devlog-godot-project-level-and.html&#34;&gt;before screenshots&lt;/a&gt; I took when I started, here&amp;rsquo;s how how the level looks now:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250901-211644.png&#34; width=&#34;600&#34; height=&#34;362&#34; alt=&#34;Auto-generated description: A pixelated video game scene shows a knight on a platform with lava below, stone blocks, and crates to the right.&#34;&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250901-211652.png&#34; width=&#34;600&#34; height=&#34;362&#34; alt=&#34;Auto-generated description: A pixelated video game scene shows a character in armor navigating a platform level with brick walls, crates, and spiked obstacles.&#34;&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250901-211659.png&#34; width=&#34;600&#34; height=&#34;362&#34; alt=&#34;Auto-generated description: A pixelated character in knight armor stands on a brick platform with a gray background, part of a retro-style video game.&#34;&gt;
&lt;p&gt;There&amp;rsquo;s still plenty of work. The background is not yet done, nor are any of the pickups placed. The HUD needs updating to show the key gems the player has, and I also need to repair some dodgy mechanics around moving platforms. But I guess that&amp;rsquo;s just a matter of plowing ahead.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Shutting Down Nano Journal</title>
      <link>https://lmika.org/2025/08/30/devlog-shutting-down-nano-journal.html</link>
      <pubDate>Sat, 30 Aug 2025 15:34:43 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/08/30/devlog-shutting-down-nano-journal.html</guid>
      <description>&lt;p&gt;With the move to Obsidian for my journalling needs, I shut down my bespoke journalling web-app. I deployed it on 26th August 2024, which makes it just over a year old. I did start using Obsidian on the 20th though, so it didn&amp;rsquo;t quite make it the entire year. Even so, not bad for something hand made and somewhat neglected. Most things I eventually abandon last way less than that.&lt;/p&gt;
&lt;p&gt;Anyway, here are some screenshots of the final version, just before I shut it down:&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250830-115806.png&#34;
     
        alt=&#34;Auto-generated description: A digital journal interface features a text box with the prompt What&amp;#39;s been happening today and buttons labeled Attach and Save.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The landing page, with a full-width post text area.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250830-115815.png&#34;
     
        alt=&#34;Auto-generated description: A journal entry interface with a textbox discussing layout issues and an attachment option is shown.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  A post with attachments.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250830-115840.png&#34;
     
        alt=&#34;Auto-generated description: A journal webpage displays a list of dated entries from May to August 2025, with links to each entry.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Clicking &amp;#39;Posts&amp;#39; will bring up a chronological list of posts from the last 6 months. Those with a paperclip are ones with attachments.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250830-115851.png&#34;
     
        alt=&#34;Auto-generated description: A digital note dated May 17, 2025, expresses frustration about relying on others for problem-solving and a desire for improvement.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  An example of a viewing a post. Attachments would appear below the body.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;You can compare this with some earlier screenshots documented in these posts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/08/22/i-enjoyed-reading.html&#34;&gt;22 August 2024&lt;/a&gt;: Development work started&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/09/15/weekly-update-sept.html&#34;&gt;15 September 2024&lt;/a&gt;: Addition of attachments and titles&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/10/04/im-not-really.html&#34;&gt;4 October 2024&lt;/a&gt;: Adding Git synchronisation of entries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I guess you can describe the ascetics of this project as the &amp;ldquo;minimum amount to get working.&amp;rdquo; That was true of the backend too, which was somewhat rushed and difficult to maintain. That might be why I never really gave this a lot of love as other projects like Blogging Tools. But I added some features I that I thought were neat: such as attachments, and keeping a copy of a draft in the browser&amp;rsquo;s local storage before it was sent to the server.&lt;/p&gt;
&lt;p&gt;Anyway, I&amp;rsquo;m now in the middle of migrating the old posts over to Obsidian. One thing I&amp;rsquo;m glad I added was Git synchronisation. And given that entries were plain markdown files, migration was simply a matter of checking out the repository and moving the posts across. Easy enough work, although I am taking my time. There&amp;rsquo;s a bit of reliving involved with moving the posts over, and I&amp;rsquo;m approaching a period where some pretty sad things happened.&lt;/p&gt;
&lt;p&gt;Anyway, that was Nano Journal. Good at what it needed to do.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Godot Project — Level 2-3 Update</title>
      <link>https://lmika.org/2025/08/26/devlog-godot-project-level-update.html</link>
      <pubDate>Tue, 26 Aug 2025 22:27:58 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/08/26/devlog-godot-project-level-update.html</guid>
      <description>&lt;p&gt;Been working on level 2-3 these past few weeks. And it&amp;rsquo;s a large one: 4,720 x 2,928 units. There are longer levels but those tend to be quite horizontal, unlike this one which feels like a mixture of the two dimensions:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250826-214656.png&#34; width=&#34;600&#34; height=&#34;318&#34; alt=&#34;Auto-generated description: A pixel art depiction of a pyramid interior displayed within a game development software interface.&#34;&gt;
&lt;p&gt;Reached a milestone this evening: the critical path has been built. And yeah, it ended up pretty much being a standard key-hunt. I did have an idea for having more puzzle elements, like requiring the player to work out combinations based on key colour, but given the length one has to travel to get the necessary keys, it felt like a hit to the pacing. I figured it would be more interesting to vary the mechanics in each zone the keys are located. They&amp;rsquo;re not complicated mechanics, but they did reach the limit of what I had in my toolbox. It got to the point where I started adding the classics, like dart shooters which fired projectiles.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250826-214959.png&#34;
     
        alt=&#34;Auto-generated description: A pixelated video game scene features a knight character standing on a wooden crate within a brick-walled level, avoiding swinging spiked traps.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Pick up these gems…
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250826-214933.png&#34;
     
        alt=&#34;Auto-generated description: A pixel art video game scene features a knight character on a brick platform navigating a level with ladders and a pickaxe-like object.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  …and place them on this holders to activate things. And yes, I stole this directly from Commander Keen.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250826-215012.png&#34;
     
        alt=&#34;Auto-generated description: A pixel art-style video game features a small knight character on green platforms above a wavy brown surface with obstacles and coins on the screen.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Given that this is in the desert world, the sand spinners make an appearance.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250826-214944.png&#34;
     
        alt=&#34;Auto-generated description: A pixelated video game scene features a knight character navigating platforms and obstacles made of spikes and bricks.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The new dart shooters and the projectiles they fire.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;There are a few mechanics I wanted the player to learn. Trying to telegraph that via gameplay is not easy, but I&amp;rsquo;m hoping it will comes through. Some approaches I&amp;rsquo;m trying are slight cues in colour and making sure interaction indicators are clear. And also level geometry, such as moving the player through one-way gates and making it obvious that all they have to proceed is right in front of them.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also decided to move away from paid checkpoints, where you need to pay 5 coins to save your progress. I&amp;rsquo;ve replaced these with hidden checkpoints, of which this level has a few. I think given the size, and that some backtracking is involved, it makes more sense being quite generous with checkpoints in this level. I think I&amp;rsquo;ll keep the 5 coin toll gates though for other things.&lt;/p&gt;
&lt;p&gt;Work remaining on this level is prettying it up (it&amp;rsquo;s still rather barebones) plus adding pickups and maybe some bonuses. Painting in the brick tiles will take a while, but could be a nice exercise in calming work when one can switch off their brain somewhat. I also need to make the background tiles and (ugh) the actual level backdrop. Not quite sure what that will be. Something deserty no doubt.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: UCL — Comparing UCL To Some Early Ideas</title>
      <link>https://lmika.org/2025/08/22/devlog-ucl-comparing-it-to.html</link>
      <pubDate>Fri, 22 Aug 2025 11:14:02 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/08/22/devlog-ucl-comparing-it-to.html</guid>
      <description>&lt;p&gt;I was browsing through some very old notes I had when I came across one that contain an idea for a hypothetical shell-like command language, sort of what UCL was designed for. It was designed for a project called Nuget, which was a CLI torrent downloader (now defunct). Much like UCL, it was REPL based with a simple token-based command language, and I was thinking of ways to extend this to including scripting. Here&amp;rsquo;s the note in full:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;The nuget command language will be something very similar to shell. It will be used to configure nuget (in an RC file), and configure extensions.&lt;/p&gt;
&lt;p&gt;Example - check that the VPN is running&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let piaCtlState = !piactl get connectionstate

if eval &#39;trim(${piaCtlState}) ne &amp;quot;Connected&amp;quot;&#39; {
    notice &amp;quot;Warn: VPN not connected.  Use &#39;piactl&#39; to connect&amp;quot;
}

on newjob {
    on done { echo &amp;quot;Job done: ${job}&amp;quot; }
}
&lt;/code&gt;&lt;/pre&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I never implemented this language, opting to simply use TCL. But it&amp;rsquo;s interesting to see the desire of having an embeddable language for both REPLs and scripting alive and well back in February 2021 (the note&amp;rsquo;s datestamp).&lt;/p&gt;
&lt;p&gt;Assuming that UCL existed at the time, I image the script would&amp;rsquo;ve looked a little less TCL-ly and a little closer to how Go templates work. Maybe something likes the following, assuming that the built-ins were implemented:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;piaCtlState = os:shell &amp;#34;piactl get connectionstate&amp;#34;

if (ne (strs:trim $piaCtlState) &amp;#34;Connected&amp;#34;) {
    notice &amp;#34;Warn: VPN not connected.  Use &amp;#39;piactl&amp;#39; to connect&amp;#34;
}

# I&amp;#39;m guessing &amp;#39;on&amp;#39; in the original idea would have job as a global variable.
# UCL can simply pass it in as an argument.
on newjob { |job|
  on done { echo &amp;#34;Job done: $job&amp;#34; }
}
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>On The Little Things of Data Representation</title>
      <link>https://lmika.org/2025/08/15/on-the-little-things-of.html</link>
      <pubDate>Fri, 15 Aug 2025 07:44:24 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/08/15/on-the-little-things-of.html</guid>
      <description>&lt;p&gt;When it comes to writing code that works with a database, I tend to, or at least try to, obsess over how the data is marshalled. That includes the things you&amp;rsquo;d expect, like how the data is structured or what information is being stored. But it also includes the little things: what case am I using for the field names; whether an absent value should be represented as either an empty value or an absent field; whether nulls should be a thing, whether they should be different from empty values, etc.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t know how prevalent these feelings are amongst the industry. I was under the impression that it&amp;rsquo;s pretty fundamental, one could even say self-evident, to know that it&amp;rsquo;s much easier to change the code than it is to change the data. But in my experience, I found that these feelings are not as widespread as I imagined. I get the impression that there are some out there that take a very… shall we say, &amp;ldquo;relaxed&amp;rdquo; approach to these decisions.&lt;/p&gt;
&lt;p&gt;I partially attribute this to the introduction of NoSQL databases, like DynamoDB. When the whole pitch with these data stores is that &amp;ldquo;you don&amp;rsquo;t need to know how the data is structured, just cram it all in,&amp;rdquo; it&amp;rsquo;s shouldn&amp;rsquo;t be a surprise that developers take on that advice. So I open up a DynamoDB table and a field might use both null or empty to represent the same thing, or one field is in camel-case and another is in capital-camel-case. And I just sigh. It&amp;rsquo;s not enough to want me to run a data fix, but I do wish it was much neater&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s not like projects using SQL databases are immune. I remember the bad old days of working with Java and Hibernate where the database schema, rather than being hand-rolled, would be generated from the type hierarchy. Really? You&amp;rsquo;re going to let an automated tool scan your classes and modify the database schema without review? You know, that thing with customer data? And you&amp;rsquo;re going to do that at runtime upon launch?&lt;/p&gt;
&lt;p&gt;And it&amp;rsquo;s not like it was a good schema. In fact, it was rather terrible. There was one table that was a single column of incrementing integers for some reason. And the application absolutely ground to a halt whenever data needed to be queried or modified. Yeah, I&amp;rsquo;m glad I&amp;rsquo;m not on that project anymore, and I&amp;rsquo;m far removed from Java that I&amp;rsquo;m not sure if ORMs are still a thing, but I hope they&amp;rsquo;re not.&lt;/p&gt;
&lt;p&gt;I guess the point I&amp;rsquo;m trying to make is this: don&amp;rsquo;t treat your data representation as an afterthought. Take good care of it: the little things like case matter. It&amp;rsquo;ll be around much longer than you think.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I share some of the blame here. After all, it&amp;rsquo;s my job to review these changes.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Blogging Tools — All About Images</title>
      <link>https://lmika.org/2025/08/10/devlog-blogging-tools-all-about.html</link>
      <pubDate>Sun, 10 Aug 2025 21:30:40 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/08/10/devlog-blogging-tools-all-about.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been working on Blogging Tools on-and-off the last few weeks, mainly around images and image management. They&amp;rsquo;re not super exciting in their own right so I&amp;rsquo;d figure I&amp;rsquo;ll briefly write about them here.&lt;/p&gt;
&lt;p&gt;The first major addition is the ability to save images to the server from the Image Tool app. There are some composition processors available and if one wanted to pre-process the image in some way, they would have to download the pre-processed image and upload it again for composition. This change will hopefully reduce the need for this back-and-forth. The download button has been replaced within the Image Tool app with a Save button, which will upload the processed image to the server.&lt;/p&gt;
&lt;p&gt;Saved images can be recalled from the processor by using a new picker. This replaced the Load image by URL option, which I never really used (although I may add it back within the Saved Image app).&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250810-212554.png&#34; width=&#34;600&#34; height=&#34;368&#34; alt=&#34;Auto-generated description: A grid layout displays images of a pigeon, crow, lion, tiger, and a cartoon fish on a blogging tool interface.&#34;&gt;
&lt;p&gt;There is also a new Saved Images app which allows one to browse the saved image, file them into categories, and download them to the local file system.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250810-212627.png&#34; width=&#34;600&#34; height=&#34;455&#34; alt=&#34;Auto-generated description: A webpage titled Blogging Tools displays five saved images including a pigeon, a crow, a lion, a tiger, and a yellow fish in a grid layout.&#34;&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250810-212648.png&#34; width=&#34;600&#34; height=&#34;455&#34; alt=&#34;Auto-generated description: A stuffed toy of a pigeon is placed on a table, visible within a web interface.&#34;&gt;
&lt;p&gt;Now those with keen eyes may notice the quality of the saved images in the screenshots above. That&amp;rsquo;s because the second change I made was to integrate calls to an image generator. There was no reason for doing this other than to try out the API, and I haven&amp;rsquo;t really used it for anything other than make test images. The API I&amp;rsquo;m using is Flux 1 provided by &lt;a href=&#34;https://together.ai/&#34;&gt;together.ai&lt;/a&gt;, but there&amp;rsquo;s also a partially-functional version of Gemini&amp;rsquo;s API too (the API is region locked and is not available in Germany, which is where Blogging Tools is running).&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250810-212658.png&#34; width=&#34;600&#34; height=&#34;387&#34; alt=&#34;Auto-generated description: A webpage titled Blogging Tools features an Image Generator section with a prompt about a cartoon person leaning out a window in comedic anger and buttons to Generate and Clear Completed.&#34;&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250810-212705.png&#34; width=&#34;600&#34; height=&#34;387&#34; alt=&#34;Auto-generated description: A user interface titled Blogging Tools with options for apps, notices, and settings, includes a request input box, a provider selection, and a generate button.&#34;&gt;
&lt;p&gt;Saved Images are build on the existing Files subsystem, which has been relegated to a new Settings section, the third change I&amp;rsquo;ve made to Blogging Tools. This allows one to manage files, see running jobs, and managed sessions from the system. That section may expand in time as I think of things I&amp;rsquo;d want to change without redeploying the app (most of the config is managed using environment variables).&lt;/p&gt;
&lt;p&gt;The last change was the addition of a new notification type. I&amp;rsquo;ve added &lt;a href=&#34;https://help.micro.blog/t/photo-collections/3366&#34;&gt;photo collections&lt;/a&gt; to my blog a few months ago, but I never remember to actually file photos into any of them. My workflow with making a photo post usually ends at the point I hit &amp;ldquo;publish&amp;rdquo;, and as far as I can tell, there&amp;rsquo;s no way to set the category of the image from the new post screen.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;ve added a new notification type to help me with this. When the RSS feed of my blog is polled, and a new post with the &amp;ldquo;Photos&amp;rdquo; category is found, the first 4 images are extracted and presented as a notification asking if I want to file the photos into a photo category.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250810-212755.png&#34; width=&#34;600&#34; height=&#34;455&#34; alt=&#34;Auto-generated description: A blogging tools interface displays a notice with an image of a parked red car with its hood open next to a white car.&#34;&gt;
&lt;p&gt;This also worked for posts you add to the &amp;ldquo;Photos&amp;rdquo; category via the notification subsystem itself. Both act on the same RSS feed and setting the category of a post to &amp;ldquo;Photos&amp;rdquo; will clear the fetch history of that item so that it can run through the processors again.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve decided to use notifications for this, rather than add something to do this automatically, as I do want to go through the candidate photos first. But there are some limitations to this approach. For one, the notification system only allows one user selection per notification, making it impossible to add a photo to more than one collection. And to keep the number of notifications low, only the first 4 images are presented, with the rest being ignored. I have some ideas of how this could be improved in the future — probably by adding a separate app — but for now, I think I could live with taking corrective action manually.&lt;/p&gt;
&lt;p&gt;One last think about this new category processor: it&amp;rsquo;s activated by a change to the RSS feed, but it scans for images using the original post retrieved from Micro.blog&amp;rsquo;s micropub API. The reason for that I&amp;rsquo;ve enabled the CDN option, and the image URLs which use the CDN do not work with the collections API. It took me a while before I figured this out, mainly because I wasn&amp;rsquo;t using a CDN on my test blog and that worked perfectly. Something to keep in mind for anyone else using this API.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Kicking the Tyres of Ollama&#39;s Native App</title>
      <link>https://lmika.org/2025/08/06/kicking-the-tyres-of-ollamas.html</link>
      <pubDate>Wed, 06 Aug 2025 16:43:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/08/06/kicking-the-tyres-of-ollamas.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve seen a &lt;a href=&#34;https://www.manton.org/2025/08/04/parker-ortolani-blogs-about-the.html&#34;&gt;few people online&lt;/a&gt; talk about &lt;a href=&#34;https://ollama.com/blog/new-app&#34;&gt;the new Ollama native macOS app&lt;/a&gt;. I&amp;rsquo;ve not used Ollama&amp;rsquo;s CLI tools myself, and have very limited experience with running models locally. I think I tried a couple of years ago, and gave up at the step when they asked me to setup a Python virtual environment. Since then I was always under the impression that setting up LLMs to run locally would be painful, and while I liked the idea of doing so, it wasn&amp;rsquo;t enough to go through that pain again. So hearing about Ollama packaging all this in a native app piqued my interest, and I thought I&amp;rsquo;d give it a go.&lt;/p&gt;
&lt;p&gt;Install was as expected: just download a DMG and add to Applications. I was half expecting the package to be huge to accomodate the models, but the download doesn&amp;rsquo;t include the models themselves, which is fair enough given that there are a few to choose from.&lt;/p&gt;
&lt;p&gt;First launched revealed a very ChatGPT-esk experience, making it absolutely clear how this app worked. I poked around a little trying to find the place where I can download a model, but I couldn&amp;rsquo;t find anything. I then tried posting a question and quickly found out that models are downloaded on demand.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250806-084256.png&#34; width=&#34;600&#34; height=&#34;520&#34; alt=&#34;Auto-generated description: A minimalistic chat window features a simple cartoonish character and a text input field.&#34;&gt;
&lt;p&gt;It was then a matter of trying out some of these models. First test was with Gemma 3:4b. I asked it a question around the tongue-twister &amp;ldquo;see shells sea shells by the sea shore&amp;rdquo;. The fact that it actually produce a result was impressive, yet the quality of the answer was questionable:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250805-083942.png&#34; width=&#34;600&#34; height=&#34;494&#34; alt=&#34;Auto-generated description: A humorous chat exchange discussing a popular saying about women selling seashells at the beach.&#34;&gt;
&lt;p&gt;For comparison, here&amp;rsquo;s the same question posed to ChatGPT:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250805-084000.png&#34; width=&#34;600&#34; height=&#34;310&#34; alt=&#34;Auto-generated description: A ChatGPT interaction shows a question about a popular saying regarding a female at a beach selling sea-shells, followed by the answer with the tongue twister, She sells sea-shells by the sea-shore.&#34;&gt;
&lt;p&gt;So this model struggled a little, probably because it was on the small size (I&amp;rsquo;m assuming the &amp;ldquo;4b&amp;rdquo; refers to the number of parameters. 4 billion?). I didn&amp;rsquo;t get a good gauge as to how slow the response was. It was slower, but not as slow as I was expecting. Plus it was coloured by the time it took to download the model, which itself was not fast (maybe 30 minutes to an hour, although I wasn&amp;rsquo;t on a fast connection).&lt;/p&gt;
&lt;p&gt;I then tried the same question with Deepseek R1:8b, and was greeted with a reminder that this is not a Western trained model. But after some clarification it got there in the end. Ollama actually displayed the reasoning steps as the model was going through it&amp;rsquo;s inference, which is a nice touch.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250805-105958.png&#34; width=&#34;600&#34; height=&#34;538&#34; alt=&#34;Auto-generated description: A text-based conversation discussing the saying She sells seashells by the sea shore with an additional note about cultural differences.&#34;&gt;
&lt;p&gt;I could go on and try Qwen 3, but you get the idea. This is not really about the models (okay, it&amp;rsquo;s sort of about the models, but I&amp;rsquo;m hardly putting them through the paces).&lt;/p&gt;
&lt;p&gt;So on the whole, I found the app itself is quite pleasant to use. The minimal aesthetics make it very approachable for someone who only had experience using ChatGPT. I also like that you can go back through your chat and edit past questions. Being able to &amp;ldquo;alter the context&amp;rdquo; like this is something I wish the online AI chat apps support, particularly when the model start going down the wrong path.&lt;/p&gt;
&lt;p&gt;At the same time, I do wish I have a bit more control over the experience. I&amp;rsquo;m not a user of Ollama&amp;rsquo;s CLI tool so I don&amp;rsquo;t know what&amp;rsquo;s possible but some things I would like to see added down the line:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A way to manage model downloads outside of the chat window.&lt;/li&gt;
&lt;li&gt;A way to configure the system prompt. I actually don&amp;rsquo;t know if Ollama&amp;rsquo;s applying one, but if it is, it would be nice to see/modify it.&lt;/li&gt;
&lt;li&gt;A way to export the chat transcript.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So, would I continue using this? Honestly, probably not, mainly because of performance. I&amp;rsquo;m not sure the computer I&amp;rsquo;m using (a 2020 MacBook Pro M1 with 16 GB RAM) is powerful enough to be competitive to the online models. Maybe if I had the latest and greatest hardware, with as much memory and significantly more disk space, it would be a viable contender. I do have access to something a little newer (Mac Mini M2) so I maybe I&amp;rsquo;ll try it out there. It would be interesting to see how the &lt;a href=&#34;https://simonwillison.net/2025/Aug/5/gpt-oss/#atom-everything&#34;&gt;OpenAI&amp;rsquo;s new open weight models&lt;/a&gt;, which were release while I was writing this, will perform.&lt;/p&gt;
&lt;p&gt;But that said, I&amp;rsquo;m quite impress that I was able to run these models at all. I have heard of others running local models for a while now, and I like the Ollama makes this easy to do so, with a nice, approachable native app.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Moving TIL Computer To Quartz</title>
      <link>https://lmika.org/2025/08/04/moving-til-computer-to-quartz.html</link>
      <pubDate>Mon, 04 Aug 2025 22:49:57 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/08/04/moving-til-computer-to-quartz.html</guid>
      <description>&lt;p&gt;Moved &lt;a href=&#34;https://til.computer&#34;&gt;TIL Computer&lt;/a&gt; over to a new technology stack. Yes, again. This frickin&amp;rsquo; site, and it&amp;rsquo;s spiritual predecessor, has seen a few tech stacks transitions recently it&amp;rsquo;s surprising that I haven&amp;rsquo;t significantly lost anything (apart from the post dates). The main reason is that I can&amp;rsquo;t decide if this should be a blog or a wiki. The site contains the accumulation of lessons learnt and other bits of knowledge acquired as part of my software engineering job, so having a way to quickly access something is moderately important. The pages themselves are relatively long-lived, and somewhat evergreen, so there&amp;rsquo;s not a huge temporal access: one could argue that an RSS feed would be unnecessary. However, I do want that buzz from seeing something I publish show up in an RSS reader, so one would be nice. Hence the reason why this site was principally a blog for most of it&amp;rsquo;s existence.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s why my interest was piqued when I saw Jessica Smith&amp;rsquo;s post on &lt;a href=&#34;https://www.jayeless.net/blog/switched-to-quartz&#34;&gt;how she moved her site to Quartz&lt;/a&gt;. After some investigation into &lt;a href=&#34;https://quartz.jzhao.xyz&#34;&gt;Quartz&amp;rsquo;s features&lt;/a&gt;, including how well it works with Obsidian, it seemed enough of an improvement to try out.&lt;/p&gt;
&lt;p&gt;So I took an export of the old site, previously hosted on Micro.blog, and imported the markdown files into a new Obsidian vault. It was also an opportunity to finish fixing up the posts that were still encoded in HTML from the previous export, which I didn&amp;rsquo;t get around to fixing when I imported them into Micro.blog:&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250804-220856.png&#34;
     
        alt=&#34;Auto-generated description: A blog post features a code snippet for printing numbers 0 to 9 using range in Go, dated Wednesday, July 16, 2025.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The home page, which posts listed in reverse chronological order.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250804-220904.png&#34;
     
        alt=&#34;Auto-generated description: An article from TIL Computer provides a guide on using cron to run scheduled jobs, focusing on running jobs with Bash to ensure user profiles are loaded correctly.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  An example of a post.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250804-221001.png&#34;
     
        alt=&#34;Auto-generated description: A webpage details the process of decoding binary Protobuf messages using a protoc command.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Another post. I will admit I will miss the fonts used in this template.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250804-220911.png&#34;
     
        alt=&#34;Auto-generated description: A website&amp;#39;s archive page displays entries from 2025 about programming topics like Go and Temporal workflows.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The archive. The categories would go on to be the top-level folders in the Quartz site.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250804-220929.png&#34;
     
        alt=&#34;Auto-generated description: A search results page displays articles related to git with titles and publication dates.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The search, which is actually quite good.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250804-221009.png&#34;
     
        alt=&#34;Auto-generated description: A webpage titled Cheatsheets lists resources on Linux commands, Git committing, and Git history.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  These cheatsheets never really developed into anything. The goal was to concatenate a collection of titleless posts into one large page. But I never got round to building the template for that, which meant I never wrote any posts that would fit that model.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;The Obsidian vault was then added to a Git repository, which&amp;rsquo;ll get pushed to my Forgejo instance when the site is to be republished. Doing so will run a CI/CD pipeline which will download Quartz, prepare a new workspace, copy the Obsidian vault into the &amp;ldquo;content&amp;rdquo; directory, copy the config and layout files, build the site, and then push it to Netlify. This is probably not the recommended approach, but I&amp;rsquo;m very interested in making sure the Git repository is as close to the original Obsidian vault as possible. I plan to relay on Obsidian and it&amp;rsquo;s vault sync for writing the site&amp;rsquo;s content, so anything that could be done to avoid disrupting that is welcomed.&lt;/p&gt;
&lt;p&gt;The layout isn&amp;rsquo;t too far from the default, apart from some light changes to the colouring, font, and layout. I already like the wiki-style sidebar on the left, which would make finding things easier. The fact that I was more-or-less limited to reverse chronological posts list was one of the downsides of the blog-style approach (well, that&amp;rsquo;s not entirely true: I could&amp;rsquo;ve adjusted the template to be more wiki-like. I just never got around to doing so).&lt;/p&gt;
&lt;p&gt;I also simplified the information design a little. The original idea I had was to separate guides from references and &amp;ldquo;cheatsheets,&amp;rdquo; a post type that was never really defined well. But I found that I preferred just lumping the posts into folders sitting at the top-level based on the technology. Pretty unsophisticated, &amp;rsquo;tis true, but given that there&amp;rsquo;s only around 60 pages there, I think it&amp;rsquo;s just enough to know where things are while keeping things simple. I&amp;rsquo;m also hope to use the search more often, a feature Quartz comes with out of the box.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250804-221330.png&#34;
     
        alt=&#34;Auto-generated description: A webpage from TTL Computer with a welcome message and navigation sidebar showcasing various tech topics.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The new Quartz site, showing off the current… let&amp;#39;s say… &amp;#39;minimalist&amp;#39; index page.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250804-221345.png&#34;
     
        alt=&#34;Auto-generated description: A digital document or webpage titled How To REALLY Use Cron To Run Scheduled Jobs with navigation sidebar and body text explaining cron job scheduling.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  An example of a page. One thing I do like about this template is that a table of contents will appear on the right if the window is wide enough.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250804-221358.png&#34;
     
        alt=&#34;Auto-generated description: A computer interface displays a folder named Go containing a list of files or items with names and timestamps from August 2025.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Selecting a top-level folder will reveal the contents. I believe this is customisable, but this will do for me now.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250804-221411.png&#34;
     
        alt=&#34;Auto-generated description: A Git documentation webpage with a sidebar menu shows detailed instructions on committing, including a section for getting the number of Git commits to squash.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The search interface. Looks a little busy, but it definitely works. I would like to hook up a hot-key to open this up eventually.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;I may need to adjust the fonts a little more, and I am thinking of hooking up a hot-key to trigger the search box, but so far, I&amp;rsquo;m quite pleased by it. Granted it&amp;rsquo;s only been a couple of days, and I still have the backups of the old site if I want to back to this being a plain-old blog. But I think the Obsidian integration plus the more wiki-like architecture will suit this site better.&lt;/p&gt;
&lt;p&gt;Oh, and one last thing: it&amp;rsquo;s been almost a year that that site has been up, and good news: I don&amp;rsquo;t hate the name. That was the issue I had with it&amp;rsquo;s spiritual predecessor, and it got to a point where I wasn&amp;rsquo;t motivated to write on it anymore. I don&amp;rsquo;t have any of these feelings with this name. Nor do I hate the idea of writing technical stuff there and everything else here. I know this is not a concern for anyone else, but for a few years I struggled with the idea of keeping two separate sites and where things should go. With having a place to keep my technical knowledge separate and in a format that&amp;rsquo;s easier to navigate, I think those feelings are at rest.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some First Impressions of iPadOS 26 Public Beta</title>
      <link>https://lmika.org/2025/07/27/some-first-impressions-of-ipados.html</link>
      <pubDate>Sun, 27 Jul 2025 07:44:23 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/07/27/some-first-impressions-of-ipados.html</guid>
      <description>&lt;p&gt;Upgraded my iPad to the iOS 26 beta, just to see what this fuss over Liquid Glass was about. First impressions after about 30 minutes of use: hmm. Certain aspects about it do look nice — I like the effect it has on the launch bar and when moving an empty app folder over the changing background. And I can see what they&amp;rsquo;re trying to achieve. But there are some aspects I&amp;rsquo;m not too sure about.&lt;/p&gt;
&lt;p&gt;The glassy lettering style for the time on the lock screen makes it harder to see at a glance. Likewise for notifications with the glassy background. Both give the impression of something being there as long as you look carefully at the edges. It reminds me of that episode of Seinfield where Kramer found a windscreen on the road and thought to make a coffee table from it, with the other characters expressing skepticism that they wouldn&amp;rsquo;t be able to see it (&amp;ldquo;you&amp;rsquo;ll sense it&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;Another thing I&amp;rsquo;m not to sure about: the lack of definitive edges between colours in app icons, like the Photos app. They now make them look a little blurry from a distance, at least to my eyes:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/screenshot-2025-07-27-at-7.07.54am.png&#34; width=&#34;600&#34; height=&#34;316&#34; alt=&#34;Auto-generated description: Six colorful app icons on a red-orange background represent Home, Photos, Camera, Contacts, Maps, and Find My.&#34;&gt;
&lt;p&gt;Might be okay if all the icons were like this, but at this stage, when you compare it to an icon that has not been updated, like Obsidians, it just looks weird:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/screenshot-2025-07-27-at-7.29.37am.png&#34; width=&#34;600&#34; height=&#34;103&#34; alt=&#34;Auto-generated description: A row of application icons is displayed on a device&#39;s dock, including Safari, Photos, Settings, and several others.&#34;&gt;
&lt;p&gt;I don&amp;rsquo;t like what they&amp;rsquo;ve done to Safari either. The header is now taller and the address bar is now shorter. Shortening the address bar actually bothers me quite a bit. I don&amp;rsquo;t know why they had to do that, it&amp;rsquo;s not like they&amp;rsquo;re short on space. Granted I&amp;rsquo;m using Safari in landscape mode, which is how I generally use my iPad, but it&amp;rsquo;s not like the address needs to be the same size in both orientations. And it&amp;rsquo;s not like they don&amp;rsquo;t know how to do this: they did it in iOS 18.&lt;/p&gt;
&lt;p&gt;Oh, and ahh, yeah: contrast bugs in Safari&amp;rsquo;s header are still a thing:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/weekly-review-20250726-mandaris.png&#34; width=&#34;600&#34; height=&#34;136&#34; alt=&#34;Auto-generated description: A dark-themed webpage displays the name Mandaris with navigation buttons like About, Links, Photos, and Archive.&#34;&gt;
&lt;p&gt;Finally, popup menus in Safari: when you bring them up for a picker, the menu shoots off from the button to the right and then resizes to left to reveal the options. This feels buggy to me, or at least I hope it&amp;rsquo;s a bug as it&amp;rsquo;s a transition animation that I don&amp;rsquo;t care for. For something that I expect to just drop down, there&amp;rsquo;s a lot of unnecessary moving about.&lt;/p&gt;
&lt;p&gt;So yeah, first impressions are kinda mixed. I can now understand what the Apple pundits I listen to are talking about. And I do get the sense they may have bitten off more than they can chew here. Hopefully they can improve on this before the general release.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Godot Project — Level 4-2 And Level 2-3</title>
      <link>https://lmika.org/2025/07/26/devlog-godot-project-level-and.html</link>
      <pubDate>Sat, 26 Jul 2025 15:47:11 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/07/26/devlog-godot-project-level-and.html</guid>
      <description>&lt;p&gt;I can&amp;rsquo;t quite remember how far I got into building of level 4-2 when I wrote my last Dev Log entry. Based on how I described it, I think I was still working on the bones of it: a level with little more than the tile-map. I got a fair bit of the level built since then. It now features three major zones, each with a different camera lock. The central mechanic of a tile layer that can be raised by hitting switches has been built, along with it&amp;rsquo;s variations within each zone. Pickups and enemies have now been placed. And decoration. A lot of decoration.&lt;/p&gt;
&lt;p&gt;I wish I had a screenshot of how it looked when I wrote that last entry, but here&amp;rsquo;s a shot of how it looks now:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250726-154404.png&#34; width=&#34;600&#34; height=&#34;362&#34; alt=&#34;Auto-generated description: A pixelated platform game scene features a character navigating underground, with obstacles and items scattered around.&#34;&gt;
&lt;p&gt;I&amp;rsquo;m quite happy with how the mid-layer tiles add some interest to the aesthetics. But while thinking of the actual background to set the scene, I now feel that simply adding sky-coloured tiles is not enough. I may need to make a proper landscape backdrop, for no other reason than to give the level some place. Not entirely sure how I&amp;rsquo;m going to do that.&lt;/p&gt;
&lt;p&gt;Anyway, that&amp;rsquo;s for later. For now, I had another go at working on world 2. Level 2-1 is still barely started: I haven&amp;rsquo;t returned to it yet, and probably won&amp;rsquo;t until I have an idea of how to put that level together. I do have some ideas for level 2-2 and 2-3 and to ease myself in working on them, I started the week by simply building game elements, like a thirst mechanic (world 2 is a desert, as pre-ordained by &lt;a href=&#34;https://youtu.be/Gvr0qU1kPng?si=k9l60HCw9JMXkVyB&#34;&gt;Yahtzee Croshaw&lt;/a&gt;). More on those in the future I&amp;rsquo;m sure, but it was enough to get me to start work on level 2-3, which I&amp;rsquo;m thinking might be set in an underground pyramid with some windy maze elements (original, I know). Not too many windy maze elements, mind you: I generally don&amp;rsquo;t like playing maze levels. But I think a little would add some variety to what I&amp;rsquo;d imagine would be simply jumping puzzles until then. So far this has meant reverting to &amp;ldquo;playing the hits&amp;rdquo; as it were, by starting off with adding a simple lock-and-key hunt, albeit with gems (shout-out to my fellow Keen 4-6 fans). I do have a twist in mind which I hope to slowly introduce to the play as they progress through the level.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll just finish this update off with some screenshots of how level 2-3 looks now in it&amp;rsquo;s super early state.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250726-154418.png&#34; width=&#34;600&#34; height=&#34;362&#34; alt=&#34;Auto-generated description: A pixelated platformer video game features a character navigating through stone and brick structures with a goal indicator at the top left.&#34;&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250726-154429.png&#34; width=&#34;600&#34; height=&#34;362&#34; alt=&#34;Auto-generated description: A pixelated video game scene features a knight in a maze-like structure with brick walls and wooden crates, aiming to collect a purple gem.&#34;&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250726-154444.png&#34; width=&#34;600&#34; height=&#34;362&#34; alt=&#34;Auto-generated description: A pixel art video game scene shows a knight character on a platform surrounded by brick-like structures and a red arrow above.&#34;&gt;
&lt;p&gt;So that&amp;rsquo;s where things currently stand.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Blogging Tools — Category Fixer</title>
      <link>https://lmika.org/2025/07/20/devlog-blogging-tools-category-fixer.html</link>
      <pubDate>Sun, 20 Jul 2025 23:09:07 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/07/20/devlog-blogging-tools-category-fixer.html</guid>
      <description>&lt;p&gt;I was going to say that this one was pretty quick to implement, but thinking of it now, it took several hours all up, so maybe it wasn&amp;rsquo;t that quick. It&amp;rsquo;s a feature I&amp;rsquo;ve creatively called the &amp;ldquo;category fixer&amp;rdquo;. Yeah, the name&amp;rsquo;s not great, but it&amp;rsquo;s been a feature I&amp;rsquo;ve been wishing for a while, and it&amp;rsquo;s nice to see it added to Blogging Tools.&lt;/p&gt;
&lt;p&gt;I post blog posts of images which could either be a photo or a screenshot, along with the occasional meme. I have categories for all three, and when I create a new post with an image, I try to make sure the appropriate category is set. Only issue is that I usually forget, and most image posts remain without any category at all. Micro.blog does has a feature of automatically adding posts with image tags into a photos category, but I&amp;rsquo;ve turned that off as I don&amp;rsquo;t like the idea of screenshots going there.&lt;/p&gt;
&lt;p&gt;So I needed a way to fix this, and that&amp;rsquo;s what this new feature does. I&amp;rsquo;ve added a RSS feed parser which will poll my blog&amp;rsquo;s RSS feed for any new or updated posts. If there are any, the HTML content will get parsed to find at least one &lt;code&gt;img&lt;/code&gt;. If it does, an in-app notification will be added. I can then go in and triage these posts, selecting the category they should be sorted into.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250720-230148.png&#34;
     
        alt=&#34;Auto-generated description: A webpage titled Blogging Tools features notices for posts without categories, each containing expandable text and links for photos, screenshots, and memes.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The in-app notifications, accessible from a new top-level menu item.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250720-230403.png&#34;
     
        alt=&#34;Auto-generated description: A modern building with red and black exterior illuminated at night, featuring large arched windows displaying the word COBURG.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Tapping the disclosure icon reveals the first image found, along with a link to view the entire post.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250720-230424.png&#34;
     
        alt=&#34;Auto-generated description: A magpie is perched on a wooden fence beside a grassy area with trees in the background.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Selecting an option will update the notice then and there (using HTMX). The notice isn&amp;#39;t cleared until the category change has been made, which does require a page refresh.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;About half the work was building out these in-app notifications. They&amp;rsquo;re all stored in a &lt;code&gt;sqlite3&lt;/code&gt; table and mainly consist of a notification type, brief message, and an extendable set of properties which are stored as a JSON object.&lt;/p&gt;
&lt;p&gt;For the feed parser, I&amp;rsquo;m using &lt;a href=&#34;https://github.com/mmcdole/gofeed/&#34;&gt;gofeed&lt;/a&gt;, the same library I&amp;rsquo;m using for reading podcast feeds. I can definitely recommend this library for parsing RSS feeds if you&amp;rsquo;re using Golang. There was a little bit I had to add around this library to detect when feed items have previously been read or updated. The way I&amp;rsquo;m detecting this is by recording the GUID, along with a SHA-1 hash of the title, description, and content in the database.  Parsing the feed HTML is done using &lt;a href=&#34;https://github.com/PuerkitoBio/goquery&#34;&gt;goquery&lt;/a&gt;, allowing me to easily select the first &lt;code&gt;img&lt;/code&gt; tag of a HTML fragment. For fetching and setting the categories, I&amp;rsquo;m using a home grown Micropub client.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s &amp;ldquo;category fixer&amp;rdquo;. A lot of infrastructure was added for this one which could be useful in the future. I do have some other ideas that would follow this sort of pattern: a new post is picked up by the RSS reader, and an in-app notification is raised for me to action. I suspect this may becoming a bit of a pattern.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Godot Project — Some Feelings</title>
      <link>https://lmika.org/2025/07/12/devlog-godot-project-some-feelings.html</link>
      <pubDate>Sat, 12 Jul 2025 15:43:59 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/07/12/devlog-godot-project-some-feelings.html</guid>
      <description>&lt;p&gt;Spent a little more time working on my Godot game, mainly around level 4-2. Got around two-thirds of it done, including one of the more difficult thirds to build. It&amp;rsquo;s nice seeing the ideas I had for this level come together. It&amp;rsquo;s not exactly how I envisioned it, but truth be told, I didn&amp;rsquo;t have many ideas solidified before I started building the level. Just some vague ideas of how I wanted the level to look, then I went where my virtual paintbrush took me.&lt;/p&gt;
&lt;p&gt;Now, some feelings about this entire project. It&amp;rsquo;s hard spending time on this knowing that it&amp;rsquo;s not really going to amount to anything.  I&amp;rsquo;ve been watching a lot of game reviews recently, particularly from &lt;a href=&#34;https://www.youtube.com/playlist?list=PLUBKwq0XD0ueR3CXGUhGpsD1puLcYJPUp&#34;&gt;Yahtzee Croshaw&lt;/a&gt;, who is also a game developer. Seeing his work, along with the work he critiques, is a little discouraging. Here are accomplished game developers doing amazing work, making games that move emotions and look and sound amazing. And here&amp;rsquo;s this project, using assets from a Godot tutorial and is little more than another Mario-esc clone. And I know going in to this that this will be little more than a bit of a time-waster and an excuse to try Godot out: something to work on evenings and weekends.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s always this niggling feeling saying, &amp;ldquo;if you&amp;rsquo;re not expecting anything, why are you wasting your time on this? Why don&amp;rsquo;t you kill this now and focus on something bigger? &amp;lsquo;Make no small plans&amp;rsquo; and all that.&amp;rdquo; And once you found the answer surrounding the underlying question motivating you to start the project in the first place, if you know it won&amp;rsquo;t amount to nothing, why continue?&lt;/p&gt;
&lt;p&gt;And yeah, ultimately might be good advice. But I think I&amp;rsquo;ve abandoned too many projects in the past. I&amp;rsquo;d like to see something through, even if it&amp;rsquo;s something other than this. So maybe I will kill this. But not yet.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Moan-routine: LO&#39;s Predicate Signatures</title>
      <link>https://lmika.org/2025/07/11/moanroutine-los-predicate-signatures.html</link>
      <pubDate>Fri, 11 Jul 2025 17:05:45 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/07/11/moanroutine-los-predicate-signatures.html</guid>
      <description>&lt;p&gt;I wish I liked using &lt;a href=&#34;https://github.com/samber/lo&#34;&gt;lo&lt;/a&gt;. People at work swear by it but whenever I use it, I just get annoyed at how much better it could be.&lt;/p&gt;
&lt;p&gt;For those unaware of what &lt;code&gt;lo&lt;/code&gt; is, it&amp;rsquo;s a Go package inspired by &lt;a href=&#34;https://lodash.com&#34;&gt;lodash&lt;/a&gt;, the utility library for JavaScript that allows one to use higher-order functions for operating over collections. Think mapping, filtering, etc. Given that &lt;code&gt;lodash&lt;/code&gt; is built for JavaScript, it&amp;rsquo;s allows for a lot of flexibility in the closures passed to the various functions. &lt;code&gt;map&lt;/code&gt;, for instance, allows a closure that takes two arguments which, when used with an array, will be set to each item&amp;rsquo;s value and index. If you need both bits of information, define a closure with two arguments. If you just need the value, just use one.&lt;/p&gt;
&lt;p&gt;Given Go&amp;rsquo;s strong type system, this flexibility is not granted to us Go developers. Whatever the closure signature is defined for &lt;a href=&#34;https://pkg.go.dev/github.com/samber/lo#Map&#34;&gt;Map&lt;/a&gt; or &lt;a href=&#34;https://pkg.go.dev/github.com/samber/lo#Map&#34;&gt;Filter&lt;/a&gt; is the one you&amp;rsquo;re stuck with (well, that&amp;rsquo;s not entirely true, but more on that in a bit). So it&amp;rsquo;s important for the library developer to come up with some good defaults.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m sure the developer of &lt;code&gt;lo&lt;/code&gt; was trying to be helpful by defining &lt;code&gt;Map&lt;/code&gt; and &lt;code&gt;Filter&lt;/code&gt;  to accept a closure that takes the item&amp;rsquo;s  value and index. But by doing so, they totally screwed me over for the simple reason in that they&amp;rsquo;re not consistent. I can&amp;rsquo;t pass the same function into both &lt;code&gt;Filter&lt;/code&gt; and &lt;a href=&#34;https://pkg.go.dev/github.com/samber/lo#EveryBy&#34;&gt;EveryBy&lt;/a&gt; , as the latter does not accept a closure with an index.&lt;/p&gt;
&lt;p&gt;This prevents me from predefining a set of predicates that I can reused. Being able to define a closure in Go is nice, but you know what&amp;rsquo;s better? Not having to define a closure. Riddle me this: which of these is nicer to read? This:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isRect&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;corners&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;doThingsWithShapes&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;rects&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Filter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;isRect&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;areAllRects&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;EveryBy&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;isRect&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or this?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;doThingsWithShapes&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;rects&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Filter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;corners&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;areAllRects&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;EveryBy&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;corners&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Moot point, as the first one is impossible.&lt;/p&gt;
&lt;p&gt;The mistake of &lt;code&gt;Map&lt;/code&gt; providing two arguments is subtler. When I&amp;rsquo;m operating on a database, I&amp;rsquo;m usually find myself having to map from the types generated from &lt;a href=&#34;https://sqlc.dev&#34;&gt;sqlc&lt;/a&gt; into my own models. Moving this logic out into a separate function makes sense, one that I can invoke when I&amp;rsquo;m dealing with a result set — in which case I could pass this mapping function to &lt;code&gt;Map&lt;/code&gt; — or a single row — in which case I just call function directly. But requiring the &lt;code&gt;Map&lt;/code&gt; closure to take the index screws me here too. I could defining a mapping function that accepts an index that I won&amp;rsquo;t use, but that just makes the direct calls look rubbish. Am I just going to pass in 0 here? Urgh!&lt;/p&gt;
&lt;p&gt;Now, I alluded to the function signatures potentially being a little more flexible. One could use the method of &lt;a href=&#34;https://en.wikipedia.org/wiki/Currying&#34;&gt;currying&lt;/a&gt; which will return a function that will take the first argument and ignore the second. This could allow the same predicate to be used in both &lt;code&gt;Filter&lt;/code&gt; and &lt;code&gt;EveryBy&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ignoreSecondArg&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;U&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;fn&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;U&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;U&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fn&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isRect&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;corners&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;doThingsWithShapes&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;rects&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Filter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ignoreSecondArg&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;isRect&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;areAllRects&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;EveryBy&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;isRect&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It would&amp;rsquo;ve been nice for &lt;code&gt;lo&lt;/code&gt; to provided these functions. The closest I see are the &lt;a href=&#34;https://pkg.go.dev/github.com/samber/lo#Partial&#34;&gt;Partial&lt;/a&gt; functions which &lt;em&gt;almost&lt;/em&gt; do what I need. Yet they&amp;rsquo;re designed for pegging the first argument to a value and returning a function that takes the second one. I&amp;rsquo;ve have no need for these sorts of function, and having something which could help in adapting these predicates would seem much more useful to me.&lt;/p&gt;
&lt;p&gt;(Deep breath)&lt;/p&gt;
&lt;p&gt;Okay, I can see how this all looks a little unfair. And I&amp;rsquo;m not going to say the developer or users are making mistakes here. Well, I am, but I&amp;rsquo;m not say that they&amp;rsquo;re incompetent or have malicious intent. I&amp;rsquo;m sure there are good reasons behind these decisions.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s just… this could be so much better than it currently is. Consider this criticism from someone who&amp;rsquo;d like to see this package succeed, like a parent or a coach trying to help a struggling child. We&amp;rsquo;re not using JavaScript here; we&amp;rsquo;re bound to the type system we have. Embrace that. Recognise that the occasional closure is fine but can get unwieldily whenever we&amp;rsquo;re forced to make one with one of your functions. The flexibility you provide here is an example of the perfect being the enemy of the good.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On the UI of Coding Assistants And Agents</title>
      <link>https://lmika.org/2025/07/09/it-was-interesting-hearing-ben.html</link>
      <pubDate>Wed, 09 Jul 2025 20:53:01 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/07/09/it-was-interesting-hearing-ben.html</guid>
      <description>&lt;p&gt;It was interesting hearing Ben Thompson compare Cursor and Claude Code in &lt;a href=&#34;https://stratechery.com/2025/tech-philosophy-and-ai-opportunity/&#34;&gt;his weekly article&lt;/a&gt; and how it contrasts with how these two companies&amp;rsquo; approach to AI:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;This, by extension, explains why Anthropic is different; the other leading independent foundational lab is clearly focused on agents, not chatbots, i.e. AI that does stuff for you, instead of a tool. Consider the contrast between Cursor and Claude Code: Cursor is an integrated development environment (IDE) that provides the best possible UI for AI-augmented programming; Claude Code, on the other hand, barely bothers with a UI at all. It runs in the terminal, which people put up with because it is the best at one-shotting outputs&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;m not sure I agree that Anthropic&amp;rsquo;s drive to be an agent company explains why they decided to use the bare minimum of UI for Claude Code. Or at least, I&amp;rsquo;m not sure the UI makes much of a difference as to whether people approaching it would consider it an agent. They may just be those that prefer using the terminal; or they already use and like an IDE for coding, and are not interested in changing. At that point, does it make a different whether the company is hoping to build an agent or merely an assistant?&lt;/p&gt;
&lt;p&gt;I played around with Claude Code a few days ago. Had it help me build an Obsidian plugin which rendered a code-block of CSV table as a HTML table. The agent was very useful for getting the basic plugin off the ground, and about half the time I was cutting my own code while turning to the agent when I got stuck with Obsidian&amp;rsquo;s plugin API. And I gotta say, I found the experience quite good. This wasn&amp;rsquo;t an example of vibe coding: the agent wasn&amp;rsquo;t doing all the work for me. Rather, it was more like playing duet, where the agent would take on some of the work I couldn&amp;rsquo;t do myself, leaving me to make the changes I was quite comfortable in doing already.&lt;/p&gt;
&lt;p&gt;This is in contrast with my brief experience with using Cursor to write a Go app. It had features offering the back-and-forth with a model that came with using Claude Code, but since it was integrated with an editor, it also offered things like smart auto-complete. And I found it to be quite an annoying experience, with the smart auto-complete disrupting me whenever I wanted to write something by hand. By Ben&amp;rsquo;s definition above I was using AI-empowered tools rather than agent which, by taking a Steve Jobs bicycle-of-the-mind definition of such things, are meant to empower me. Yet it was more likely more to get in my way.&lt;/p&gt;
&lt;p&gt;So, given that I found working with Claude Code a more pleasant experience, am I someone who prefers to work with AI agents more than mere AI tools? I didn&amp;rsquo;t think I would be. I mean, I have no real desire for agents to do my personal projects for me: half the fun of such projects is being able to work on them. But I guess there&amp;rsquo;s nothing stopping me from using an agent to, say, get enough of the scaffolding in place for me to get started with my own skills: to play duet, as it were. Then for areas where I can play solo, the agent can be set aside and I can do what I need to do unencumbered.&lt;/p&gt;
&lt;p&gt;I guess this is just a long-winded way to say that minimalist approach taken by Anthropic for Claude Code works for me. And Anthropic can build what they think. I have no use for an agent yet, but an assistant could be helpful.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Ranting</title>
      <link>https://lmika.org/2025/06/29/on-ranting.html</link>
      <pubDate>Sun, 29 Jun 2025 08:52:45 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/06/29/on-ranting.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://manuelmoreale.com/on-complaining&#34;&gt;Manuel Moreale&lt;/a&gt; writes about complaining and how it differs from ranting. I must say that ranting, if done well and respectively, can be a very entertaining form of criticism. Thinking of the rants from John Siracusa or Ben Thompson on their respective podcasts, they&amp;rsquo;re usually my favourite parts.&lt;/p&gt;
&lt;p&gt;But it got me thinking about what makes a good rant. Manuel first reason on why he enjoys ranting is that his rants are about things that don&amp;rsquo;t really bother him. Not entirely sure I agree with him on that point. There&amp;rsquo;s no reason why you need to really be bothered by the thing you&amp;rsquo;re ranting about, but the truly great rants I enjoy and listen to again and again are about things that obviously bother the ranter. John and the MacOS Photos App, Ben and the elected Boeing chair. It&amp;rsquo;s clear in their delivery that they really care about these things.&lt;/p&gt;
&lt;p&gt;No, I think what makes a good rant is: one, it&amp;rsquo;s an issue that you obviously care deeply about, and two, you have enough knowledge about what is wrong and how it should be. Both of these add weight to your criticism: having an unformed prick ranting about something they know nothing about is not nearly as interesting as someone who knows what they&amp;rsquo;re talking about. The rest is in your delivery: examples, counter-examples, lots of personal anecdotes about your experience in these things. Oh, and don&amp;rsquo;t be cruel: attack the problem, not individuals.&lt;/p&gt;
&lt;p&gt;That makes for a good rant.&lt;/p&gt;
&lt;p&gt;Well, this post was longer than I was expecting. But I truly am a fan of the art form. I guess the next thing for me is to find opportunities for a good rant when I encounter something that I simply want to complain about.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>I.S. Know: An Online Quiz About ISO and RFC Standards</title>
      <link>https://lmika.org/2025/06/23/is-know-an-online-quiz.html</link>
      <pubDate>Mon, 23 Jun 2025 22:22:52 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/06/23/is-know-an-online-quiz.html</guid>
      <description>&lt;p&gt;About a week ago, while I was trying to lookup the ISO standard for dates, I got confused and looked up the ISO standard for character encodings. This brought to mind all the ISO and RFC standards I have to deal with as part of my work. I thought it would make for an interesting quiz.&lt;/p&gt;
&lt;p&gt;So I made one, call &lt;a href=&#34;https://isknow.lmika.app&#34;&gt;I.S. Know&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s an online quiz for software developers, primarily those dealing with web technologies, to see how well they know their ISO and RFC standards. It&amp;rsquo;s meant to be just a bit of fun, although given the subject matter, I wonder if I&amp;rsquo;ve made it a little hard. It was also a good excuse to try out full-screen web transitions which I think I&amp;rsquo;m starting to like (for some things).&lt;/p&gt;
&lt;p&gt;Anyway, hope you enjoy it.&lt;/p&gt;
&lt;p&gt;P.S. This is a server-rendered website but nothing&amp;rsquo;s being stored or tracked on the backend. I just wanted to use Go templates without having to make a full-on Hugo site.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Brief Look At Microsoft&#39;s New CLI Text Editor</title>
      <link>https://lmika.org/2025/06/23/took-a-look-at-microsofts.html</link>
      <pubDate>Mon, 23 Jun 2025 08:09:38 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/06/23/took-a-look-at-microsofts.html</guid>
      <description>&lt;p&gt;Took a look at &lt;a href=&#34;https://devblogs.microsoft.com/commandline/edit-is-now-open-source/&#34;&gt;Microsoft&amp;rsquo;s new CLI text editor&lt;/a&gt;. There&amp;rsquo;s no MacOS version yet but &lt;a href=&#34;https://simonwillison.net/2025/Jun/21/edit-is-now-open-source/#atom-everything&#34;&gt;Simon Willison&lt;/a&gt; has made a Docker image for it, and once I satisfied MacOS&amp;rsquo; insatiable fear that I don&amp;rsquo;t know what I&amp;rsquo;m doing regarding Terminal and Docker&amp;rsquo;s access to other files&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, I managed to launch it.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250623-075947.png&#34; width=&#34;600&#34; height=&#34;395&#34; alt=&#34;Auto-generated description: A text editor window displays a simple script mentioning Microsoft&#39;s CLI text editor, with menus for File, Edit, View, and Help visible at the top.&#34;&gt;
&lt;p&gt;And yeah, works well. Reminds me of &lt;code&gt;EDIT.EXE&lt;/code&gt; from my DOS using days.&lt;/p&gt;
&lt;p&gt;Since I was running this in Docker, I knew my experience may be slightly off from what&amp;rsquo;s expected. But one thing I would suggest Microsoft doing if they do want to bring this to the Mac is to have a version that works with terminals with light backgrounds: the grey body text is readable but a bit more contrast would be welcome. Also, adding some Emacs/Readline keyboard bindings would be nice. I found myself pressing Option+Left, which I&amp;rsquo;ve mapped to Ctrl+W, to try and go back a word, and I kept getting asked if I wanted to close and save the file. Seems like the keyboard bindings are mapped to what is typical on Windows, which I can understand.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250623-080114.png&#34; width=&#34;600&#34; height=&#34;787&#34; alt=&#34;Auto-generated description: Two text editor windows display partial menus including file options like New File, with some text in the lower window reading Hello.&#34;&gt;
&lt;figcaption&gt;I think the colours shown on the menu are little buggy.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;But on the whole, I can see this being useful for Windows users that need to duck into a text file when they&amp;rsquo;re using the CLI. I don&amp;rsquo;t think it&amp;rsquo;ll be useful to me — I do know how to quit from Vim — but I frequently need to do this myself whenever I&amp;rsquo;m using the Terminal, and having to switch modes from CLI to GUI always includes a risk of loosing context. So I could imagine appreciating this if I were a Windows users.&lt;/p&gt;
&lt;p&gt;One last thing: I like how they managed to get it down to less than 250kB. A worthy goal for any editor that is not aiming to be feature rich enough to replace your daily driver.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Four prompts in total today. Four!&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>On MacOS Permissions Again </title>
      <link>https://lmika.org/2025/06/21/on-macos-permissions-again.html</link>
      <pubDate>Sat, 21 Jun 2025 15:45:56 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/06/21/on-macos-permissions-again.html</guid>
      <description>&lt;p&gt;On the latest episode of Dithering, John Gruber and Ben Thompson were talking about the iPad and the Mac. I&amp;rsquo;m paraphrasing here but Ben recounted his story about needing to screenshare into his Mac Mini after his Terminal threw up a permission dialogue about attached storage. John was understanding, and made the point that he&amp;rsquo;s been told by Apple that there were reasons for why these permission models were introduced, that some customers had some bad things happen to them. The topic ended with John saying that he thought on balance that the permissions on the Mac were in a good place.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m sorry but I cannot disagree with John enough regarding Apple&amp;rsquo;s chosen balance point on their permission prompting. I&amp;rsquo;m sure that Apple devs have seen a lot of terrible things that have happened to customers of their devices. That&amp;rsquo;s what happens when your customer base is in the hundreds of millions. Should the rest of us be constantly inconvenienced with these models because of what could happen? Should I be forbidden to cross the road at anything other than a pedestrian crossing?&lt;/p&gt;
&lt;p&gt;I am happy to settle for one permission prompt asking if I allow Terminals to access external files (a key responsibly of a Terminal I&amp;rsquo;d argue). Maybe once a month at absolute most. But I&amp;rsquo;ve been getting this effing prompt weekly at the moment. And always when I&amp;rsquo;m in the middle of trying to get something done. How many times do I have to tell MacOS that yes, I want — nay, need — the Terminal to access external files? Is five times over two months not enough already? Take an effin hint, Apple!&lt;/p&gt;
&lt;p&gt;Also, on that same episode, John and Ben expressed their concern about Apple taking their eye off the Mac should the iPad become used more frequently for &amp;ldquo;real work&amp;rdquo;. I doubt that will happen. I&amp;rsquo;ve been trying to use the iPad for development work over the last week and a half, and I can safely say that it&amp;rsquo;s definitely not fit for purpose. Aside from the one-full-screen-app-at-a-time model (which they are working on) it&amp;rsquo;s just really buggy. It always takes a couple of seconds after changing apps when I can start using the keyboard. I could attribute this to the app developers. Well, I would if the apps I am using aren&amp;rsquo;t just glorified web-apps pinned to the home screen because Apple cannot allow anyone to make any money on these platforms without getting their cut, thus throwing away the incentives of anyone to make any  productivity apps on their platform.&lt;/p&gt;
&lt;p&gt;Then again, maybe Apple assumes that &amp;ldquo;real work&amp;rdquo; is anything involving a spreadsheet or mail app. I don&amp;rsquo;t see any permission models thrown at my face when I&amp;rsquo;m using those.&lt;/p&gt;
&lt;p&gt;Anyway, that&amp;rsquo;s my rant on this topic for the week.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Gallery: Day Trip to Yass</title>
      <link>https://lmika.org/2025/06/15/gallery-day-trip-to-yass.html</link>
      <pubDate>Sun, 15 Jun 2025 16:56:07 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/06/15/gallery-day-trip-to-yass.html</guid>
      <description>&lt;p&gt;Decided to go to Yass today, a small town in NSW just north of where I&amp;rsquo;m staying in Canberra. I pass by Yass every time I drive to Canberra from Melbourne, and I wanted to see what it was like, at least once. And this morning I discovered that it had a railway museum, which sealed the deal. Unfortunately the weather was not kind: it was bitterly cold and rainy the whole time I was there.&lt;/p&gt;
&lt;p&gt;First stop was the Railway Museum, which featured quite a few bits of rolling stock that operated on the Yass tramway. Yes, Yass had a tramway which, given the size of the town, I was not expecting.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/130d844790.jpg&#34;
     
        alt=&#34;Auto-generated description: A small railway yard features vintage train carriages and wooden buildings on a rainy day.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The Yass Railway Museum, showing off some of the rolling stock on display.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/7609fe1d17.jpg&#34;
     
        alt=&#34;Auto-generated description: A vintage tram marked with Day In and the number 86 is on railway tracks near a wooden building on an overcast day.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Apparently it&amp;#39;s required that every rail museum in Australia have at least one W-class tram. This one they hope to get working again and run along the tram line.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/8f762e873e.jpg&#34;
     
        alt=&#34;Auto-generated description: A vintage train cab with a steering wheel and control panel overlooks railway tracks stretching into the distance.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The cab of the W-class tram.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/eaf8063421.jpg&#34;
     
        alt=&#34;Auto-generated description: A vintage tram interior features wooden paneling, brown leather benches, and visible footprints on the floor.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The saloon, with some vintage ads from the 80&amp;#39;s.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/9aad0bae1f.jpg&#34;
     
        alt=&#34;Auto-generated description: A vintage train sits on tracks beside a historic station building, surrounded by grassy fields and a cloudy sky.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  An X203 diesel locomotive, built 1963. Built primarily for putting trains together, yet this one was used on the Yass tramway.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/c0cb6af9f9.jpg&#34;
     
        alt=&#34;Auto-generated description: A weathered control cabin with vintage dials and levers is set against a backdrop of industrial buildings visible through the windows.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Inside the cab of the X203 loco. It&amp;#39;s amazing how primitive the instrumentation was.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/42383a0eaa.jpg&#34;
     
        alt=&#34;Auto-generated description: A vintage, weathered control panel featuring dials, switches, and gauges is shown with signs of rust and wear.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Close up of the instrument panel.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/cd8966efff.jpg&#34;
     
        alt=&#34;Auto-generated description: Three control levers with round knobs are situated within a vintage, worn-out vehicle interior.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Control leavers. I think the one on the left is the reverser. Not sure what the other two are (throttle and break?)
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/99b2bc109e.jpg&#34;
     
        alt=&#34;Auto-generated description: An interior of a vintage train car features wooden seats, luggage racks, and a sign indicating Non Smoking.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Inside a 1908 carriage. The doorway leads to toilets and then through to 1st class.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/ca0b34a03c.jpg&#34;
     
        alt=&#34;Auto-generated description: A small yellow maintenance vehicle sits on train tracks beside a vintage station building with a sign reading Yass Town.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  A &amp;#39;covered trike&amp;#39; used by railway maintenance staff.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/18222f5457.jpg&#34;
     
        alt=&#34;Auto-generated description: An old, weathered rail cart sits on tracks at a rustic train station, with empty seats and a control panel visible.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Seats two, with controls in the middle.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/2b08ee4b4d.jpg&#34;
     
        alt=&#34;Auto-generated description: A vintage steam locomotive with the number 1307 is on a railway track near a historical station on a cloudy day.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  1307 steam locomotive, built 1877. A standard design for suburban Sydney services, yet this was used for the Yass tramway. Was eventually replaced by the X203 diesel locomotive, and was retired in 1972.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/b9d9b0c8a8.jpg&#34;
     
        alt=&#34;Auto-generated description: A view inside a vintage steam locomotive cab, showing the control panels and gauges.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  I&amp;#39;ve been in enough steam locomotive cabs to know that this one had most of it&amp;#39;s controls removed. A bit of a shame.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/0af297fa5e.jpg&#34;
     
        alt=&#34;Auto-generated description: A vintage green and yellow tram is situated on an outdoor track with a small set of wooden steps leading up to its open door.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Another W-class tram, this one in gold and green livery of the Met. Real nostelga fix for me. Also has the trolley pole.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/024e9734b1.jpg&#34;
     
        alt=&#34;Auto-generated description: A sign on a window advises passengers to have a valid ticket to avoid an on-the-spot fine, with a view of grass and trees outside.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  I remember these stickers stuck up on the doorways of the Comeng trains.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/400505123f.jpg&#34;
     
        alt=&#34;Auto-generated description: A sign detailing fare prices and conditions for riding The Met public transport system is displayed above a door.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  These prices may have been during the first hole-punch tickets. Before that we had scratchy tickets, where you scratched the date and hour you bought the ticket for. Relied on the honour system. Can&amp;#39;t believe that was a thing.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;This was followed by lunch at a local cafe, then a brief walk by the Yass river, which had an old tram bridge. I managed to walk around 500 metres from the bridge to the footy oval. It would&amp;rsquo;ve been nice to walk a bit longer, but it was cold, wet, and the ground was quite slippery. Plus I had to get home to spend some time with the birds. But I did make a brief diversion to Yass Junction railway station to see if there were any trains passing through (unfortunately, there were none).&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/69740a9b6a.jpg&#34;
     
        alt=&#34;Auto-generated description: A cafe patio with green chairs, tables with a number 20, and plants along a red brick wall.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  A very brisk lunch. Good thing it was covered or I would&amp;#39;ve been soaking wet.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/0783ed0c7d.jpg&#34;
     
        alt=&#34;Auto-generated description: A wet, empty street runs parallel to old train tracks in a quiet, suburban area on a cloudy day.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Off to the river path. Ran into the old tramway on the way.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/e591d0972e.jpg&#34;
     
        alt=&#34;Auto-generated description: An abandoned railway track overgrown with foliage winds towards an old, rusty bridge.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  East side of the old tram bridge.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/f66b8928c0.jpg&#34;
     
        alt=&#34;Auto-generated description: A metal truss bridge spans across a river surrounded by trees and rocky banks under a cloudy sky.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The main span of the tram bridge crossing the Yass river. Pretty impressive.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/3187f431ec.jpg&#34;
     
        alt=&#34;Auto-generated description: A disused railway track leads to a deserted bridge surrounded by bare trees and overcast skies.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  West side of the bridge.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/c9c600928a.jpg&#34;
     
        alt=&#34;Auto-generated description: A narrow, overgrown railway track runs between leafless trees and a grassy area on an overcast day.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  What remains of the disused tram line heading off to who knows where.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/91932398c9.jpg&#34;
     
        alt=&#34;Auto-generated description: A small train station building is situated beside a wet parking lot on an overcast day.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Yass Junction station, currently open.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/15f3e74dd0.jpg&#34;
     
        alt=&#34;Auto-generated description: A wet train platform with a pedestrian crossing sign and tracks leading into the distance.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  First time I&amp;#39;ve seen a road pedestrian sign for railed vehicles. Also, could this have been where the tram line went to?
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/e875bb803d.jpg&#34;
     
        alt=&#34;Auto-generated description: A railway station platform is shown on a rainy day with wet tracks and platform surfaces.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Platform 1, looking west towards Melbourne. No trains approaching.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/7f855cbbb4.jpg&#34;
     
        alt=&#34;Auto-generated description: A train station platform features tracks, a covered waiting area, and signage, with overcast skies above.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Platform 2, currently unused.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/7ff3b86d9a.jpg&#34;
     
        alt=&#34;Auto-generated description: A pedestrian bridge with stairs spans over wet railway tracks in a quiet, rural setting on a cloudy day.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The pedestrian overpass, unused and unusable.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/963d03f0ac.jpg&#34;
     
        alt=&#34;Auto-generated description: A rural railway signal box stands next to train tracks surrounded by fields and trees under an overcast sky.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Looking east towards Sydney. No trains coming from there either.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: UCL — More About The Set Operator</title>
      <link>https://lmika.org/2025/06/14/devlog-ucl-more-about-the.html</link>
      <pubDate>Sat, 14 Jun 2025 11:35:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/06/14/devlog-ucl-more-about-the.html</guid>
      <description>&lt;p&gt;I made a decision around the set operator in UCL this morning.&lt;/p&gt;
&lt;p&gt;When I added the set operator, I made it such that when setting variables, you had to include the leading dollar sign:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$a = 123
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The reason for this was that the set operator was also to be used for setting pseudo-variables, which had a different prefix character.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;@ans = &amp;#34;this&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I needed the user to include the &lt;code&gt;@&lt;/code&gt; prefix to distinguish the two, and since one variable type required a prefix, it made sense to require it for the other.&lt;/p&gt;
&lt;p&gt;I’ve been trying this for a while, and I’ve deceided I didn’t like it. It felt strange to me. It shouldn&amp;rsquo;t, really, as it&amp;rsquo;s similar to how variable assignments work in Go’s templating language, which I consider an inspiration for UCL. On the other hand, TCL and Bash scripts, which are also inspirations, require the variable name to be written without the leading dollar sign in assignments. Heck, UCL itself still had constructs where referencing a name for a variable is done so without a leading dollar sign, such as block inputs. And I had no interest in changing that:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;proc foo { |x|
    echo $x
}

for [1 2 3] { |v| foo $v }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So I made the decision to remove the need for the dollar sign prefix in the set operator. Now, when setting a variable, only the variable name can be used:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;msg = &amp;#34;Hello&amp;#34;
echo $msg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In fact, if one were to use the leading dollar sign, the program will fail with an error.&lt;/p&gt;
&lt;p&gt;This does have some tradeoffs. The first is that I still need to use the &lt;code&gt;@&lt;/code&gt; prefix for setting pseudo variables, and this change will violate the likeness of how the two look in assignments:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;@ans = 123
bla = 234
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The second is that this breaks the likeness of how a sub-index looks  when reading it, verses how it looks when it&amp;rsquo;s being modified:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;a = [1 2 3]
a.(1) = 4
$a
--&amp;gt; [1 4 3]
$a.(1)
--&amp;gt; 4
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(One could argue that the dollar sign prefix makes sense here as the evaluator is dereferencing the list in order to modify the specific index. That&amp;rsquo;s a good argument, but it feels a little bit too esoteric to justify the confusion it would add).&lt;/p&gt;
&lt;p&gt;This sucks, but I think they&amp;rsquo;re tradeoffs worth making. UCL is more of a command language than a templating language, so when asked to imagine similar languages, I like to think one will respond with TCL or shell-scripts, rather than Go templates.&lt;/p&gt;
&lt;p&gt;And honestly, I think I just prefer it this way. I feel that I&amp;rsquo;m more likely to set regular variables rather than pseudo-variables and indicies. So why not go with the approach that seems nicer if you&amp;rsquo;re likely to encounter more often.&lt;/p&gt;
&lt;p&gt;Finally, I did try support both prefixed and non-prefixed variables in the set operator, but this just felt like I was shying away from making a decision. So it wasn&amp;rsquo;t long before I scrapped that.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some Morning AI Thoughts</title>
      <link>https://lmika.org/2025/06/14/richard-griffiths-wrote-an-excellent.html</link>
      <pubDate>Sat, 14 Jun 2025 08:46:52 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/06/14/richard-griffiths-wrote-an-excellent.html</guid>
      <description>&lt;p&gt;I read two things about AI this morning.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://writingslowly.com/2025/02/24/what-comes-after-content.html&#34;&gt;The first was this excellent post&lt;/a&gt; by Richard Griffiths on his blog about what creation will mean after AI has slopped everyone:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;I believe we’re on the cusp of a seismic change in the culture every bit as significant as the shift around 1910 when it was suddenly impossible to be a Victorian any more1. Just as no one can be Charles Dickens these days, very soon, no one will be able to market anything that looks like what AI could produce. Sure, we’ll make use of AI tools in the background, but readers, listeners and viewers won’t accept what AI offers unless it has first passed through the distinctively human creative imagination.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I’ve been trying to observe how my feelings about AI and it’s role in creation over the last few weeks, and I feel my position is very much aligned with this one. I can definitely see AI tools be useful in the creation process but in a similar role that code completion is useful in a code editor. The best use of AI is not an ends to itself, but a means to an end, overseen and review by a human.&lt;/p&gt;
&lt;p&gt;These feelings were in contrast to those I got from reading this quote from &lt;a href=&#34;https://www.deeplearning.ai/the-batch/issue-305/&#34;&gt;Andrew Ng&lt;/a&gt;, via &lt;a href=&#34;https://simonwillison.net/2025/Jun/13/andrew-ng/#atom-everything&#34;&gt;Simon Willison&lt;/a&gt; blog:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;There’s a new breed of GenAI Application Engineers who can build more-powerful applications faster than was possible before, thanks to generative AI. Individuals who can play this role are highly sought-after by businesses, but the job description is still coming into focus. [&amp;hellip;]&lt;/p&gt;
&lt;p&gt;Skilled GenAI Application Engineers meet two primary criteria: (i) They are able to use the new AI building blocks to quickly build powerful applications. (ii) They are able to use AI assistance to carry out rapid engineering, building software systems in dramatically less time than was possible before. In addition, good product/design instincts are a significant bonus.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;There were couple of things that I disliked about this position, like the idea that a “GenAI Application Engineer” is different from a regular software developer, as if the introduction of AI tools warrants a completely different job title. Replace “AI” with “IDE editor” in the quote about to see how ridiculous this sounds.&lt;/p&gt;
&lt;p&gt;But it also revealed to me why this quote rubbed me the wrong way. The motivation of these AI proponents&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, they’re not interested in quality or human connection: they’re interested in cheap and fast. Get to market first in the crappiest way possible so they can crush their competitors with unlimited venture funding by undercutting them in cost, only to jack up prices and start introducing taxes once the competition is dead.&lt;/p&gt;
&lt;p&gt;I am not an AI hater: I’m using AI in my own personal work, and anyone that’s been on this blog as seen me add a cheeky AI image from time to time. But I cannot sign on to this: shunning quality for moving fast just feels like more of the same crappy playbook that’s going on in the tech world.&lt;/p&gt;
&lt;p&gt;The alternative, the one proposed by Richard, will mean moving slower to ensure that quality and human connection is there. And maybe that’s the winning move in this new AI world. Despite what new technologies are out there, the idea of good things taking time has not stopped being true.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I don’t want to pick on Andrew. I don’t know his real position on AI. He was just the unlucky one to have his post show up in my RSS feed this morning that stirred these feelings.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>That Which Didn&#39;t Make the Cut: a Hugo CMS</title>
      <link>https://lmika.org/2025/06/09/that-which-didnt-make-the.html</link>
      <pubDate>Mon, 09 Jun 2025 16:32:49 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/06/09/that-which-didnt-make-the.html</guid>
      <description>&lt;p&gt;You&amp;rsquo;ve probably noticed&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; that I&amp;rsquo;ve stopped posting links to Open Bookmarks, and have started posting them here again. The main reason for this is that I&amp;rsquo;ve abandoned work on the CMS I was working on that powered that bookmarking site. Yes, yes, I know: another one. Open Bookmarks was basically a static Hugo site, hosted on Netlify. But being someone that wanted to make it easy for me to post new links without having to do a Git checkout, or fiddle around YAML front-matter, I thought of building a simple web-service for this.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t want to say too much about it, other than I managed to get the post functionality working. Creating a new link post would involve fetching the page, and pre-populating the link and optional via link with the fetched page title. I&amp;rsquo;d just finish the post with some quotes or quips, then click Post. That&amp;rsquo;ll save the post in a local database, write it to a staged Hugo site, run Hugo to generate the static site, and upload it to Netlify. There was nothing special about link posts per se: they were just a specialisation of the regular posting feature — a template if you will — which would work the same way, just without the pre-fills.&lt;/p&gt;
&lt;p&gt;The other thing I added was support for adding arbitrary pages. This was a dramatic simplification to what is possible in Hugo, in that only one &lt;a href=&#34;https://gohugo.io/content-management/page-bundles/&#34;&gt;page bundle&lt;/a&gt; was supported. And it was pretty compromised: you had to set the page title to &amp;ldquo;Index&amp;rdquo; to modify the home page. But this was enough for that, plus some additional pages at the top level.&lt;/p&gt;
&lt;p&gt;One other feature was that you can &amp;ldquo;preview&amp;rdquo; the site if you didn&amp;rsquo;t have a Netlify site setup and you wanted to see the Hugo site within the app itself. I had an idea of adding support for staging posts prior to publishing them to Netlify, so that one could look at them. But this never got built.&lt;/p&gt;
&lt;p&gt;Finally there were some options for configuring the site properties. Uploads were never implemented.&lt;/p&gt;
&lt;p&gt;Here are some screenshots, which isn&amp;rsquo;t much other than evidence that I was prioritising the backend over the user experience:&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250609-155850.png&#34;
     
        alt=&#34;Auto-generated description: A webpage displays a list titled Sites with links to good-socials, open-bookmarks, juniper-course-notes, and a Create Site button.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Once authenticated, you&amp;#39;re presented with this: the list of your sites.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250609-155900.png&#34;
     
        alt=&#34;Auto-generated description: A website interface displays a content management system with sections for posts, pages, and various entries related to JavaScript animation, Twitter viewing, and a Kanban board.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Clicking on a site opens up the list of posts. Clicking &amp;#39;New Post&amp;#39; will take you directly to the editor.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250609-155930.png&#34;
     
        alt=&#34;Auto-generated description: A simplified webpage interface for Hugo CMS displays options like Posts, Pages, Uploads, and Settings along with fields for a Link URL and Via URL.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Clicking &amp;#39;New Link Post&amp;#39; will bring up this form, where you can specifying the post link and &amp;#39;via&amp;#39; link.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250609-155909.png&#34;
     
        alt=&#34;Auto-generated description: A webpage showcasing the Hugo CMS with a draft post discussing the GSAP JavaScript animation library.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Clicking through will preload the post title and body with the page title and URL. If a via is set, the page title and URL of the via link will also be included.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250609-155917.png&#34;
     
        alt=&#34;Auto-generated description: A webpage from Hugo CMS with sections for posts, pages, uploads, settings, and a description of Open Bookmarks.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Also supported were the ability to add standalone pages, accessible by clicking the &amp;#39;Pages&amp;#39; section in the side-bar. Most created sites come with an index page (which needed to have the title &amp;#39;index&amp;#39;).
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250609-155923.png&#34;
     
        alt=&#34;Auto-generated description: A Hugo CMS interface is displayed, showing options to edit the site title, theme, URL, and notify site ID, with links for posts, pages, uploads, and settings.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The &amp;#39;Settings&amp;#39; nav item bought up the site settings, including the site tile, theme, and Netlify site ID.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;So, why was this killed? Well, apart from the fact that it seemed like remaking prior art — I&amp;rsquo;m sure there are plenty of Hugo CMSes out there — it seemed strange having two systems that relate to blogging. Recognising this, I decided to add this functionality to Blogging Tools:&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250609-155937.png&#34;
     
        alt=&#34;Auto-generated description: A webpage titled Blogging Tools includes a saved post notification, a button to create a new post, and a linked article titled The Who Cares Era.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The &amp;#39;Posts&amp;#39; app in Blogging Tools. Unlike Hugo CMS, this is just a databased-backed list of posts.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250609-161952.png&#34;
     
        alt=&#34;Auto-generated description: A webpage titled Blogging Tools offers options for Apps, Files, and Jobs, with a section titled New From Template and the author&amp;#39;s name at the bottom.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The goal with this app is to easily create posts from templates, so there&amp;#39;s no way to create a blank post. Clicking New will require one to select a template to use. There are currently only two: creating a post with a Mastodon embed, and creating a link post.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250609-155944.png&#34;
     
        alt=&#34;Auto-generated description: A webpage titled Blogging Tools features a form for linking a post, including fields for Post URL and Via URL, with options for a leading emoji and a Create button.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Once the template is selected, the steps are the same: specify the post link, optional via link, and link emoji.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/out-20250609-155952.png&#34;
     
        alt=&#34;Auto-generated description: A text editor screenshot features a blog post titled Retirement Day by Brent Simmons, reflecting on his career and retirement.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  These get pre-filled as before. It&amp;#39;s just a matter of adding any commentary after that. Clicking &amp;#39;Save&amp;#39; will only save the post locally: one needs to click &amp;#39;Publish&amp;#39; in the posts list to send it to the Micropub service.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;This can&amp;rsquo;t really be described as CMS. It&amp;rsquo;s more of a place to create posts from a template which can then be published via a Micropub endpoint. But it does the job I need, which is creating link posts with the links pre-filled.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Or not. I mean, I&amp;rsquo;m not expecting you to notice. You&amp;rsquo;ve got lives of your own, after all.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Encapsulation In Software Development Is Underrated</title>
      <link>https://lmika.org/2025/06/04/abstraction-in-software-is-good.html</link>
      <pubDate>Wed, 04 Jun 2025 12:47:54 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/06/04/abstraction-in-software-is-good.html</guid>
      <description>&lt;p&gt;There is much to complain about the object-oriented approach to software development: how it tends to result in big systems, that it favours over-engineering, etc. But one principle I think OO gets right is encapsulation: the idea of coming up with a domain model, exposing the operations as methods, and completely hiding how these operations are implemented.&lt;/p&gt;
&lt;p&gt;It would do well for devs to relearn this principal, including myself. When I moved to Go, I experienced a subtle shift away from the practice of encapsulating types, favouring Go primitives that made sense where they could. This is not the fault of the language designers: the standard library is full of opaque types that can only be used with public methods. Maybe it&amp;rsquo;s simply an over-correction on my part after designing huge, complicated type hierarchies during my Java days.&lt;/p&gt;
&lt;p&gt;But I think encapsulation is a principal that&amp;rsquo;s worth relearning. Why? Well, let&amp;rsquo;s travel to a world where encapsulation was never a thing. You&amp;rsquo;re working as a software developer that&amp;rsquo;s building an e-commerce site, and you need to add the notion of a shopping basket. This is a pretty typical thing such a site might have: a customer adds products they wish to buy to the basket, remove products they&amp;rsquo;re no longer interested in, or empty the basket completely when they release they&amp;rsquo;re not interested in participating in rampant capitalism and want to do something meaningful with their day.&lt;/p&gt;
&lt;p&gt;How would you do implement this? Well, something like a map might do, where the product ID is mapped to an integer representing the number of items of said product that&amp;rsquo;s in the basket. Adding a product will add one to the quantity mapped to the  product ID, and removing a product will decrement it by one. When the customer removes the last item of a product, the quantity will go to zero.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;But wait,&amp;rdquo; you say. &amp;ldquo;Should the product quantity go to zero, or should the product be removed completely from the map?&amp;rdquo; No reason why either shouldn&amp;rsquo;t work, and one option&amp;rsquo;s as good as the other. So you choose the zero quantity approach. You continue on until you get to the part where you need to save your basket in the database. Uh-oh: the database doesn&amp;rsquo;t support zero map values. Looks like you&amp;rsquo;ll have to go with removing the zero items from map. So you back-up and make the change. PR raised, merged, and shipped to prod. All good.&lt;/p&gt;
&lt;p&gt;Months pass and someone new is working in that area of the code base. You show them what you did and assume that they recognise the approach you took when customers remove items form the basket. You didn&amp;rsquo;t make clear to anyone the issue you had with the database, but you figured others will cotton on and keep zero values out of the map.&lt;/p&gt;
&lt;p&gt;In my experience, this assumption is flawed. Might be that you&amp;rsquo;re working in a language where zero values can be returned if a  key doesn&amp;rsquo;t exist in a map&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. This other dev may wonder what difference it makes whether the zero quantity item is in the map or not? They&amp;rsquo;re not thinking about how this looks in the database. Why would they? That&amp;rsquo;s been working in prod for a while. And they&amp;rsquo;ve got their own stuff to do.&lt;/p&gt;
&lt;p&gt;Now, this is a contrived example: modern databases are pretty robust, after all. But I just encountered something very much like this in my real job that introduced a subtle bug which was difficult to track down. It was only my chance that I noticed how it looked in the database.&lt;/p&gt;
&lt;p&gt;So what&amp;rsquo;s the solution? Well, encapsulation. Build a &lt;code&gt;Basket&lt;/code&gt; type with the relevant operations — &lt;code&gt;addItemToBasket&lt;/code&gt;, &lt;code&gt;removeItemFromBasket&lt;/code&gt;, etc. — and hide how it&amp;rsquo;s implemented from others. That shields everyone from knowing about the unseen decisions imposed by external forces you had to make to get it working in the first place. They don&amp;rsquo;t even get to know that it even is a map. As a bonus, it make this a little more understandable from a business logic perspective. There is such a thing as being too concise, and simply peppering your service methods with elementary operations directly on maps can introduce noise when you&amp;rsquo;re simply trying to understand how something works.&lt;/p&gt;
&lt;p&gt;So, encapsulation. A good thing. Do more of it.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Go supports this in the form of the two result index lookup.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Rubberducking: Of Brass and Browsers</title>
      <link>https://lmika.org/2025/05/31/rubberducking-of-brass-and-browsers.html</link>
      <pubDate>Sat, 31 May 2025 11:52:56 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/05/31/rubberducking-of-brass-and-browsers.html</guid>
      <description>&lt;blockquote class=&#34;dialogue&#34;&gt;
&lt;style&gt;
@scope {
  :scope {
    --dialogue-m1-color: #4C6C8C;
    --dialogue-m1-background: no-repeat center / 105% url(https://avatars.micro.blog/avatars/2024/15/55331.jpg);
    --dialogue-m2-color: #625309;
    --dialogue-m2-background: white no-repeat center / 75% url(https://lmika.org/uploads/2025/rubber-duck.jpg);
    --dialogue-m3-color: #4C6C8C;
  }
}
&lt;/style&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Did you hear about The Browser Company?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Oh yeah, I heard the &lt;a href=&#34;https://spyglass.org/the-new-ai-browsers/&#34;&gt;CEO wrote a letter about Arc&lt;/a&gt;.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Yeah, did you ever use Arc?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Nah. Probably won’t now that it seems like they’ve stopped work on it. Heard it was pretty nice thought.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Yeah, I heard Scott Forstall had an early look at it.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Oh yeah, and how he compared it to a saxophone and recommended making it more like a piano.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Yeah.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yeah. Not sure I agree with him.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Oh really?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yeah. I mean, there’s nothing wrong with pianos. Absolutely love them. But everyone seems to be making those, and no-one’s making saxophones, violins, etc. And we need those instruments too.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Yeah, I suppose an orchestra with 30 pianos would sound pretty bland.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yeah, we need all the instruments: the one’s that are approachable, and the ones for those with the technical skills to get the best sound.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;And no-one&#39;s a beginner for ever. I&#39;m sure there are piano players out there who would like to try something else eventually, like a saxophone.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Would you say Vivaldi is like a saxophone?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; I&#39;d probably say Vivaldi is like a synthesiser. The basics are approachable for the beginners, yet it&#39;s super customisable for those that want to go beyond the basics.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;And just like a synthesiser, it can be easy to get it sounding either really interesting, or really bizarre. You can get in a state where you can&#39;t back out and you&#39;ll have to start from scratch.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Oh, I can&#39;t imagine that being for everyone.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; No, indeed. Probably for those piano players that would want to try something else.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Dynamo-Browse Now Scanning For UCL Extensions</title>
      <link>https://lmika.org/2025/05/25/devlog-dynamobrowse-now-scanning-for.html</link>
      <pubDate>Sun, 25 May 2025 13:55:49 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/05/25/devlog-dynamobrowse-now-scanning-for.html</guid>
      <description>&lt;p&gt;Almost finished integrating UCL with Dynamo-Browse. As stated earlier, the goal is to use UCL for both the command and scripting language. Today, the goal was closer, with a development version of Dynamo-Browse no longer using old extension scripting language, and now scanning and evaluating UCL-based extensions on launch time.&lt;/p&gt;
&lt;p&gt;This required some changes in how UCL evaluated script files. There&amp;rsquo;s a new &lt;code&gt;ucl.WithSubEnv()&lt;/code&gt; option that can be passed into  &lt;code&gt;inst.Eval()&lt;/code&gt; to instruct the evaluator to fork the environment prior to evaluating the script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ReadFile&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-file.ucl&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;inst&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ucl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;inst&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Eval&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;bytes&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewReader&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;ucl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;WithSubEnv&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Under the hood, this forks the top-level environment frame and marks it as the frame to store all procs and global variables for the evaluated file. This effectively makes it a separate namespace, separating it from all other scripts and preventing cross-contamination when scripts share the same symbol.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m hoping that this will eventually be the basis for UCL modules, but for now, this is use within Dynamo-Browse to isolate each UCL extension. The downside of this is that it&amp;rsquo;s no longer possible to add new commands simply using the &lt;code&gt;proc&lt;/code&gt; keyword. I think this may end up being a good thing though, as extensions will probably not be interested in exposing internal functions defined just for their own use.&lt;/p&gt;
&lt;p&gt;To compensate for this, there is now a new command — &lt;code&gt;ui:command&lt;/code&gt; — which allows the script author to expose a function as a command. In fact, what it does is save the command in the root environment frame, effectively making it work the old way &lt;code&gt;proc&lt;/code&gt; did, and acting like a poor-mans extension export.&lt;/p&gt;
&lt;p&gt;Now with all the extension loading in place, it&amp;rsquo;s time for the inaugural UCL extension. Here it is:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# Gets the partition key of the currently selected row, and adds it
# to the pasteboard. Bound to the &amp;#39;U&amp;#39; key.

ui:command copy-pk {
    # Get the partition key value from the currently selected item
    $pkKey = @table.Keys.PK
    $pkValue = @item.($pkKey)

    # Place it in the pasteboard
    pb:put $pkValue

    echo &amp;#34;$pkKey copied to clipboard&amp;#34;
}

ui:bind &amp;#34;U&amp;#34; { copy-pk }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All the features are not quite implemented yet, but I think it&amp;rsquo;s good enough to start using it as part of my normal workflow. That&amp;rsquo;s usually the best way to find the rough edges.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: UCL — Assignment</title>
      <link>https://lmika.org/2025/05/18/devlog-ucl-assignment.html</link>
      <pubDate>Sun, 18 May 2025 09:35:57 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/05/18/devlog-ucl-assignment.html</guid>
      <description>&lt;p&gt;Toying with changing how assignment works in UCL. Up to now, assigning a variable to a value involved calling the &lt;code&gt;set&lt;/code&gt; command, which took a variable name, and the value to assign:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set a &amp;#34;hello&amp;#34;
$a
--&amp;gt; &amp;#34;hello&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This has been fine, but I&amp;rsquo;m now running into few limitations with this approach. The first is that it doesn’t support setting subscript values within lists or values. Because &lt;code&gt;set&lt;/code&gt; is just a regular command, the parser will evaluate any dot or sub-pipe expressions prior to invoking &lt;code&gt;set&lt;/code&gt;. One way around this is to quoted the variable to assign as a string:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set list [1 2 3]
set &amp;#34;$list.(1)&amp;#34; 4
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But this looks ugly, and will involve another pass of the parser every time &lt;code&gt;set&lt;/code&gt; is called.&lt;/p&gt;
&lt;p&gt;Another approach is converting &lt;code&gt;set&lt;/code&gt; to a macro, which receives the arguments as the parse tree. This gives &lt;code&gt;set&lt;/code&gt; more control over how to interpret the arguments. But it doesn&amp;rsquo;t resolve the second issue, which is an inconsistency introduced with a new feature I&amp;rsquo;m planning.&lt;/p&gt;
&lt;p&gt;I want to add the notion of pseudo variables, which act like regular variables except that getting or setting them will invoke some hander logic from the embedding system, rather than set a value in memory. Think of them similar to how &lt;code&gt;document&lt;/code&gt; and &lt;code&gt;window&lt;/code&gt; work in a browser&amp;rsquo;s JavaScript runtime. My plan for them is to embed this Dynamo Browse to allow the user to access or modify the result-set or selected item, which will result in the UI changing. I&amp;rsquo;m sure I&amp;rsquo;ll have more to say about that in the future.&lt;/p&gt;
&lt;p&gt;To distinguish them from normal variables, they&amp;rsquo;ll have an at-sign instead of a dollar sign:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;@value
--&amp;gt; something
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But as far as the user is concerned, they should act just like normal variables. And just like normal variables, they should be modifiable using the &lt;code&gt;set&lt;/code&gt; command. I don&amp;rsquo;t want the variable and pseudo-variable to share the same namespace (since they look different, there&amp;rsquo;s no reason for them to share one), so I needed a way to distinguish  between setting a pseudo-variable from a regular variable.&lt;/p&gt;
&lt;p&gt;This first means they needed to be quoted but I hacked a quick version of &lt;code&gt;set&lt;/code&gt; on this feature branch to
support setting them:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set &amp;#34;@value&amp;#34; &amp;#34;new value&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But compare this with the first example. Notice that when we were setting &lt;code&gt;a&lt;/code&gt;, we didn&amp;rsquo;t need to include the dollar sign. Here we have a case where one sort of variable requires the prefix symbol, and the other doesn&amp;rsquo;t. This form of inconsistency was not appealing to me.&lt;/p&gt;
&lt;p&gt;Since the various modes of assignments has outgrown the ability of one command to do it all, I’ve decided to remove &lt;code&gt;set&lt;/code&gt; and add basic assignments to the language. These look like typical assignments you see in most other languages:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$a = &amp;#34;hello&amp;#34;
$a
--&amp;gt; &amp;#34;hello&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But they’ll be able to fix the issues from using &lt;code&gt;set&lt;/code&gt;, such as assign the value of subsets and showing consistent representation of what you&amp;rsquo;re actually trying to modify:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;@value = &amp;#34;new value&amp;#34;

$list = [1 2 3]
$list.(1) = 4
$list
--&amp;gt; [1 4 3]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It also leads to some improvements of the common case. One small issue with using &lt;code&gt;set&lt;/code&gt; is that it always required one to wrap the result of a sub-expression in parenthesis.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set h (strs:to-upper &amp;#34;Hello&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But since this is a grammar change, these parenthesis are no longer necessary.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$h = strs:to-upper &amp;#34;Hello&amp;#34;

$h = &amp;#34;Hello&amp;#34; | strs:to-upper
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There may also be room for different forms of assignment, such as ensuring the value you&amp;rsquo;re trying to set is not nil. There was a variant of set, called &lt;code&gt;set!&lt;/code&gt;, which threw an error when attempting to assign a variable to nil. This could be expressed as a different assignment form:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$h = &amp;#34;this is fine&amp;#34;

$h =! ()
--&amp;gt; error: trying to set $h to a nil value
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I will acknowledge that doing this will mean loosing out on some theoretical benefits that came from using &lt;code&gt;set&lt;/code&gt;.  For one thing, it will no longer be possible to indirectly set a value. You can&amp;rsquo;t, for example, do this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set b &amp;#34;a&amp;#34;
set $b &amp;#34;hello&amp;#34;
$a
--&amp;gt; &amp;#34;hello&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I&amp;rsquo;ve never really needed to do this, but I could see this being potentially useful. One way to support this might be to &lt;a href=&#34;https://lua.org/manual/5.4/manual.html#2.2&#34;&gt;do something similar to what Lua does&lt;/a&gt;, and expose the environment as a hash. Pseudo-variables could be useful here:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$b = &amp;#34;a&amp;#34;
@_G.($b) = &amp;#34;hello&amp;#34;
$a
--&amp;gt; &amp;#34;hello&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I&amp;rsquo;ll hold off from adding something like this until I absolutely need to.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s the current idea. I spent around an hour on this so far, just trying it out and seeing how it feels, and I think it&amp;rsquo;s got promise. I&amp;rsquo;ll keep it on the feature branch for now, but I suspect this will eventually become the new way to do assignment in UCL going forward.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Serious Maintainers</title>
      <link>https://lmika.org/2025/05/17/serious-maintainers.html</link>
      <pubDate>Sat, 17 May 2025 08:29:11 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/05/17/serious-maintainers.html</guid>
      <description>&lt;p&gt;I just learnt that &lt;a href=&#34;https://gohugo.io/templates/new-templatesystem-overview/&#34;&gt;Hugo has changed their layout directory structure&lt;/a&gt; (&lt;a href=&#34;https://mandarismoore.com/2025/05/16/i-dont-know-if-you.html&#34;&gt;via&lt;/a&gt;) and has done so without bumping the major version. I was a little peeved by this: this is a breaking change&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; and they’re not indicating the “semantic versioning” way by going from 1.x.x to 2.0.0. Surely they know that people are using Hugo, and that an ecosystem of sorts has sprung up around it.&lt;/p&gt;
&lt;p&gt;But then a thought occurred: what if they don’t know? What if they’re plugging away at their little project, thinking that it’s them and a few others using it? They probably think it’s safe for them to slip this change in, since it’ll only inconvenience a handful of users.&lt;/p&gt;
&lt;p&gt;I doubt this is actually the case: it’s pretty hard to avoid the various things that are using Hugo nowadays. But this thought experiment led to some reflection on the stuff I make. I’m planning a major change to one of my projects that will break backwards compatibility too. Should I bump the major version number? Could I slip it in a point release? How many people will this touch?&lt;/p&gt;
&lt;p&gt;I could take this route, with the belief it’s just me using this project, but do I actually know that? And even if no-one’s using it now, what would others coming across this project think? What’s to get them to start using it, knowing that I just “pulled a Hugo”? If I’m so carefree about such changes now, could they trust me to not break the changes they depend on later?&lt;/p&gt;
&lt;p&gt;Now, thanks to website analytics, I know for a fact that only a handful of people are using the thing I built, so I’m hardly in the same camp as the Hugo maintainers. But I came away from this wondering if it’s worth pretending that making this breaking change will annoy a bunch of users. That others may write their own post if I’m not serious about it. I guess you could call this an example of “fake it till you make it,” or, to borrow a quote from of Logan Roy in Succession: being a “serious” maintainer. If I take this project seriously, then others can do so too.&lt;/p&gt;
&lt;p&gt;It might be worth a try. Highly unlikely that it itself will lead to success or adoption, but I can’t see how it will hurt.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Technically it’s not a breaking change, and they will maintain backwards compatibility, at least for a while. But just humour me here.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Blogging Tools — Finished Podcast Clips</title>
      <link>https://lmika.org/2025/05/15/devlog-blogging-tools-finished-podcast.html</link>
      <pubDate>Thu, 15 May 2025 23:18:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/05/15/devlog-blogging-tools-finished-podcast.html</guid>
      <description>&lt;p&gt;Well, it&amp;rsquo;s done. I&amp;rsquo;ve finally finished adding the podcast clip to Blogging Tools. And I won&amp;rsquo;t lie to you, it took longer than expected, even after enabling some of the AI features my IDE came with. Along with the complexity that came from implementing this feature, that touched on most of the key subsystems of Blogging Tools, the biggest complexity came from designing how the clip creation flow should work. Blogging Tools is at a disadvantage over clipping features in podcast players in that it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Doesn&amp;rsquo;t know what feeds you&amp;rsquo;ve subscribed to,&lt;/li&gt;
&lt;li&gt;Doesn&amp;rsquo;t know what episode you&amp;rsquo;re listening to, and&lt;/li&gt;
&lt;li&gt;Doesn&amp;rsquo;t know where in the episode you are.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Blogging Tools needs to know this stuff for creating a clip, so there was no alternative to having the user input this when they&amp;rsquo;re creating the clip. I tried to streamline this in a few ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Feeds had to be predefined:&lt;/strong&gt; While it&amp;rsquo;s possible to create a clip from an arbitrary feed, it&amp;rsquo;s a bit involved, and the path of least resistence is to set up the feeds you want to clip ahead of time. This works for me as I only have a handful of feeds I tend to make clips from.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prioritise recent episodes:&lt;/strong&gt; The clips I tend to make come from podcasts that touch on current events, so any episode listings should prioritise the more recent ones. The episode list is in the same order as the feed, which is not strictly the same, but fortunately the shows I subscribe to list episodes in reverse chronological order.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy course and fine positioning of clips:&lt;/strong&gt; This means going straight to a particular point in the episode by entering the timestamp. This is mainly to keep the implementation simple, but I&amp;rsquo;ve always found trying to position the clip range on a visual representation of a waveform frustrating. It was always such a pain trying to make fine adjustments to where the clip should end. So I just made this simple and allow you to advance the start time and duration by single second increments by tapping a button.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rather than describe the whole flow in length, or prepare a set of screenshots, I&amp;rsquo;ve decided to record a video of how this all fits together.&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://media.lmika.org/videos/2025/podcast-clip-feature.mp4&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;The rest was pretty straightforward: the videos are made using &lt;code&gt;ffmpeg&lt;/code&gt; and publishing it on Micro.blog involved the Micropub API. There were some small frills added to the UI using both HTMX and Stimulus.JS so that job status updates could be pushed via web-sockets. They weren&amp;rsquo;t necessary, as it&amp;rsquo;s just me using this, but this project is becoming a bit of a testbed for stretching my skills a little, so I think small frills like this helped a bit.&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t made a clip for this yet or tested out how this will feel on a phone, but I&amp;rsquo;m guessing both will come in time. I also learnt some interesting tidbits, such that the source audio of an &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; tag requires a HTTP response that supports &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Range_requests&#34;&gt;range requests&lt;/a&gt;. Seeking won&amp;rsquo;t work otherwise: trying to change the time position will just seek the audio back to the start.&lt;/p&gt;
&lt;p&gt;Anyway, good to see this in prod and moving onto something else. I&amp;rsquo;ve getting excited thinking about the next thing I want to work on. No spoilers now, but it features both Dynamo Browse and UCL.&lt;/p&gt;
&lt;p&gt;Finally, I just want to make the point that this would not be possible without the open RSS podcasting ecosystem. If &lt;a href=&#34;https://justinjackson.ca/youtube-kill-podcasting&#34;&gt;I was listening to podcasts in YouTube&lt;/a&gt;, forget it: I wouldn&amp;rsquo;t have been able to build something like this. I know for myself that I&amp;rsquo;ll continue to listen to RSS podcasts for as long as podcasters continue to publish them. Long may it be so.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Rubberducking: More On Mocking</title>
      <link>https://lmika.org/2025/05/10/rubberducking-more-on-mocking.html</link>
      <pubDate>Sat, 10 May 2025 09:44:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/05/10/rubberducking-more-on-mocking.html</guid>
      <description>&lt;blockquote class=&#34;dialogue&#34;&gt;
&lt;style&gt;
@scope {
  :scope {
    --dialogue-m1-color: #4C6C8C;
    --dialogue-m1-background: no-repeat center / 105% url(https://avatars.micro.blog/avatars/2024/15/55331.jpg);
    --dialogue-m2-color: #625309;
    --dialogue-m2-background: white no-repeat center / 75% url(https://lmika.org/uploads/2025/rubber-duck.jpg);
  }
}
&lt;/style&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; I&#39;ve got it!&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Got what?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; I&#39;ve finally figured out why mocking in unit tests sucks.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Oh?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yeah. It seems obvious, but the issue is that it requires one to assume that you care about the outcome of all dependent methods a service needs to call.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Could you explain?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; So lets say you start off with a service method that simply needs to write to a database. You write the unit-test out and mock the database calls, simulating both the happy path and the error cases. Easy, right?&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Yeah, I&#39;d imagine that would be pretty straightforward.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Then years go by, and now you need this service method to write a message to a queue. So you add unit-test to verify that that works correctly, both the happy path and the error cases.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Okay.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; But you still need to mock out the database call.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Yeah, so?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; But those calls add noise to your unit-tests. You now have mock setup code in unit-test that have nothing to do with what you&#39;re trying to verify. The database needs to be called before you send the message to the queue, so you need to set those mocks up in your unit-tests about the queue.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; But it&#39;s just a call to the database. Is that so much more?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yes, but you&#39;re not thinking through the long term it takes to maintain this service. Let&#39;s say in 6 months you need to add two more dependent calls. Now that&#39;s three additional things you need to setup for every unit-test your writing, along with the setup for the dependency you actually want to verify.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Why are your service methods making all these calls?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Aren&#39;t all service methods like this in a micro-service architecture?&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Oh, you&#39;re using micro-services. 😧&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yeah, although I can&#39;t imagine monoliths being much better.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Could you split this service method up into smaller methods, and just write unit-tests for those? You know, use decomposition?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yeah, but that goes against the principal of writing unit-tests for just the public methods.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; What if you made all these methods public?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Nah, that&#39;ll just push the problem up a layer.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Okay, so I see that you the issue of a service method doing a lot, maybe even too much, yet one that needs to be exposed to the user as one operation.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yes.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; And obviously you&#39;d like that service method to be functional, so you&#39;re writing unit-tests to make sure they&#39;re behaving correctly.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yes, and in order to verify that it&#39;s functioning correctly, it needs to verify that it calls out to the dependent services correctly. Hence, all the need for mocking.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Yeah, but you&#39;ll need to verify these somehow. The fact that it&#39;s calling out to multiple services doesn&#39;t make the act of mocking suck. It sucks that your service method is making all these dependent calls.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Oh, yeah. I guess that&#39;s true. So how do I make it suck less?&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Well, I guess it depends on your coding practices, but I can think of either:&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; 1. Decomposing the large service method into multiple methods, or even services, with each one calling out to one of these dependencies, and simply writing unit-tests for those.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; 2. Have all the mocked calls in a common &#34;setup&#34; method, done in such a way that they&#39;re all functioning correctly, with each test somehow &#34;overriding&#34; the setup of a dependency to test the case where that particular dependencies fails.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; 3. Dealing with it.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Hmm, some food for thought there. I still don&#39;t like mocks and rather use the real implementation instead, but I see that it&#39;s not the mocks themselves that&#39;s causing all these sucking.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Well, if it makes sense to use a real database or message queue in your test, then I&#39;m not going to stop you. Just remember there are downsides to that too. I know you&#39;ve seen those test fail because a Docker container isn&#39;t running or a database isn&#39;t cleaned up.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yeah, yeah. I know. Really should fix those.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Well, that&#39;s something for later. Just have a think about these approaches and seeing if they help. Learn to &#34;mock smarter.&#34;&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Okay, I will. Next large system I need to work on.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Oh yeah. And also it&#39;s a journey. Seems like there&#39;s no silver bullet to this. Just trade-offs. You just need to know which ones you&#39;re willing to take. And which will work for the particular project.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yeah, that&#39;s some good advice. Okay, cool. I&#39;ll let you go.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Yeah, catch you later. Enjoy your weekend.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yeah, you too.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Blogging Tools — Ideas For Stills For A Podcast Clips Feature</title>
      <link>https://lmika.org/2025/05/07/devlog-blogging-tools-podcast-clips.html</link>
      <pubDate>Wed, 07 May 2025 23:18:48 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/05/07/devlog-blogging-tools-podcast-clips.html</guid>
      <description>&lt;p&gt;I recently discovered that Pocketcasts for Android have changed their clip feature. It still exists, but instead of producing a video which you could share on the socials, it produces a link to play the clip from the Pocketcasts web player. Understandable to some degree: it always took a little bit of time to make these videos. But hardly a suitable solution for sharing clips of private podcasts: one could just listen to the entire episode from the site. Not to mention relying on a dependent service for as long as those links (or the original podcast) is around.&lt;/p&gt;
&lt;p&gt;So… um, yeah, I&amp;rsquo;m wondering if I could building something for myself that could replicate this.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m thinking of another module for Blogging Tools. I was already using this tool to &lt;a href=&#34;https://lmika.org/2024/11/11/oof-finally-fixed.html&#34;&gt;crop the clip videos&lt;/a&gt; that came from Pocketcasts so it was already in my workflow. It also has &lt;code&gt;ffmpeg&lt;/code&gt; bundled in the deployable artefact, meaning that I could use to produce video. Nothing fancy: I&amp;rsquo;m thinking of a still showing the show title, episode title, and artwork, with the audio track playing. I pretty confident that &lt;code&gt;ffmpeg&lt;/code&gt; can handle such tasks.&lt;/p&gt;
&lt;p&gt;I decided to start with the fun part: making the stills. I started with using &lt;a href=&#34;https://github.com/llgcode/draw2d&#34;&gt;Draw2D&lt;/a&gt; to provide a very simple frame where I could place the artwork and render the text. I just started with primary colours so I could get the layout looking good:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/podcast-background-1.png&#34; width=&#34;600&#34; height=&#34;338&#34; class=&#34;block-center&#34; alt=&#34;Auto-generated description: A date, episode title, and show name are displayed alongside an image of ocean waves against rocks in a colorful border.&#34;&gt;
&lt;p&gt;I&amp;rsquo;m using Roboto Semi-bold for the title font, and Oswald Regular for the date. I do like the look of Oswald, the narrower style contrasts nicely with the neutral Roboto. Draw2D provides methods for measuring text sizes, which I&amp;rsquo;m using to power the text wrapping layout algorithm (it&amp;rsquo;s pretty dumb. It basically adds words to a line until it can&amp;rsquo;t fit the available space)&lt;/p&gt;
&lt;p&gt;The layout I got nailed down yesterday evening. This evening I focused on colour.&lt;/p&gt;
&lt;p&gt;I want the frame to be interesting and close to the prominent colours that come from the artwork. I found &lt;a href=&#34;https://github.com/EdlinOrg/prominentcolor&#34;&gt;this library&lt;/a&gt; which returns the dominant colours of an image using K-means clustering. I&amp;rsquo;ll be honest: I haven&amp;rsquo;t looked at how this actually works. But I tried the library out with some random artwork from &lt;a href=&#34;https://picsum.photos&#34;&gt;Lorem Picsum&lt;/a&gt;, and I was quite happy with the colours it was returning. After adding &lt;a href=&#34;https://github.com/teacat/noire&#34;&gt;this library&lt;/a&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; to calculate the contract for the text colour, plus a slight shadow, and the stills started looking pretty good:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250507-213441.png&#34; width=&#34;600&#34; height=&#34;517&#34; alt=&#34;Auto-generated description: Six rectangular cards each feature a different background image with the date 14 April 2020, text A pretty long episode title, and My test show.&#34;&gt;
&lt;p&gt;I then tried some real podcast artwork, starting with &lt;a href=&#34;https://atp.fm&#34;&gt;ATP&lt;/a&gt;. And that&amp;rsquo;s where things started going off the rails a little:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250507-224100.png&#34; width=&#34;600&#34; height=&#34;342&#34; alt=&#34;Auto-generated description: Four color variations of a promotional card design featuring a logo with rainbow stripes, a date of 14 April 2020, and text stating A pretty long episode title and My test show.&#34;&gt;
&lt;p&gt;The library returns the colours in order of frequency, and I was using the first colour as the border and the second as the card background. But I&amp;rsquo;m guessing since the ATP logo has so few actual colour, the K-means algorithm was finding those of equal prominence and returning them in a random order. Since the first and second are of equal prominence, the results were a little garish and completely random.&lt;/p&gt;
&lt;p&gt;To reduce the effects of this, I finished the evening by trying a variation where the card background was simply a shade of the border. That still produced random results, but at least the colour choices were a little more harmonious:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250507-224112.png&#34; width=&#34;600&#34; height=&#34;342&#34; alt=&#34;Auto-generated description: A series of four visually distinct cards display a logo, date, episode title, and show subtitle, each set against different colored backgrounds.&#34;&gt;
&lt;p&gt;I&amp;rsquo;m not sure what I want to do here. I&amp;rsquo;ll need to explore the library a little, just to see whether it&amp;rsquo;s possible to reduce the amount of randomness. Might be that I go with the shaded approach and just keep it random: having some variety could make things interesting.&lt;/p&gt;
&lt;p&gt;Of course, I&amp;rsquo;m still doing the easy and fun part. How the UI for making the clip will look is going to be a challenge. More on that in the future if I decide to keep working on this. And if not, at least I&amp;rsquo;ve got these nice looking stills.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;The annoying thing about this library is that it doesn&amp;rsquo;t use Go&amp;rsquo;s standard &lt;code&gt;Color&lt;/code&gt; type, nor does it describe the limits of each component. So for anyone using this library: the range for R, G, and B go from 0 to 255, and A goes from 0 to 1.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>The Alluring Trap Of Tying Your Fortunes To AI</title>
      <link>https://lmika.org/2025/05/05/kind-of-realised-something-about.html</link>
      <pubDate>Mon, 05 May 2025 08:01:33 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/05/05/kind-of-realised-something-about.html</guid>
      <description>&lt;p&gt;Kind of realised something about these AI tools: it&amp;rsquo;s easy to tie your fortunes to the existence and operations of these models. This might seem like a good thing — for those unskilled at a particular domain, it may be possible to get something off the ground quickly. But it&amp;rsquo;s ultimately a trap: if what is produced is not what you&amp;rsquo;re looking for, and there&amp;rsquo;s no way to get it to be what you&amp;rsquo;re looking for, you&amp;rsquo;re really left with no recourse. You want/need to make a change, but you yourself don&amp;rsquo;t have the skills to produce what these models are producing: that&amp;rsquo;s probably why you&amp;rsquo;re using these models. So what are you going to do? Will you going to settle with what was produced? Try a different LLM?&lt;/p&gt;
&lt;p&gt;Worse still, you&amp;rsquo;re dependent on these AI companies for as long as you need to produce this output. Might be that in time that use of these models becomes so widespread that relying on AI is similar to relying on a persistent internet connection. But I know for myself that I&amp;rsquo;m not comfortable with having this dependency in my life just yet.&lt;/p&gt;
&lt;p&gt;This trap is obvious when you think about it for a few minutes. But it really hit home when I was tinkering with a logo design in ChatGPT and getting nowhere. I don&amp;rsquo;t possess the skills needed to produce such a logo myself, and I couldn&amp;rsquo;t get the LLM to change it the way that I wanted. So I either need to keep asking it to make corrections — &amp;ldquo;no, make it larger still&amp;rdquo; — or just accept it as what it is.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s not to say I don&amp;rsquo;t think there&amp;rsquo;s room for AI in any area of production. I still think using it as an assistant, in areas where you yourself possess the skills to &amp;ldquo;take over&amp;rdquo; should you need to, is useful&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. Or in situations where it doesn&amp;rsquo;t really matter if what is produced is not exactly what your or others expect.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s pretty much the case here: this logo I was trying to generate was just a bit of fun, and I haven&amp;rsquo;t tied my salary to the correct output of the model. But those that have — &amp;ldquo;vibe coders&amp;rdquo; and those starting tech businesses without any human coders — should probably beware. It&amp;rsquo;s an alluring trap, and it could spring when you lease expect it.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I think Github naming their AI coding assistant &amp;ldquo;Copilot&amp;rdquo; is genius. The assistant could be involved in the creation of code, but at any time, the human can &amp;ldquo;take over&amp;rdquo;, much like a pilot can &amp;ldquo;take over&amp;rdquo; control of a plane.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Dialogues</title>
      <link>https://lmika.org/2025/05/04/added-something-fun-and-potentially.html</link>
      <pubDate>Sun, 04 May 2025 10:45:36 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/05/04/added-something-fun-and-potentially.html</guid>
      <description>&lt;p&gt;Added something fun, and potentially useless, to this blog: styling for dialogues:&lt;/p&gt;
&lt;blockquote class=&#34;dialogue&#34;&gt;
&lt;style&gt;
@scope {
  :scope {
    --dialogue-m1-color: #4C6C8C;
    --dialogue-m1-background: no-repeat center / 105% url(https://avatars.micro.blog/avatars/2024/15/55331.jpg);
    --dialogue-m2-color: #625309;
    --dialogue-m2-background: white no-repeat center / 75% url(https://lmika.org/uploads/2025/rubber-duck.jpg);
  }
}
&lt;/style&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Hello?&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Oh, hello. What&#39;s up?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Just showing how dialogues look on this site.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Oh, wow. Super nice.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I don&amp;rsquo;t &lt;a href=&#34;https://en.wikipedia.org/wiki/Rubber_duck_debugging&#34;&gt;rubber-duck&lt;/a&gt; often, but I have a few times in the past, and I&amp;rsquo;ve published those &lt;a href=&#34;https://lmika.org/2024/09/10/rubberducking-of-config.html&#34;&gt;as blog posts&lt;/a&gt; &lt;a href=&#34;https://lmika.org/2024/02/09/rubberducking-on-context.html&#34;&gt;on this site&lt;/a&gt;. And it&amp;rsquo;s been fun. Not only has it been helpful to work through the problem this way, there&amp;rsquo;s an element of creativity involved in the whole process: imagining two characters working through a problem like this. The exchange is purely fictional, but the benefits are very real.&lt;/p&gt;
&lt;p&gt;And part of me is wondering whether to do more of this style of writing. Not only when I&amp;rsquo;m facing a programming dilemma, but when I&amp;rsquo;m feeling… I don&amp;rsquo;t know, &amp;ldquo;whimsical.&amp;rdquo;  I&amp;rsquo;ve seen a few other sites having this style of dialogues, both real and imagined; and I&amp;rsquo;ve recently read posts from others &lt;a href=&#34;https://danilafe.com/blog/blog_microfeatures/#dialogues&#34;&gt;wishing more sites had them&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So all these justifications — along with boredom, a desire to work on something novel, and ship something small — accumulated into this silly little change.&lt;/p&gt;
&lt;p&gt;The dialogues themselves are all HTML. If you were to view source, you&amp;rsquo;ll see that they&amp;rsquo;re essentially styled blockquotes. It was important to me to make sure that came through correctly on RSS feeds, with all the styling stripped away. In a browser, they&amp;rsquo;re meant to resemble an imaginary chat experience. I&amp;rsquo;m hoping this is versatile enough: the one concern I have is that this style of interaction seems to encourage short exchanges between the participants, so I may adjust the styling a little in the future. But I&amp;rsquo;ll start with this and see how it goes. If you&amp;rsquo;re curious as to how the CSS looks, &lt;a href=&#34;https://github.com/lmika/lmika.org-extra-assets/blob/main/static/styles/dialogue.css&#34;&gt;you can find it here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Of course, most people would stop here; but because I&amp;rsquo;m me, and will take any opportunity to spend several hours on something if I think it would save me a few minutes, I added a module to Blogging Tools to render dialogs from a mini-language. This uses a small parser written in &lt;a href=&#34;https://github.com/alecthomas/participle&#34;&gt;participal&lt;/a&gt; that will produce the HTML for a dialog that I can copy and paste directly into the Micro.blog editor. There&amp;rsquo;s also a tiny bit of AI that would automatically generate a name for the interaction if I were to leave it blank. For that, I&amp;rsquo;m using &amp;ldquo;Meta Llama 3.1.8B Instruct Turbo&amp;rdquo; made available via &lt;a href=&#34;https://together.ai&#34;&gt;together.ai&lt;/a&gt; (the name is not published, it&amp;rsquo;s just used for listing the dialogue).&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/dialogue-1.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The new Dialogues module.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/dialogue-2.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The dialogue editor. This is essentially a plain-text post editor.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/dialogue-3.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  An example of the mini-language used to styling the dialogue. The real avatar properties are slightly different to what appears here.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/dialogue-4.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Click Render to generate the HTML. Copy and paste at will.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;blockquote class=&#34;dialogue&#34;&gt;
&lt;style&gt;
@scope {
  :scope {
    --dialogue-m1-color: #4C6C8C;
    --dialogue-m1-background: no-repeat center / 105% url(https://avatars.micro.blog/avatars/2024/15/55331.jpg);
    --dialogue-m2-color: #625309;
    --dialogue-m2-background: white no-repeat center / 75% url(https://lmika.org/uploads/2025/rubber-duck.jpg);
  }
}
&lt;/style&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Great work.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; So are you going to use this often?&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Well, probably not. I think it&#39;s one of those features where most of the fun is building it. Then once you have it, you&#39;re left wondering what to do with it.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Oh, okay.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yeah, it&#39;s not the first time I did this. I did it before with &lt;a href=&#34;https://lmika.org/2024/06/28/trying-something-new.html&#34;&gt;marginalias&lt;/a&gt;. Built the Hugo short-code, added it to a few posts, then never looked at it again.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Oh, yeah. I vaguely remember you doing that before.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yeah, I may actually revisit that some time. Might be that doing something similar to what I&#39;ve done here with dialogues, i.e. adding a module in this Blogging Tool, will make it that I&#39;m more likely to use this.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Well, let&#39;s see. The feature is here now, so there&#39;s no reason why you wouldn&#39;t use this in the future. Maybe next time in one of our rubber-ducking sessions.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Yeah, we&#39;ll see.&lt;/p&gt;
&lt;p class=&#34;member member-m1&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;L:&lt;/b&gt; Anyway, I&#39;ll catch you then.&lt;/p&gt;
&lt;p class=&#34;member member-m2&#34;&gt;&lt;b class=&#34;avatar&#34;&gt;🦆:&lt;/b&gt; Yeah, see ya later.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So anyway, that&amp;rsquo;s what I&amp;rsquo;ve been doing the last week.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On AI, Process, and Output</title>
      <link>https://lmika.org/2025/05/04/on-ai-process-and-output.html</link>
      <pubDate>Sun, 04 May 2025 08:22:06 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/05/04/on-ai-process-and-output.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://manuelmoreale.com/a-thought-on-ai-and-creativity&#34;&gt;Manuel Moreale’s latest post&lt;/a&gt; about AI was thought-provoking:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;One thing I’m finding interesting is that I see people falling into two main camps for the most part. On one side are those who value output and outcome, and how to get there doesn’t seem to matter a lot to them. And on the other are the people who value the process over the result, those who care more about how you get to something and what you learn along the way.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I recently turned on the next level of AI assistence in my IDE. Previously I was using line auto-complete, which was quite good. This next level gives me something closer to Cursor: prompting the AI to generate full method implementations or having a chat interaction.&lt;/p&gt;
&lt;p&gt;And I think I’m going to keep it on. One nice thing about this is that it’s on-demand: it stays out of the way, letting me implement something by hand if I want to. This is probably going to be the majority of the time, as I do enjoy the process of software creation.&lt;/p&gt;
&lt;p&gt;But other times, I just want a capability added, such as marshalling and unmarshalling things to a database. In the past, this would largely be the code copied and pasted from another file. With the AI assistence, I can get this code generated for me. Of course I review it — I’m not vibe coding here — but it saves me from making a few subtle bugs and some pretty boring editing.&lt;/p&gt;
&lt;p&gt;I guess my point is that I think these two camps are more porous then people think. There are times where the process is half the fun in making the thing, and others where it’s a slog, and you just want the thing to be. This is true for me in programming, and I can only guess that it’ll be similar in other forms of art. I guess the trap is choosing to join one camp, feeling that’s the only camp that people should be in, and refusing to recognise that others may feel differently.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Merge Schema Changes Only When The Implementation Is Ready</title>
      <link>https://lmika.org/2025/04/30/merge-schema-changes-alongside-the.html</link>
      <pubDate>Wed, 30 Apr 2025 08:53:57 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/04/30/merge-schema-changes-alongside-the.html</guid>
      <description>&lt;p&gt;For anyone working on gRPC or other technologies that involve generating code from a schema, may I recommend this approach to integration. If what you&amp;rsquo;re working on involves a change the schema along with a change the implementation, then when it comes time to merging your changes to &lt;code&gt;main&lt;/code&gt;, merge them at the same time. You can raise the merge request for the schema changes when they&amp;rsquo;re ready, and have others look at it while you work on the implementation, but hold off from pushing that &amp;ldquo;Merge&amp;rdquo; button until the implementation is also ready to merge.&lt;/p&gt;
&lt;p&gt;The reason for doing it this way is that it keeps others working on the same project from pulling in your schema changes before you&amp;rsquo;ve finished the implementation. If they&amp;rsquo;re making their own changes to the schema, and have to regenerate the code, your changes will be pulled in too, and they&amp;rsquo;ll be left to figure out how to implement the schema changes you introduce. This either lead to build errors, test failures, or adding stub panics with messages like &amp;ldquo;implement me,&amp;rdquo; which will either lead to conflicts or worse, these errors being deployed to prod. In other words: disharmony across the team is introduced.&lt;/p&gt;
&lt;p&gt;So resist the urge to merge schema changes before the implementation is done. I know, it&amp;rsquo;s tempting: to be able to click that one &amp;ldquo;Merge&amp;rdquo; button and get the rush of accomplishment as that one branch is closed off. But think of it like this: hold off for now, and then you get that feeling doubly so when you merge multiple branches at once.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>You Probably Do Want To Know What You Had for Lunch That Other Day</title>
      <link>https://lmika.org/2025/04/25/you-probably-do-want-to.html</link>
      <pubDate>Fri, 25 Apr 2025 09:26:48 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/04/25/you-probably-do-want-to.html</guid>
      <description>&lt;p&gt;There’s no getting around the fact that some posts you make are banal. You obviously thought your lunch was posting about at the time was worthy of sharing: after all, you took the effort to share it. Then a week goes buy and you wonder why you posted that. “Nobody cares about this,” you say to yourself. “This isn’t giving value to anyone.”&lt;/p&gt;
&lt;p&gt;But I’d argue, as Doc did in Back to the Future, that you’re just not thinking forth-dimensionally enough. Sure it may seem pretty banal to you now, but what about 5 years in the future? How about 10? You could be persuing your old posts when you come across the one about your lunch, and be reminded of the food, the atmosphere, the weather, the joys of youth. It could be quite the bittersweet feeling.&lt;/p&gt;
&lt;p&gt;Or you could feel nothing. And that’s fine too. The point is that you don’t know how banal a particular post is the moment you make it.&lt;/p&gt;
&lt;p&gt;Worse still, you don’t know how banal anything will be 5 years from now, (as in right now, the moment you’re reading this sentence). The banality of anything is dynamic: it changes as a funcion of time. It could be completely irrelevant next week, then the best thing that’s happened to you a week later.&lt;/p&gt;
&lt;p&gt;This is why I don’t understand the whole “post essays on my blogs and the smaller things on Twitter/Bluesky/Mastodon/whatever” dichotomy some writers have out there. Is what you write on those other sites less worthy than what you write on the site you own? Best be absolutely sure about that when you post it then, as you may come to regret &lt;a href=&#34;https://zeldman.com/2008/05/29/fish-tacos-ftw-nom-nom-nom/&#34;&gt;making a point about posting banal tweets 17 years ago&lt;/a&gt;, only for that &lt;a href=&#34;http://twitter.com/zeldman/statuses/822068988&#34;&gt;moratorium about banal tweets to be lost&lt;/a&gt; when you decided to move away from that micro-blogging site.&lt;/p&gt;
&lt;p&gt;But whatever, you do you. I know for myself that I rather keep those supposedly banal thoughts on this site. And yeah, that’ll mean a lot of pretty pointless, uninteresting things get published here: welcome to life.&lt;/p&gt;
&lt;p&gt;But with the pressure of time, they could turn into nice, shiny diamonds of days past. Or boring, dirty lumps of coal. Who knows? Only time will answer that.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Gallery: Morning In Sherbrooke</title>
      <link>https://lmika.org/2025/04/21/gallery-morning-in-sherbrooke.html</link>
      <pubDate>Mon, 21 Apr 2025 16:07:12 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/04/21/gallery-morning-in-sherbrooke.html</guid>
      <description>&lt;p&gt;This Easter Monday I had the pleasure of going to Sherbrooke, in the Dandenong Ranges, and walk the falls track. I can&amp;rsquo;t remember the last time I walked this track. It was certainly longer than five years ago, or I would&amp;rsquo;ve written about it here. Nonetheless, the urge to return there was growing and thanks to an opportune time to rendezvous with family for lunch nearby, I&amp;rsquo;d figured I&amp;rsquo;d spend the morning there.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250421-010338034.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  It&amp;#39;s wonderful to walking amongst the majestic Mountain Ash once again.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250421-011017361.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  I refer to this corner as Attenborough Curve, as there&amp;#39;s a shot of Sir David Attenborough walking this path in The Private Life of Plants. It&amp;#39;s not this exact corner, mind you, but it was definitely a path I recognised when I watched this series, way back when.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250421-012628099.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The highlight of the day: a spotting of what I&amp;#39;m pretty sure is a Superb Lyrebird. I think this is a female.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250421-012650383.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Close-up of the Lyrebird. I didn&amp;#39;t want to get any closer lest I scare it away.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250421-014301540.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  After seeing the falls, I wanted to get back to the road. The path I knew that would take me there was close, as the bridge was being replaced. Fortunately, a friendly walker pointed me to this path, and this bridge across the creek.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250421-022115436.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  A speed run of Alfred Nicholas Memorial Garden. The autumnal setting certainly drew a crowd, with many people taking photos with the assumed intention of posting it online (as witnessed by this person taking this photo with the intention of posting it online).
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250421-021257334.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  I didn&amp;#39;t stay long as I had to meet people for lunch, but I did manage to get to the pond at the bottom of the garden.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250421-022055298.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Always good to see when an exit is on right now.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>New Desk Chair Day</title>
      <link>https://lmika.org/2025/04/11/new-desk-chair-day.html</link>
      <pubDate>Fri, 11 Apr 2025 15:06:48 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/04/11/new-desk-chair-day.html</guid>
      <description>&lt;p&gt;Earlier this year — in mid January! — I ordered a replacement chair for my desk: a &lt;a href=&#34;https://www.hermanmiller.com/en_au/products/seating/office-chairs/aeron-chair/&#34;&gt;Herman Miller Aeron Task Chair&lt;/a&gt;, B-size. Today, after 4 months, it finally arrived.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250411-043650318.jpg&#34; width=&#34;600&#34; height=&#34;796&#34; alt=&#34;Auto-generated description: A modern ergonomic office chair with a mesh backrest and seat is positioned on a carpeted floor in a room with bookshelves and a glimpse into a kitchen area.&#34;&gt;
&lt;figcaption&gt;The new Herman Miller Aeron chair&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;And after sitting in it for amount of time it took me to write this post, it&amp;rsquo;s great. The chair is comfortable, and I like that I can tilt it back slightly. I do wonder if I should&amp;rsquo;ve gotten the one with arm-rests, but none of my previous desk chairs had them and I&amp;rsquo;d probably would&amp;rsquo;ve regretted it if I did (taking them off is not a user serviceable activity). I do wish the new chair was just a little higher — the B-Size is the one with the highest lift — but it&amp;rsquo;s higher than my old one so I&amp;rsquo;m counting that as a win.&lt;/p&gt;
&lt;p&gt;In fact, it&amp;rsquo;s a much better chair than my old one all round. That&amp;rsquo;s not saying much at all: what I had was a sub-$100 chair I bought from Officeworks. It was starting to wear and it was actually giving me some mild back pain. I also remember it being higher when I bought it, I guess the riser was starting to wear a little too.&lt;/p&gt;
&lt;p&gt;Well, that old desk chair can take the load off (ha-ha) and go into retirement. Thank you for your years of service, sub-$100 chair from Officeworks, but your service is no longer required.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250411-043752380.jpg&#34; width=&#34;600&#34; height=&#34;796&#34; alt=&#34;Auto-generated description: A blue office chair with a small tear in the seat is placed on a carpeted floor.&#34;&gt;
&lt;figcaption&gt;The old sub-$100 Officeworks chair&lt;/figcaption&gt;
&lt;/figure&gt;
</description>
    </item>
    
    <item>
      <title>Airing Of Draft Posts</title>
      <link>https://lmika.org/2025/04/07/airing-on-draft-microposts.html</link>
      <pubDate>Mon, 07 Apr 2025 20:52:38 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/04/07/airing-on-draft-microposts.html</guid>
      <description>&lt;p&gt;I was going through my drafts this evening and I found that I had a number of them. Around 26 in total, created over the last year or so, many of them half-finished or almost finished thoughts. Some are not worth keeping around, and will be going straight to the big /dev/null in the sky. But I found a few that are worth preserving, at least in some form. And since they&amp;rsquo;re no longer worth publishing as separate posts in-and-of-themselves — which is probably why they were kept as drafts — I&amp;rsquo;d thought I&amp;rsquo;d just list them here.&lt;/p&gt;
&lt;p&gt;So here they are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Feb 2024&lt;/strong&gt;: Tried the &lt;a href=&#34;https://12daysofweb.dev/2023/popover-api/&#34;&gt;popover API&lt;/a&gt; for the first time today, and was quite impressed as to how easy it was to use. I needed to add a new modal for Photo Bucket&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, and to be able to do so using only HTML is great.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feb 2024&lt;/strong&gt;: B-movie idea: Two mystical titans — Gozilla and Kong — team up to fight the biggest villain of them all: humanity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mar 2024&lt;/strong&gt;: I read a blog post about travel tips, and the poster recommend &amp;ldquo;tak[ing] one week trips. Turns out, two weeks is just too long.&amp;rdquo; This is where the tannery of distance places a role. Being ask to travel half-way round the world for five or six days at a time is not a great feeling. By the time I recover from jet-lag it&amp;rsquo;s time to head back home again. So I think a week is too short a time for me. Experience has taught me that I can probably go up to three weeks before I start feeling homesick, so I think 2-3 weeks would do it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apr 2024&lt;/strong&gt;: Forgot how much of a pain configuring Linux audio using the command line actually is. Got there in the end with some old hardware that I know works with Ubuntu.&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;May 2024&lt;/strong&gt;: I think the secret to sustainable mocking is to avoid trying to litter your tests with hard-coded inputs and outputs, and instead implement mocks which satisfies the contract with the function under test with naive, in-memory implementations of dependent services.&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sept 2024&lt;/strong&gt;: I don&amp;rsquo;t know how this came about, but there seems like there&amp;rsquo;s an approach to Go code which shuns writing a lot of little private functions that in favour of big ones that inline all the logic. The problem is that this results in a lot of noise. Go&amp;rsquo;s naturally a boiler-plate heavy language, and if a large routine contains both the business logic and all the low-level mechanics of, say, trying get a unique list of things in a slice, it&amp;rsquo;s hard to work out whether that if statement is  handling control logic, business logic, or a mixture of the two. It becomes difficult trying to follow what the routine&amp;rsquo;s trying to accomplish. That&amp;rsquo;s why I still prefer to write a lot of small methods that break up these large routines. It means a deeper stack and more jumping around a file, which does make it easier to loose your place, but what it gives me is the ability to abstract things out so that the business logic is clearer to others.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sept 2024&lt;/strong&gt;: Sometimes I wonder if I should work towards something that can be a good enough showcase of my professional self that can replace my LinkedIn profile. To have &lt;a href=&#34;https://pca.st/upv34u7h?t=5m7s&#34;&gt;something akin to inessential.com&lt;/a&gt;. Not sure what that could be, but I don&amp;rsquo;t feel like I&amp;rsquo;ve got that something just yet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sept 2024&lt;/strong&gt;: I may have found a potential use for GitHub Copilot after all. It&amp;rsquo;s not to give insight or ask questions about my own code. After all, I like to think I know my own code. No, it&amp;rsquo;s to provide insight or ask questions about some other open-source project, especially when the documentation is unclear or incomplete.&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Oct 2024&lt;/strong&gt;: I&amp;rsquo;m not a blogger, but I play one on the Internet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Oct 2024&lt;/strong&gt;: Upgraded Bike to the latest release which has a caret &amp;ldquo;bounce&amp;rdquo; animation when you deselect something, probably to indicate where the carat is at any point. And I absolutely do not like it.&lt;sup id=&#34;fnref:5&#34;&gt;&lt;a href=&#34;#fn:5&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feb 2025&lt;/strong&gt;: How much did the Vic government pay to host the NFL for a showcase game at the MCG? If they paid more than $0.00, they paid too much.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feb 2025&lt;/strong&gt;: Someone at worked asked if I missed working in Java. I don&amp;rsquo;t think I&amp;rsquo;d say I hate working in Java. I think the language itself is pretty decent. What got me frustrated working it it was all the non-Java aspects of working on enterprise applications intended for application containers. Many of these technologies involved a lot of configuration — usually XML — that provided no easy way to troubleshoot should applications fail to deploy, an almost near certainly when spinning up a new project.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mar 2025&lt;/strong&gt;: Complaining about excessive pop-overs and chat assistants on websites is a perennial topic, but good grief… do these site owners ever use these sites themselves?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mar 2025&lt;/strong&gt;: It seems to me like people are expecting too much from LLMs. I see take-down posts from people using them to get suggestions on how to do something, mocking the suggestions, then concluding that LLMs lack the nuance decision making that comes from an expert. Yeah, of course they do. They&amp;rsquo;re statistics-based averaging machines. They&amp;rsquo;re not following the same thought process that a human will go through.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And that&amp;rsquo;s it. Hope this wasn&amp;rsquo;t a significant waste of time.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Since abandoned.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;I forget what the hardware was, but I got there, apparently.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;I&amp;rsquo;ve got a whole draft post about mocking that I&amp;rsquo;ve yet to publish, and I&amp;rsquo;m not certain that I will since I don&amp;rsquo;t want to be talking about mocking on this site all the time.&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:4&#34;&gt;
&lt;p&gt;I&amp;rsquo;ve yet to verify this.&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:5&#34;&gt;
&lt;p&gt;I haven&amp;rsquo;t used Bike recently, but I probably will still find this distracting.&amp;#160;&lt;a href=&#34;#fnref:5&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>On Go And Using Comments For Annotations</title>
      <link>https://lmika.org/2025/04/04/its-a-little-strange-that.html</link>
      <pubDate>Sat, 05 Apr 2025 05:53:37 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/04/04/its-a-little-strange-that.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s a little strange that Go doesn&amp;rsquo;t have first-class language features for annotations: arbitrary bits of non-executing metadata that you can add to the code base. There are struct tags, yes, but they&amp;rsquo;re only applicable for fields on a struct type. Nothing analogous really exists for functions, or even just files. And yet there seems to be a need for something to declare build flags, go generate commands, and lint opt-outs.&lt;/p&gt;
&lt;p&gt;The approach taken for this are well-formed comment tags, like &lt;code&gt;//go:generate&lt;/code&gt;, but this seems like a design smell to me. Comments should be limited to the realm of the developer, and the language should stay away from using it for their purpose. Why? Because the language designer can set the syntax. The Go language doesn&amp;rsquo;t need to be syntactically compatible with anything other than itself, so there&amp;rsquo;s nothing really stopping the developers from adding dedicated syntax for such a purpose.&lt;/p&gt;
&lt;p&gt;Java behaved in a similar way before annotations were added and it was pretty hacky. You&amp;rsquo;re left adding non-standard constructs to the Javadoc and relying on a code parser to extract that. When Java annotation where added, it became the responsibility of the compiler to take on that role, and annotations was available in the reflection package. You could walk the type hierarchy to find annotations for fields and methods without having to resort to parsing. Go offers something similar for struct tags, but nothing really for functions or files.&lt;/p&gt;
&lt;p&gt;And they probably don&amp;rsquo;t need to. While the Java annotation features were powerful, it came at the cost of complexity. The tags need to be well-defined, including whether or not they should be made available at runtime. I&amp;rsquo;m not saying Go needs all that. Since the &amp;ldquo;well-formed comment tag&amp;rdquo; approach is little more than just a convention, it means anyone can make use of it with little more than a sentence saying &amp;ldquo;set this tag like this.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;So the sweet spot would be to keep this light-handed approach to tags, while having some dedicated syntax for it. Maybe something similar to preprocessors? You could have a tag begin with a hash and have arbitrary &amp;ldquo;arguments&amp;rdquo; which, like struct tags, will be taken as a single string the tag implementor will need unpack:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#go:build javascript

package main

import (
    &amp;#34;embed&amp;#34;
    &amp;#34;log&amp;#34;
)

#go:generate make-mocks -mode annoying

#go:embed ./myfiles
var files embed.FS

func main() {
    log.Println(&amp;#34;I like hashes&amp;#34;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Not sure it&amp;rsquo;s entirely better than the comments — or maybe my eyes are not used to such constructs. And it probably wouldn&amp;rsquo;t work for all cases, such as embedded C code when using &amp;ldquo;cgo&amp;rdquo;. But I do like that they&amp;rsquo;re not comments. Their purpose is absolutely clear.&lt;/p&gt;
&lt;p&gt;Anyway, just a passing thought.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Don&#39;t Be Afraid Of Types</title>
      <link>https://lmika.org/2025/03/18/dont-be-afraid-of-types.html</link>
      <pubDate>Tue, 18 Mar 2025 08:03:51 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/03/18/dont-be-afraid-of-types.html</guid>
      <description>&lt;p&gt;I found that there&amp;rsquo;s a slight aversion to creating new types in the codebases I work in. I saw it during my early days while I was working in Java projects, and I see it today in the occasional Go project. Function bodies with lots of local variables, functions that take a large number of arguments or returning a large number of results, extensions to existing types rather than making new ones. It&amp;rsquo;s a strange phenomenon.&lt;/p&gt;
&lt;p&gt;I can&amp;rsquo;t explain why this is. Maybe it&amp;rsquo;s a fear of feeling like you&amp;rsquo;re tampering with the &amp;ldquo;grand design&amp;rdquo; of the codebase. This is plausible as it was the feeling I had as a junior dev. Afraid to create new classes in Java thinking that I&amp;rsquo;m introducing a new concept to the project that others had to deal with going forward. _I can add all the verbs I want, but who am _ I &lt;em&gt;to introduce a new noun?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is obviously a ridiculous notion when you think about it for more than a few seconds. If you come up with a concept or a series of values that naturally go together, so much so that you&amp;rsquo;re carrying them together as a series of arguments through multiple function calls, it&amp;rsquo;s probably in your interested to make a type for it. That&amp;rsquo;s what the type system is for: a means of grouping similar bits of information into an easy-to-use whole.&lt;/p&gt;
&lt;p&gt;This makes total sense for the application models: the entities to which you&amp;rsquo;re software&amp;rsquo;s reason for being hinges on. But I&amp;rsquo;ve found it useful to make types for the lesser bits of information: requests from handlers passed through to the service layer, for instance. Just now, I&amp;rsquo;m working on some code that deals with creating subscriptions. I need to carry the office ID, customer ID, price ID, the subscription quantity, the tax settings, and the subscription metadata from the API handler all the way through to the Stripe client. This is less than what the subscription model deals with, but it&amp;rsquo;s still a pain to carry these six bits of information separately through the unmarshalling logic, the validation logic, and then through to the server.&lt;/p&gt;
&lt;p&gt;So what did I do? I made a &amp;ldquo;CreateSubscriptionRequest&amp;rdquo; struct, a new type. Yes, it&amp;rsquo;s not going to be reusable, but who cares? It makes the code and my life simpler. And honestly, I think the whole &amp;ldquo;object-orientated approach&amp;rdquo; to software design really screwed up our thinking here. There was this feeling in the zeitgeist that &lt;a href=&#34;https://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html&#34;&gt;types and classes are sacred&lt;/a&gt;, and that to create a new one is a privilege bestowed only to the leads, architects, and anyone else that had write access to the UML diagrams. Each type was to be an artefact of design, probably because of how much baggage came from defining a new one: they had to be in a separate file, must have seven different constructors, and the fields must be mediated through the use of getters and setters. And if you need something similar to what you&amp;rsquo;re working on, you didn&amp;rsquo;t &amp;ldquo;copy-and-paste&amp;rdquo; like some animal; you inherited or composed what was there. Given all this, it&amp;rsquo;s probably understandable that creating new types felt like a decision with a significant bit of &amp;ldquo;weight&amp;rdquo;; and who are you, mere lowly junior developer, to make such a decision to create a type just to make it easier to handle data from your handler?&lt;/p&gt;
&lt;p&gt;I think the culture around C and Go have got it right. Need to carry a few things for a single function? Create a new type. Don&amp;rsquo;t worry that it&amp;rsquo;s used only for a single function. Don&amp;rsquo;t worry that it only contains a subset of fields of the model you&amp;rsquo;re operating on.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Now obviously it&amp;rsquo;s possible to go too far, and start having way too many types than is necessary. Don&amp;rsquo;t forget that a new type is a bit more cognitive load, as the person maintaining you application will now need to unpack and reference your type when they need to work on it. Just stick with what you need, and make it clear what the purpose of the type is. &amp;ldquo;CreateSubscriptionRequest&amp;rdquo; makes it plan that this type only deals with the areas of a code that creates subscriptions, and will probably only make sense through those code paths.&lt;/p&gt;
&lt;p&gt;But take it from someone that&amp;rsquo;s had do deal with codes passing through and returning several values of strings, ints, and bools through a series of function calls: a single struct value is much easier to work with. All it takes is the courage for someone to say &amp;ldquo;yes, &lt;em&gt;that&lt;/em&gt; should be a type.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t be afraid for that someone to be you.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;In fact, that might actually better than using the model type and adding &amp;ldquo;this field is ignored, that field must be zero, etc. etc.&amp;rdquo; in the function docs.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Replacing A Side Mirror Of A Toyota Echo</title>
      <link>https://lmika.org/2025/03/15/replacing-a-side-mirror.html</link>
      <pubDate>Sat, 15 Mar 2025 10:42:41 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/03/15/replacing-a-side-mirror.html</guid>
      <description>&lt;p&gt;About a week ago, I foolishly broke the side mirror of my car. I ordered a replacement, which came yesterday,  and this morning I set about installing it. The site recommends getting it installed by a trained mechanic, and that&amp;rsquo;s probably good advice. But given that my car was about 20 years old, and after &lt;a href=&#34;https://youtu.be/dOibAESE3bU&#34;&gt;checking out this YouTube video&lt;/a&gt;, I figured I&amp;rsquo;d give it a shot doing it myself.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll spare you the suspense: I got it installed, although it wasn&amp;rsquo;t as smooth sailing as I hoped. First was getting the tools: a Phillips head screwdriver, and a 10mm a socket ratchet. I had the screwdriver but I needed to get the ratchet socket and drive. Easy enough to acquire after a quick drive to Bunnings.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250314-224155144.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  New socket ratchet and mirror. Scissors were used to get the tags off.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250314-224531028.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Removing these plastic &amp;#39;screws&amp;#39; to remove the door covering was not easy. They&amp;#39;re essentially clips and if you were to use too much force, the clips would break. I think I broke at least one.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250314-234847044.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Not sure why we even have a measuring system, given that an 6 mm nut and 1/4 inch washer was used for a 10 mm bolt. 🤷
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250314-234631354.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  New mirror finally installed. Looks okay, although don&amp;#39;t go prodding the door cover.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250314-235425252.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  This mirror is now the cleanest part of the car.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/pxl-20250314-235058246.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The old broken mirror, ready for disposal.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Next, was taking part of the passenger door cover off. This involved unscrewing one screw and two plastic &amp;ldquo;clip&amp;rdquo; screws. I&amp;rsquo;m a little unimpressed by how flimsy these plastic screws are: a bit too much force and they start breaking. I think I broke part of one when I was trying to get one off. Once the screws were out, part of the door cover had to be removed. The video made it seem like taking the covering off was as easy as lifting the clips out from the door, but I couldn&amp;rsquo;t get much of it off, and I think I may have broken some of the clips too.&lt;/p&gt;
&lt;p&gt;After removing the cover from the mirror itself, I then had to use the socket ratchet to remove the bolts and nuts holding the mirror to the door. The mirror was one whole assembly: there&amp;rsquo;s no way to just replace the mirror part without replacing the housing. So the whole thing needed to come off. I&amp;rsquo;ve never used a socket ratchet set before, but after a few false starts, I managed to get two bolts off rather easily. The third one was a fair way in and required a longer socket. I loosened the bolt but when I tried pulling it out, it fell out of the socket, down into the cavity between the panelling and door cover. That door&amp;rsquo;s going to rattle for the rest of the car&amp;rsquo;s life now.&lt;/p&gt;
&lt;p&gt;Short one nut, I needed to get a new one. The new mirror I bought didn&amp;rsquo;t come with any (which I found a little strange) and of course I didn&amp;rsquo;t have any 10mm nuts on me, so it was back to the hardware store to get some. They had 10mm nuts, but they seemed a bit big for what I needed. They certainly didn&amp;rsquo;t fit the socket. So I bought a collection of nuts and washers of various sizes, just to cover my bases. An 8mm nut and a 1/4 inch washer did the trick.&lt;/p&gt;
&lt;p&gt;Back home, after attaching the new mirror, it was just a matter of putting the door cover back. I did as good a job as I could, but I struggled a lot here. I don&amp;rsquo;t think I got all the tabs back, and the plastic screws just spun around when I tried to tighten them. It looks all there, but if one were to poke and prod it a little, it wouldn&amp;rsquo;t feel as solid as it once did. It&amp;rsquo;s probably a good thing I don&amp;rsquo;t usually carry passengers.&lt;/p&gt;
&lt;p&gt;So, that&amp;rsquo;s pretty much my adventure at car repair. The mirror&amp;rsquo;s pretty secure, but the door is not great. Despite all that, it was a good experience. But I think if that happened again on a newer car, I&amp;rsquo;d probably employ someone who knows what they&amp;rsquo;re doing.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Adventures In Godot: Respawning A Falling Platform</title>
      <link>https://lmika.org/2025/03/08/adventures-in-godot-respawning-a.html</link>
      <pubDate>Sat, 08 Mar 2025 11:35:07 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/03/08/adventures-in-godot-respawning-a.html</guid>
      <description>&lt;p&gt;My taste of going through a Godot tutorial last week has got me wanting more, so I&amp;rsquo;ve set about building a game with it. Thanks to my limited art skills, I&amp;rsquo;m using &lt;a href=&#34;https://brackeysgames.itch.io/brackeys-platformer-bundle&#34;&gt;the same asset pack&lt;/a&gt; that was used in the video, although I am planning to add a bit of my own here and there.&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s the new mechanics I enjoy working on, such as adding falling platforms. If you&amp;rsquo;ve played any platformer, you know what these look like: platforms that are suspended in air, until the player lands on them, at which point gravity takes a hold and they start falling, usually into a pit killing the player in the process:&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://media.lmika.org/videos/2025/falling-platform-1.mp4&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;The platforms are built as a &lt;code&gt;CharacterBody2D&lt;/code&gt; with an &lt;code&gt;Area2D&lt;/code&gt; that will detect when a player enters the collision shape. When they do, a script will run which will have the platform &amp;ldquo;slipping&amp;rdquo; for a second, before the gravity is turned on and the platform falls under it&amp;rsquo;s own weight. The whole thing is bundled as a reusable scene which I could drag into the level I&amp;rsquo;m working on.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250308-123202.png&#34; width=&#34;600&#34; height=&#34;391&#34; alt=&#34;Auto-generated description: A game development interface with a sprite and code editor is shown, from a software environment like Godot.&#34;&gt;
&lt;p&gt;I got the basics of this working reasonably quickly, yet today, I had a devil of a time going beyond that.
The issue was that I wanted the platform to respawn after it fell off the map, so that the player wouldn&amp;rsquo;t get soft-locked at at an area where the platform was needed to escape. After a false-start trying to reposition the platform at it&amp;rsquo;s starting point after it fell, I figured it was just easier to respawn the platform when the old one was removed. To do this I had to solve two problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;How do I get the platform starting point?&lt;/li&gt;
&lt;li&gt;How can I actually respawn the platform?&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;getting-the-starting-point&#34;&gt;Getting The Starting Point&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;Node2D&lt;/code&gt; object has the property &lt;a href=&#34;https://docs.godotengine.org/en/stable/classes/class_node2d.html#class-node2d-property-global-position&#34;&gt;global_position&lt;/a&gt;, which returns the position of the object based on the world coordinates. However, it seems like this position is not correct when the &lt;a href=&#34;https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-private-method-init&#34;&gt;_init&lt;/a&gt; function of the attached script is called. I suspect this is because this function is called before the platform is added to the scene tree, when the final world coordinates are known.&lt;/p&gt;
&lt;p&gt;Fortunately, there exists the &lt;code&gt;_ready&lt;/code&gt; notification, which is invoked when the node is added to the scene tree. After some experimentation, I managed to confirm that &lt;code&gt;global_position&lt;/code&gt; properly was correct. So tracking the starting point is a simple as storing that value in a variable:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var init_global_position = null

func _ready():
	init_global_position = global_position
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Another option is to use the &lt;code&gt;_enter_tree()&lt;/code&gt; notification. From &lt;a href=&#34;https://docs.godotengine.org/en/stable/tutorials/best_practices/godot_notifications.html#ready-vs-enter-tree-vs-notification-parented&#34;&gt;the documentation&lt;/a&gt;, it looks like either would probably work here, with the only difference being the order in which this notification is invoked on parents and children (&lt;code&gt;_enter_tree&lt;/code&gt; is called by the parent first, whereas &lt;code&gt;_ready&lt;/code&gt; is called by the children first).&lt;/p&gt;
&lt;h2 id=&#34;respawning-the-platform&#34;&gt;Respawning The Platform&lt;/h2&gt;
&lt;p&gt;The next trick was finding out how to respawn the platform. The usual technique for doing so, based on the results of my web searching, is to load the platform scene, &lt;a href=&#34;https://docs.godotengine.org/en/stable/classes/class_packedscene.html#description&#34;&gt;instantiate a new instance&lt;/a&gt; of it, and added it to the scene tree.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;@onready var FallingPlatform = preload(&amp;#34;res://scenes/falling_platform.tscn&amp;#34;)

func respawn():	
    var dup = FallingPlatform.instantiate()
    add_child(dup)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Many of the examples I&amp;rsquo;ve seen online added the new scene node as a child of the current node. This wouldn&amp;rsquo;t work for me as I wanted to free the current node at the same time, and doing so would free the newly instantiated child. The fix for this was easy enough: I just added the new node as a child of the current scene.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;@onready var FallingPlatform = preload(&amp;#34;res://scenes/falling_platform.tscn&amp;#34;)


func respawn():	
    var dup = FallingPlatform.instantiate()
    get_tree().current_scene.add_child(dup)
    queue_free()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I still had to reposition the new node to the spawn point. Fortunately the &lt;code&gt;global_position&lt;/code&gt; property is also settable, so it was simply a matter of setting that property before adding it to the tree (this is so that it&amp;rsquo;s correct when the newly instantiated node receives the &lt;code&gt;_ready&lt;/code&gt; notification).&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;@onready var FallingPlatform = preload(&amp;#34;res://scenes/falling_platform.tscn&amp;#34;)

func respawn():	
    var dup = FallingPlatform.instantiate()
    dup.global_position = init_global_position
    get_tree().current_scene.add_child(dup)
    queue_free()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This spawned the platform at the desired positioned, but there was a huge problem: when the player jumped on the newly spawn platform, it wouldn&amp;rsquo;t fall. The &lt;code&gt;Area2D&lt;/code&gt; connection was not invoking the script to turn on the gravity:&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://media.lmika.org/videos/2025/falling-platform-2.mp4&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;It took me a while to figured out what was going on, but I came to the conclusion that the packed scene was loading properly, but without the script attached. Turns out a &lt;a href=&#34;https://docs.godotengine.org/en/stable/classes/class_script.html&#34;&gt;Script&lt;/a&gt; is a resource separate from the scene, and can be loaded and attached to an object via the &lt;a href=&#34;https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-method-set-script&#34;&gt;set_script&lt;/a&gt; method:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;@onready var FallingPlatform = preload(&amp;#34;res://scenes/falling_platform.tscn&amp;#34;)
@onready var FallingPlatformScript = preload(&amp;#34;res://scripts/falling_platform.gd&amp;#34;)

func respawn():	
    var dup = FallingPlatform.instantiate()
    dup.set_script(FallingPlatformScript)

    dup.global_position = init_global_position
    get_tree().current_scene.add_child(dup)
    queue_free()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, after figuring all this out, I was able to spawn a new falling platform, have it positioned at the starting position of the old platform, and react to the player standing on it.&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://media.lmika.org/videos/2025/falling-platform-3.mp4&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;The time it took to work this out is actually a little surprising. I was expecting others to run into the same problem I was facing, where they were trying to instantiate a scene only to have the scripts not do anything. Yet it took me 45 minutes of web searching through Stack Overflow and forum posts that didn&amp;rsquo;t solve my problem. It was only after a bit of experimentation and print-debugging on my own that I realised that I actually had to attached the script after instantiating the node.&lt;/p&gt;
&lt;p&gt;To be fair, I will attribute some of this to not understanding the problem at first: I actually thought the &lt;code&gt;Area2D&lt;/code&gt; wasn&amp;rsquo;t actually being instantiated at all. Yet not one of the Stack Overflow answers or forum post floated the possibility that the script wasn&amp;rsquo;t being loaded alongside the scene.  This does suggest to me that my approach may not be optimal. There does exist a &amp;ldquo;Local to Scene&amp;rdquo; switch in the script inspector that could help, although turning it on doesn&amp;rsquo;t seem to do much. But surely there must be some way to instantiate the script alongside the scene.&lt;/p&gt;
&lt;p&gt;Anyway, that&amp;rsquo;s for later. For now, I&amp;rsquo;m happy that I&amp;rsquo;ve got something that works.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Running PeerTube In Coolify</title>
      <link>https://lmika.org/2025/03/01/running-peertube-in-coolify.html</link>
      <pubDate>Sat, 01 Mar 2025 14:39:21 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/03/01/running-peertube-in-coolify.html</guid>
      <description>&lt;p&gt;A few months ago, I moved my PeerTube instance over to Coolify, so that I could shutdown the Linux instance it was running on. I had a PeerTube instance for a little over a year, although I hardly posted anything on there and it&amp;rsquo;s never seen much traffic. I did want to keep it around though, as I had a few video embeds scattered around the internet. It just didn&amp;rsquo;t need to be on it&amp;rsquo;s own server.&lt;/p&gt;
&lt;p&gt;This post is not about how I ported this instance over to Coolify. It&amp;rsquo;s been a few months, and several hours of trial and error to get that working. But I had been asked how one could setup their own PeerTube instance in Coolify, and I wanted a documented approach for how one could do so, should there be a need to spin up a new instance from scratch. PeerTube is not one of the builtin services that Coolify offers, at least at the time of this post, so a manual process to setting this up is required.&lt;/p&gt;
&lt;p&gt;A few notes about the PeerTube instance you will have at the end of this process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The database and videos will be stored on the host&amp;rsquo;s file system. This should keep them persistent over container redeployments but will mean you&amp;rsquo;ll need to make sure the host&amp;rsquo;s file system is backed up somehow. Most VPS&amp;rsquo;s have a backup option, and PeerTube does offer a way to upload videos to an object store, although I&amp;rsquo;m not sure how to set that up.&lt;/li&gt;
&lt;li&gt;Email sending will be disabled. This works for me as I&amp;rsquo;m hosting these videos for myself, and I didn&amp;rsquo;t want to go through the trouble of deploying Postfix or paying for a hosted SMTP.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note also that this is not the approach I took to setup my instance, so some things may not be correct or may require further efforts to get working properly. Consider this as more of a &amp;ldquo;getting starting&amp;rdquo; guide, rather a comprehensive go-to-production process.&lt;/p&gt;
&lt;h2 id=&#34;prerequisite&#34;&gt;Prerequisite&lt;/h2&gt;
&lt;p&gt;You will need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.coolify.io&#34;&gt;Coolify&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A suitable Linux host managed by Coolify that meets &lt;a href=&#34;https://joinpeertube.org/faq#should-i-have-a-big-server-to-run-peertube&#34;&gt;PeerTube&amp;rsquo;s minimum specs&lt;/a&gt;. It can be the same host that&amp;rsquo;s running Coolify if necessary, but a separate one is probably worth considering, just so that it doesn&amp;rsquo;t interfere with Coolify itself.&lt;/li&gt;
&lt;li&gt;A domain name to use, for example &amp;ldquo;peertube-demo.lmika.xyz&amp;rdquo;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;d recommend using &lt;a href=&#34;https://coolify.io/docs/knowledge-base/proxy/caddy/overview&#34;&gt;Caddy as a Coolify proxy&lt;/a&gt; so as to get automatic TLS certificates.&lt;/p&gt;
&lt;h2 id=&#34;docker-compose&#34;&gt;Docker Compose&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ll be using a modified version of &lt;a href=&#34;https://docs.joinpeertube.org/install/docker&#34;&gt;PeerTube&amp;rsquo;s Docker Compose&lt;/a&gt; file, with the .env file embedded and the Nginx deployments removed. A copy of the file is provided below:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;x-env&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;&amp;amp;env&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Database / Postgres service configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;POSTGRES_USER&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;peertube&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;POSTGRES_PASSWORD&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;&amp;lt;database-password&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;POSTGRES_DB&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;peertube&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_DB_USERNAME&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;peertube&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_DB_PASSWORD&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;&amp;lt;database-password&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_DB_SSL&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_DB_HOSTNAME&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;postgres&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# PeerTube server configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_WEBSERVER_HOSTNAME&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;&amp;lt;peertube-domain&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_WEBSERVER_HTTPS&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_WEBSERVER_PORT&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;443&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# If you need more than one IP as trust_proxy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# pass them as a comma separated array:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_TRUST_PROXY&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;[&amp;#34;127.0.0.1&amp;#34;, &amp;#34;loopback&amp;#34;, &amp;#34;172.18.0.0/16&amp;#34;]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PUBLIC&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;public-read&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PRIVATE&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;private&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Generate one using `openssl rand -hex 32`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_SECRET&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;&amp;lt;random-string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;#PEERTUBE_LOG_LEVEL=info&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# /!\ Prefer to use the PeerTube admin interface to set the following configurations /!\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;#PEERTUBE_SIGNUP_ENABLED=true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;#PEERTUBE_TRANSCODING_ENABLED=true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;#PEERTUBE_CONTACT_FORM_ENABLED=true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# E-mail configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# If you use a Custom SMTP server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;#PEERTUBE_SMTP_USERNAME:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;#PEERTUBE_SMTP_PASSWORD:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Default to Postfix service name &amp;#34;postfix&amp;#34; in docker-compose.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# May be the hostname of your Custom SMTP server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_SMTP_HOSTNAME&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;postfix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_SMTP_PORT&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;25&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_SMTP_FROM&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;noreply@&amp;lt;peertube-domain&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_SMTP_TLS&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_SMTP_DISABLE_STARTTLS&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;PEERTUBE_ADMIN_EMAIL&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;&amp;lt;peertube-admin-address&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;peertube&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;chocobozzz/peertube:production-bookworm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;*env&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;ports&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1935:1935&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;9000&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;./docker-volume/data:/data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;depends_on&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;postgres&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;redis&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;# - postfix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;restart&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;always&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;postgres&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;postgres:13-alpine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;*env&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;./docker-volume/db:/var/lib/postgresql/data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;restart&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;always&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;redis&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;redis:6-alpine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;./docker-volume/redis:/data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;restart&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;always&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Uncomment if you want to use postfix (I don&amp;#39;t so I left it disabled)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# postfix:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#   image: mwader/postfix-relay&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#   &amp;lt;&amp;lt;: *env&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#   volumes:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#     - ./docker-volume/opendkim/keys:/etc/opendkim/keys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#   restart: &amp;#34;always&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Make a copy of this file in a text editor and replace the following placeholders:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;database-password&amp;gt;&lt;/code&gt;: Choose a suitable, preferably random, password to use for the database.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;peertube-domain&amp;gt;&lt;/code&gt;: Your domain of choice, e.g. &lt;code&gt;peertube-demo.lmika.xyz&lt;/code&gt;. This should be without the &amp;ldquo;https://&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;random-string&amp;gt;&lt;/code&gt;: Generate a random string by running &lt;code&gt;openssl rand -hex 32&lt;/code&gt; and use that here&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;peertube-admin-address&amp;gt;&lt;/code&gt;: This is unused but should be set to avoid server errors. Set it to something suitable, such as &lt;code&gt;admin@peertube.example.com&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;deploying-the-service&#34;&gt;Deploying The Service&lt;/h2&gt;
&lt;p&gt;Log into Coolify, go to &amp;ldquo;Projects&amp;rdquo;, and click &amp;ldquo;Add&amp;rdquo;.  Give your project a name then click &amp;ldquo;Continue&amp;rdquo;.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250301-113259.png&#34; width=&#34;600&#34; height=&#34;395&#34; alt=&#34;Auto-generated description: A user interface displays a New Project creation window with fields for name and description, surrounded by navigation options like Projects, Servers, and Settings.&#34;&gt;
&lt;p&gt;Within the project, add a new resource. The resource you&amp;rsquo;d want to choose is &amp;ldquo;Docker Compose Empty&amp;rdquo;.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250301-113306.png&#34; width=&#34;600&#34; height=&#34;400&#34; alt=&#34;Auto-generated description: A screenshot of the Coolify application interface displays a menu with various options like Projects, Servers, and Databases, highlighting Docker Compose Empty.&#34;&gt;
&lt;p&gt;Select the server you want to deploy PeerTube to, then copy and paste the compose file (make sure the placeholders are set).&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250301-113312.png&#34; width=&#34;600&#34; height=&#34;336&#34; alt=&#34;Auto-generated description: A Docker Compose configuration window is open in a development environment with code centered on PostgreSQL service settings.&#34;&gt;
&lt;p&gt;Before deploying, we&amp;rsquo;ll need to setup the proxy. In the Services list, locate the &amp;ldquo;PeerTube&amp;rdquo; one and click &amp;ldquo;Settings&amp;rdquo;.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250301-113319.png&#34; width=&#34;600&#34; height=&#34;336&#34; alt=&#34;Auto-generated description: A configuration dashboard displays sections for service stacks, environment variables, services, networking, deployments, and tasks, with an option to deploy changes.&#34;&gt;
&lt;p&gt;In the &amp;ldquo;Domains&amp;rdquo; text field, enter &lt;code&gt;https://&amp;lt;peertube-domain&amp;gt;:9000&lt;/code&gt; (the port number is used by Coolify to map the service to the proxy, but the service will be listening on port 443).&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250301-113327.png&#34; width=&#34;600&#34; height=&#34;336&#34; alt=&#34;Auto-generated description: Configuration settings for a service called PeerTube are displayed, with options to save or delete, and a highlighted domain URL.&#34;&gt;
&lt;p&gt;Click &amp;ldquo;Save&amp;rdquo;, then click &amp;ldquo;Back&amp;rdquo; to go back to the list of services, then click &amp;ldquo;Deploy&amp;rdquo;. Wait for the containers to be provisioned, then try visiting &lt;code&gt;https://&amp;lt;peertube-domain&amp;gt;/&lt;/code&gt; to see if it deployed successfully.&lt;/p&gt;
&lt;p&gt;The last thing you&amp;rsquo;ll need to do is &lt;a href=&#34;https://docs.joinpeertube.org/install/docker#obtaining-your-automatically-generated-admin-credentials&#34;&gt;get the generated password&lt;/a&gt; for the &lt;code&gt;root&lt;/code&gt; user. You can do that by clicking the &amp;ldquo;Logs&amp;rdquo; tab, getting the recent log messages from the &amp;ldquo;PeerTube&amp;rdquo; container, and doing a find-in-page search for &amp;ldquo;User password&amp;rdquo;.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250301-113553.png&#34; width=&#34;600&#34; height=&#34;353&#34; alt=&#34;Auto-generated description: A dark-themed dashboard interface displays log entries for PeerTube, showing various status messages and timestamps.&#34;&gt;
&lt;p&gt;Alternatively, you can set the password yourself by going to the Terminal, connecting to the PeerTube container, and typing in the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;npm run reset-password -- -u root
&lt;/code&gt;&lt;/pre&gt;&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250301-113518.png&#34; width=&#34;600&#34; height=&#34;358&#34; alt=&#34;Auto-generated description: A terminal window is open on a dashboard interface with a command to reset a password being displayed.&#34;&gt;
&lt;p&gt;You then should be able to login into the frontend as &amp;ldquo;root&amp;rdquo; and continue setting up your instance from there.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250301-113600.png&#34; width=&#34;600&#34; height=&#34;355&#34; alt=&#34;Auto-generated description: A login page for PeerTube is displayed, featuring fields for username and password, and links for terms of service and password recovery.&#34;&gt;
&lt;p&gt;At this point, you should have a functioning PeerTube instance. The rest of the setup you can do from the web interface.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll need to perform additional steps to get email working; and you&amp;rsquo;ll need to make additional changes if you want to host your videos in an object store, and potentially use some of the advanced features like uploading via Torrent or publishing a live. I&amp;rsquo;ve not done any of these myself, but information on how to do this should be findable within &lt;a href=&#34;https://docs.joinpeertube.org&#34;&gt;PeerTube&amp;rsquo;s documentation page&lt;/a&gt;. The Docker Compose file should be editable from within Coolify; the trick is finding out which environment variables you&amp;rsquo;ll need to set (and just beware that Docker compose &amp;ldquo;normalises&amp;rdquo; the YAML file so you may need to adjust environment variables in both the &lt;code&gt;x-env&lt;/code&gt; and the service environments section). But hopefully this should be enough if all you want to do is host videos on your own infrastructure.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Attending the DDD Melbourne 2025 Conference</title>
      <link>https://lmika.org/2025/02/23/yesterday-i-attended-the-ddd.html</link>
      <pubDate>Sun, 23 Feb 2025 20:08:20 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/02/23/yesterday-i-attended-the-ddd.html</guid>
      <description>&lt;p&gt;Yesterday, I attended the DDD Melbourne 2025 conference. This was in service of my yearly goal to get out more, to be around people more often than I have been. So the whole reason I attended was to meet new people. That didn&amp;rsquo;t happen: I said hi to a few people I once worked with, and spoke to a few sponsors, but that was it. So although I marked it off my goal list, it wasn&amp;rsquo;t a huge success.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250221-214355756.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A stage featuring a Melbourne 2025 banner is set up in a theatre with people seated and colourful lighting.&#34;&gt;
&lt;p&gt;But a dev conference is still a dev conference and I&amp;rsquo;d thought I&amp;rsquo;d write a few notes of the sessions I attended, just to record what I did get out of it.&lt;/p&gt;
&lt;h2 id=&#34;keynote&#34;&gt;Keynote&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Emerging trends in robots&lt;/em&gt;, by Sue Keay.&lt;/p&gt;
&lt;p&gt;The keynote interesting session about the state of robotics in Australia. Didn&amp;rsquo;t get a lot of specifics, but I did get a &lt;a href=&#34;https://en.wikipedia.org/wiki/Pepper_(robot)&#34;&gt;name for the robot&lt;/a&gt; I saw once in a Tokyo department store that, let&amp;rsquo;s just say, left an impression on me.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250221-222943526.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A speaker is presenting on stage with a slide displaying images of humanoid robots.&#34;&gt;
&lt;h2 id=&#34;first-session&#34;&gt;First Session&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Are you overcomplicating software development? I certainly have been…&lt;/em&gt;, by Ian Newmarch.&lt;/p&gt;
&lt;p&gt;This speaker was absolutely preaching my gospel around complexity in software development. But it took someone to go deeper into into why developers are prone to take an inherently complex practice and add additional complexity (so call &amp;ldquo;accidental complexity&amp;rdquo;). This is mainly due to human factors: ego, fear, imposter syndrome, and to some extent, to keep the job interesting.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250221-234121311.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A large screen displays a quote by Neal Ford about developers and complexity, set in a grand hall with colorful lighting.&#34;&gt;
&lt;p&gt;Very reliable. Only real way to mitigate this is going back to principals such as avoiding premature abstraction, YAGNI, and KISS. Thing about principals is that they&amp;rsquo;re always a little hard to know when you need it. So remember to always keep a focus on the problem - what you&amp;rsquo;re trying to solve - and working with people can help here.&lt;/p&gt;
&lt;h2 id=&#34;second-session&#34;&gt;Second Session&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;How to Design Your Next Career Move&lt;/em&gt;, by Emily Conaghan.&lt;/p&gt;
&lt;p&gt;This speaker went through a process of how one could reflect on what they want out of their career, and how to come up with what they need to do to bridge the gap to get to it. The process is rather methodical, which is not a bad thing, and there&amp;rsquo;s a whole &lt;a href=&#34;https://drive.google.com/file/d/1B_IEPrWjuyC3pUJ32f1cb9rL3qrlRBXY/view&#34;&gt;workbook component&lt;/a&gt; to this. This might be something that&amp;rsquo;s personally worth doing though: it does feel like I&amp;rsquo;m drifting aimlessly a little.&lt;/p&gt;
&lt;h2 id=&#34;third-session&#34;&gt;Third Session&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;The Lost Art of good README documentation&lt;/em&gt;, by Swapnil Ogale.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250222-011709465.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A speaker is presenting on The lost art of good README documentation to an audience in a formal room with flags and ornate decor.&#34;&gt;
&lt;p&gt;I found this one to be quite good. It touched on the properties of what makes a good README for a project, and why you&amp;rsquo;d want to (the reason is that a developer&amp;rsquo;s or user&amp;rsquo;s trust in a project directly relates to the support document). In short, a good readme should have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A project overview: basically answering the question of what this project is, why it s, and why should one use it.&lt;/li&gt;
&lt;li&gt;How to instructions: how does one install it, get started using it, etc.&lt;/li&gt;
&lt;li&gt;How does one engage and contribute to the project: how they can get help, contribute changes, etc.&lt;/li&gt;
&lt;li&gt;Credits, license and contact details&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But even though these could be described as what a &amp;ldquo;good&amp;rdquo; README looks like, a takeaway is there&amp;rsquo;s no such thing as a bad README, apart from not having any README at all.&lt;/p&gt;
&lt;p&gt;One other thing that I didn&amp;rsquo;t know was that README&amp;rsquo;s are traditionally capitalised so that they appear near the top in a alphanumerical listing of files. That was interesting to know.&lt;/p&gt;
&lt;h2 id=&#34;lunch&#34;&gt;Lunch&lt;/h2&gt;
&lt;p&gt;Yeah, this was the hardest part of the day. But it&amp;rsquo;s amazing how much time you can kill just by waiting in lines.&lt;/p&gt;
&lt;h2 id=&#34;forth-session&#34;&gt;Forth Session&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Being consistently wrong&lt;/em&gt;, by Tom Ridge.&lt;/p&gt;
&lt;p&gt;I was expecting this to be a bit more general, like techniques for keeping an open mind or learning from one&amp;rsquo;s mistakes. But it was largely focused on task estimations, which is a weakness of mine, but seeing that this was after lunch and I was getting a bit tired around this time, I only halved listened. But the takeaways I did get are the importance of measuring, how long tasks take to travel across the board, how long they&amp;rsquo;re in progress, in review, etc.; using those measurements to determine capacity using formula&amp;rsquo;s derived from queuing theory; keeping the amount of work in progress low; and keeping task duration variance low by slicing.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250222-023849838.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A speaker standing at a podium addresses an audience with a presentation slide that reads Estimation is waste.&#34;&gt;
&lt;p&gt;These are all valid points, although I&amp;rsquo;m not sure how applicable they are to how we work at my job. But it may be a worthy talk to revisit if that changes.&lt;/p&gt;
&lt;h2 id=&#34;fifth-session&#34;&gt;Fifth Session&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;On The Shoulders Of Giants — A Look At Modern Web Development&lt;/em&gt;, by Julian Burr.&lt;/p&gt;
&lt;p&gt;Despite being a backend developer by day, I am still curious of the state of web developer. This talk was a good one, where the speaker went through the various &amp;ldquo;milestones&amp;rdquo; of major technical developments in web technology — such as when Javascript and jQuery was introduce, when AJAX became a thing, and when CSS was developed (I didn&amp;rsquo;t know CSS was devised at CERN).&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250222-033231409.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A presenter stands in front of an audience, showing a slide with two individuals wearing t-shirts that read I blame Immanuel for this behavior.&#34;&gt;
&lt;p&gt;Going back in time was fun (R.I.P. Java applets &amp;amp; Flash) but it seems the near-term future is all React, all the time. And not just React in the traditional sense, but React used for zero-hydration server side components (&lt;a href=&#34;https://qwik.dev&#34;&gt;Qwik&lt;/a&gt;) and out of order streaming (&lt;a href=&#34;https://react.dev/reference/react/Suspense&#34;&gt;React Suspense&lt;/a&gt;). Not sure that appeals to me. Although one thing that does is that &lt;a href=&#34;https://vite.dev&#34;&gt;Vite&lt;/a&gt; is becoming the build tool &lt;em&gt;du jour&lt;/em&gt; for frontend stuff. This I may look at, since it looks simple enough to get started.&lt;/p&gt;
&lt;p&gt;Some other fun things: &lt;a href=&#34;https://en.wikipedia.org/wiki/JavaScript_Style_Sheets&#34;&gt;JavaScript style sheets&lt;/a&gt; was a thing, and &lt;a href=&#34;https://developer.chrome.com/docs/css-ui/houdini&#34;&gt;Houdini&lt;/a&gt; still is a thing.&lt;/p&gt;
&lt;h2 id=&#34;sixth-session&#34;&gt;Sixth Session&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Dungeons and… Developers? Building skills in tech teams with table top role playing games&lt;/em&gt;, by Kirsty McDonald.&lt;/p&gt;
&lt;p&gt;This was the talk that got me in the door in some respects. I&amp;rsquo;ve heard of role-playing games being a thing for scenario planning, so the idea of doing it for team development and practice responding to things like production incidents. This consisted of the normal thing&amp;rsquo;s you&amp;rsquo;d expect from a role playing game, like &lt;a href=&#34;https://linktr.ee/kirstymcdonald&#34;&gt;character cards&lt;/a&gt;, a game master, and scenario events with a random-number generator component it it.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250222-051743988.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: Four people are sitting on a stage with a large screen behind them displaying a web page and a wooden surface with a geometric object.&#34;&gt;
&lt;p&gt;I&amp;rsquo;ve never played D&amp;amp;D before, so I was curious as to how these games actually ran. Fortunately, I was not disappointed, as the last part of the talk was walking through an example game with a couple of volunteers from the audience. Definitely a talk worth staying back for.&lt;/p&gt;
&lt;h2 id=&#34;locknote&#34;&gt;Locknote&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Coding Like it&amp;rsquo;s 2005&lt;/em&gt;, by Aaron Powell&lt;/p&gt;
&lt;p&gt;This was a fun look-back on the state of the art of web development back in 2005, before jQuery, AJAX, decent editors, when annoying workarounds in JavaScript and CSS were necessary to get anything working in Internet Explorer. This was just before my time as a practicing dev, and apparently trying to replicate rich-client applications in the web browser were all the rage, which was something I missed. It was mainly focused on Microsoft technology, something I don&amp;rsquo;t have a lot of personal experience in, but I did get flashbacks of using Visual Studio 2003 and version 1 of Firefox.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250222-054826827.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A computer screen displays a code editor with HTML and ASP.NET source code open.&#34;&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250222-055522047.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A lecturer stands in front of a large projected screen displaying a webpage titled Retro ChatGPT with colorful sections of text.&#34;&gt;
&lt;p&gt;Lot&amp;rsquo;s of fun going down memory lane (R.I.P clearfix &amp;amp; YUI; rot in hell, IE6 😛).&lt;/p&gt;
&lt;h2 id=&#34;overall&#34;&gt;Overall&lt;/h2&gt;
&lt;p&gt;I was contemplating not showing up to this, and even while I was there, I was considering leaving at lunchtime, but overall I&amp;rsquo;m glad that I stayed the whole day. It got me out of the house, and I learnt a few interesting things. And let me be clear: DDD Melbourne and the volunteers did an excellent job! It was a wonderfully run conference with a lot of interesting speakers. I hope to see some of the talks on YouTube later.&lt;/p&gt;
&lt;p&gt;But, I don&amp;rsquo;t think I&amp;rsquo;ll be going to a conference by myself again. I mean, it&amp;rsquo;s one thing to go if work asks you to: I can handle myself in that situation. But under my own volition? Hmm, it would be much easier going with someone else, just so that I have someone to talk to. It&amp;rsquo;s clear that I need to do something about my fear of approaching someone I don&amp;rsquo;t know and start speaking to them. Ah well, it was worth a try.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>An Incomplete List of DRM-Free Media Stores</title>
      <link>https://lmika.org/2025/02/18/an-incomplete-list-of-drmfree.html</link>
      <pubDate>Tue, 18 Feb 2025 07:09:11 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/02/18/an-incomplete-list-of-drmfree.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been seeing a bunch of links to online stores that sell digital media — books, music, etc. — that is without DRM. I&amp;rsquo;ve thought of starting a collection of some of them here for my own reference. This is an evolving list and is by no means complete, but I want a central place to put these links.&lt;/p&gt;
&lt;p&gt;Some notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This collection is mainly to catalogue places where I can get contemporary mainstream works, so sites like Project Gutenberg and Bandcamp have been left out (although they&amp;rsquo;re pretty good places to get works).&lt;/li&gt;
&lt;li&gt;These are the Australian links, mainly because I live in Australia, but I&amp;rsquo;m sure you can find the storefronts specific to your region by following them (that&amp;rsquo;s how I found these ones).&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;ve not tried all these links myself. Those I&amp;rsquo;ve had that I&amp;rsquo;ve been happy with I&amp;rsquo;ve marked with a ✅. Those that I’ve tried that I haven’t been happy with I’ve marked with a 👎.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;books&#34;&gt;Books&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.ebooks.com/en-au/drm-free/&#34;&gt;eBooks DRM-Free Section&lt;/a&gt;: Looks to be a good place to get technology related books, such as the O&amp;rsquo;Reilly series.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;✅ &lt;a href=&#34;https://www.manning.com&#34;&gt;Manning&lt;/a&gt;: I&amp;rsquo;m not entirely sure about this one, yet I have had some success buying DRM-free content from here very recently. Mainly technology related books.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://standardebooks.org&#34;&gt;Standard eBooks&lt;/a&gt;: Looks to be mainly out-of-copyright works.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;👎 &lt;a href=&#34;https://www.kobo.com/au/en/p/drm-free&#34;&gt;Kobo DRM-Free Section&lt;/a&gt;: Looks to be mainly novels. Just beware that the mainstream sections may NOT be DRM-free.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;music&#34;&gt;Music&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;✅ &lt;a href=&#34;https://www.qobuz.com/&#34;&gt;Qobuz&lt;/a&gt;: This is pretty great. A lot of mainstream albums here.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.zdigital.com.au/&#34;&gt;ZDigital&lt;/a&gt;: Known as 7digital outside of AU (probably clashes with channel 7 here). I&amp;rsquo;ve not tried this yet, but looks to have a decent collection of mainstream albums too.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;audiobooks&#34;&gt;Audiobooks&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;a href=&#34;https://libro.fm&#34;&gt;Libro.fm&lt;/a&gt;: Not really an audio-book person, probably because I&amp;rsquo;m not interested in trying anything more from Amazon. When I&amp;rsquo;m ready, I&amp;rsquo;ll come here first.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many of these were shared on Micro.blog and Mastodon. Shoutouts to &lt;a href=&#34;https://micro.blog/sod&#34;&gt;sod&lt;/a&gt;, &lt;a href=&#34;https://micro.blog/toddgrotenhuis&#34;&gt;toddgrotenhuis&lt;/a&gt;, and &lt;a href=&#34;https://writing.exchange/@cheribaker&#34;&gt;cheribaker&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Apple AI in Mail and What Could Be</title>
      <link>https://lmika.org/2025/02/13/now-that-ive-got-a.html</link>
      <pubDate>Thu, 13 Feb 2025 11:58:45 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/02/13/now-that-ive-got-a.html</guid>
      <description>&lt;p&gt;The emails I usually get at work are not super useful — I mainly conduct work via Slack — but we get lunch at work, and the menu is usually sent to us as an email at the start of the week. I have food allergies so I try to keep atop of these just to know if I need to organise lunch for myself one day.&lt;/p&gt;
&lt;p&gt;I was a little careless this week, and missed a lunch that had satay sauce, resulting in an unpleasent allergic reaction (nothing too serious, but still not a good time). I resolved to prevent this happening again by adding a rule that highlighted emails containing &amp;ldquo;nut&amp;rdquo; or &amp;ldquo;satay.&amp;rdquo; Adding the rule is… fine. It kinda sorta works, but it&amp;rsquo;s not perfect: running the rule flagged a few false positives.&lt;/p&gt;
&lt;p&gt;But upgrading to a version of MacOS that had Apple AI, it struck me that these emails are getting processed by an LLM already, in order to power Mail&amp;rsquo;s email summaries and priorities. Like many others I find these features pretty useless, mainly because what is considered important it&amp;rsquo;s usually not relevant to me (I&amp;rsquo;m on heaps of mailing lists).&lt;/p&gt;
&lt;p&gt;But what if I could tell Mail what&amp;rsquo;s important to me, and how I want it to respond? One could imagine me clicking into one of these weekly menu emails, engaging the LLM, and typing in an instruction like this:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;This email comes in on Monday and list the lunch menu for each day of the week. When you see an item that has nuts, or an ingredient that is derived from nut, such as &amp;ldquo;satay&amp;rdquo;, highlight the email in red and flag it using the red flag.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I have no idea whether this is even possible using current tech. Certaintly it&amp;rsquo;s possible for an LLM to ingest the body of an email and work out if there is any indication of ingredients containing nuts (I&amp;rsquo;ve tested this using OpenAI&amp;rsquo;s API) but it might be more difficult mixing both the condition and action in a single request like this. Might be that a more guided experience is warrented, maybe in the form of a &amp;ldquo;if email looks like this, then do this&amp;rdquo;:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250213-125125.png&#34; width=&#34;600&#34; height=&#34;530&#34; alt=&#34;Auto-generated description: An email received on Monday from yada-yada@example.com, resembling a lunch menu with a peanut-containing item like Satay, should be marked in red and flagged.&#34;&gt;
&lt;p&gt;I don&amp;rsquo;t know. I&amp;rsquo;m not a designer. But I know such a feature would be more useful to me that what&amp;rsquo;s in Mail right now. And I think the seeds are there for this to work. Apple just needs to let us users take the reins a little more.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>First Impressions of the Cursor Editor</title>
      <link>https://lmika.org/2025/02/10/first-impressions-of-the-cursor.html</link>
      <pubDate>Mon, 10 Feb 2025 21:49:09 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/02/10/first-impressions-of-the-cursor.html</guid>
      <description>&lt;p&gt;Along with Image Playgrounds, I also gave the &lt;a href=&#34;https://www.cursor.com&#34;&gt;Cursor editor&lt;/a&gt; a try today. I needed a tool to take a Micro.blog export and effectively &amp;ldquo;move&amp;rdquo; it to another blog, complete with uploaded images. This is something that&amp;rsquo;s not hard to write myself, yet it&amp;rsquo;s quite tedious: there&amp;rsquo;s HTTP calls, parsing Markdown, dealing with front-matter, etc.&lt;/p&gt;
&lt;p&gt;So I was quite impressed at how much Cursor manage to automate for me: a good 60% of the codebase was generated using the Claud 3.5 Sonnet model. And it was pretty decent code for what this tool is, which is more-or-less a throwaway script. It suffered a bit when I needed to interact with the Micropub API though: that I just used an existing library I&amp;rsquo;ve written. But it was enough to get something working in about 2 hours. I doubt I would&amp;rsquo;ve finished it this evening if I was writing it myself.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250210-223603.png&#34; width=&#34;600&#34; height=&#34;383&#34; alt=&#34;Auto-generated description: A dark-themed code editor window is displayed, featuring Go programming language code on the right and a file directory on the left.&#34;&gt;
&lt;p&gt;I&amp;rsquo;m not sure if I would use this for anything long-term projects. The quality of the code is good, but not great, at least not yet. And I kinda wish there was an &amp;ldquo;I&amp;rsquo;ll drive&amp;rdquo; mode, where the tab completions and generation prompts took a backseat from me just wanting to cut the code myself.&lt;/p&gt;
&lt;p&gt;And I don&amp;rsquo;t know about you, but I&amp;rsquo;m someone that needs to be involved in the writing of the code to understand it. I find it difficult to simply jump into a project I&amp;rsquo;ve not worked on myself, and be expected to make significant changes. That always takes a while for me. It&amp;rsquo;s a bit like writing: the act of producing it is where half the benefit is.&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s a very intriguing editor and well worth the try. We&amp;rsquo;ll see if I turn to it again in the future.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Playing Around With MacOS Image Playground</title>
      <link>https://lmika.org/2025/02/10/ive-upgraded-my-mac-to.html</link>
      <pubDate>Mon, 10 Feb 2025 21:48:22 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/02/10/ive-upgraded-my-mac-to.html</guid>
      <description>&lt;p&gt;I know I&amp;rsquo;m late to the party, and that this has been reviewed already by those that write about Apple software and anyone with an iPhones. Nevertheless, I&amp;rsquo;ve upgraded my Mac to the version of MacOS that has Apple AI, and along with it came Image Playgrounds. I thought I&amp;rsquo;d give Image Playground a go.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s my first attempt. May I present to you: &lt;em&gt;generic white technologist in a bow tie using a computer with a confused expression on their face&lt;/em&gt;:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/image-playgrounds-1.jpeg&#34; width=&#34;600&#34; height=&#34;600&#34; alt=&#34;Auto-generated description: An AI generated image of a person with glasses and a red bow tie sits behind a laptop, looking intently at the screen.&#34;&gt;
&lt;figcaption&gt;Based on true events.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It took me a little while to understand how to compose the image. I knew that this was going to be a very on-rails experience, but I was still expecting that prompts that would work for DALL-E would work here too. But when I tried the prompt &amp;ldquo;a technologist using a computer with a confused expression on their face,&amp;rdquo; I just got back an error. Eventually, I figured out that I needed to build up the image by adding additional clarifiers, which would floating around a pulsating orb while the image was being generated. This was a little more on-rails than I was expecting but I guess it&amp;rsquo;s a way for Apple to make this approachable for those not use to using ChatGPT, along with controlling the suitability of the output.&lt;/p&gt;
&lt;p&gt;As for the image itself, all I could say is that it&amp;rsquo;s… okay. A little bit on the cartoonish side, but I&amp;rsquo;m guessing Apple prefers that over something with a high degree of realism. The subject looks nothing like me but that&amp;rsquo;s by design: I dared not use myself as the subject, given how bad the preview photo I assume it was going to use was. I am a little disappointed by how expressionless the generated people are. I&amp;rsquo;m not sure how well the underlying model actually understands expression like &amp;ldquo;confused&amp;rdquo; given that most of the suggested images had a neutral expression, with one exhibiting something that I can only describe as &amp;ldquo;surprise and delight.&amp;rdquo; This is the best one I could find.&lt;/p&gt;
&lt;p&gt;Finally, it seems the developers of this app has never heard of the file system. I could share the generated image to a bunch of Apple apps like Mail and Notes, but I couldn&amp;rsquo;t find a way to save it as an image file. If there is a way, it&amp;rsquo;s not obvious: I checked the toolbar, menu bar and context menu. Copy to clipboard was an option so I ended up copying it to Acorn and saving it as a JPEG there. This is definitely something that would get any technologist confused.&lt;/p&gt;
&lt;p&gt;So yeah, I don&amp;rsquo;t see this being a regular part of my workflow. It&amp;rsquo;s better than I expected, but this is definitely something that Apple has built for people who have not used DALL-E or any of the other AI image generators out there (and, I suspect, to appease their shareholders). For myself, I&amp;rsquo;ll stick with what I&amp;rsquo;m using now.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll close this with a gallery of a few other images I&amp;rsquo;ve tried:&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/image-playgrounds-2.jpeg&#34;
     
        alt=&#34;Auto-generated description: A tiger sits serenely in a lush forest reading an open book, wearing a green robe.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  This one&amp;#39;s a bit of fun: a tiger in a dressing gown reading a book. Maybe it&amp;#39;s reading about LLMs and Transformers.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/image-playgrounds-3.jpeg&#34;
     
        alt=&#34;Auto-generated description: A cockatiel with a striking yellow crest is perched on a chessboard surrounded by chess pieces.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  This is an attempt to try out the cockatiel as a chess piece image I generated using Gemini. There&amp;#39;s no contest: Gemini does a much better job, probably because I could be a bit more specific on the materials and background.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/image-playgrounds-4.jpeg&#34;
     
        alt=&#34;Auto-generated description: A sailboat with red sails navigates a tranquil ocean at sunset, surrounded by palm-covered islands.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  This was going so well: sunset, yacht, red sail. That is until I asked for a lighthouse, and the model decided that the best place to put that was at the top of the mast. It was also interesting that &amp;#39;yacht&amp;#39; without any additional context resulted in images of superyachts without sails.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/image-playgrounds-5.jpeg&#34;
     
        alt=&#34;Auto-generated description: A large airship floats above industrial buildings with tall smoke stacks against a cloudy sky.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  This was a trial of the &amp;#39;illustration&amp;#39; mode. Phrases here were steampunk, blimp, smokestack, and &amp;#39;victorian era.&amp;#39; It&amp;#39;s a little unremarkable, and I was hoping for the illustrative nature of the image to be a bit more obvious. But that&amp;#39;s fine.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>UCL: Some Updates</title>
      <link>https://lmika.org/2025/02/08/ucl-some-updates.html</link>
      <pubDate>Sat, 08 Feb 2025 09:32:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/02/08/ucl-some-updates.html</guid>
      <description>&lt;p&gt;Made a few minor changes to UCL. Well, actually, I made one large change. I&amp;rsquo;ve renamed the &lt;code&gt;foreach&lt;/code&gt; builtin to &lt;code&gt;for&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I was originally planning to have a &lt;code&gt;for&lt;/code&gt; loop that worked much like other languages: you have a variable, a start value, and an end value, and you&amp;rsquo;d just iterate over the loop until you reach the end.  I don&amp;rsquo;t know how this would&amp;rsquo;ve looked, but I imagined something like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for x 0 10 {
    echo $x
}
# numbers 0..9 would be printed.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But this became redundant after adding the &lt;code&gt;seq&lt;/code&gt; builtin:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;foreach (seq 10) { |x|
    echo $x
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This was in addition to all the other useful things you could do with the foreach loop&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, such as loop over lists and hashes, and consume values from iterators. It&amp;rsquo;s already a pretty versatile loop. So I elected to go the Python way and just made it so that the &lt;code&gt;for&lt;/code&gt; loop is the loop to use to iterate over collections.&lt;/p&gt;
&lt;p&gt;This left an opening for a loop that dealt with guards, so I also added the &lt;code&gt;while&lt;/code&gt; loop. Again, much like most languages, this loop would iterate over a block until the guard becomes false:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set x 0
while (lt $x 5) {
    echo $x
    set x (add $x 1)
}
echo &amp;#34;done&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Unlike the &lt;code&gt;for&lt;/code&gt; loop, this is unusable in a pipeline (well, unless it&amp;rsquo;s the first component). I was considering having the loop return the result of the guard when it terminates, but I realised that would be either false, nil, or anything else that was &amp;ldquo;falsy.&amp;rdquo; So I just have the loop return nil. That said, you can break from this loop, and if the break call had a value, that would be used as the result of the loop:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set x 0
while (lt $x 5) {
    set x (add $x 1)
    if (ge $x 3) {
        break &amp;#34;Ahh&amp;#34;
    }
} | echo &amp;#34; was the break&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The guard is optional, and if left out, the &lt;code&gt;while&lt;/code&gt; loop will iterate for ever.&lt;/p&gt;
&lt;h2 id=&#34;the-set-builtin&#34;&gt;The Set! Builtin&lt;/h2&gt;
&lt;p&gt;Many of these changes come from using of UCL for my job, and one thing I found myself doing recently is writing a bunch of migration scripts. This needed to get data from a database, which may or may not be present. If it&amp;rsquo;s not, I want the script to fail immediately so I can check my assumptions.  This usually results in constructs like the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set planID (ls-plans | first { |p| eq $p &amp;#34;Plan Name&amp;#34; } | index ID)
if (not $planID) {
    error &amp;#34;cannot find plan&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And yeah, adding the if block is fine — I do it all the time when writing Go — but it would be nice to assert this when you&amp;rsquo;re trying to set the variable, for no reason other than the fact that you&amp;rsquo;re thinking about nullability while writing the expression to fetch the data.&lt;/p&gt;
&lt;p&gt;So one other change I made was add the &lt;code&gt;set!&lt;/code&gt; builtin. This will basically set the variable only if the expression is not nil. Otherwise, it will raise an error.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set! planID (ls-plans | first { |p| eq $p &amp;#34;Missing Plan&amp;#34; } | index ID)
# refusing to set! `planID` to nil value
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This does mean that &lt;code&gt;!&lt;/code&gt; and &lt;code&gt;?&lt;/code&gt; are now valid characters to appear in identifiers, just like Ruby. I haven&amp;rsquo;t decided whether I want to start following the Ruby convention of question marks indicating a predicate or bangs indicating a mutation. Not sure that&amp;rsquo;s going to work out now, given that the bang is being used here to assert non-nullability.  In either case, could be useful in the future.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;And the &lt;code&gt;seq&lt;/code&gt; builtin&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>About My New Cooler&#39;s Programming Feature</title>
      <link>https://lmika.org/2025/02/07/about-my-new-coolers-programming.html</link>
      <pubDate>Fri, 07 Feb 2025 08:18:26 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/02/07/about-my-new-coolers-programming.html</guid>
      <description>&lt;p&gt;There&amp;rsquo;s lots to like about my new cooler, but the programming feature is not one of them. My old unit had a very simple timer with two modes: turn cooler on after N hours, or turn cooler off after N hours. Anything else requires manual intervention.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/img-20161221-215727-edit.jpg&#34; width=&#34;600&#34; height=&#34;479&#34; alt=&#34;Auto-generated description: A wall-mounted thermostat with buttons labeled for power, mode, and temperature adjustment options.&#34;&gt;
&lt;figcaption&gt;The old control panel (turns out I did have a photo, albeit an old one). Set the mode: cool/vent (fan), the power setting, then tap Timer Select to choose between turn on or off after N hours.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I really liked this simple setup. Many times in summer, when the days are warm but not hot, and the nights are cool, it was nice to turn the cooler&amp;rsquo;s fan on and set the timer to turn it off after 2 to 3 hours, maybe longer if the days were a bit warmer&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. The cooler will simply pull cool air from outside and circulate it around the room. This was enough for me to get to sleep, at which point the cooler would shut itself off in the middle of the night.&lt;/p&gt;
&lt;p&gt;With the new cooler comes a control panel that is effectively a cheap Android phone, so it&amp;rsquo;s capable of much more. You can now set a program that has four separate modes set to four separate times of the day. Each day of the week now has it&amp;rsquo;s own program too. A particular mode can either be a desired temperature setting, or &amp;ldquo;off&amp;rdquo;.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250206-204434810.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A digital control panel for a Seeley International air conditioning system displays a program schedule with times and statuses.&#34;&gt;
&lt;figcaption&gt;The new control panel, showing the four mode settings for a particular day of the week.
&lt;/figure&gt;
&lt;p&gt;To recreate what I previously had, I would now have to choose a specific shutoff time for every day of the week. No longer am I able to set the running time based on how I feel: it has to be an actual timestamp, with several taps involve if you want to change it. This timestamp can only be set up to 11:59 PM so if you want the unit to shut off after midnight, you&amp;rsquo;ll have to remember to choose the program for the next day.&lt;/p&gt;
&lt;p&gt;Oh, and mercy on you if you wanted a timestamp that didn&amp;rsquo;t land on the hour. The minutes can only be changed by 1, so you&amp;rsquo;ll be tapping 30 times if you want the unit to shut-off at the half hour.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250206-204455735.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A digital thermostat screen displays settings including start time, day selection, temperature, and status options.&#34;&gt;
&lt;figcaption&gt;Adjusting the settings of a particular mode, one minute at a time.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You also have no control over the fan speed. That was another nice thing about the old unit: you set the speed to what you want, and then you set the timer. The unit will stay in that mode until it shuts off. I don&amp;rsquo;t want the fan to be blowing a gale when I&amp;rsquo;m trying to get to sleep, so the fan was usually set to the lowest or second-lowest setting.&lt;/p&gt;
&lt;p&gt;This new programming modes only have a temperature setting, so if the house is warm, the cooler will crank up the fan until it reaches half-speed or just above; speeds I usually use in the middle of a very hot day. This means noise that will change in intensity as the target temperature is reached. I&amp;rsquo;m not a great sleeper so any additional noise like that is disruptive.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pxl-20250206-204522236.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A Seeley International MagiQtouch controller displays temperature settings and a schedule for cooling with the message PREWET IN PROGRESS.&#34;&gt;
&lt;figcaption&gt;The unit operating in program mode.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;So I&amp;rsquo;m a little sad that I lost this simple timer-based approach to operating the cooler. I&amp;rsquo;m not even sure who this programming feature is built for. It sort of exists in that nether region where it&amp;rsquo;s too complicated for the simple things, yet useless for anything other than a set weekly routine. I set my cooler based on the weather conditions which, you may be surprised to know, does not fall into a nice weekly routine. Granted, it may make it possible to use this to recreate the simple timer approach I had before: I just preset everything and only activate the program when I want it. And yeah, it&amp;rsquo;ll probably be fine, but I do feel like I&amp;rsquo;ve lost something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Apparently the cooler &lt;em&gt;does&lt;/em&gt; have a shutoff after N hours feature. It&amp;rsquo;s just &lt;a href=&#34;https://lmika.org/2025/02/10/ok-i-admit-that-i.html&#34;&gt;buried in the settings menu&lt;/a&gt;. The post still stands, as it would&amp;rsquo;ve been nice that this was a feature of the Program mode, but at least there&amp;rsquo;s something I can use.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;If the days and nights are hot, I don&amp;rsquo;t bother with the timer and just leave it running all night long.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Idea for UCL: Methods </title>
      <link>https://lmika.org/2025/02/02/idea-for-ucl-methods.html</link>
      <pubDate>Sun, 02 Feb 2025 14:46:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/02/02/idea-for-ucl-methods.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m toying with the idea of adding methods to UCL. This will be similar to the methods that exist in Lua, in that they&amp;rsquo;re essentially functions that pass in the receiver as the first argument, although methods would only be definable by the native layer for the first version.&lt;/p&gt;
&lt;p&gt;Much like Lua though, methods would be invokable using the &lt;code&gt;:&lt;/code&gt; &amp;ldquo;pair&amp;rdquo; operator.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strs:to-upper &amp;#34;Hello&amp;#34;
--&amp;gt; HELLO
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The idea is to make some of these methods on the types themselves, allowing their use on literals and the result of pipelines, as well as variables:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set h &amp;#34;Hello&amp;#34;
$hello:to-upper
--&amp;gt; HELLO

&amp;#34;Hello&amp;#34;:to-upper
--&amp;gt; HELLO

(cat &amp;#34;Hello &amp;#34; &amp;#34;world&amp;#34;):to-upper
--&amp;gt; HELLO WORLD
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The use of methods would be purely conveience. One could conceive of a type, like a CSV table, where there&amp;rsquo;s a need to perform a series of operations over it.&lt;/p&gt;
&lt;p&gt;The potential downside of using &lt;code&gt;:&lt;/code&gt; is that it may make defining dictionaries more ambiguous. When the parser sees something that could either be a list or a dictionary, it does a scan to search for any pairs that may exist. If so, it treats the literal as a dictionary. But would this work with using &lt;code&gt;:&lt;/code&gt; as the method separator?&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[&amp;#34;Hello&amp;#34;:to-upper]
--&amp;gt; Is this a dictionary {&amp;#34;Hello&amp;#34;:&amp;#34;to-upper&amp;#34;}, or the list [&amp;#34;HELLO&amp;#34;]?
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So that would require something. Might be that I need to require method invocations to occur within parenthesis for list literals.&lt;/p&gt;
&lt;p&gt;Ambiguities like this aside, I&amp;rsquo;m planning to keep it simple for now. Methods will not be user definable within UCL out of the gate, but would be effectively another interface available to native types. For the builtin ones that exist now, it&amp;rsquo;ll most likely be little more than syntactic sugar over the standard library functions:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$hello:to-upper

# equivalent to

strs:to-upper $hello
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Speaking of the standard library, the use of &lt;code&gt;:&lt;/code&gt; as a module-function separator may need some changes. A the moment it&amp;rsquo;s a bit of a hack: when a module is loaded, any procs defined within that module is stored in the environment with the operator: &lt;code&gt;strs:to-upper&lt;/code&gt; for example. The parser is sophisticated enough to recognised &lt;code&gt;:&lt;/code&gt; as a token, but when it parses two or more identifiers separated with &lt;code&gt;:&lt;/code&gt;, it just joins them up into a single identifier.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s needed is something else, maybe using something based on methods. The current idea is to define modules as being some special form of object, where the &amp;ldquo;methods&amp;rdquo; are simply the names of the exported symbol.&lt;/p&gt;
&lt;p&gt;I was curious to know whether the language was capable of doing something similar now. It&amp;rsquo;s conceivable that a similar concept could be drafted with procs returning dictionaries of procs, effectively acting as a namespace for a collection of functions.  So a bit of time in the playground resulted in this experiment:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;proc fns {
  return [
    upper: (proc { |x| strs:to-upper $x })
    lower: (proc { |x| strs:to-lower $x })
  ]
}

(fns).upper &amp;#34;Hello&amp;#34;
--&amp;gt; HELLO

(fns).lower &amp;#34;Hello&amp;#34;
--&amp;gt; hello
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A good first start. This is theoretically possible in the language that exists at the moment. It&amp;rsquo;s not perfect thought. For one thing, the call to &lt;code&gt;fns&lt;/code&gt; needs to be enclosed in parenthesis in order to invoke it. Leaving them out results in an error:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;fns.upper &amp;#34;Hello&amp;#34;
--&amp;gt; error: command is not invokable
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The same is true when using variables instead of procs. I tried this experiment again using variables and it came to the same limitations:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set fns [
  upper: (proc { |x| strs:to-upper $x })
  lower: (proc { |x| strs:to-lower $x })
]

($fns).upper &amp;#34;Hello&amp;#34;
--&amp;gt; HELLO

$fns.upper &amp;#34;Hello&amp;#34;
--&amp;gt; error: command is not invokable
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Obviously the parser needs to be changed to add additional support for the &lt;code&gt;:&lt;/code&gt; operator, but I also need to fix how strong &lt;code&gt;.&lt;/code&gt; binds to values too.  But I think this may have legs.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Slash Pages Verses Blog Posts</title>
      <link>https://lmika.org/2025/01/14/on-slash-pages-verses-blog.html</link>
      <pubDate>Tue, 14 Jan 2025 06:59:25 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/01/14/on-slash-pages-verses-blog.html</guid>
      <description>&lt;p&gt;Interesting discussion on &lt;a href=&#34;https://pca.st/zsguwgn7?t=9m33s&#34;&gt;ShopTalk about slash pages&lt;/a&gt; and whether blog posts may make more sense for some of them. Chris and Dave makes the point that blog posts have the advantage of syndicating updates, something that static pages lack on most CMSs. It&amp;rsquo;s a good point, and a tension I feel occasionally. Not so much on this site, but there&amp;rsquo;ve been several attempts where I tried to make a site for technical knowledge, only to wonder whether a blog or a wiki makes more sense. I&amp;rsquo;d like the pages to be evergreen yet I also like to syndicate updates when I learn new stuff.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve currently settled on the &lt;a href=&#34;https://til.computer&#34;&gt;blog format&lt;/a&gt; for now, and it&amp;rsquo;s fine — tags tend to help here — but I wonder if  something smarter could be useful here. One idea I have is to have a page with &amp;ldquo;sections&amp;rdquo; where each one could be seen as a mini blog post. You add and modify sections over time, and when you do, each section would be syndicated individually. Yet the page will be rendered as a whole when viewing it in the browser. It&amp;rsquo;s almost like the RSS feed contains diffs for the page, albeit something self contained and readable by humans. There might be a CMS that does this already, I haven&amp;rsquo;t looked. But I get the sense that most RSS feeds of wiki pages actually contain a diff, or a simple message saying &amp;ldquo;this page has been updated.&amp;rdquo; There&amp;rsquo;s nothing to suggest that what&amp;rsquo;s out there has this sections semantics.&lt;/p&gt;
&lt;p&gt;In lieu of that, I like the idea proposed by Chris and Dave where you basically new versions of these slash pages as blog posts and redirect the slash URL to the latest one, kind of like a bookmark. I may start doing these for some of them, starting with &lt;a href=&#34;https://lmika.org/defaults&#34;&gt;/defaults&lt;/a&gt; which is, conveniently, already a blog post.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Project Update: DSL Formats For Interactive Fiction</title>
      <link>https://lmika.org/2025/01/12/still-bouncing-around-things-to.html</link>
      <pubDate>Sun, 12 Jan 2025 10:39:47 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/01/12/still-bouncing-around-things-to.html</guid>
      <description>&lt;p&gt;Still bouncing around things to work on at the moment. Most of the little features have been addressed, and I have little need to add anything pressing for the things I&amp;rsquo;ve been working on recently. As for the large features, well apathy&amp;rsquo;s taking care of those. But there is one project that is tugging at my attention. And it&amp;rsquo;s a bit of a strange one, as part of me just wants to kill it. Yet it seems to be resisting.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://lmika.org/2024/07/29/current-project-update.html&#34;&gt;About 6 months ago&lt;/a&gt;, I started working on some interactive fiction using &lt;a href=&#34;https://evergreen.ink/&#34;&gt;Evergreen&lt;/a&gt;. I got most of the story elements squared away but much of the interactive elements were still left to be done. And as good as Evergreen is for crafting the story, I found it a little difficult tracking down all the scripts I needed to write, debug and test.&lt;/p&gt;
&lt;p&gt;So in early November, I had a look at porting this story over to a tool of my own, called Pine Needle (yes, the name is a bit of a rip-off). Much like Evergreen, the artefact of this is a choose-your-own-adventure story implemented as a static webpage. Yet the means of building the story couldn&amp;rsquo;t be more different. Seeing that I&amp;rsquo;m more comfortable working with code and text files, I eschewed building any UI in favour of a tool that simply ran from the command line.&lt;/p&gt;
&lt;p&gt;But this meant that I needed someway to represent the story in text. Early versions simply had the story hard coded in Go, but it wasn&amp;rsquo;t long before I started looking a using a DSL. My first attempt was a hand-built one based on Markdown with some additional meta-elements. The goal was to keep boilerplate to a minimum, with the meta-elements getting out of the way of the prose. Here&amp;rsquo;s a sample of what I had working so far:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// Three dashes separate pages, with the page ID following on.
// Also these are comments, as the hash is reserved for titles.
--- tulips-laundry-cupboard

You open the cupboard door and look at the shelf
above the brooms. There are a couple of aerosol cans up there,
including a red one that says &amp;#34;Begone Insecticide&amp;#34;.
You bring it out and scan the active ingredients. There are a
bunch of letters and numbers back there, and none of them have
the word &amp;#34;organic.&amp;#34;

\choice &amp;#39;Take Insecticide&amp;#39; to=tulips-take-insecticide
\choice &amp;#39;Leave Insecticide&amp;#39; to=tulips-leave-insecticide

--- tulips-take-insecticide

You return to the tulips with the insecticide, and start 
spraying them. The pungent odour of the spray fills the air, 
but you get the sense that it&amp;#39;s helping a little.

\choice &amp;#39;Continue&amp;#39; to=tulips-end

--- tulips-leave-insecticide

You decide against using the can of insecticide. You put the
can back on the shelf and close the cupboard door.

\choice &amp;#39;Look Under The Trough&amp;#39; to=tulips-laundry-trough
\choice &amp;#39;Exit Laundry&amp;#39; to=tulips-exit-laundry
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The goal was to have the meta-elements look like LaTeX macros — for example, &lt;code&gt;\option{Label}{target-screen}&lt;/code&gt; — but I didn&amp;rsquo;t get far in finishing the parser for this. And I wasn&amp;rsquo;t convinced it had the flexibility I wanted. LaTeX macros relies pretty much on positional arguments, but I knew I wanted key-value pairs to make it easier to rely on defaults, plus easier to extend later.&lt;/p&gt;
&lt;p&gt;I did imagine a fully LaTeX inspired DSL for this, but I quickly dismissed it for how &amp;ldquo;macro-heavy&amp;rdquo; it would be. For reference, here&amp;rsquo;s how I imagined it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-latex&#34; data-lang=&#34;latex&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;\screen&lt;/span&gt;{tulips-laundry-cupboard}{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  You open the cupboard door and lo ok at the shelf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  above the brooms. There are a couple of aerosol cans up there,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  including a red one that says &amp;#34;Begone Insecticide&amp;#34;.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  You bring it out and scan the active ingredients. There are a
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  bunch of letters and numbers back there, and none of them have
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  the word &amp;#34;organic.&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;\choice&lt;/span&gt;{Take Insecticide}{to=tulips-take-insecticide}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;\choice&lt;/span&gt;{Leave Insecticide}{to=tulips-leave-insecticide}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;\screen&lt;/span&gt;{tulips-take-insecticide}{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  You return to the tulips with the insecticide, and start 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  spraying them. The pungent odour of the spray fills the air, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  but you get the sense that it&amp;#39;s helping a little.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;\choice&lt;/span&gt;{Continue}{to=tulips-end}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;\screen&lt;/span&gt;{tulips-leave-insecticide}{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  You decide against using the can of insecticide. You put the
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  can back on the shelf and close the cupboard door.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;\choice&lt;/span&gt;{Look Under The Trough}{to=tulips-laundry-trough}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;\choice&lt;/span&gt;{Exit Laundry}{to=tulips-exit-laundry}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I wasn’t happy with the direction of the DSL, so I looked for something else. I briefly had a thought about using JSON. I didn’t go so far as to try it, but the way this could work is something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;screens&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tulips-laundry-cupboard&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;body&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    You open the cupboard door and look at the shelf
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    above the brooms. There are a couple of aerosol cans up 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    there, including a red one that says \&amp;#34;Begone Insecticide\&amp;#34;.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    You bring it out and scan the active ingredients. There
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    are a bunch of letters and numbers back there, and none of 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    them have the word \&amp;#34;organic.\&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;options&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;screen&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tulips-take-insecticide&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Take Insecticide&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;screen&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tulips-leave-insecticide&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Leave Insecticide&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}, &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tulips-take-insecticide&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;body&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    You return to the tulips with the insecticide, and start 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    spraying them. The pungent odour of the spray fills the air, 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    but you get the sense that it&amp;#39;s helping a little.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;options&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;screen&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tulips-end&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Continue&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I generally like JSON as a transport format, but it didn’t strike me as a format that suited the type of data I wanted to encode. Most of what this format would contain would be prose, which I’d prefer to keep as Markdown. But this would clash with JSON’s need for explicit structure. Setting aside the additional boilerplate this structure would require, all the prose would have to be encoded as one big string, which didn’t appeal to me.
Also no comments, especially within string literals, which is a major deal breaker.&lt;/p&gt;
&lt;p&gt;So, the current idea is to use something based on XML. This has some pretty significant benefits: editors have good support for XML, and Go has an unmarshaller which can read an XML directly into Go structures. JSON has this too, but I think it’s also a pretty decent format for at editing documents by hand, so long as you keep your XML elements to a minimum.&lt;/p&gt;
&lt;p&gt;I think one aspect that turned people off XML back in the day was format designer’s embrace of XML’s ability to represent hierarchical data without leaning into it’s use as a language for documents. The clunky XML documents I had to deal with were  purely used to encode structure, usually in a way that mapped directly to an domain’s class model. You had formats where you need 10 nested elements to encode a single bit of information that were a pain to read or edit by hand. These were usually dismissed by the designers with promises like, “Oh, you won’t be editing this by hand most of the time. You’ll have GUI design tools to help you.” But these were awful to use too, and that’s if they were available, which they usually were not (did you know building GUIs are hard?)&lt;/p&gt;
&lt;p&gt;If you have an XML format that skews closer to HTML rather than something that’s representable in JSON, I think it could be made to work. So yesterday I had a go at seeing whether it this could work for Pine Needle. Here’s what I’ve got so far:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;lt;&lt;/span&gt;?xml version=&amp;#34;1.0&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;story&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;screen&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tulips-laundry-cupboard&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  You open the cupboard door and look at the shelf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  above the brooms. There are a couple of aerosol cans up 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  there, including a red one that says &amp;#34;Begone Insecticide&amp;#34;.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  You bring it out and scan the active ingredients. There
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  are a bunch of letters and numbers back there, and none of 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  them have the word &amp;#34;organic.&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;option&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;screen=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tulips-take-insecticide&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;Take Insecticide&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;option&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;screen=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tulips-leave-insecticide&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;Leave Insecticide&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/screen&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;screen&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tulips-take-insecticide&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  You return to the tulips with the insecticide, and start 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  spraying them. The pungent odour of the spray fills the air, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  but you get the sense that it&amp;#39;s helping a little.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;option&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;screen=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tulips-end&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;Continue&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/screen&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;screen&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tulips-leave-insecticide&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  You decide against using the can of insecticide. You put the
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  can back on the shelf and close the cupboard door.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;option&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;screen=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tulips-laundry-trough&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;Look Under The Trough&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;option&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;screen=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tulips-exit-laundry&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;Exit Laundry&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/screen&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/story&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The idea is that the prose will still be Markdown, so things like blank lines will still be respected (the parser strips all the leading whitespace, allowing one to easily indent the prose). Attributes satisfy the key/value requirement for the elements, and I get the features that make this easy to modify by hand, such as comments and good editor support.&lt;/p&gt;
&lt;p&gt;I think it’s going to work. It would require some custom code, as Go’s unmarshaller doesn’t quite like the mix of prose and declared &lt;code&gt;&amp;lt;option&amp;gt;&lt;/code&gt; elements, but I think it’s got the bones of a decent format for this interactive fiction. Already I’m coming up with ideas of how to add script elements and decompose fragments into sub-files to make it easier to test.&lt;/p&gt;
&lt;p&gt;I’ll talk more about this project in the future if I’m still working on it. I don’t know if the story that started all this  will see the light of day. I’ve gone through it a few times, and it’s not great. But shipping stuff you’re proud of comes from shipping stuff you’re not proud of, and given how far along it is, it probably deserved to be release in one form or another. That’s probably why it’s been saved from the chopping block so far&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Yes, this is probably just a rationalisation for trying to minimise sunk-costs, but I’ve got nothing else to work on, so why not this?&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Gallery of Fake Logo For Test Organisations</title>
      <link>https://lmika.org/2025/01/10/gallery-of-fake-logo-for.html</link>
      <pubDate>Fri, 10 Jan 2025 15:44:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/01/10/gallery-of-fake-logo-for.html</guid>
      <description>&lt;p&gt;In lieu of anything else, I thought I&amp;rsquo;d put together a gallery of the logos I use for test organisations. I occasionally need to create fake organisations for testing at work, and to add a bit of amusement to the mix, I sometimes make fake logo for them. These logos are typically made using ChatGPT, although if I&amp;rsquo;m particularly bored, I sometimes touched them up by hand (nothing too drastic, usually things like cropping or adding titles). Most of the fake organisation are film and media production studios, as that&amp;rsquo;s typically the client base of our product.&lt;/p&gt;
&lt;p&gt;I do this mainly for fun, but there is some utility to it. A user can be a member of zero or more organisations, and can change the one they&amp;rsquo;re acting in at any time. Having a unique avatar for each one helps in distinguishing which one I have active. I do get cute with the names, and for that, I make no apologies. 🙂&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/589883bbf7.jpg&#34;
     
        alt=&#34;Auto-generated description: A vintage-style logo for Content Mill Productions features a central windmill design with the year 2019 and the word STUI included.&#34; 
     
     
        title=Content&amp;#32;Mill&amp;#32;Productions 
      /&gt;

  &lt;figcaption&gt;
  
    &lt;strong&gt;Content Mill Productions  — &lt;/strong&gt;
  
  The original logo made as a tongue-in-cheek joke to those online making content for the sake of chasing the &amp;#39;views&amp;#39;.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/07648921f3.jpg&#34;
     
        alt=&#34;Auto-generated description: A robotic rover is illustrated in front of a reddish planet with the words Martian Productions beneath it.&#34; 
     
     
        title=Marian&amp;#32;Film&amp;#32;House 
      /&gt;

  &lt;figcaption&gt;
  
    &lt;strong&gt;Marian Film House  — &lt;/strong&gt;
  
  Named in honour of Spirit and Opportunity, which was passing through my head while coming up with this one.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/seedglass.jpeg&#34;
     
        alt=&#34;Auto-generated description: A translucent, teardrop-shaped glass sculpture with intricate internal textures is displayed against a soft, gradient background.&#34; 
     
     
        title=Seedglass 
      /&gt;

  &lt;figcaption&gt;
  
    &lt;strong&gt;Seedglass  — &lt;/strong&gt;
  
  Not really a film production studio. I had the domain seedglass.net at the time and I needed to test single sign on. I&amp;#39;m actually pretty impressed with what ChatGPT came up with for this one.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/good-vibes.jpg&#34;
     
        alt=&#34;Auto-generated description: A stylized illustration of a rodent wearing headphones.&#34; 
     
     
        title=Good&amp;#32;Vibe&amp;#32;Productions 
      /&gt;

  &lt;figcaption&gt;
  
    &lt;strong&gt;Good Vibe Productions  — &lt;/strong&gt;
  
  No remarks on this one, other than I was feeling a bit troubled by what was going in in the news at the time.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2025/fistey-feline.jpeg&#34;
     
        alt=&#34;Auto-generated description: An elephant is depicted inside a square with the text Fisty Feline Films below it.&#34; 
     
     
        title=Feisty&amp;#32;Feline&amp;#32;Films 
      /&gt;

  &lt;figcaption&gt;
  
    &lt;strong&gt;Feisty Feline Films  — &lt;/strong&gt;
  
  I did add the title for this one by hand but the colouring and illustration was all ChatGPT. And yeah, this is a Simpson reference.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>2024 Year In Review</title>
      <link>https://lmika.org/2025/01/01/year-in-review.html</link>
      <pubDate>Wed, 01 Jan 2025 10:55:48 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/01/01/year-in-review.html</guid>
      <description>&lt;p&gt;It’s a few minutes to 12:00 PM on the 1st January 2025 when I published this. Thanks to time-zones, that means it’s just about to turn 12:00 AM one hour to the west of Greenwich, meaning that it’s still 2024 in much to the west of the prime meridian. So I’m &lt;em&gt;technically&lt;/em&gt; still within the window of time where I could say I got a year in review post out for 2024.&lt;/p&gt;
&lt;p&gt;Turns out it’s not the first time I’ve used this excuse. Looking at other posts I&amp;rsquo;ve published on the 1st of January, I’ve done this &lt;a href=&#34;https://lmika.org/2023/12/27/year-in-review.html&#34;&gt;twice&lt;/a&gt; &lt;a href=&#34;https://lmika.org/2021/01/01/a-quick-review.html&#34;&gt;before&lt;/a&gt;. And I guess this pattern of posting a year in review on the first of the next year makes some sense: it’s usually a quiet day, with nothing open, and I’m usually a little tired and listless&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;That means there’s usually a large block of time available to me, and despite &lt;a href=&#34;https://lmika.org/2024/12/31/oh-no-what-a-shame.html&#34;&gt;what I wrote yesterday&lt;/a&gt;, it is a good practice to do some reflecting, however brief it may be.&lt;/p&gt;
&lt;p&gt;So I’d figured I’d might as well drag iA Writer out and scribble  out a brief review of the year that was.&lt;/p&gt;
&lt;h2 id=&#34;online-presence&#34;&gt;Online Presence&lt;/h2&gt;
&lt;p&gt;I made a point of wanting to cut down the number of domains I acquire in my &lt;a href=&#34;https://lmika.org/on-this-day/#:~:text=Ultimately%20I%E2%80%99d%20like%20to%20continue%20cutting%20down%20the%20number%20of%20new%20domains%20I%20register.%20It%E2%80%99s%20getting%20to%20be%20an%20expensive%20hobby.&#34;&gt;year in review for 2023&lt;/a&gt;. This is still an ongoing progress, but year-on-year, I’m down 2 domains on net, which is an improvement. 2024 saw the acquisition of 7 new domains, of which 2 I’m using for something, and 1 I plan to keep around:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Domains&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;2024&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;2023&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Registered&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;23&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;25&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;With auto-renew turned on&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;17&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;16&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Currently used for something&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;15&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;13&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Not currently for something but worth keeping&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;2&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;3&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Want to remove but stuck with it because it’s been shared by others&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;0&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;1&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I’m still trying to keep this blog alive by posting regularly here. I’ve must’ve felt more comfortable with doing so as it’s been a record breaking year, &lt;a href=&#34;https://lmika.org/stats/&#34;&gt;with 840 posts&lt;/a&gt;, beating the previous record by 594 posts. The initial fear of falling out the practice has subsided to one where I find joy in posting here. That’s probably why the post count is so much higher.&lt;/p&gt;
&lt;p&gt;Of course the quality of a blog doesn’t correlate with one’s ability to “post the most”, and I do feel like there have been times where I felt a little blasé about what I write here. I do want to be better here, or at least be a little more conscious about it. But not at the expense of turning this into solely a soapbox/marking/punditry site: there’s plenty of those out there already. So you’ll probably continue to see the cringey, irrelevant, lame, and potentially disposable posts here. I’ll just try to make sure that it’s balanced out with posts that are actually good.&lt;/p&gt;
&lt;p&gt;Some notable posts of the year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/01/05/for-reasons-that.html&#34;&gt;Detecting A Point In a Convex Polygon&lt;/a&gt;: the subject matter is a little dry, but one thing that’s notable about this one was the interactive elements. This took a few weeks to get right and I’m quite happy with how they turned out.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/02/06/golang-debugger-not.html&#34;&gt;Goland Debugger Not Working? Try Upgrading All The Things&lt;/a&gt;: notable as it seems to be the post that got the most traffic from Google.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/05/11/poking-through-some.html&#34;&gt;BASIC.HTM&lt;/a&gt;: notable as it was the first post of mine that appeared on Hacker News.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/07/09/a-tour-of.html&#34;&gt;A Tour Of My New Self-Hosted Code Setup&lt;/a&gt;: longest post to date, with probably the longest bit of spoken audio I’ve published on this site. Getting my hands dirty with editing the narration was an interesting experience.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/09/20/microfiction-get-a.html&#34;&gt;Micro-fiction: Get A Horse&lt;/a&gt;: notable in that this was my first post that was a work of fiction.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One other thing about blogs: I still have that wandering eye for other blogging CMSs. I did start three other blogs, of which two I shut-down and rolled into this one. That just leaves &lt;a href=&#34;https://til.computer/&#34;&gt;one new blog created this year&lt;/a&gt; that I’m planning to keep separate, mainly because I feel the subject matter warrants a dedicated site.&lt;/p&gt;
&lt;h2 id=&#34;projects&#34;&gt;Projects&lt;/h2&gt;
&lt;p&gt;2024 didn’t feel like a big project year, probably because most of the projects I’ve started I kept to myself. But I did manage to release a couple this year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://sidebar-for-tiny-theme.lmika.dev/&#34;&gt;Sidebar For Tiny Theme&lt;/a&gt;: This came with the additions for recommendations to Micro.blog, and the addition of sidebars listing those recommendations in a couple of the themes. I use Tiny Theme, which didn’t have a way to do this, so what &lt;a href=&#34;https://lmika.org/2024/03/14/adding-a-sidebar.html&#34;&gt;started as a blog-post&lt;/a&gt; eventually became a Micro.blog plugin. It’s been good seeing this adopted by other bloggers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cyberburger.lmika.app/&#34;&gt;Cyber Burger&lt;/a&gt;: A Pico-8 game, which I hoped would’ve only taken a few weeks to build. Overall it took 3 months, largely because I spent large stretches of time not working on it. But I’m glad I actually got this finished and released.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The other things I’ve worked on that I built mainly for myself:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://ucl.lmika.dev&#34;&gt;UCL&lt;/a&gt;: A tool-command language, similar to TCL, that I’ve built for work. The state of this is smack-bang in the middle of “sort-of-usable” and I’ll need to spend some effort cleaning this up and documenting this if I ever want this to see wide release. I’m not sure that I want that though. at least not yet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blogging Tools&lt;/strong&gt;: A set of tools that help me post here. What started as something to assist with &lt;a href=&#34;https://lmika.org/2024/06/13/blogging-gallery-tool.html&#34;&gt;making photo galleries&lt;/a&gt; has grown to a &lt;a href=&#34;https://lmika.org/2024/12/28/more-fun-today-working-on.html&#34;&gt;suite of tools&lt;/a&gt; dealing with images, audio and video. Really useful for my needs.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/05/05/tape-playback-site.html&#34;&gt;Tape Playback Site&lt;/a&gt;: A private site for browsing old cassette tapes that have been digitised. I still have a pile of tapes I need to digitise though (are you getting a sense that I find it hard to finish things? 😀).&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/08/22/i-enjoyed-reading.html&#34;&gt;Nano-Journal&lt;/a&gt;: My version of Kev Quirk’s web-based journalling app. After adding Git synchronisation and attachment support, I’ve use this now for all my journalling. It doesn’t do everything that Day One does (off-line support’s a big one) but it does enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, some of the projects that have been abandoned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Photo Bucket&lt;/strong&gt;: My third attempt at making something to publish image galleries online. I think the need for this has been made somewhat redundant with Blogging Tools.&lt;/li&gt;
&lt;li&gt;I also started two interactive fiction stories in &lt;a href=&#34;https://evergreen.ink/&#34;&gt;Evergreen&lt;/a&gt; that I haven’t finished yet. One is close, and I probably should get it done. The other is about halfway finished and probably won’t see the light of day.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;books-and-media&#34;&gt;Books And Media&lt;/h2&gt;
&lt;p&gt;Given that I exceeded my reading goal for 2023, I thought I could push myself a bit more for 2024 and go for finishing 10 books. Sadly, I was nowhere near meeting it. I only got to finishing 4 this year:&lt;/p&gt;
&lt;div class=&#34;bookgoals&#34;&gt;&lt;a href=&#34;https://micro.blog/books/9780473453053&#34;&gt;&lt;img src=&#34;https://cdn.micro.blog/photos/300x/https%3A%2F%2Flmika.org%2Fuploads%2F2024%2Fimg-0157.jpeg&#34; alt=&#34;Useful Not True&#34; width=&#34;100&#34; height=&#34;120&#34; class=&#34;cover&#34;&gt;&lt;/a&gt;&lt;a href=&#34;https://micro.blog/books/9781988575971&#34;&gt;&lt;img src=&#34;https://cdn.micro.blog/photos/300x/https%3A%2F%2Fbooks.google.com%2Fbooks%2Fcontent%3Fid%3DfeWfzgEACAAJ%26printsec%3Dfrontcover%26img%3D1%26zoom%3D5%26source%3Dgbs_api&#34; alt=&#34;Hell Yeah or No&#34; width=&#34;100&#34; height=&#34;120&#34; class=&#34;cover&#34;&gt;&lt;/a&gt;&lt;a href=&#34;https://micro.blog/books/9780007381432&#34;&gt;&lt;img src=&#34;https://cdn.micro.blog/photos/300x/https%3A%2F%2Fbooks.google.com%2Fbooks%2Fcontent%3Fid%3D7notgaTVUFAC%26printsec%3Dfrontcover%26img%3D1%26zoom%3D5%26source%3Dgbs_api&#34; alt=&#34;Magician (The Riftwar Saga, Book 1)&#34; width=&#34;100&#34; height=&#34;120&#34; class=&#34;cover&#34;&gt;&lt;/a&gt;&lt;a href=&#34;https://micro.blog/books/9798218058517&#34;&gt;&lt;img src=&#34;https://cdn.micro.blog/photos/300x/https%3A%2F%2Fimages.isbndb.com%2Fcovers%2F15242713488674.jpg&#34; alt=&#34;Twenty Bits I Learned about Making Websites&#34; width=&#34;100&#34; height=&#34;120&#34; class=&#34;cover&#34;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;(not pictured: Twenty Bits I Learned about Making Websites)&lt;/p&gt;
&lt;p&gt;I think I probably started more books than I’ve finished. Not that I have to finish every book I’ve started, but I think my problem is one of focus. The books I have started could be interesting, and I have plans to finish some of them. I just need to spend more time reading.&lt;/p&gt;
&lt;p&gt;I’m not a movie person but I did manage to watch a few this year. Here are the ones I’ve posted reviews for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/03/24/072940.html&#34;&gt;Dune: Part One&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/09/20/dune-part.html&#34;&gt;Dune: Part Two&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/04/28/mission-impossible-fallout.html&#34;&gt;Mission: Impossible — Fallout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/09/26/office-space.html&#34;&gt;Office Space&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/11/06/howls-moving-castle.html&#34;&gt;Howl’s Moving Castle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There was a period of time where I felt burnt out by scripted TV shows, favouring YouTube over much else. I eventually got back into watching a few “high production value” shows, some of which I enjoyed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/11/23/house-of-the.html&#34;&gt;House of the Dragon: Season 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/12/12/house-of-the.html&#34;&gt;House of the Dragon: Season 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/04/04/andor-series.html&#34;&gt;Andor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Reacher: Season 2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And some that I enjoyed far less:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/04/28/sugar-season.html&#34;&gt;Sugar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2024/06/25/fallout-season.html&#34;&gt;Fallout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;onwards-to-2025&#34;&gt;Onwards To 2025&lt;/h2&gt;
&lt;p&gt;I know it’s cliché to look back on the last year and feel pretty crappy about it. And yeah, not every day of your life is going to be great. They’ve been some rotten days in 2024 that I haven’t included in this review (I have written about them so check out the archive if your curious).&lt;/p&gt;
&lt;p&gt;So, was 2024 a good year? Well, I’ll start by saying that it hasn’t been wholly a remarkable year. There’s no one event that I can point to and say “this is what made 2024 great.” Maybe this past year was like that to others, but such events didn’t happen to me&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. And recency bias has made it difficult for me to say there were more good days then bad (the last few months have been rough).&lt;/p&gt;
&lt;p&gt;But I wouldn’t say 2024 was a bad year. Certainly a busy one, with a lot going on. But on balance, I’d say it’s been one of the better ones.&lt;/p&gt;
&lt;p&gt;So that’s it, year in review for 2024 done. Have a happy new year and onwards to a great 2025.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I don’t do much on New Years Eve, electing to go to bed early. Yet, I usually get awaken at midnight for various reasons: fireworks, messages from people wising me a Happy New Year. Last night was no exception.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Well, that’s not entirely true. Becoming an uncle was an example of such an event. But again, such personal news, is currently outside the scope of this post.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Home Screen Of 2024</title>
      <link>https://lmika.org/2024/12/29/home-screen-of.html</link>
      <pubDate>Sun, 29 Dec 2024 20:51:21 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/12/29/home-screen-of.html</guid>
      <description>&lt;p&gt;It’s just turned 3:00 in the afternoon, and I was alternating between the couch and the computer desk, racking my brain on what to do. With no ongoing projects — a few ideas have been  bouncing around, yet none has grabbed me so far, and I had nothing else in a state where I could just slip on some music or a podcast and work on — and seeing a few others make similar posts on their blogs, I’d figured I talk about my home screens.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-20241229-214649.png&#34; width=&#34;600&#34; height=&#34;414&#34; alt=&#34;A smartphone home screen features various app icons arranged across three panels, set against a blue floral background.&#34;&gt;
&lt;p&gt;I realised that I haven’t actually done this before, mainly because my home screens change very slowly (the background hardly ever). Dramatic changes usually come about when I’m setting up a new phone.&lt;/p&gt;
&lt;p&gt;And yet, I do want to talk a little about the apps I have at the moment, and I did want to make sure I had a record of how the home screens looked. And seeing that I needed to keep myself occupied doing something, now is as good a time as any.&lt;/p&gt;
&lt;h2 id=&#34;screen-one&#34;&gt;Screen One&lt;/h2&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/phone-screenshot-1.png&#34; width=&#34;600&#34; height=&#34;1300&#34; alt=&#34;Auto-generated description: A mobile home screen displays weather information, calendar events, and various app icons against a blurred background of yellow and gray flowers.&#34; class=&#34;block-center&#34;&gt;
&lt;p&gt;This screen contains two widgets — the date and weather widget at the top, and the calendar widget on the right — plus a small collection of apps placed deliberately where they are. The apps I have here are not necessarily the most used (although two of them are) but I like having easy access to them for various reasons.&lt;/p&gt;
&lt;p&gt;Aside from the widgets, the apps I have on this screen — from left to right, top to bottom — are as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Micropub Checkin:&lt;/strong&gt; A silly little Flutter app I used for adding check-ins to &lt;a href=&#34;https://lmika.day/&#34;&gt;lmika.day&lt;/a&gt;. The apps in a bit of a neglected state, but I still use it as I get value from tracking places I’ve been.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strata:&lt;/strong&gt; The note’s app from Micro.blog. This is where I write my short-term notes. I use Google Keep for shopping lists, but everything else goes here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alto:&lt;/strong&gt; A music app I wrote, and the main music app I listen to.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pocket Casts:&lt;/strong&gt; The podcast player app I use. Apart from the web-browser, this and Alto are two of the most used apps I have on my phone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VSReader:&lt;/strong&gt; Another silly little Flutter app. This is a test build for an RSS reader I was working on a couple of months ago. It’s been a while since I’ve opened this, and I probably should just kill it given that I haven’t made any recent changes to it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Wallet:&lt;/strong&gt; Google’s digital wallet (well, at least their current iteration of their digital wallet). I use it mainly for my train ticket but I do have my credit card in there, just in case I walk out without my “real” wallet.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The items in the dock are as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Phone:&lt;/strong&gt; My family and I still use the phone quite frequently so this app has remained in the dock since I set the phone up.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Messages:&lt;/strong&gt; This is Android’s messaging app. Much like the phone, I communicate with family mostly via SMS, and now RCS, messages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Play Store:&lt;/strong&gt; I rarely go to the Play Store, so there’s no real need for this icon to be here. But I haven’t got around to removing it yet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vivaldi:&lt;/strong&gt; My web browser of choice.&lt;/li&gt;
&lt;li&gt;The right most icon changes based on the last used app, which I’m not a huge fan of, as it occasionally changes just as I go to tap it and I launch the wrong app by mistake.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;screen-two&#34;&gt;Screen Two&lt;/h2&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/phone-screenshot-2.png&#34; width=&#34;600&#34; height=&#34;1300&#34; alt=&#34;Auto-generated description: A smartphone screen displays various app icons arranged in a grid over a floral background.&#34; class=&#34;block-center&#34;&gt;
&lt;p&gt;A grab-bag of apps I frequently use. Some of them probably should be on the first screen, but since real-estate is at a bit of a premium I just keep them here, and swipe over when I need them.&lt;/p&gt;
&lt;p&gt;From left to right, top to bottom, the apps on this screen is as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PTV:&lt;/strong&gt; The Victorian public transport app. I usually use it to know the arrival time of the tram I take going home. Also useful for trip planning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plex:&lt;/strong&gt; I generally don’t watch things on my phone, but before I got my Nvidia Shield, I used this Plex app to Chromecast shows to the TV. It was never great at it though, as it sometimes disconnected from the Chromecast session while the video was running, leaving me with no means of stopping it  until I unplugged the Chromecast.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kindle:&lt;/strong&gt; Kept here as I occasionally use it to read books if I’ve read through my RSS feeds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ChatGPT:&lt;/strong&gt; I don’t use ChatGPT on my phone that often, but it does occasionally come in useful when a web-search proves fruitless.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FastMail:&lt;/strong&gt; My email provider of choice. Given how often I use it, this is arguably one of those apps that should be on the first screen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pager Duty:&lt;/strong&gt; The twenty-four hours on-call paging software I had to use for work. I’m no longer on the on-call roster so it’s probably something I can remove.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WhatsApp:&lt;/strong&gt; What I use for messaging friends. I don’t like the fact that I have a Meta app on my phone, but that’s what my friends chose to use so I’m stuck with it (it’s also better than Viber, which is what we used before).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WireGuard:&lt;/strong&gt; Personal VPN, although I’m currently not using WireGuard for anything right now. I like to keep it mainly because I like the logo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discord:&lt;/strong&gt; I’m a member of a few Discord servers, but I use the mobile client mainly to check into the Hemispheric Views Discord.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notion:&lt;/strong&gt; Where I store my “long term” notes, at least for now.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tusky:&lt;/strong&gt; Mastodon client.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Splitwise:&lt;/strong&gt; Group expense management and splitting app. This was useful during our European trip last year, where each of us would take in turn to pay for the group.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SunSmart:&lt;/strong&gt; Used to track the current and forecasted UV index. Useful around this time of year if I’m planning to be outside for an extended period of time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Micro.blog:&lt;/strong&gt; The Micro.blog app, although I occasionally     use the web version too.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1Password:&lt;/strong&gt; My password manager of choice.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Realestate.com:&lt;/strong&gt; Used to browse real-estate, out of curiosity more than anything else.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spotify:&lt;/strong&gt; My “secondary” music app. I don’t use it for anything that I regularly listen to, but it’s occasionally useful for those once-off tracks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Authenticator:&lt;/strong&gt; Where I keep my 2FA codes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Day One:&lt;/strong&gt; Before I moved to a web-based journalling app, I used this Day One client for writing journal entries. It wasn’t perfect: there was always syncing delays to/from the Apple platform instances of Day One. But it was fine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Slack:&lt;/strong&gt; Used mainly for work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Camera:&lt;/strong&gt; I’m not sure why I have this here, since I almost always use the double power-button tap to bring up the camera. I guess I moved it here from screen one and never removed it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;screen-three&#34;&gt;Screen Three&lt;/h2&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/phone-screenshot-3.png&#34; width=&#34;600&#34; height=&#34;1300&#34; alt=&#34;Auto-generated description: A smartphone home screen displays a vibrant wallpaper of yellow flowers and foliage, with apps like Booking, Airalo, and Emirates icons visible at the top.&#34; class=&#34;block-center&#34;&gt;
&lt;p&gt;This is a screen I hardly ever used, as it’s mainly reserved for apps that are useful while travelling. The &lt;strong&gt;Booking.com&lt;/strong&gt; app and &lt;strong&gt;Emirates&lt;/strong&gt; apps I can probably remove: I was using them mainly to track flights and accomodation during my European trip last year.&lt;/p&gt;
&lt;p&gt;The only one worth keeping is &lt;strong&gt;Airalo&lt;/strong&gt;, which allows you to buy and setup data SIMs that work overseas. This has been really useful to me during my last couple of trips, and I hope to keep using it for trips in the future. It doesn’t offer a lot of data, but any data is better than zero data, as my friends — who continued asking to use &lt;em&gt;my&lt;/em&gt; data when we’re out of WiFi range — can attest.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>2024 Song of The Year</title>
      <link>https://lmika.org/2024/12/24/song-of-the-year.html</link>
      <pubDate>Tue, 24 Dec 2024 17:30:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/12/24/song-of-the-year.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s Christmas Eve once again, which means it&amp;rsquo;s time for the Song of The Year for 2024. Looking at the &lt;a href=&#34;https://albumwhale.com/lmika/albums-2024&#34;&gt;new and rediscovered albums&lt;/a&gt; for the year, there are quite a few to choose from.&lt;/p&gt;
&lt;p&gt;The runners up are pretty much all from Lee Resevere, a new artist I&amp;rsquo;ve started listening to, and includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://albumwhale.com/albums/22217&#34;&gt;Should I Run&lt;/a&gt;, by Kristen Martell, arranged by Lee Rosevere&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Miles Wide&lt;/em&gt;, from &lt;a href=&#34;https://albumwhale.com/albums/6914&#34;&gt;Synths Working Overtime&lt;/a&gt;, by Lee Rosevere&lt;/li&gt;
&lt;li&gt;&lt;em&gt;We&amp;rsquo;ve Been Here Before&lt;/em&gt; and &lt;em&gt;Hide Your Heart&lt;/em&gt;, from &lt;a href=&#34;https://albumwhale.com/albums/16841&#34;&gt;Stationary Loops&lt;/a&gt;, by Lee Rosevere&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But there can only be one winner, and this year it&amp;rsquo;s &lt;a href=&#34;https://albumwhale.com/albums/25907&#34;&gt;Oxygene, Pt. 20&lt;/a&gt; by Jean-Michel Jarre. 👏&lt;/p&gt;
&lt;img class=&#34;block-center max-half-width&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/oxygene-trilogy.jpg&#34; width=&#34;300&#34; height=&#34;300&#34; alt=&#34; A globe is depicted with a skull emerging from its surface, set against a blue background with the text &#39;Jean-Michel Jarre Oxygene Trilogy&#39; above it.&#34;&gt;
&lt;p&gt;Oxygene is actually an album in my regular rotation, but I always stopped listening to it after Part 19. The strange organ at the start of Part 20 put me off. But one day this year, feeling a little down, I decided to work my way through it and give it a listen, and after the 30 or so seconds, it turned into quite a lovely piece. A nice contrast to the rest of the disk, and a suitable conclusion to the album itself. I&amp;rsquo;ve even grown to like the organ at the start.&lt;/p&gt;
&lt;p&gt;Honourable Mentions this year include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://albumwhale.com/albums/22907&#34;&gt;Dark Sky Island&lt;/a&gt;, by &lt;em&gt;Enya&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://albumwhale.com/albums/21073&#34;&gt;The Truman Show OST&lt;/a&gt;, by &lt;em&gt;Burkhard Dallwitz&lt;/em&gt; and &lt;em&gt;Philip Glass&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://albumwhale.com/albums/20412&#34;&gt;Before Too Long&lt;/a&gt;, by &lt;em&gt;Paul Kelly&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An actually bumper crop this year in terms of music. Let&amp;rsquo;s hope 2025 is just as good.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>That Which Didn&#39;t Make The Cut</title>
      <link>https://lmika.org/2024/12/20/the-cutting-room.html</link>
      <pubDate>Sat, 21 Dec 2024 08:38:09 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/12/20/the-cutting-room.html</guid>
      <description>&lt;p&gt;I did a bit of a clean-up of my projects folder yesterday, clearing out all the ideas that never made it off the ground. I&amp;rsquo;d figured it&amp;rsquo;d be good to write a few words about each one before erasing them from my hard drive for good.&lt;/p&gt;
&lt;p&gt;I suppose the healthiest thing to do would be to just let them go. But what can I say? Should a time come in the future where I wish to revisit them, it&amp;rsquo;d be better to have something written down than not. It wouldn&amp;rsquo;t be the first time I wished this was so.&lt;/p&gt;
&lt;p&gt;Anyway, here are the ones that were removed today. I don&amp;rsquo;t have dates of when these were made or abandoned, but it&amp;rsquo;s likely somewhere between 2022 and 2024.&lt;/p&gt;
&lt;h2 id=&#34;interlaced&#34;&gt;Interlaced&lt;/h2&gt;
&lt;p&gt;This was an idea for a YouTube client&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; that would&amp;rsquo;ve used YouTube&amp;rsquo;s RSS feeds to track subscriptions. The idea came about during a time when I got frustrated with YouTube&amp;rsquo;s ads. I think it was an election year and I was seeing some distasteful political ads that really turned me off. This would&amp;rsquo;ve been a mobile app, most likely built using Flutter, and possibly with a server component to get this working with Chromecast, although I had no idea how that would work.&lt;/p&gt;
&lt;p&gt;This never got beyond the UI mock-up stage, mainly because the prospect of working on something this large seemed daunting. Probably just as well, as YouTube solved the ads problem for me, with the release of YouTube Premium.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/interlaced-ui-mockups.png&#34; width=&#34;600&#34; height=&#34;528&#34; alt=&#34;Auto-generated description: A smartphone interface mockup displays a channels list with annotations highlighting features like a navigation tab, subscription indicators, filter options, and a Chromecast button.&#34;&gt;
&lt;h2 id=&#34;red-crest&#34;&gt;Red Crest&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.manton.org/2024/12/19/everyone-thinks-they.html&#34;&gt;I thought I could build my own blogging engine&lt;/a&gt; and this is probably the closest I got (well, in recent years). This project began as an alternative frontend for Dave Winer&amp;rsquo;s Drummer, rendering posts that would be saved in OPML. But it eventually grew into something of it&amp;rsquo;s own with the introduction of authoring features.&lt;/p&gt;
&lt;p&gt;I got pretty far on that front, allowing draft posts and possibly even scheduled posts (or at least the mechanics for scheduled posts). One feature I did like was the ability to make private posts. These would be interleaved with the public ones once I logged in, giving me something of a hybrid between a blogging CMS and a private journal. It was also possible to get these posts via a private RSS feed. I haven&amp;rsquo;t really seen a CMS do something quite like this. I know of some that allow posts to be visible to certain cohorts of readers, but nothing for just the blog author.&lt;/p&gt;
&lt;p&gt;In the end, it all got a bit much. I started preparing the screen for uploading and managing media, I decided it wasn&amp;rsquo;t worth the effort. After all, there were so many other blogging CMS&amp;rsquo;s already out there that did 90% of what I wanted.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/out-20241220-155617.png&#34;
     
        alt=&#34;Auto-generated description: A blog page titled Random Posts Of Consciousness contains several brief reflections and thoughts on privacy and writing styles.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The public post list, styled using simple.css, naturally.  I&amp;#39;m not sure how I came up with the name &amp;#39;Random Posts of Consciousness.&amp;#39; Might&amp;#39;ve just been a working title.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/out-20241220-155630.png&#34;
     
        alt=&#34;Auto-generated description: A blog post discusses the positive impact of Google Reader shutting down on RSS services.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Click the hash to go to a single post. Simple enough.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/out-20241220-155242.png&#34;
     
        alt=&#34;Auto-generated description: A blog page features a post about JavaScript&amp;#39;s null and undefined types, along with options for Home and Admin.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  But once you logged in, you get to see all the private posts, plus an option to add a new post straight from the home page.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/out-20241220-155254.png&#34;
     
        alt=&#34;Auto-generated description: A blog titled Random Posts Of Consciousness discusses the differences between `null` and `undefined` types in JavaScript.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Options to edit and delete posts also appear on a single post page.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/out-20241220-155417.png&#34;
     
        alt=&#34;Auto-generated description: A user interface displays a text editing section with a draft about JavaScript handling `null` and `undefined` values, along with buttons for Update and Details.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The post editor. This was a simple text that accepted Markdown. Titles are optional, as they should be.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/out-20241220-155433.png&#34;
     
        alt=&#34;Auto-generated description: A text editor interface on a webpage is visible, showcasing options for publishing or setting content to private.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Click &amp;#39;Details&amp;#39; to edit the publish date (either a past date or the future date to schedule it) and choose whether the post is private or not.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/out-20241220-155445.png&#34;
     
        alt=&#34;Auto-generated description: A webpage interface displays a draft post section with options to edit or delete, alongside buttons for generating a private RSS link and a logout option.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The General admin section, showing Draft posts and proving access to the private RSS feed. I think the drafts table was a mock-up; I can&amp;#39;t remember draft posts being a thing at the time.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/out-20241220-155537.png&#34;
     
        alt=&#34;Auto-generated description: A webpage showcasing an admin panel with draft and scheduled posts, including options to edit, delete, publish, or make a draft.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The Post admin section, showing any draft and scheduled posts. The scheduled post table was definitely a mock-up.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/out-20241220-155552.png&#34;
     
        alt=&#34;Auto-generated description: A web page from a Redcrest Admin panel displays a text message stating Bunch of pretty pictures.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The Media admin section, and the ultimate realisation that I didn&amp;#39;t want to work on this anymore.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;h2 id=&#34;reno&#34;&gt;Reno&lt;/h2&gt;
&lt;p&gt;As in &amp;ldquo;Renovation&amp;rdquo;. Not much to say about this one, other than it being an attempt to make a Pipe Dreams clone. I think I was exploring a Go-based game library and I wanted to build something relatively simple. This didn&amp;rsquo;t really go any further that what you see here.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-20241220-155129.png&#34; width=&#34;600&#34; height=&#34;484&#34; alt=&#34;Auto-generated description: A grid of dark squares is displayed on a computer screen, with one square featuring two horizontal white lines.&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/reno-tileset.png&#34; width=&#34;128&#34; height=&#34;128&#34; class=&#34;block-center&#34; alt=&#34;Auto-generated description: A grid of interconnected circuit-like lines on a dark background.&#34;&gt;
&lt;figcaption&gt;Tileset free for anyone who wants it.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&#34;slog&#34;&gt;SLog&lt;/h2&gt;
&lt;p&gt;Short for &amp;ldquo;Structured Log&amp;rdquo;.  This was a tool for reading JSON log messages, like the ones produce by &lt;a href=&#34;https://pkg.go.dev/github.com/rs/zerolog&#34;&gt;zerolog&lt;/a&gt;. It&amp;rsquo;s always difficult to read these in a regular text editor, and to be able to list them in a table made sense to me. This one was built for the terminal but I did make a few other attempts building something for this; one using a &lt;a href=&#34;https://wails.io&#34;&gt;web-based GUI tool&lt;/a&gt;, and another as a native MacOS app. None of these went very far — turns out there&amp;rsquo;s a lot of tedious code involved — but this version was probably the furthest along before I stopped work.&lt;/p&gt;
&lt;p&gt;Despite appearing on this list, I think I&amp;rsquo;ll keep this one around. The coding might be tedious, but I still have need something like this, and spending the time to build this properly might be worth it one day.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-20241220-155211.png&#34; width=&#34;600&#34; height=&#34;445&#34; alt=&#34;Auto-generated description: A terminal window displays log messages with levels and a table summarizing error, ID, level, message, and time values.&#34;&gt;
&lt;h2 id=&#34;miscellany&#34;&gt;Miscellany&lt;/h2&gt;
&lt;p&gt;Here are all the others that didn&amp;rsquo;t even get to the point that warranted a screenshot or a paragraph of text:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;s3-browse:&lt;/strong&gt; a TUI tool for browsing S3 buckets. This didn&amp;rsquo;t go beyond simply listing the files of a directory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;scorepeer:&lt;/strong&gt; An attempt to make a collection of online score-cards much like the &lt;a href=&#34;https://finska.lmika.app/&#34;&gt;Finska one&lt;/a&gt; I built.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;withenv:&lt;/strong&gt; Preconfigure the environment for a command with the values of an &lt;code&gt;.env&lt;/code&gt; file (there must be something out there that does this already).&lt;/li&gt;
&lt;li&gt;About 3 aborted attempts to make a wiki-style site using Hugo (one called &amp;ldquo;Techknow Space&amp;rdquo; which I though was pretty cleaver).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;m sure there&amp;rsquo;ll be more projects down the line that would receive the same treatment as these, so expect similar posts in the future.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Or possibly a Peertube client.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>A Summer Theme</title>
      <link>https://lmika.org/2024/12/01/a-summer-theme.html</link>
      <pubDate>Sun, 01 Dec 2024 09:12:07 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/12/01/a-summer-theme.html</guid>
      <description>&lt;p&gt;Made a slight tweak to my blog&amp;rsquo;s theme today, to &amp;ldquo;celebrate&amp;rdquo; the start of summer.&lt;/p&gt;
&lt;p&gt;I wanted a colour scheme that felt suitable for the season, which usually means hot, dry conditions. I went with one that uses yellow and brown as the primary colours. I suppose red would&amp;rsquo;ve been a more traditional representation of &amp;ldquo;hot&amp;rdquo;, but yellow felt like a better choice to invoke the sensation of dry vegetation. I did want to make it subtle though: it&amp;rsquo;s easy for a really saturated yellow to be quite garish, especially when used as a background.&lt;/p&gt;
&lt;p&gt;My original idea was to use yellow as the link colour, but there wasn&amp;rsquo;t a good shade that worked well with a white background that had a decent contract&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. So I pivoted, making the background yellow instead, and throwing in a brown for the link colour. That improved the contrast dramatically, and helped to make the theme a little more interesting.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/out-20241201-093302.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Original light theme.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/out-20241201-093316.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Summer light theme.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/out-20241201-093330.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Original dark theme.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/out-20241201-093338.png&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Summer dark theme.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;One thing I did do was make it conditional to the &lt;code&gt;data-theme&lt;/code&gt; attribute in the &lt;code&gt;html&lt;/code&gt; tag, leaving me the option of adding a theme picker in the future. If you&amp;rsquo;re interested in the CSS, here it is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root[data-theme=&amp;quot;summer&amp;quot;] {
    --background: #FFFEF5;
    --link: #895238;
    --link_visited: #895238;
}

@media (prefers-color-scheme: dark) {
    :root[data-theme=&amp;quot;summer&amp;quot;] {
        --text: #f8f8f2;
        --link: #fce98a;
        --link_visited: #fce98a;
        --background: #30302a;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I plan to keep this theme for the next three months, then look at changing it again when summer turns into autumn. It&amp;rsquo;s probably not a great colour scheme, as I generally don&amp;rsquo;t have the patience for making minute adjustments to get the style &amp;ldquo;just right&amp;rdquo;. I guess it follows on from my feeling of the season: I generally don&amp;rsquo;t like summer and I just want to get it over with. Perhaps doing something small like this is a way of enjoying it a little more.&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-summer-theme.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;It was much easier for the dark theme.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Delta of the Defaults 2024</title>
      <link>https://lmika.org/2024/11/26/its-a-little.html</link>
      <pubDate>Tue, 26 Nov 2024 12:38:47 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/11/26/its-a-little.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s a little over a year since &lt;a href=&#34;https://defaults.rknight.me/&#34;&gt;Dual of the Defaults&lt;/a&gt;, and I see that &lt;a href=&#34;https://rknight.me/blog/app-defaults-2024/&#34;&gt;Robb&lt;/a&gt; and &lt;a href=&#34;https://maique.eu/2024/11/25/app-defaults.html&#34;&gt;Maique&lt;/a&gt; are posting their updates for 2024, so I&amp;rsquo;d thought I do the same. There&amp;rsquo;ve only been a few changes since &lt;a href=&#34;https://lmika.org/2023/11/04/defaults.html&#34;&gt;last year&lt;/a&gt;, so much like Robb, I&amp;rsquo;m only posting the delta:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Notes:&lt;/strong&gt; Obsidian for work. Notion for personal use if the note is long-lived. But I&amp;rsquo;ve started using Micro.blog notes and Strata for the more short-term notes I make from day to day.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;To-do:&lt;/strong&gt; To-do&amp;rsquo;s are still going to where my notes go, which is now Strata. Although I&amp;rsquo;m still using Google Keep for shopping lists.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Browser:&lt;/strong&gt; &lt;a href=&#34;https://lmika.org/2024/02/10/safari-what-the.html&#34;&gt;Safari&amp;rsquo;s out&lt;/a&gt; from all machines. It&amp;rsquo;s Vivaldi everywhere now. Except iPad, where I don&amp;rsquo;t have a choice.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Presentations:&lt;/strong&gt; Believe it or not, I haven&amp;rsquo;t had to make a presentation since last year. I am still paying for iA Presenter, and &lt;a href=&#34;https://lmika.org/2023/11/16/a-few-thoughts.html&#34;&gt;despite some thoughts&lt;/a&gt;, I think I&amp;rsquo;ll continue to use it for future presentations. I should also add that I&amp;rsquo;m using iA Writer for prose writing, not that I do much of that either.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Social Clients:&lt;/strong&gt; Still Tusky for now, but I&amp;rsquo;m wondering how long it&amp;rsquo;ll be before I install a BlueSky client too.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Journalling App:&lt;/strong&gt; I didn&amp;rsquo;t include this in last year&amp;rsquo;s list, but it&amp;rsquo;s worth mentioning here as I&amp;rsquo;ve moved away from Day One to a &lt;a href=&#34;https://lmika.org/2024/08/22/i-enjoyed-reading.html&#34;&gt;home grown web-app&lt;/a&gt;, similar to the one built by Kev Quirk.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;POSSE:&lt;/strong&gt; Micro.blog, EchoFeed. Also a new category, now I&amp;rsquo;m doing this a bit more nowadays.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Looking At Coolify</title>
      <link>https://lmika.org/2024/11/23/to-deploy-from.html</link>
      <pubDate>Sat, 23 Nov 2024 15:24:15 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/11/23/to-deploy-from.html</guid>
      <description>&lt;p&gt;While reading Robb Knight&amp;rsquo;s post about &lt;a href=&#34;https://rknight.me/blog/installing-gotosocial-on-coolify/&#34;&gt;setting up GoToSocial in Coolify&lt;/a&gt;, I got curious as to what this Coolify project actually is. I&amp;rsquo;m a happy user of &lt;a href=&#34;https://dokku.com/&#34;&gt;Dokku&lt;/a&gt;, but being one with magpie tendencies, plus always on the lookout for ways to make the little apps I make for myself easier to deploy, I thought I&amp;rsquo;d check it out.&lt;/p&gt;
&lt;p&gt;So I spun up a Coolify instance on a new Hetzner server this morning and tried deploying a simple Go app, complete with automatic deployments when I push changes to a Forgejo repository. And yeah, I must say it works pretty well. I haven&amp;rsquo;t done anything super sophisticated, such as setting up a database or anything. But it&amp;rsquo;s almost as easy as deploying something with Dokku, and I&amp;rsquo;m please that I was able get it working with &lt;a href=&#34;https://lmika.org/2024/07/09/a-tour-of.html&#34;&gt;my Forgejo setup&lt;/a&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Anyway, this post is just a few things I want to make a note about for next time I want to setup a Coolify instance. It&amp;rsquo;s far from a comprehensive set-up guide: there&amp;rsquo;s &lt;a href=&#34;https://coolify.io/docs/&#34;&gt;plenty of documentation&lt;/a&gt; on the project website. But here are a few things I&amp;rsquo;d like to remember.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Changing the Proxy to Caddy:&lt;/strong&gt; Soon after setting up your Coolify instance, you probably want to change the proxy to Caddy, just so that you can easily get Lets Encrypt certificates. Do this before you setup a domain as you&amp;rsquo;ll need direct access to Coolify via the port.&lt;/p&gt;
&lt;p&gt;Go to &amp;ldquo;Servers → localhost&amp;rdquo; and in the &amp;ldquo;Proxy&amp;rdquo; tab, stop the current proxy. You then have the option of changing it to Caddy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Setting Up a Domain For Coolify Itself:&lt;/strong&gt; Once you&amp;rsquo;ve change the proxy, you&amp;rsquo;d probably want to setup a domain so as to avoid accessing it via IP address and port number. You can do so by going to &amp;ldquo;Settings,&amp;rdquo; and within &amp;ldquo;Instance Settings&amp;rdquo; changing &amp;ldquo;Domain&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;If you prepend your domain with &lt;code&gt;https&lt;/code&gt;, a certificate will be setup for you. I&amp;rsquo;m guessing it&amp;rsquo;s using Lets Encrypt for this, which is fine. I&amp;rsquo;d probably do likewise if I had to set it up manually.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deploying From a Private Forgejo Repository:&lt;/strong&gt; To deploy from a private Forgejo repository, follow the &lt;a href=&#34;https://coolify.io/docs/knowledge-base/git/gitea/integration/&#34;&gt;Gitea integration instructions&lt;/a&gt; on setting up a private key. This is basically creating a new key in &amp;ldquo;Keys And Tokens&amp;rdquo;, and adding it as a key in Forgejo.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-20241123-160059.png&#34; width=&#34;600&#34; height=&#34;646&#34; alt=&#34;The Add Key modal showing options to generate an RSA or elliptical curve key&#34;&gt;
&lt;figcaption&gt;The Add Key modal&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;As far as I&amp;rsquo;m aware, it&amp;rsquo;s not possible to change an application source from a public Git repo to a private one. I tried that and I got a few deploy errors, most likely because I didn&amp;rsquo;t set the key. I had to delete it and start from scratch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Setting a Domain For a Project:&lt;/strong&gt; Setting up a domain is pretty simple: just add a new A record pointing to the IP address of the service the application is running on. Much like the Coolify domain, prefacing your domain with &lt;code&gt;https&lt;/code&gt; will provision a TLS certificate for you (&lt;a href=&#34;https://coolify.io/docs/applications/#force-https&#34;&gt;docs&lt;/a&gt;):&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-20241123-160248.png&#34; width=&#34;493&#34; height=&#34;364&#34; alt=&#34;The Domain settings for the deployable project resource&#34;&gt;
&lt;figcaption&gt;The Domain settings for the deployable project resource&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Unlike Dokku, your app doesn&amp;rsquo;t need to support the &lt;code&gt;PORT&lt;/code&gt; environment variable. You should be able to start listening on a port and simply setup a mapping in the project page. The default seems to be port 3000, just in case you&amp;rsquo;re not interested in changing it:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automatic Deployments From Forgejo:&lt;/strong&gt; Coolio looks to have some nice integrations with Github, but that doesn&amp;rsquo;t help me and my use of Forgejo. So the setup is a little more manual: adding some web-hook to automatically deploy when pushing commits to Forgejo. In Coolify, you&amp;rsquo;d want to use the Gittea web-hook:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-20241123-160606.png&#34; width=&#34;600&#34; height=&#34;384&#34; alt=&#34;The web-hook settings for the deployable project resource, with the Gittea web-hook highlight&#34;&gt;
&lt;figcaption&gt;The Gittea web-hook is the one to use&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You&amp;rsquo;ll need to generate the web-hook secret yourself. Running &lt;code&gt;head -c 64 /dev/urandom | base64&lt;/code&gt; or similar should give you something somewhat secure.&lt;/p&gt;
&lt;p&gt;Setting up the web-hook on Forgejo&amp;rsquo;s side was a little confusing. Clicking &amp;ldquo;Add Webhook&amp;rdquo; just brought up a list of integrations, which I&amp;rsquo;m guessing are geared towards particular form of web-hook payloads. You want to select the &amp;ldquo;Forgejo&amp;rdquo; one.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-20241123-160735.png&#34; width=&#34;600&#34; height=&#34;348&#34; alt=&#34;Project web-hooks in Forgejo, with the Gittea domain from Coolify set as the target URL, the secret set, and everything else left as the default&#34;&gt;
&lt;figcaption&gt;How the web-hook looks on Forgejo&#39;s side&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Use the URL that Coolify is showing for the Gittea web-hook, leave the method as &amp;ldquo;POST&amp;rdquo; and set the secret you generated. The rest you can configure based on your preferences.&lt;/p&gt;
&lt;p&gt;So that it. So far I&amp;rsquo;m liking it quite a bit, and I look forward to going a bit further than simple Go apps that serve a static message (some of the pre-canned applications look interesting). I&amp;rsquo;d like to try it for a bit longer before I consider it as a replacement for Dokku, but I suspect that may eventually happen.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-20241123-162029.png&#34; width=&#34;600&#34; height=&#34;472&#34; alt=&#34;A screenshot of a browser window with a plain text message saying: &#39;Hello World. This is deployed via Coolify via a private repo that is auto-deployed.&#39;&#34;&gt;
&lt;figcaption&gt;Hello Coolify&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I say &amp;ldquo;it&amp;rsquo;s almost as easy&amp;rdquo; as Dokku, but one thing going for Coolify is that I don&amp;rsquo;t need to SSH into a Linux box to do things. When it comes to creating and operating these apps, doing it from a dashboard is a nicer experience.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Cropping A &#34;Horizontal&#34; PocketCast Clip To An Actual Horizontal Video</title>
      <link>https://lmika.org/2024/11/11/oof-finally-fixed.html</link>
      <pubDate>Mon, 11 Nov 2024 21:40:30 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/11/11/oof-finally-fixed.html</guid>
      <description>&lt;p&gt;Finally fixed the issue I was having with my ffmpeg incantation to crop a PocketCast clip. When I was uploading the clip to Micro.blog, the video wasn&amp;rsquo;t showing up. The audio was fine, but all I got for the visuals was a blank void&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;For those that are unaware, clips from PocketCast are always generated as vertical videos. You can change how the artwork is presented between vertical, horizontal, or square; but that doesn’t change the dimensions of the video itself. It just centers it in a vertical video geared towards TikTok, or whatever the equivalent clones are.&lt;/p&gt;
&lt;p&gt;This, I did not care for. So I wanted to find a way to crop the videos to dimensions I find more reasonable (read: horizontal).&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the ffmpeg command I&amp;rsquo;m using to do so. This takes a video of the &amp;ldquo;horizontal&amp;rdquo; PocketCast clip type and basically does a crop at the centre to produce a video with the 16:9 aspect ratio. &lt;a href=&#34;https://lmika.org/2024/11/06/just-to-add.html&#34;&gt;This post&lt;/a&gt; shows how the cropped video turns out.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ffmpeg -i &amp;lt;in-file&amp;gt; \
  -vf &amp;#34;crop=iw:iw*9/16:(iw-ow)/2:(ih-oh)/2, scale=640:360&amp;#34; \
  -vcodec libx264 -c:a copy &amp;lt;out-file&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Anyway, back to the issue I was having. I suspect the cause was that the crop was producing a video with an uneven width. When I upload the cropped video to Micro.blog, I&amp;rsquo;ve saw in the logs that Micro.blog was downscaling video to a height of 360. This was using a version of the command that didn’t have the &lt;code&gt;scale&lt;/code&gt; filter, and the original clip was 1920 x 1080. If you downscale it while maintaining the original 15:9 aspect ratio, the new dimensions should be 640 x 360. But for some reason, the actual width of the cropped video was 639 instead.&lt;/p&gt;
&lt;p&gt;I’m not sure if this was the actual problem. I had no trouble playing the odd-width video in QuickTime. The only hint I had that this might be a problem was when I tried downscaling in ffmpeg myself, and ffmpeg threw up an error complaining that the width was not divisible by two. After forcing the video size to 640 x 360, and uploading it to Micro.blog, the video started coming through again. So there might be something there.&lt;/p&gt;
&lt;p&gt;Anyway, it&amp;rsquo;s working now. And with everything involving ffmpeg, once you get something working, you never touch it again. 😄&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Not that there&amp;rsquo;s much to see. It&amp;rsquo;s just the podcast artwork. Not even a rendered scrubber.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>WeblogPoMo AMA #3: Best Music Experience</title>
      <link>https://lmika.org/2024/11/04/im-on-a.html</link>
      <pubDate>Mon, 04 Nov 2024 06:49:46 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/11/04/im-on-a.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m on a roll with these, but I must warn you, this streak may end at any time. Anyway, todays question is from &lt;a href=&#34;https://social.lol/@hiro/113408214376231178&#34;&gt;Hiro&lt;/a&gt; who asked it to &lt;a href=&#34;https://gabz.blog/posts/the-best-music-related-experience&#34;&gt;Gabz&lt;/a&gt;, and discovered via &lt;a href=&#34;https://rknight.me/blog/weblogpomo-ama/&#34;&gt;Robb&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;Despite attending only a hand-full of concerts in my life — live music is not really my jam — I&amp;rsquo;ve had some pretty wonderful music-related experiences in my life, both through listing to it or by performing it. Probably my most memorial experience was playing in the pit orchestra for our Year 10 production of Pippin. This was during the last few weeks before the show opened and we attended a music camp for a weekend to do full day rehearsals with the music director. The director had a reputation of being a bit of a hard man, prone to getting a bit angry, and not afraid to raise his voice. It was intimidating to me at the time, but in hindsight I can appreciate that he was trying to get the best from us. And with us being a group of teenage boys who were prone to loosing focus, I think we were deserving of his wrath.&lt;/p&gt;
&lt;p&gt;One evening, we were rehearsing late, and the director was spending a lot of time going through some aspect of the music. I can&amp;rsquo;t remember what was being discussed but it was one of those times where everyone was tired, yet each knew what they were meant to be doing and was still happy to be working. You feel something special during those moments, when the group was doing their best, not out of coercion but because we were trying to &amp;ldquo;get the work done&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Probably a very close second was discovering Mike Oldfield for the first time. This was probably when I was 11 or 12, and I wasn&amp;rsquo;t a bit music listener back then (I mean, we did have a home stereo but I wasn&amp;rsquo;t listening to a walkman or anything like that). Dad was working one night and I came up to him. He then started playing track 1 of &lt;a href=&#34;https://albumwhale.com/albums/16049&#34;&gt;Tubular Bells II&lt;/a&gt;, thinking that I would appreciate it. I was more intrigued at first, as it wasn&amp;rsquo;t the type of music I was use to at the time: long, instrumental pieces. Yet I found it to be decent, and something I could see myself liking the future&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. He then played track 7, and I was absolutely hooked after that.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;In my experience, the tracks that take some time to grow to like turn out to be the best ones to listen to.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>WeblogPoMo AMA #2: One Thing I Wish I Could Change About Myself</title>
      <link>https://lmika.org/2024/11/03/heres-my-answer.html</link>
      <pubDate>Sun, 03 Nov 2024 09:26:05 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/11/03/heres-my-answer.html</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s my answer to another question asked by Annie for &lt;a href=&#34;https://social.lol/tags/WeblogPoMoAMA&#34;&gt;WebogPoMoAMA&lt;/a&gt;. This was previously answered by &lt;a href=&#34;https://gkeenan.co/avgb/i-remember-every-mean-thing-anyone-ever-said-to-me/&#34;&gt;Keenan&lt;/a&gt;, &lt;a href=&#34;https://blog.estebantxo.com/2024/11/01/if-you-could.html&#34;&gt;Estebanxto&lt;/a&gt;, &lt;a href=&#34;https://krueger.ink/when-perfectionism-hurts/&#34;&gt;Kerri Ann&lt;/a&gt;, and &lt;a href=&#34;https://louplummer.lol/please-make-it-easier/&#34;&gt;Lou Plummer&lt;/a&gt;:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;If you could instantly change one internal pattern/thing about yourself, what would it be?&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;My answer is that I wish I found it easier meeting new people. Not only am I quite introverted, I&amp;rsquo;m also really shy, and I find it extremely hard to introduce myself to new people in social situations. That is, if I ever find myself going to these social situations. I rarely do, and if I do attend, I usually stay quietly to the side, keeping with company that I know. It was at one time bad enough that I&amp;rsquo;d find excuses to avoid going out to see those I &lt;em&gt;do&lt;/em&gt; know.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m trying to get better at this. For starters, I&amp;rsquo;m no longer staying away from friends, and I am trying to make the effort of going to more social events as they come. It&amp;rsquo;s still not great though, and I do struggle when being around a group of strangers. I guess the secret is just practice, and maybe trying to make a game of it: setting goals like saying hello to at least one new person every hour or so. I don&amp;rsquo;t think I&amp;rsquo;ll ever get over my shyness, but I&amp;rsquo;m hoping I can find away to at least manage it a little better than I have been.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Phaedra, The lmika Track Arrangement</title>
      <link>https://lmika.org/2024/10/31/phaedra-the-lmika.html</link>
      <pubDate>Thu, 31 Oct 2024 20:06:14 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/10/31/phaedra-the-lmika.html</guid>
      <description>&lt;p&gt;I recently learnt that the version of &lt;a href=&#34;https://albumwhale.com/albums/23166&#34;&gt;Phaedra&lt;/a&gt; I&amp;rsquo;ve been listening to for the past 15 years had not only the wrong track order, but also the wrong track names. This is not entirely surprising, given how this version was… ah, acquired.&lt;/p&gt;
&lt;p&gt;But after learning what the order and names should&amp;rsquo;ve been, I think I still prefer my version. And yes, that&amp;rsquo;s probably because I&amp;rsquo;m use to it, but if the official album were to have these names and this order, I think it would actually work really way. I may go so far as to say that if I got a copy of the official album, I&amp;rsquo;d probably change it to match the version I been listening to.&lt;/p&gt;
&lt;p&gt;In case your curious, here&amp;rsquo;s how the tracks are named in my version:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Official Version&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;lmika Version&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Phaedra&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Mysterious Semblance At The Strand Of Nightmares&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Mysterious Semblance At The Strand Of Nightmares&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Phaedra&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Movements Of A Visionary&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Sequent &amp;lsquo;C&amp;rsquo;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Sequent &amp;lsquo;C&amp;rsquo;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Movements Of A Visionary&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I&amp;rsquo;m actually a little surprised that my version of &lt;em&gt;Sequent &amp;lsquo;C&amp;rsquo;&lt;/em&gt; is officially called &lt;em&gt;Movements Of A Visionary&lt;/em&gt; and visa-versa. The name &lt;em&gt;Movements Of A Visionary&lt;/em&gt; gives it a more mysterious feeling, which fits well with the small, soft, reverb-filled piece of music that it is. As for the track with has that name officially… well I just assumed the name &lt;em&gt;Sequent &amp;lsquo;C&amp;rsquo;&lt;/em&gt; made the most logical sense for a piece of music with a sequencer in the key of C. I don&amp;rsquo;t have an explanation for &lt;em&gt;Phaedra&lt;/em&gt; or &lt;em&gt;Semblance&lt;/em&gt; other than &amp;ldquo;long piece == long title,&amp;rdquo; but &lt;em&gt;Phaedra&lt;/em&gt; just feels like a title that fits better for a piece of music that predominantly features a mellotron.&lt;/p&gt;
&lt;p&gt;The tracks in the version I listen too are arrange in the following order:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th style=&#34;width:50px&#34;&gt;No.&lt;/th&gt;
    &lt;th&gt;Official Version Name&lt;/th&gt;
    &lt;th&gt;lmika Version Name&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
  &lt;tr&gt;
    &lt;td&gt;1.&lt;/td&gt;
    &lt;td&gt;Sequent &#39;C&#39;&lt;/td&gt;
    &lt;td&gt;Movements Of A Visionary&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;2.&lt;/td&gt;
    &lt;td&gt;Phaedra&lt;/td&gt;
    &lt;td&gt;Mysterious Semblance At The Strand Of Nightmares&lt;/td&gt;
  &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;3.&lt;/td&gt;
    &lt;td&gt;Mysterious Semblance At The Strand Of Nightmares&lt;/td&gt;
    &lt;td&gt;Phaedra&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;4.&lt;/td&gt;
    &lt;td&gt;Movements Of A Visionary&lt;/td&gt;
    &lt;td&gt;Sequent &#39;C&#39;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The fact that &lt;em&gt;Phaedra&lt;/em&gt; is the first track in the official version make sense, given that on vinyl it would&amp;rsquo;ve taken up an entire side, but I reckon starting the album with a small, soft piece — acting almost like a prelude — whets the appetite for the heavier stuff. This would be track two, which is 17 minutes long, and is quite dynamic in it&amp;rsquo;s contract across the piece. You then climb down from that into what I thought was the title track which — given that it appears as the third one in my version — gives the artists an opportunity to have a something simpler to act as the centrepiece of the album. Then you end with a relatively lively piece with a driving sequencer, that finishes with a decisive C(7) chord, making it clear that the album is now over.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s how I&amp;rsquo;d name and arrange the tracks in this album. I don&amp;rsquo;t want to say that Tangerine Dream got it wrong but… they did get it pretty wrong. 😀&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-phaedra.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;&lt;/audio&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>My Favourite Watch</title>
      <link>https://lmika.org/2024/10/12/my-favourite-watch.html</link>
      <pubDate>Sat, 12 Oct 2024 09:12:51 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/10/12/my-favourite-watch.html</guid>
      <description>&lt;p&gt;Seeing all the nostalgia for digital watches of the &amp;rsquo;90s and early 2000s, following the release of &lt;a href=&#34;https://www.theverge.com/2024/10/9/24265964/casio-digital-watch-desk-a158w-clock-japan-retro-vintage&#34;&gt;retroest desk clock shaped like a large Casio digital watch&lt;/a&gt;, it got me thinking of the watches I owned growing up. I started off as a Casio person but I eventually moved on to Timex watches. I was pretty happy with all the watches I owned, but my favourite was the &lt;a href=&#34;https://en.wikipedia.org/wiki/Timex_Datalink#Timex_Datalink_USB&#34;&gt;Timex Datalink USB Sports Edition&lt;/a&gt;, which stood head and shoulders about the rest.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/timex-datalink-usb.jpg&#34; width=&#34;312&#34; height=&#34;500&#34; alt=&#34;Auto generated description:  A Timex Ironman digital watch with a black strap displays the time as 3:41 and is water-resistant up to 100 metres&#34; class=&#34;block-center x-tiny-img&#34;&gt;
&lt;figcaption&gt;Source: &lt;a href=&#34;https://sites.google.com/site/hamonoaneraea1144/timex-men-s-t53722-ironman-data-link-usb-watch&#34;&gt;Hamonoaneraea&lt;/a&gt; (site no longer online)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Not only was this watch featureful out of the box — having the usual stopwatch, timers, and alarms — it was also reprogrammable. There was some Windows software that allowed you to install new modes and arrange them in the mode menu. I remember a few of these, such as a mode allowing you to browse data arranged in a tree; a simple note taking mode; and a horizontal game of Tetris.&lt;/p&gt;
&lt;p&gt;There was also an SDK, allowing you to build new modes in assembly. I remember building a score keeping mode, where you could track points for a game between two or four competitors, with an optional auxiliary counter used to keep track of things like fouls. I also remember building a dice rolling mode, allowing you to roll up to 6 dice, with each dice having between 2 to 9 sides, and the total automatically being displayed to you.&lt;/p&gt;
&lt;p&gt;I never used these modes for anything — I&amp;rsquo;m neither sporty nor much of a gamer to have any real need for tracking scores or rolling multiple dice — but they were super fun to build, and I got a fair bit of experience learning assembly from it. And the SDK was pretty well built, with predefined entry points for the mode, reacting to events like button presses, and displaying things on the LCD. The fact that the SDK came with a random-number generator, which wasn&amp;rsquo;t even used with any of the built-in modes, just showed how well Timex thought about what was possible with this watch.&lt;/p&gt;
&lt;p&gt;This was the last watch I regularly wore: I&amp;rsquo;ve moved to using phones to keep track of time. But it was a great watch while it lasted.&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-favourite-watch.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;&lt;/audio&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Why I Keep Multiple Blogs</title>
      <link>https://lmika.org/2024/10/12/why-i-keep.html</link>
      <pubDate>Sat, 12 Oct 2024 08:09:35 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/10/12/why-i-keep.html</guid>
      <description>&lt;p&gt;Kev Quirk &lt;a href=&#34;https://kevquirk.com/blog/why-have-multiple-blogs&#34;&gt;wrote a post yesterday&lt;/a&gt; wondering why people have multiple blogs for different topics:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;A few people I follow have multiple blogs that they use for various topics, but I don&amp;rsquo;t really understand why. […] I personally prefer to have a single place where I can get all your stuff. If you&amp;rsquo;re posting about something I&amp;rsquo;m not interested in, I&amp;rsquo;ll just skip over it in my RSS feed. I don&amp;rsquo;t have to read everything in my feed reader.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I’ve &lt;a href=&#34;https://lmika.org/2024/04/16/yeah-honestly-i.html&#34;&gt;written about this before&lt;/a&gt;, and after taking a quick look at that post, most of those reasons still stand. So if you’ve read that post, you can probably stop reading this one at reason number two (unless you’re listening to the audio narration of this, in which case, please listen on as that last post predated that feature 🙂).&lt;/p&gt;
&lt;p&gt;I’m currently keeping four separate blogs: this one, one for &lt;a href=&#34;https://lmika.day/&#34;&gt;checkins to places I’ve visited&lt;/a&gt;, one for &lt;a href=&#34;https://til.computer&#34;&gt;remembering how to do something for work&lt;/a&gt;, and &lt;a href=&#34;https://workpad.dev/&#34;&gt;one for projects I’m working on&lt;/a&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. This arrangement came about after a few years of spinning out and combining topics to and from a single blog, generally following the tension I felt after publishing something, wondering if that was the right place for it. As strange as it is to say it, this multi-blogging arrangement gives me the lowest amount of tension for writing online.&lt;/p&gt;
&lt;p&gt;There are a few reasons for this. First is that for certain topics, I like an easy way to reference posts quickly. This is the primary reason why I keep that work-related reference blog, so that when I&amp;rsquo;m faced with a software-related problem I know I&amp;rsquo;ve seen in the past, I can quickly lookup how I solved it. I’ve tried keeping those posts here, but it was always difficult finding them again amongst all the frequent, day-to-day stuff.&lt;/p&gt;
&lt;p&gt;It mainly comes down to the online reading experience. Categories can only do so much, and that’s if I’m categorising posts rigorously, which is not always the case. Here, the posts are displayed in full, encouraging the reader to browse. But for my reference blog, a list of bare links works better for going directly to the post I need.&lt;/p&gt;
&lt;p&gt;The second reason is the writing experience. For me, certain CMSes work better for certain types of posts. Micro.blog works well for micro-posts or mid-sized posts like this one, but for longer ones, I prefer the editors of either Scribbles or Pika. I don’t know why this is. Might be because all the code-blocks I tend to use on those blogs are easier to write using a WYSIWYG editor rather than Markdown.&lt;/p&gt;
&lt;p&gt;And finally, it’s a good excuse to try out multiple CMSes. I have no rational explanation for this one: it’s an arrangement that costs me more money and requires learning new software. Might be that I just like the variety.&lt;/p&gt;
&lt;p&gt;So that’s why I keep multiple blogs. I do recognise that it does make it harder for others to find my writing online, not to mention following along using RSS. But that’s a tradeoff I’m willing to make for a writing and reading arrangement that works better for me. Of course, like I said in my previous post, this might change in due course.&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/nartation-multiple-blogs.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Actually, I have a fifth blog which is for projects I’m working on that I rather keep private. Oh, and a sixth, which is a travel blog that I really should maintain better. Might be that I have a few too many blogs.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>On Panic, iA, and Google Drive</title>
      <link>https://lmika.org/2024/10/09/i-see-that.html</link>
      <pubDate>Wed, 09 Oct 2024 12:16:44 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/10/09/i-see-that.html</guid>
      <description>&lt;p&gt;I see that &lt;a href=&#34;https://blog.panic.com/end-of-the-road-for-google-drive-and-transmit/&#34;&gt;Panic&lt;/a&gt; is shutting down their Google Drive integration in their Android app, much like &lt;a href=&#34;https://ia.net/topics/our-android-app-is-frozen-in-carbonite&#34;&gt;iA did&lt;/a&gt; a few weeks ago. This doesn&amp;rsquo;t affect me directly: even though I am a user of both Android and Google Drive, I regret to say that I don&amp;rsquo;t use apps from either company on my phone (I do use a few things from both on my Apple devices).&lt;/p&gt;
&lt;p&gt;But I do wonder why Google is enacting policies that push developers away from using Drive as general purpose user storage. That&amp;rsquo;s what Drive was meant to be used for, no? Does Google not think that by adding these security conditions, and not getting back to developers trying to satisfy them, is maybe pushing the scale between security and usefulness a bit too far out of balance? Are they thinking through the implication of any of this at all?&lt;/p&gt;
&lt;p&gt;If you were to ask me, my guess would probably be that no, they&amp;rsquo;re not thinking about it. In fact, I get the sense that they&amp;rsquo;re making these decisions unconsciously, at least at an organisation level. Probably someone said to the Drive devision that they need to &amp;ldquo;improve security&amp;rdquo; and that their performance will be measured against them doing so. So they drafted up these conditions and said &amp;ldquo;job done&amp;rdquo; without thinking through how it may affect the actual usefulness of Drive.&lt;/p&gt;
&lt;p&gt;And it just reveals to me how large Google is, possibly too large to know why they do anything at all. It&amp;rsquo;s not like they&amp;rsquo;re being malicious or anything: they&amp;rsquo;re a victim of their own success, with way too many product lines making zero dollars that distract them from their raison d&amp;rsquo;être, which is getting that sweet, sweet ad money. After-all, what does Drive matter to Google in terms of increasing advertising revenue? It&amp;rsquo;s probably a division making a loss more than anything else.&lt;/p&gt;
&lt;p&gt;I suppose, given that I do use both Drive and Android, that I should care more about it. And yeah, I care enough to write about it, but that&amp;rsquo;s barely above the level of mild curiosity I&amp;rsquo;m feeling as to why Google is letting this happen. Might be that I&amp;rsquo;ve just gotten numb to Google not caring about their own products themselves.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Passing</title>
      <link>https://lmika.org/2024/10/03/passing.html</link>
      <pubDate>Thu, 03 Oct 2024 09:32:49 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/10/03/passing.html</guid>
      <description>&lt;p&gt;Three nights ago, and two months before her 94th birthday, my Nonna, my maternal grandmother, suffered a stroke. She&amp;rsquo;s now in palliative care and there&amp;rsquo;s no telling how much longer she has left. Over the last few years she was slowing down, yet was still quite aware and was able to do many things on her own, even travel to the shops by bus. She had a scare over the weekend but was otherwise in reasonably good health. So all of this is incredibly sudden.&lt;/p&gt;
&lt;p&gt;I was unsure as to whether or not I wanted to actually write this post. I did have a draft planned yesterday, with the assumption that she wouldn&amp;rsquo;t make it through the night. Delaying it any further did not seem right. Neither is making this an eulogy or display of public grief — that&amp;rsquo;s not how I like to do thing. But to not acknowledge that any of this is happening felt just as wrong, at least for now.&lt;/p&gt;
&lt;p&gt;But what seemed right was a public declaration that I love her and I&amp;rsquo;ll miss her. I consider myself lucky to have said that to her in person, while she was lucid.&lt;/p&gt;
&lt;p&gt;So, what now? Timelines at this stage are uncertain. Would it be hours? Days? Who can say. I guess following that would be the funeral and other matters pertaining to the estate, but that won&amp;rsquo;t happen for a week or so. What about today? Does one simply go about their day as one normally would? Does life go on? Seems wrong that it should be so, yet I&amp;rsquo;m not sure there&amp;rsquo;s anything else that I&amp;rsquo;m capable of doing. Just the daily routine smeared with sadness and loss.&lt;/p&gt;
&lt;p&gt;I heard someone say that grief comes from love, that you can&amp;rsquo;t have one without the other. I can attest to that, but the edges of that double-edge sword are razor sharp. I know that eventually the pain will dull, and all that would remain are the memories. All it takes is time.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Tools And Libraries I Use For Building Web-Apps In Go</title>
      <link>https://lmika.org/2024/09/28/tools-and-libraries.html</link>
      <pubDate>Sat, 28 Sep 2024 13:45:21 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/09/28/tools-and-libraries.html</guid>
      <description>&lt;p&gt;I think I&amp;rsquo;ve settled on a goto set of tools and libraries for building web-apps in Go. It used to be that I would turn to Buffalo for these sorts of projects, which is sort of a &amp;ldquo;Ruby on Rails but for Go&amp;rdquo; type of web framework. But I get the sense that Buffalo is no longer being maintained. And although it was easy to get a project up and running, it was a little difficult to go beyond the CRUD-like layouts that it would generate (or it didn&amp;rsquo;t motivate me enough to do so). Plus, all that JavaScript bundling… ugh!&lt;/p&gt;
&lt;p&gt;Huge pain to upgrade any of that.Since I&amp;rsquo;ve moved away from Buffalo, I&amp;rsquo;m now left to do more of the work up-front, but I think it helps me to be a little more deliberate in how I build something. And after getting burned with Buffalo shutting down, I think it&amp;rsquo;s was time to consider a mix of tools and libraries that would give me the greatest level of code stability while still being relatively quick to get something up and running.&lt;/p&gt;
&lt;p&gt;So, here&amp;rsquo;s my goto list of tools and libraries for building web-apps in Go.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HTTP Routing:&lt;/strong&gt; For this, I use &lt;a href=&#34;https://docs.gofiber.io&#34;&gt;Fiber&lt;/a&gt;. I suppose using Go&amp;rsquo;s builtin HTTP router is probably the best approach, but I do like the utility Fiber gives for doing a lot of the things that go beyond what the standard library provides, such as session management and template rendering. Speaking of…&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server Side Templating:&lt;/strong&gt; Nothing fancy here. I just use &lt;a href=&#34;https://pkg.go.dev/html/template&#34;&gt;Go&amp;rsquo;s template engine&lt;/a&gt; via &lt;a href=&#34;https://docs.gofiber.io/template/html/&#34;&gt;Fiber&amp;rsquo;s Render integration&lt;/a&gt;. It has pretty much all I need, so I don&amp;rsquo;t really look at anything else.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Database:&lt;/strong&gt; If I need one, then I&amp;rsquo;ll first take a look at &lt;a href=&#34;https://www.sqlite.org/index.html&#34;&gt;Sqlite&lt;/a&gt;. I use the &lt;a href=&#34;https://pkg.go.dev/modernc.org/sqlite&#34;&gt;modernc.org&lt;/a&gt; Sqlite driver, as it doesn&amp;rsquo;t require CGo, making deployments easier (more on that later).  If I need something a bit larger, I tend to go with &lt;a href=&#34;https://postgresql.org/&#34;&gt;PostgreSQL&lt;/a&gt; using the &lt;a href=&#34;https://pkg.go.dev/github.com/jackc/pgx/v5&#34;&gt;pgx&lt;/a&gt; driver. I would also like to use &lt;a href=&#34;https://github.com/asdine/storm&#34;&gt;StormDB&lt;/a&gt; if I could, but it doesn&amp;rsquo;t play well with how I like to deploy things, so I tend to avoid that nowadays.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Database ORM:&lt;/strong&gt; I don&amp;rsquo;t really use an ORM (too much PTSD from using the various Java ORMs), but I do use &lt;a href=&#34;https://sqlc.dev&#34;&gt;sqlc&lt;/a&gt; to generate the Go code that interacts with the database. It&amp;rsquo;s not perfect, and it does require some glue code which is tedious to write. But what it does it does really well, and it&amp;rsquo;s better than writing all that SQL marshalling code from scratch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Database Migration:&lt;/strong&gt; I&amp;rsquo;ve tried using &lt;a href=&#34;https://github.com/golang-migrate/migrate&#34;&gt;golang-migrate&lt;/a&gt; before, and we do use it at work for PostgreSQL databases, but it doesn&amp;rsquo;t work well with the modernc.org Sqlite driver. So I ended up &lt;a href=&#34;https://lmika.dev/pkg/litemigrate&#34;&gt;writing my own&lt;/a&gt;. But if it makes sense to use golang-migrate, I will.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JavaScript:&lt;/strong&gt; I try to keep my JavaScript usage to a minimum, favouring vanilla JavaScript if I only need a few things. For anything else, I usually turn to &lt;a href=&#34;stimulus.hotwired.dev/&#34;&gt;Stimulus.js&lt;/a&gt;, which adds just enough &amp;ldquo;magic&amp;rdquo; for the slightly more involved pieces of front-end logic. I&amp;rsquo;m also looking at &lt;a href=&#34;https://htmx.org/&#34;&gt;HTMX&lt;/a&gt;, and have tried it for a few things, but I&amp;rsquo;ve yet to use it for a complete project. I use &lt;a href=&#34;https://esbuild.github.io&#34;&gt;esbuild&lt;/a&gt; if I need to bundle my JavaScript, but I&amp;rsquo;m trying to go &amp;ldquo;builderless&amp;rdquo; for most things nowadays, relying on &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap&#34;&gt;import maps&lt;/a&gt; and just serving the JavaScript as is.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CSS:&lt;/strong&gt; Much like JavaScript, I still prefer to use vanilla CSS served directly for most things. I tend to start new projects by importing &lt;a href=&#34;https://simplecss.org/&#34;&gt;SimpleCSS&lt;/a&gt; by Kev Quirk. It makes the HTML look good right out of the gate, but it does make each project look a little &amp;ldquo;samey&amp;rdquo; but that&amp;rsquo;s up to me to address.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Live Reloading:&lt;/strong&gt; I&amp;rsquo;ve only recently been a convert to live reloading. I did use it when I was bundling JavaScript, but since moving away from that, plus doing most things server-side anyway, I needed something that would build the entire app. I&amp;rsquo;ve started using &lt;a href=&#34;https://github.com/air-verse/air&#34;&gt;Air&lt;/a&gt; for this, and it&amp;rsquo;s… fine. There are certain things that I don&amp;rsquo;t like about it — particularly that it tends to favour configuration over convention — but it does the job.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deployment:&lt;/strong&gt; Finally, when I&amp;rsquo;m ready to deploy something, I do so using &lt;a href=&#34;https://dokku.com/&#34;&gt;Dokku&lt;/a&gt; running on a Linux server. I bundle the app in a Docker container, mainly using a Go builder image, and a scratch image for the run-time container (this scratch container has nothing else in it, not even libc, which is why I use the modernc.org Sqlite driver). All I need to do is run &lt;code&gt;git push&lt;/code&gt;, and Dokku does the rest. Dokku also makes it easy to provision PostgreSQL databases with automated backups, and HTTPS certificates using Lets Encrypt. Deploying something new does involve logging into the remote server to run some commands, but having been burned by PaaS providers that are either too pricy, or not pricy enough to stay in business, I&amp;rsquo;ve found this setup to be the most stable way to host apps.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, that&amp;rsquo;s my setup. It&amp;rsquo;s a collection that&amp;rsquo;s geared towards keeping the code low maintenance, even if it may come at the cost of scalability. I can&amp;rsquo;t tell you anything about that myself: I&amp;rsquo;m not running anything that has more than a couple of users anyway, and most things I&amp;rsquo;m running are only being used by myself. But I think that&amp;rsquo;s a problem for later, should it ever arise.&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-go-web-apps.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;&lt;/audio&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Micro-fiction: Get A Horse</title>
      <link>https://lmika.org/2024/09/20/microfiction-get-a.html</link>
      <pubDate>Fri, 20 Sep 2024 17:37:27 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/09/20/microfiction-get-a.html</guid>
      <description>&lt;p class=&#34;callout&#34;&gt;Trying something new here. I came up with the concept of this short-story while riding home on the tram yesterday. The rest of it sort-of fell into place when I woke up at 5AM this morning, unable to get back to sleep. Hope you enjoy it.&lt;/p&gt;
&lt;p&gt;Josh was riding the scooter on the city footpath, not trying super hard to avoid the other pedestrians. He was going at a speed that was both unsafe and illegal, but it was the only speed he knew that would prevent that horse from showing up. Besides, he had something that he needed to do, and it was only at such reckless speeds that he knew that that thing would work. Well, he didn’t know; but being at his wits&amp;rsquo; end after trying everything else, he had to try this. He picked his target ahead and sped up towards it. Good thing he was wearing his helmet.&lt;/p&gt;
&lt;p&gt;Josh never used these sorts of scooters before the collision two weeks ago. He was walking to work that day, when he saw someone on such a scooter coming towards him, helmet on head. The rider was going a ridiculous speed, and Josh tried to get out of his way as he approached, but the scooter driver turned towards him, not slowing down at all. Josh tried again but was not fast enough. The scooter rider ran straight into him and bowled him over onto the footpath. Before Josh could gather himself, the scooter rider slap his helmet onto Josh&amp;rsquo;s head and shouted, “Get a horse!” He got back onto the scooter and sped away.&lt;/p&gt;
&lt;p&gt;Josh got up, fighting the various aching muscles from the fall. He dusted himself down, took the helmet from his head and looked at it. It was very uncharacteristic of those worn by scooter riders. Most of them were plastic things, either green or orange, yet this one was grey, made of solid leather that was slightly fuzzy to the touch. Josh looked inside the rim and found some printed writing: &lt;em&gt;Wilkinsons Equestrian Helmet. One side fits all.&lt;/em&gt; The &lt;em&gt;one&lt;/em&gt; was underlined with some black marker.&lt;/p&gt;
&lt;p&gt;Josh put the helmet in his backpack and was about to resume his commute, when he stopped in place. Several metres away, a white horse stood, staring at him. Or at least it looked like a horse. The vision was misty and slightly transparent, giving the sense that it was not real. Yet after blinking and clearing his eyes, it didn’t go away. Josh started to move towards it, and when he was just within arms reach, it disappeared. Josh shook his head, and starting walking. But when he turned the next corner, there it was again: a horse, standing in the middle of the footpath several metres away, staring at him intently.&lt;/p&gt;
&lt;p&gt;Since that day that horse has been haunting Josh. On his walk, at his workplace, in his home, even on the tram. Always staring, always outside of reach. Either standing in his path or following close behind him. The vision will go whenever Josh approached it, only to reappear when he turned to look in another direction. Naturally, no one else could see it. When that horse was in a public place, people seemed to instinctively walk around it. Yet when he asked them if they could see it, they had no idea what he was talking about. But Josh couldn’t do anything to stop seeing it. At every waking hour of the day, from when he got out of bed to when he got back in, there it was, always staring. Never looking away.&lt;/p&gt;
&lt;p&gt;And he knew it had something to do with that helmet. He tried a few things to dispel the vision, such as leaving the helmet at  home or trying to give to random strangers (who always refused it). Yet nothing worked to clear the vision. That is, nothing other than what had worked on him. Now was the time to test that theory out.&lt;/p&gt;
&lt;p&gt;His target was ahead, a man in a business suit walking at a leisurely pace. He had his back to Josh, so he couldn&amp;rsquo;t see Josh turn his scooter towards him and accelerate. The gap between them rapidly closed, and Josh made contact with the man, slowing a little to avoid significant injury, but still fast enough to knock him over. Josh got off the scooter and stood by the man, sprawled on the footpath. Once again the horse appeared, as he knew it would. He looked down to see the man starting to get up. Josh had to go for it now! He took his helmet from his head, slapped it on the man and shouted, “Get a horse!”&lt;/p&gt;
&lt;p&gt;Josh got back on the scooter and sped away for few seconds, then stopped to look behind him. He saw the man back on his feet, helmet in hand, looking at it much like Josh did a fortnight ago. He saw the horse as well, but this time it had its back to Josh, staring intently at the man, yet Josh could see that the man hasn&amp;rsquo;t noticed yet. He could see the man put the helmet by side of the road and walk away, turning a corner. The horse was fading from Josh&amp;rsquo;s eyes, yet it was still visible enough for Josh to see it follow the man around the corner, several metres behind.&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-get-a-horse.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;&lt;/audio&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Select Fun From PostgreSQL</title>
      <link>https://lmika.org/2024/09/11/select-fun-from.html</link>
      <pubDate>Wed, 11 Sep 2024 16:20:50 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/09/11/select-fun-from.html</guid>
      <description>&lt;p&gt;Using PostgreSQL these last few months reminds me of just how much fun it is to work with a relational database. DynamoDB is very capable, but I wouldn&amp;rsquo;t call it fun. It&amp;rsquo;s kinda boring, actually. Not that that&amp;rsquo;s a bad thing: one could argue that &amp;ldquo;boring&amp;rdquo; is what you want from a database.&lt;/p&gt;
&lt;p&gt;Working with PostgreSQL, on the other hand, has been fun. There&amp;rsquo;s no better word to describe it. It&amp;rsquo;s been quite enjoyable designing new tables and writing SQL statements.&lt;/p&gt;
&lt;p&gt;Not sure why this is, but I&amp;rsquo;m guessing it&amp;rsquo;s got something to do with working with a schema. It exercises the same sort of brain muscles&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; as designing data structures or architecting an application. Much more interesting than dealing with a schemaless database, where someone could simply say &amp;ldquo;ah, just shove this object it a DynamoDB table.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s either that, or just that PostgreSQL has a more powerful query language than what DynamoDB offers. I mean, DynamoDB&amp;rsquo;s query capabilities need to be pretty restricted, thanks to how it stores it&amp;rsquo;s data. That&amp;rsquo;s the price you pay for scale.&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-select-fun-from-postgresql.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;My brain muscles couldn&amp;rsquo;t come up with a better term here. 😄&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Rubberducking: Of Config And Databases</title>
      <link>https://lmika.org/2024/09/10/rubberducking-of-config.html</link>
      <pubDate>Tue, 10 Sep 2024 08:24:55 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/09/10/rubberducking-of-config.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s been a while since my last &lt;a href=&#34;https://lmika.org/2024/02/09/rubberducking-on-context.html&#34;&gt;rubber-ducking session&lt;/a&gt;. Not that I&amp;rsquo;m in the habit of seeking them out: I mainly haven&amp;rsquo;t been in a situation when I needed to do one. Well that chance came by yesterday, when I was wondering whether to put queue configuration either in the database as data, or in the environment as configuration.&lt;/p&gt;
&lt;p&gt;This one&amp;rsquo;s relatively short, as I was leaning towards one method of the other before I started. But doubts remained, so having the session was still useful.&lt;/p&gt;
&lt;p&gt;So without further ado, let&amp;rsquo;s dive in. Begin scene.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Hello&lt;/p&gt;
&lt;p&gt;🦆: Oh, you&amp;rsquo;re back. It&amp;rsquo;s been a while. How did that thing with the authorisation go?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Yeah, good. Turns out doing that was a good idea.&lt;/p&gt;
&lt;p&gt;🦆: Ah, glad to hear it. Anyway, how can I help you today?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Ok, so I&amp;rsquo;m working on this queue system that works with a database. I&amp;rsquo;ve got a single queue working quite well, but I want to extend it to something that works across multiple queues.&lt;/p&gt;
&lt;p&gt;🦆: Okay&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; So I&amp;rsquo;m wondering where I could store the configuration to these queues. I&amp;rsquo;m thinking either in the database as data, or in the configuration. I&amp;rsquo;m thinking the database as: A) a reference to the queue needs to be stored alongside each item anyway, and B) if we wanted to add more queues, we can almost do so by simply adding rows.&lt;/p&gt;
&lt;p&gt;🦆: &amp;ldquo;almost do so?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Yeah, so this is where I&amp;rsquo;m a little unsure. See, I don&amp;rsquo;t want to spend a lot of effort building out the logic to deal with relaunching the queue dispatcher when the rows change. I rather the dispatcher just read how the queues are configured during startup and stick with that until the application is restarted.&lt;/p&gt;
&lt;p&gt;🦆: Okay&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; And such an approach is closer to configuration. In fact, it could be argued that having the queues defined as configuration would be better, as adding additional queues could be an activity that is considered &amp;ldquo;intentional&amp;rdquo;, with a proper deployment and release process.&lt;/p&gt;
&lt;p&gt;I wonder if a good middle-ground might be to have the queues defined in the database as rows, yet managed via the migration script. That way, we can have the best of both worlds.&lt;/p&gt;
&lt;p&gt;🦆: Why not just go with configuration?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; The main reason is that I don&amp;rsquo;t want to add something like string representations of the queue to each queue item. I&amp;rsquo;m okay if it was just a UUID, since I&amp;rsquo;d imagine PostgreSQL could handle such fields relatively efficiently. But adding queue names like &amp;ldquo;default&amp;rdquo; or &amp;ldquo;test&amp;rdquo; as a string on each queue item seems like a bit of a waste.&lt;/p&gt;
&lt;p&gt;🦆: Do they need to be strings? Could the be like an enum?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; I rather they&amp;rsquo;re strings, as I want this arrangement to be relatively flexible. You know, &amp;ldquo;policy vs. mechanism&amp;rdquo; and all that.&lt;/p&gt;
&lt;p&gt;🦆: So how would this look in the database?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Well, each row for a queue would have a string, say like a queue name. But each individual queue item would reference the queue via it&amp;rsquo;s ID.&lt;/p&gt;
&lt;p&gt;🦆: Okay, so it sounds like adding it to the database yet managing it with the migration script is the way to go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Yeah, that is probably the best approach.&lt;/p&gt;
&lt;p&gt;🦆: Good. I&amp;rsquo;m glad you can come away with thinking this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Yeah, honestly that was the way I was leaning anyway. But I&amp;rsquo;m glad that I can completely dismiss the configuration approach now.&lt;/p&gt;
&lt;p&gt;🦆: Okay, good. So I&amp;rsquo;m guessing my job is done here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Yeah, thanks again.&lt;/p&gt;
&lt;p&gt;🦆: No worries.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>About Those STOP Messages</title>
      <link>https://lmika.org/2024/08/29/about-those-stop.html</link>
      <pubDate>Thu, 29 Aug 2024 16:26:32 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/08/29/about-those-stop.html</guid>
      <description>&lt;p&gt;John Gruber, discussing &lt;a href=&#34;https://daringfireball.net/linked/2024/08/28/messages-political-spam&#34;&gt;political spam text messages&lt;/a&gt; on Daring Fireball:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;About a month ago I switched tactics and started responding to all such messages with “STOP”. I usually send it in all caps, just like that, because I’m so annoyed. I resisted doing this until a month ago thinking that sending any reply at all to these messages, including the magic “STOP” keyword, would only serve to confirm to the sender that an actual person was looking at the messages sent to my phone number. But this has actually worked. Election season is heating up but I’m getting way way fewer political spam texts now. Your mileage may vary, but for me, the “STOP” response works.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;As someone who use to work for a company that operated a SMS messaging gateway, allow me to provide some insight into how this works. When you send an opt-out keyword — usually &amp;ldquo;STOP&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&amp;rdquo; although there are a few others — this would be received by our messaging gateway, and your number would be added to an opt-out list for that sender. From that point on, any attempt by that sender to send a message to your number would fail.&lt;/p&gt;
&lt;p&gt;Maintaining these opt-out lists is a legal requirement with some significant penalties, so the company I worked for took this quite seriously. Once, the service maintaining this list went down, and we couldn&amp;rsquo;t know whether someone opted-out or not. We actually stopped all messaging completely until we got that service back up again. I still remember that Friday afternoon (naturally, it happened on a Friday afternoon).&lt;/p&gt;
&lt;p&gt;Now, if memory serves, there was a way for a sender to be notified when an opt-out occurred. This was mainly for customers that decided to take on the responsibility — and thus legal accountability — of maintaining the opt-out lists themselves. There were a handful of customers that had this enabled, and it was something that we had to enable for them on the backend, but most customers simply delegated this responsibility to us (I can&amp;rsquo;t remember if customers that had this feature off could still receive opt-out notifications).&lt;/p&gt;
&lt;p&gt;Finally, there is a way, a variant of the &amp;ldquo;STOP&amp;rdquo; message, in which someone could opt-out of any message sent from our gateway, basically adding themselves to a global opt-out list which applies to everyone. The only way someone could remove themselves from this list was to call support, so I wouldn&amp;rsquo;t recommend doing this unless you know you would never need another 2FA code via SMS again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Addendum:&lt;/strong&gt; The customer never had access to these opt-out lists but I believe they could find out when a message they tried to send was blocked. This is because they would be charged per message sent, and if a message was blocked, they would receive a credit. There was also an API to return the status of a message, so if you knew the message ID, it was possible to call the API to know whether a message was blocked.&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-about-those-stop-messages.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I can&amp;rsquo;t remember if this is case insensitive, although I think it is.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>My Home Computer Naming Scheme</title>
      <link>https://lmika.org/2024/08/19/my-home-computer.html</link>
      <pubDate>Mon, 19 Aug 2024 13:29:38 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/08/19/my-home-computer.html</guid>
      <description>&lt;p&gt;I enjoyed &lt;a href=&#34;https://www.manton.org/2024/08/18/microblog-servers-for.html&#34;&gt;Manton&amp;rsquo;s&lt;/a&gt; post about the naming scheme he uses for Micro.blog servers. I see these names pop up in the logs when I go to rebuild my blog, each with a Wikipedia link explaining the origins of the name (that&amp;rsquo;s a really nice touch).&lt;/p&gt;
&lt;p&gt;Having a server or desktop naming scheme is one of those fun little things to do when working with computers. Growing up we named our home desktops after major characters of Lord of the Rings, such as Bilbo, or Frodo, but I never devised a scheme for myself when I started buying my own computers. I may have kept it up if we were doing likewise at work, but when AWS came onto the scene, the prevailing train of thought was to treat your servers like cattle rather than pets. Granted, it is probably the correct approach, especially when the lifecycle of a particular EC2 instance could be as short as a few minutes.&lt;/p&gt;
&lt;p&gt;But a few years ago, after buying a new desktop and setting up the old one to be a home server, and finding that I need a way to name them, I figured now was the time for a naming scheme. Being a fan of A Game of Thrones, both the book and the TV series, I&amp;rsquo;ve came up with one based on the major houses of Westeros.&lt;/p&gt;
&lt;p&gt;So, to date, here are the names I&amp;rsquo;ve chosen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stark&lt;/strong&gt; — the M2 Mac Mini that I use as my desktop&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tully&lt;/strong&gt; — the Intel Mac Mini that I use as my home server&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Crow&lt;/strong&gt; — a very old laptop that I occasionally use when I travel (this one is a reference to the Night&amp;rsquo;s Watch)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I think at one point I had an Intel Nuc that was called Ghost, a reference to John Snow&amp;rsquo;s dire wolf, but I haven&amp;rsquo;t used that in a while so I may be misremembering things. I also don&amp;rsquo;t have a name for my work laptop: it&amp;rsquo;s simply called &amp;ldquo;work laptop.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-home-computer-naming-scheme.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;&lt;/audio&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Go Feature Request: A &#39;Rest&#39; Operator for Literals</title>
      <link>https://lmika.org/2024/07/25/go-feature-request.html</link>
      <pubDate>Thu, 25 Jul 2024 10:59:35 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/07/25/go-feature-request.html</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s a feature request for Go: shamelessly copying JavaScript and adding support for the &amp;ldquo;rest&amp;rdquo; operator in literals. Go does have a rest operator, but it only works in function calls. I was writing a unit test today and I was thinking to myself that it would be nice to use this operator in both slice and struct literals as well.&lt;/p&gt;
&lt;p&gt;This could be useful for making copies of values without modifying the originals. Imagine the following bit of code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Vector&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;X&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Y&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Z&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;oldInts&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;oldVec&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Vector&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;X&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;newInts&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; append([]&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;}, &lt;span style=&#34;color:#a6e22e&#34;&gt;oldInts&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;newVec&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;oldVec&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;newVec&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Y&lt;/span&gt; = &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;newVec&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Z&lt;/span&gt; = &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now imagine how it would look if rest operators in literals were supported:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Vector&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;X&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Y&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Z&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;oldInts&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;oldVec&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Vector&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;X&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;newInts&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;oldInts&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;newVec&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Vector&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;Y&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Z&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;oldVec&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I hope you&amp;rsquo;ll agree that it looks a bit neater than the former. Certainly it looks more pleasing to my eyes. True, this is a contrived example, but the code I&amp;rsquo;m writing for real is not too far off from this.&lt;/p&gt;
&lt;p&gt;On the other hand, Go does prefer clarity over brevity; and I have seen some JavaScript codebases which use these &amp;ldquo;rest&amp;rdquo; operators to an absurd level, making the code terribly hard to read. But I think the Go user-base is pretty good at moderating themselves, and just because it could result in unreadable code, doesn&amp;rsquo;t make it a forgone conclusion. Just look at Go&amp;rsquo;s use of type parameters.&lt;/p&gt;
&lt;p&gt;Anyway, if the Go team is looking for things to do, here&amp;rsquo;s one.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Follow-Up To Mockless Unit Testing</title>
      <link>https://lmika.org/2024/07/23/a-followup-to.html</link>
      <pubDate>Tue, 23 Jul 2024 17:53:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/07/23/a-followup-to.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m sure everyone&amp;rsquo;s dying to hear how the &lt;a href=&#34;https://lmika.org/2024/06/03/some-more-thoughts.html&#34;&gt;mockless unit tests&lt;/a&gt; are going. It&amp;rsquo;s been almost two months since we started this service, and we&amp;rsquo;re smack bang in the middle of brownfield iterative development: adding new features to existing ones, fixing bugs, etc. So it seems like now is a good time to reflect on whether this approach is working or not.&lt;/p&gt;
&lt;p&gt;And so far, it&amp;rsquo;s been going quite well. The amount of code we have to modify when refactoring or changing existing behaviour is dramatically smaller than before. Previously, when a service introduces a new method call, every single test for that service needed to be changed to handle the new mock assertions. Now, in most circumstances, it&amp;rsquo;s only one or maybe two tests that need to change. This has made maintenance so much easier, and although I&amp;rsquo;m not sure it mades us any faster, it just feels faster. Probably because there&amp;rsquo;s less faffing around unrelated tests that broke due to the updated mocks.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t think of it at the time, but it also made code reviews easer too. The old way meant longer, noisier PRs which — and I know this is a quality of mine that I need to work at — I usually ignore (I know, I know, I really shouldn&amp;rsquo;t). With the reviews being smaller, I&amp;rsquo;m much more likely to keep atop of them, and I attribute this to the way in which the tests are being written.&lt;/p&gt;
&lt;p&gt;Code hygiene plays a role here. I got into the habit of adding test helpers to each package I work on. Much like the package is responsible for fulfilling the contract it has with its dependants, so too is it responsible for providing testing facilities for those dependants. I found this to be a great approach. It simplified the level of setup each dependant package needed to do in their tests, reducing the amount of copy and pasted code and, thus, containing the &amp;ldquo;blast radius&amp;rdquo; of logic changes. It&amp;rsquo;s not perfect — there are a few tests where setup and teardown were simply copied-and-pasted from other tests — but it is better.&lt;/p&gt;
&lt;p&gt;We didn&amp;rsquo;t quite get rid of all the mocking though. Tests that exercise the database and message bus do so by calling out to these servers running in Docker, but this service also had to make calls to another worker. Since we didn&amp;rsquo;t have a test service available to us, we just implemented this using old-school test mocks. The use of the package test helpers did help here: instead of having each test declare the expected calls on this mock, the helper just made &amp;ldquo;maybe&amp;rdquo; calls to each of the service methods, and provided a way to assert what calls were recorded.&lt;/p&gt;
&lt;p&gt;Of course, much like everything, there are trade-offs. The tests run much slower now, thanks to the database setup and teardown, and we had to lock them down to run on a single thread. It&amp;rsquo;s not much of an issue now, but we could probably mitigate this with using random database names, rather than have all the test run against the same one. Something that would be less easy to solve are the tests around the message bus, which do need to wait for messages to be received and handled. There might be a way to simplify this too, but since the tests are verifying the entire message exchange, it&amp;rsquo;d probably be a little more involved.&lt;/p&gt;
&lt;p&gt;Another trade-off is that it does feel like you&amp;rsquo;re repeating yourself. We have tests that check that items are written to the database correctly for the database provider, the service layer, &lt;em&gt;and&lt;/em&gt; the handler layer. Since we&amp;rsquo;re writing tests that operate over multiple layers at a time, this was somewhat expected, but I didn&amp;rsquo;t expect it to be as annoying as I found it to be. Might be that a compromise is to write the handler tests to use mocks rather than call down to the service layer. Those tests really only validate whether the hander converts the request and response to the models correctly, so best to isolate it there, and leave the tests asserting whether the business rules are correct to the server layer.&lt;/p&gt;
&lt;p&gt;So if I were to do this again, that&amp;rsquo;ll be the only thing I change. But, on the whole, it&amp;rsquo;s been really refreshing writing unit tests like this. And this is not just my own opinion; I asked my colleague, who&amp;rsquo;s has told me how difficult it&amp;rsquo;s been maintaining tests with mocks, and he agrees that this new way has been an improvement. I&amp;rsquo;d like to see if it&amp;rsquo;s possible doing this for all other services going forward.&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-mockless-unit-test-followup.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;&lt;/audio&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On the Easy Pit To Fall Into</title>
      <link>https://lmika.org/2024/07/14/090308.html</link>
      <pubDate>Sun, 14 Jul 2024 09:03:08 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/07/14/090308.html</guid>
      <description>&lt;p&gt;From Matt Bircher’s &lt;a href=&#34;https://birchtree.me/blog/the-pit-its-so-easy-to-fall-into/&#34;&gt;latest post on Birchtree&lt;/a&gt;:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;One of the hard parts about sharing one&amp;rsquo;s opinions online like I do is that it&amp;rsquo;s very easy to fall into the trap of mostly complaining about things.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;This is something I also think about. While I haven’t done anything scientific to know what my ratio of posting about things I like vs. things I don’t, I feel like I’m getting the balance better. It might still be weighted too much on writing about the negatives, but I am trying to write more about things I think are good.&lt;/p&gt;
&lt;p&gt;I do wonder, though, why it’s so easy to write about things you hate. Matt has a few theories regarding the dynamics of social media, but I wonder if it more about someone’s personal experience of that thing in question. You hear about something that you&amp;rsquo;d thought would be worth a try. I doubt many people would actually try something they know they&amp;rsquo;re going to dislike. If that&amp;rsquo;s the case, they wouldn&amp;rsquo;t try it at&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. So I&amp;rsquo;m guessing that there&amp;rsquo;s some expectation that you&amp;rsquo;ll like the thing.&lt;/p&gt;
&lt;p&gt;So you start experience the thing, and maybe it all goes well at first. Then you encounter something you don’t like about it. You make a note of it and keep going, only to encounter another thing you don’t like, then another. You eventually get to the point where you’ve had enough, and you decided to write about it. And lo, you&amp;rsquo;ve got this list of paper-cuts that can be easily be used as arguments as to why the thing is no good.&lt;/p&gt;
&lt;p&gt;Compare this to something that you do end up liking. You can probably come up with a list of things that are good about it, but you’re less likely to encounter them while you’re experiencing the thing. You just experience them, and it flows through you like water. When the time comes to write about it, you can recall liking the plot, or this character, etc., but they’re more nebulous and it takes effort to solidify them into a post. The drive to find the path of least resistance prevails, and you decided that it’s enough to just like it.&lt;/p&gt;
&lt;p&gt;Anyway, this is just a hypothesis. I’m not a psychologist and I’ve done zero research to find out if any of this is accurate. In the end, this post might simply describe why my posting seems to be more weighted towards things I find annoying.&lt;/p&gt;
&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-easy-pit-to-fall-into.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I&amp;rsquo;m ignoring those that come at things with bad faith.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>A Tour Of My New Self-Hosted Code Setup</title>
      <link>https://lmika.org/2024/07/09/a-tour-of.html</link>
      <pubDate>Tue, 09 Jul 2024 21:50:38 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/07/09/a-tour-of.html</guid>
      <description>&lt;p&gt;While working on the draft for this post, &lt;a href=&#34;https://www.youtube.com/watch?v=YzBrI71FC44&#34;&gt;a quote from Seinfield&lt;/a&gt; came to mind which I thought was a quite apt description of this little project:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Breaking up is knocking over a Coke machine. You can&amp;rsquo;t do it in one push. You gotta rock it back and forth a few times and then it goes over.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;ve been thinking about &amp;ldquo;breaking up&amp;rdquo; with Github on and off for a while now. I know I&amp;rsquo;m not the only one: I&amp;rsquo;ve seen a few people online talk about leaving Github too. They have their own reasons for doing so: some because of AI, others are just not fans of Microsoft. For me, it was getting bitten by the indie-web bug and wanting to host my code on my own domain name. I have more than a hundred repositories in Github, and that single &lt;code&gt;github.com/lmika&lt;/code&gt; namespace was getting quite crowded. Being able to organise all these repositories into groups, without fear of collisions or setting up new accounts, was the dream.&lt;/p&gt;
&lt;p&gt;But much like the Seinfield quote, it took a few rocks of that fabled Coke machine to get going. I dipped my toe in the water a few times: launching Gitea instances in PikaPod, and also spinning up a Gitlab instance in Linode during a hackathon just to see how well it would feel to manage code that way. I knew it wouldn&amp;rsquo;t be easy: not only would I be paying more for doing this, it would involve a lot of effort up front (and on an ongoing basis), and I would be taking on the responsibility of backups, keeping CI/CD workers running, and making sure everything is secured and up-to-date. Not difficult work, but still an ongoing commitment.&lt;/p&gt;
&lt;p&gt;Well, if I was going to do this at all, it was time to do it for real. I decided to set up my own code hosting properly this time, complete with CI/CD runners, all hosted under my own domain name. And well, that Coke machine is finally on the floor. I&amp;rsquo;m striking out on my own.&lt;/p&gt;
&lt;p&gt;Let me give you a tour of what I have so far.&lt;/p&gt;
&lt;h2 id=&#34;infrastructure&#34;&gt;Infrastructure&lt;/h2&gt;
&lt;p&gt;My goal was to have a setup with the following properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A self-hosted SCM (source code management) system that can be bound to my own domain name.&lt;/li&gt;
&lt;li&gt;A place to store Git repositories and LFS objects that can be scaled up as my needs for storage grow.&lt;/li&gt;
&lt;li&gt;A CI/CD runner of some sort that can be used for automated builds, ideally something that supports Linux and MacOS.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the SCM, I settled on &lt;a href=&#34;https://forgejo.org&#34;&gt;Forgejo&lt;/a&gt;, which is a fork of Gitea, as it seemed like the one that required the least amount of resources to run. When I briefly looked at doing this a while back, Forgejo didn&amp;rsquo;t have anything resembling GitHub Actions, which was a non-starter for me. But they&amp;rsquo;re now in Forgejo as an alpha, preview, don&amp;rsquo;t-use-it-for-anything-resembling-production level of support, and I was curious to know how well they worked, so it was worth trying it out.&lt;/p&gt;
&lt;p&gt;I did briefly look at Gitea&amp;rsquo;s hosted solution, but it was relatively new and I wasn&amp;rsquo;t sure how long their operations would last. At least with self-hosting, I can choose to exit on my own terms.&lt;/p&gt;
&lt;p&gt;It was difficult thinking about how much I was willing to budget for this, considering that it&amp;rsquo;ll be more than what I&amp;rsquo;m currently paying for GitHub now, which is about $9 USD /month ($13.34 AUD /month). I settled for a budget of around $20.00 AUD /month, which is a bit much, but I think would give me something that I&amp;rsquo;d be happy with without breaking the bank.&lt;/p&gt;
&lt;p&gt;I first had a go at seeing what Linode had to offer for that kind of money. A single virtual CPU, with 2 GB RAM and 50 GB storage costs around $12.00 USD /month ($17.79 AUD /month). This would be fine if it was just the SCM, but I also want something to run CI/CD jobs. So I then took a look at Hetzner. Not only do they charge in Euro&amp;rsquo;s, which works in my favour as far as currency conversions go, but their shared-CPU virtual servers were much cheaper. A server with the same specs could be had for only a few euro.&lt;/p&gt;
&lt;p&gt;So after a bit of looking around, I settled for the following bill of materials:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2x vCPU (CX22) instances, each with 4 GB RAM and 40 GB storage&lt;/li&gt;
&lt;li&gt;A virtual network which houses these two instances&lt;/li&gt;
&lt;li&gt;One public IP address&lt;/li&gt;
&lt;li&gt;One 50 GB volume which can be resized&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This came to €10.21, which was around $16.38 AUD /month. Better infrastructure for a cheaper price is great in my books. The only downside is that they don&amp;rsquo;t have a data-centre presences in Australia. I settled for the default placement of Falkenstein, Germany and just hoped that the latency wasn&amp;rsquo;t too slow as to be annoying.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/code-architecture.png&#34; width=&#34;600&#34; height=&#34;292&#34; alt=&#34;Architecture drawing of my coding setup, showing two CX22 virtual hosts, within a virtual network, with one connected to the internet, and one 50 GB volume&#34;&gt;
&lt;h2 id=&#34;installing-forgejo&#34;&gt;Installing Forgejo&lt;/h2&gt;
&lt;p&gt;The next step was setting up Forgejo. This can be done using official channels by either downloading a binary, or by installing a Docker image. But there&amp;rsquo;s also a &lt;a href=&#34;https://codeberg.org/forgejo-contrib/delightful-forgejo#packaging&#34;&gt;forgejo-contrib&lt;/a&gt; repository that distributes it via common package types, with Systemd configurations that launch Forgejo on startup. Since I was using Ubuntu, I downloaded and installed &lt;a href=&#34;https://codeberg.org/forgejo-contrib/forgejo-deb&#34;&gt;the Debian package&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Probably the easiest way to get started with Forgejo is to use the version that comes with Sqlite, but since this is something that I&amp;rsquo;d rather keep for a while, I elected to use Postgre for my database. I installed the latest Ubuntu distribution of Postgres, and setup the database &lt;a href=&#34;https://forgejo.org/docs/latest/admin/installation-binary/#optional-set-up-database&#34;&gt;as per the instructions&lt;/a&gt;. I also made sure the mount point for the volume was ready, and created a new directory with the necessary owner and permissions so that Forgejo can write to it.&lt;/p&gt;
&lt;p&gt;At this point I was able to launch Forgejo and go through the first launch experience. This is where I configured the database connection details, and set the location of the repository and LFS data (I didn&amp;rsquo;t take a screenshot at the time, sorry). Once that was done, I shut the server down again as I needed to make some changes within the config file itself:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I turned off the ability for others to register themselves as users, an important first step.&lt;/li&gt;
&lt;li&gt;I changed the bind address of Forgejo. It listens to &lt;code&gt;0.0.0.0:3000&lt;/code&gt; by default, but I wanted to put this behind a reverse proxy, so I changed it to &lt;code&gt;127.0.0.1:3000&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;I also reduced the minimum size of SSH RSA keys. The default was 3,072, but I still have keys of length 2,048 that I wanted to use. There was also an option to turn off this verification.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After that, it was a matter of setting up the reverse proxy. I decided to use &lt;a href=&#34;https://caddyserver.com&#34;&gt;Caddy&lt;/a&gt; for this, as it comes with HTTPS out of the box. This I installed as a Debian package also. &lt;a href=&#34;https://caddyserver.com/docs/quick-starts/reverse-proxy#https-from-client-to-proxy&#34;&gt;Configuring the reverse proxy&lt;/a&gt; by changing the Caddyfile deployed in &lt;code&gt;/etc&lt;/code&gt; was a breeze and after making the changes and starting Caddy, I was able to access Forgejo via the domain I setup.&lt;/p&gt;
&lt;p&gt;One quick note about performance: although logging in via SSH was a little slow, I had no issues with the speed of accessing Forgejo via the browser.&lt;/p&gt;
&lt;h2 id=&#34;the-runners&#34;&gt;The Runners&lt;/h2&gt;
&lt;p&gt;The next job was setting the runners. I thought this was going to be easier than setting up Forgejo itself, but I did run into a few snags which slowed me down.&lt;/p&gt;
&lt;p&gt;The first was finding out that a Hetzner VM running without a public IP address actually doesn&amp;rsquo;t have any route to the internet, only the local network. The way to fix this is to setup one of the hosts which did have a public IP address to act as a NAT gateway. &lt;a href=&#34;https://community.hetzner.com/tutorials/how-to-set-up-nat-for-cloud-networks&#34;&gt;Hetzner has instructions&lt;/a&gt; on how to do this, and after performing a hybrid approach of following both the Ubuntu 20.04 instructions and Ubuntu 22.04 instructions, I was able to get the runner host online via the Forgejo host. Kinda wish I knew about this before I started this.&lt;/p&gt;
&lt;p&gt;For the runners, I elected to go with the Docker-based setup. Forgejo has pretty straightforward instructions for &lt;a href=&#34;https://forgejo.org/docs/next/admin/actions/#installation-of-the-oci-image&#34;&gt;setting them up using Docker Compose&lt;/a&gt;, and I changed it a bit so that I could have two runners running on the same host.&lt;/p&gt;
&lt;p&gt;Setting up the runners took multiple attempts. The first attempts failed when Forgejo couldn&amp;rsquo;t locate any runners for an organisation to use. I&amp;rsquo;m not entirely sure why this was, as the runners were active and were properly registered with the Forgejo instance. It could be magical thinking, but my guess is that it was because I didn&amp;rsquo;t register the runners with an instance URL that ended with a slash. It seems like it&amp;rsquo;s possible to register runners that are only available to certain organisations or users. Might be that there&amp;rsquo;s some bit of code deep within Forgejo that&amp;rsquo;s expecting a slash to make the runners available to everyone? Not sure. In either case, after registering the runners with the trailing slash, the organisations started to recognise them.&lt;/p&gt;
&lt;p&gt;The other error was seeing runs fail with the error message &lt;code&gt;cannot find: node in PATH&lt;/code&gt;. This resolved itself after I changed the &lt;code&gt;run-on&lt;/code&gt; label within the action YAML file itself from &lt;code&gt;linux&lt;/code&gt; to &lt;code&gt;docker&lt;/code&gt;. I wasn&amp;rsquo;t expecting this to be an issue — I though the &lt;code&gt;run-on&lt;/code&gt; field was used to select a runner based on their published labels, and that &lt;code&gt;docker&lt;/code&gt; was just one such label. The Forgejo documentation was not super clear on this, but I got the sense that the &lt;code&gt;docker&lt;/code&gt; label was special in some way. I don&amp;rsquo;t know. But whatever, I can use &lt;code&gt;docker&lt;/code&gt; in my workflows.&lt;/p&gt;
&lt;p&gt;Once these battles were won, the runners were ready, and I was able to build and test a Go package successfully. One last annoying thing is that Forgejo doesn&amp;rsquo;t enable runners by default for new repositories — I guess because they&amp;rsquo;re still considered an alpha release. I can live with that in the short term, or maybe there&amp;rsquo;s some configuration I can enable to always have them turned on. But in either case, I&amp;rsquo;ve now got two Linux runners working.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-20240704-084448.png&#34; width=&#34;600&#34; height=&#34;461&#34; alt=&#34;Screenshot of a completed CI/CD run within Forgejo&#34;&gt;
&lt;figcaption&gt;The first successful CI/CD run using these Linux runners.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&#34;macos-runner-and-repository-backup&#34;&gt;MacOS Runner And Repository Backup&lt;/h2&gt;
&lt;p&gt;The last piece of the puzzle was to setup a MacOS runner. This is for the occasional MacOS application I&amp;rsquo;d like to build, but it&amp;rsquo;s also to run the nightly repository backups. For this, I&amp;rsquo;m using a Mac Mini currently being used as a home server. This has an external hard drive connect, with online backups enabled, which makes it a perfect target for a local backup of Forgejo and all the repository data should the worse come to pass.&lt;/p&gt;
&lt;p&gt;Forgejo does&amp;rsquo;t have an official release of a MacOS runner, but Gitea does, and I managed to &lt;a href=&#34;https://gitea.com/gitea/act_runner&#34;&gt;download a MacOS build of act_runner&lt;/a&gt; and deploy it onto the Mac Mini. Registration and performing a quick test with the runner running in the foreground went smoothly. I then went through the process of setting it up as a MacOS launch agent. This was a pain, and it took me a couple of hours to get this working. I won&amp;rsquo;t go through every issue I encountered, mainly because I couldn&amp;rsquo;t remember half of them, but here&amp;rsquo;s a small breakdown of the big ones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I was unable to register the launch agent definition within the user domain. I had to use the &lt;code&gt;gui&lt;/code&gt; domain instead, which requires the user to be logged in. I&amp;rsquo;ve got the Mac Mini setup to login on startup, so this isn&amp;rsquo;t a huge issue, but it&amp;rsquo;s not quite what I was hoping for.&lt;/li&gt;
&lt;li&gt;Half the commands in &lt;code&gt;launchctl&lt;/code&gt; are deprecated and not fully working. Apples documentation on the command is sparse, and many of the Stack Exchange answers are old. So a lot of effort was spent fumbling through unfinished and outdated documentation trying to install and enable the launch service.&lt;/li&gt;
&lt;li&gt;The actual runner is launch using a shell script, but when I tried the backup job, Bash couldn&amp;rsquo;t access the external drive. I had to explicitly add Bash to Privacy &amp;amp; Security → Full Disk Access within the Settings app.&lt;/li&gt;
&lt;li&gt;Once I finally got the runner up and running as a launch agent, jobs were failing because &lt;code&gt;.bash_profile&lt;/code&gt; wasn&amp;rsquo;t been loaded. I had to adjust the launch script to include this explicitly, so that the PATH to Node and Go were set properly.&lt;/li&gt;
&lt;li&gt;This was further exacerbated by two runners running at the same time. The foreground runner I was using to test with was configured correctly, while the one running as a launch agent wasn&amp;rsquo;t fully working yet. This manifested as the back-up job randomly failing with the same &lt;code&gt;cannot find: node in PATH&lt;/code&gt; error half the time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It took me most of Saturday morning, but in the end I managed to get this MacOS runner working properly. I&amp;rsquo;ve not done anything MacOS-specific yet, so I suspect I may have some XCode related stuff to do, but the backup job is running now and I can see it write stuff to the external hard drive.&lt;/p&gt;
&lt;p&gt;The backup routine itself is a simple Go application that&amp;rsquo;s kicked off daily by a scheduled Forgejo Action (it&amp;rsquo;s not in the documentation yet, but the version of Forgejo I deployed does support scheduled actions). It makes a backup of the Forgejo instance, the PostgreSQL database, and all the repository data using SSH and Rsync.&lt;/p&gt;
&lt;p&gt;I won&amp;rsquo;t share these repositories as they contain references to paths and such that I consider sensitive; but if you&amp;rsquo;re curious about what I&amp;rsquo;m using for the launch agent settings, here&amp;rsquo;s the plist file I&amp;rsquo;ve made:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- dev.lmika.repo-admin.macos-runner.plist --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!DOCTYPE plist PUBLIC &amp;#34;-//Apple//DTD PLIST 1.0//EN&amp;#34; &amp;#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;#34;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;plist&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;version=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;dev.lmika.repo-admin.macos-runner&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/lmika/opt/macos-runner/scripts/run-runner.sh&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;KeepAlive&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;RunAtLoad&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardErrorPath&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/tmp/runner-logs.err&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardOutPath&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/tmp/runner-logs.out&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is deployed by copying it to &lt;code&gt;$HOME/Library/LaunchAgents/dev.lmika.repo-admin.macos-runner.plist&lt;/code&gt;, and then installed and enabled by running these commands:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;launchctl bootstrap gui/$UID &amp;#34;Library/LaunchAgents/dev.lmika.repo-admin.macos-runner.plist&amp;#34;
launchctl kickstart gui/$UID/dev.lmika.repo-admin.macos-runner
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;the-price-of-a-name&#34;&gt;The Price Of A Name&lt;/h2&gt;
&lt;p&gt;One might see this endeavour, when viewed from a pure numbers and effort perspective, as a bit of a crazy thing to do. Saying &amp;ldquo;no&amp;rdquo; to all this cheap code hosting, complete with the backing of a large cooperation, just for the sake of a name? I can&amp;rsquo;t deny that this may seem a little unusual, even a little crazy. After all, it&amp;rsquo;s more work and more money. And I&amp;rsquo;m not going to suggest that others follow me into this realm of a self-hosted SCM.&lt;/p&gt;
&lt;p&gt;But I think my code deserves it&amp;rsquo;s own name now. After all, my code is my work; and much like we encourage writers to write under their own domain name, or for artists and photographers to move away from the likes of Instagram and other such services, so too should my work be under a name I own and control. The code I write may not be much, but it is my own.&lt;/p&gt;
&lt;p&gt;Of course, I&amp;rsquo;m not going to end this without my usual &amp;ldquo;we&amp;rsquo;ll see how we go&amp;rdquo; hedge against myself. I can only hope I got enough safeguards in place to save me from my own decisions, or to easily move back to a hosted service, when things go wrong or when it all becomes just a bit much. More on that in the future, I&amp;rsquo;m sure.&lt;/p&gt;
&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-tour-of-self-hosted-code-setup.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;
</description>
    </item>
    
    <item>
      <title>Zerolog’s API Mistake</title>
      <link>https://lmika.org/2024/07/09/zerologs-api-mistake.html</link>
      <pubDate>Tue, 09 Jul 2024 08:17:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/07/09/zerologs-api-mistake.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ll be honest, I was expecting a lot more moan-routine posts than I&amp;rsquo;ve written to date. Guess I&amp;rsquo;ve been in a positive mood. That is, until I started using Zerolog again this morning.&lt;/p&gt;
&lt;p&gt;Zerolog is a Go logging package that we use at work. It&amp;rsquo;s pretty successful, and all in all a good logger. But they made a fundamental mistake in their API which trips me up from time to time: they&amp;rsquo;re not consistent with their return types.&lt;/p&gt;
&lt;p&gt;When you want to create a new logger, you get a &lt;code&gt;zerolog.Logger&lt;/code&gt; instance. This is also returned when you&amp;rsquo;re creating a new logger from an existing one, say with some extra string attributes. All good. But, when you want to retrieve a logger from the context, it returns a &lt;code&gt;*zerolog.Logger&lt;/code&gt; instance.&lt;/p&gt;
&lt;p&gt;This makes it difficult to write functions that accept logger instances from arbitrary sources. If there&amp;rsquo;s any chance that you need to supply a logger from a context, you find yourself writing parameters with the &lt;code&gt;*zerolog.Logger&lt;/code&gt; type. But that makes passing in new logs annoying, as you&amp;rsquo;d have to instantiate the log into a variable, then pass that variable address through. You could use &lt;code&gt;zerolog.Logger&lt;/code&gt; instead, but then you&amp;rsquo;ve got the opposite problem, where you&amp;rsquo;ll need to dereference the logger returned from the context.&lt;/p&gt;
&lt;p&gt;In short, this is impossible:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;myFunc&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;???&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// do something&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;myFunc&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;zerolog&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Stderr&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;myFunc&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;zerolog&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Ctx&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And yeah, in the grand scheme of things, this is not a huge mistake. And usually I&amp;rsquo;m passing in logs exclusively from the context, so &lt;code&gt;*zerolog.Logger&lt;/code&gt; works just fine most of the time. But I&amp;rsquo;m never comfortable with deciding which is the right type to use, and when I get it wrong, and I resort to using temporary variables, it just leaves the code looking untidy.&lt;/p&gt;
&lt;p&gt;So please, when you&amp;rsquo;re writing an API that returns a specific entity, be consistent in your types. Either return the struct itself, or the pointer. I don&amp;rsquo;t care which one you use, but please, just choose one.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Bit of &#39;Illuminating&#39; Computer Humour</title>
      <link>https://lmika.org/2024/07/05/a-bit-of.html</link>
      <pubDate>Fri, 05 Jul 2024 07:56:25 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/07/05/a-bit-of.html</guid>
      <description>&lt;p&gt;Here’s some more computer-related humour to round out the week:&lt;/p&gt;
&lt;p&gt;How many software developers does it take to change a lightbulb? Just one.&lt;/p&gt;
&lt;p&gt;How many software developers does it take to change 2 lightbulbs? Just 10.&lt;/p&gt;
&lt;p&gt;How many software developers does it take to change 7 lightbulbs? One, but everyone within earshot will know about it.&lt;/p&gt;
&lt;p&gt;How many software developers does it take to change 32 lightbulbs? Just one, provided the space is there.&lt;/p&gt;
&lt;p&gt;How many software developers does it take to change 35 lightbulbs? Just one. #lightbulbs&lt;/p&gt;
&lt;p&gt;How many software developers does it take to change 65 lightbulbs? Just one, if they&amp;rsquo;re on their A grade.&lt;/p&gt;
&lt;p&gt;How many software developers does it take to change 128 lightbulbs? Just one, but they&amp;rsquo;ll be rather negative about it.&lt;/p&gt;
&lt;p&gt;How many software developers does it take to change 256 lightbulbs? What lightbulbs?&lt;/p&gt;
&lt;p&gt;Enjoy your Friday.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/lightbulb.png&#34; width=&#34;400&#34; height=&#34;400&#34; alt=&#34;A meme with a grey background and a lightbulb in the centre that&#39;s not illuminated. The text reads: Q: How many QAs does it take to change the lightbulbs changed by the software developers? A: How many have you got?&#34;&gt;
</description>
    </item>
    
    <item>
      <title>Asciidoc, Markdown, And Having It All</title>
      <link>https://lmika.org/2024/06/21/asciidoc-markdown-and.html</link>
      <pubDate>Fri, 21 Jun 2024 12:07:30 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/06/21/asciidoc-markdown-and.html</guid>
      <description>&lt;p&gt;Took a brief look at &lt;a href=&#34;https://asciidoc.org/&#34;&gt;Asciidoc&lt;/a&gt; this morning.&lt;/p&gt;
&lt;p&gt;This is for that Markdown document I&amp;rsquo;ve been writing in Obsidian. I&amp;rsquo;ve been sharing it with others using PDF exports, but it&amp;rsquo;s importance has grown to a point where I need to start properly maintaining a change log. And also… sharing via PDF exports? What is this? Microsoft Word in the 2000s?&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;m hoping to move it to a Gitlab repo. Gitlab does support Markdown with integrated Mermaid diagrams, but not Obsidian&amp;rsquo;s extension for callouts. I&amp;rsquo;d like to be able to keep these callouts as I used them in quite a few places.&lt;/p&gt;
&lt;p&gt;While browsing through Gitlabs&amp;rsquo;s help guide on Markdown extensions, I came across their support for Asciidoc. I&amp;rsquo;ve haven&amp;rsquo;t tried Asciidoc before, and after taking a brief look at it, it seemed like a format better suited for the type of document I&amp;rsquo;m working on. It has things like auto-generated table of contents, builtin support for callouts, proper title and heading separations; just features that work better than Markdown for long, technical documents. The language syntax also supports a number of text-based diagram formats, including Mermaid.&lt;/p&gt;
&lt;p&gt;However, as soon as I started porting the document over to Asciidoc, I found it to be no Markdown in terms of mind share. Tool support is quite limited, in fact it&amp;rsquo;s pretty bad. There&amp;rsquo;s nothing like iA Writer for Asciidoc, with the split-screen source text and live preview that updates when you make changes. There&amp;rsquo;s loads of these tools for Markdown, so many that I can&amp;rsquo;t keep track of them (the name of the iA Writer alternative always eludes me).&lt;/p&gt;
&lt;p&gt;Code editors should work, but they&amp;rsquo;re not perfect either. GoLand supports Asciidoc, but not with embedded Mermaid diagrams. At least not out of the box: I had to get a separate JAR which took around 10 minutes to download. Even now I&amp;rsquo;m fighting with the IDE, trying to get it to find the Mermaid CLI tool so it can render the diagrams. I encountered none of these headaches when using Markdown: GoLand supports embedded Mermaid diagrams just fine. I guess I could try VS Code, but to download it just for this one document? Hmm.&lt;/p&gt;
&lt;p&gt;In theory the &lt;a href=&#34;https://asciidoctor.org&#34;&gt;de-facto CLI tool&lt;/a&gt; should work, but in order to get Mermaid diagrams working there I need to download a Ruby gem and bundle it with the CLI tool (this is in addition to the same Mermaid command-line tool GoLand needs). Why this isn&amp;rsquo;t bundled by default in the Homebrew distribution is beyond me.&lt;/p&gt;
&lt;p&gt;So for now I&amp;rsquo;m abandoning my wish for callouts and just sticking with Markdown. This is probably the best option, even if you set tooling aside. After all, everyone knows Markdown, a characteristic of the format that I shouldn&amp;rsquo;t simply ignore. Especially for these technical documents, where others are expected to contribute changes as well.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a bit of a shame though. I still think Asciidoc could be better for this form of writing. If only those that make writing tools would agree.&lt;/p&gt;
&lt;p&gt;Addendum: after drafting this post, I found that Gitlab actually supports auto-generated table of contents in Markdown too. So while I may not have it all with Markdown — such as callouts — I can still have a lot.&lt;/p&gt;
&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-asciidoc-markdown.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;
</description>
    </item>
    
    <item>
      <title>My Position On Blocking AI Web Crawlers</title>
      <link>https://lmika.org/2024/06/18/183404.html</link>
      <pubDate>Tue, 18 Jun 2024 18:34:04 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/06/18/183404.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m seeing a lot of posts online about sites and hosting platforms blocking web crawlers used for AI training. I can completely understand their position, and fully support them: it&amp;rsquo;s their site and they can do what they want.&lt;/p&gt;
&lt;p&gt;Allow me to lay my cards on the table. My current position is to allow these crawlers to access my content. I&amp;rsquo;m choosing to opt in, or rather, not to opt out. I&amp;rsquo;m probably in the minority here (well, the minority of those I follow), but I do have a few reasons for this, with the principal one being that I use services like ChatGTP and get value from them. So to prevent them from training their models on my posts feels personally hypocritical to me. It&amp;rsquo;s the same reason why I don&amp;rsquo;t opt out of Github Copilot crawling my open source projects (although that&amp;rsquo;s a little more theoretical, as I&amp;rsquo;m not a huge user of Copilot). To some, this position might sound weird, and when you consider the gulf between what value these AI companies get from scraping the web verses what value I get from them as a user, it may seem downright stupid. And if you approach it from a logical perspective, it probably is. But hey, we&amp;rsquo;re in the realm of feelings, and right now this is just how I feel. Of course, if I were to make a living out of this site, it would be a different story. But I don&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;And this leads to the tension I see between site owners making decisions regarding their own content, and services making decisions on behalf of their users. This site lives on Micro.blog, so I&amp;rsquo;m governed by what Manton chooses to do or not do regarding these crawlers. I&amp;rsquo;m generally in favour of what Micro.blog has chosen so far: allowing people to block these scrapers via &amp;ldquo;robots.txt&amp;rdquo; but not yet blocking requests based on their IP address. I&amp;rsquo;m aware that others may not agree, and I can&amp;rsquo;t, in principal, reject the notion of a hosting provider choosing to block this crawlers at the network layer. I am, and will continue to be, a customer of such services.&lt;/p&gt;
&lt;p&gt;But I do think some care should be considered, especially when it comes to customers (and non-customer) asking these services to add these network blocks. You may have good reason to demand this, but just remember there are users of these services that have opinions that may differ. I personally would prefer a mechanism where you opt into these crawlers, and this would be an option I&amp;rsquo;ll probably take (or probably not; my position is not &lt;em&gt;that&lt;/em&gt; strong). I know that&amp;rsquo;s not possible under all circumstances so I&amp;rsquo;m not going to cry too much if this was not offered to me in lieu of a blanket ban.&lt;/p&gt;
&lt;p&gt;I will make a point on some comments that I&amp;rsquo;ve seen that, if taken in an uncharitable way, imply that creators that have no problem with these crawlers do not care about their content. I think such opinions should be worded carefully. I know how polarising the use of AI currently is, and making such remarks, particularly within posts that are already heated due to the author&amp;rsquo;s feelings regarding these crawlers, risks spreading this heat to those that read it. The tone gives the impression that creators okay with these crawlers don&amp;rsquo;t care about what they push online, or should care more than they do. That might be true for some — might even be true for me once in a while — but to make such blanket assumptions can come off as a little insulting. And look, I know that&amp;rsquo;s not what they&amp;rsquo;re saying, but it can come across that way at times.&lt;/p&gt;
&lt;p&gt;Anyway, that&amp;rsquo;s my position as of today. Like most things here, this may change over time, and if I become disenfranchised with these companies, I&amp;rsquo;ll join the blockade. But for the moment, I&amp;rsquo;m okay with sitting this one out.&lt;/p&gt;
&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-ai-web-crawlers.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;
</description>
    </item>
    
    <item>
      <title>Thinking About Plugins In Go</title>
      <link>https://lmika.org/2024/06/12/thinking-about-plugins.html</link>
      <pubDate>Wed, 12 Jun 2024 15:14:49 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/06/12/thinking-about-plugins.html</guid>
      <description>&lt;p&gt;Thought I&amp;rsquo;d give Go&amp;rsquo;s plugin package a try for something. Seems to works fine for the absolutely simple things. But start importing any dependencies and it becomes a non-starter. You start seeing these sorts of error messages when you try to load the plugin:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;plugin was built with a different version of package golang.org/x/sys/unix
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Looks like the host and plugins need to have exactly the same dependencies. To be fair, the package documentation says as much, and also states that the best use of plugins is for dynamically loaded modules build from the same source. But that doesn&amp;rsquo;t help me and what I&amp;rsquo;m trying to do, which is encoding a bunch of private struct types as Protobuf messages.&lt;/p&gt;
&lt;p&gt;So might be that I&amp;rsquo;ll need to find another approach. I wonder how others would do this. An embedded scripting language would probably not be suitable for this, since I&amp;rsquo;m dealing with Protobuf and byte slices. Maybe building the plugin as a C shared object? That could work, but then I&amp;rsquo;d loose all the niceties that come from using Go&amp;rsquo;s type system.&lt;/p&gt;
&lt;p&gt;Another option would be something like WASM. It&amp;rsquo;s interesting seeing WASM modules becoming a bit of a thing for plugin architectures. There&amp;rsquo;s even a &lt;a href=&#34;https://github.com/wasmerio/wasmer-go&#34;&gt;Go runtime&lt;/a&gt; to host them. The only question is whether they would have the same facilities as regular process would have, like network access; or whether they&amp;rsquo;re completely sandboxed, and you as the plugin host would need to add support for these facilities.&lt;/p&gt;
&lt;p&gt;I guess I&amp;rsquo;d find out if I were to spend any more time looking at this. But this has been a big enough distraction already. Building a process to shell-out to would work just fine, so that&amp;rsquo;s probably what I&amp;rsquo;ll ultimately do.&lt;/p&gt;
&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-thinking-about-go-plugins.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;
</description>
    </item>
    
    <item>
      <title>Word Cloud</title>
      <link>https://lmika.org/2024/06/11/word-cloud.html</link>
      <pubDate>Tue, 11 Jun 2024 17:36:48 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/06/11/word-cloud.html</guid>
      <description>&lt;p&gt;From &lt;a href=&#34;https://seths.blog/2024/06/an-overlooked-and-powerful-editing-tool/&#34;&gt;Seth&amp;rsquo;s blog&lt;/a&gt;:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Consider building a word cloud of your writing.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;Seems like a good idea so that&amp;rsquo;s what I did, taking the contents of the first page of this blog. Here it is:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/wordcloud.jpg&#34; width=&#34;600&#34; height=&#34;600&#34; alt=&#34;A word cloud containing the words of the first page of this blog&#34;&gt;
&lt;p&gt;Some observations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One of the most prominent words is &amp;ldquo;just&amp;rdquo;, with &amp;ldquo;it&amp;rsquo;s&amp;rdquo; not far behind. I though it&amp;rsquo;s because I started a lot of sentences with &amp;ldquo;it&amp;rsquo;s just&amp;rdquo;, but it turns out I&amp;rsquo;ve only used that phrase once, while the individual words show up around 10 times each. I guess I use &amp;ldquo;just&amp;rdquo; a lot (apparently, so does Seth). I am surprise to see the word &amp;ldquo;anyway&amp;rdquo; only showing up twice.&lt;/li&gt;
&lt;li&gt;Lots of first-person pronouns and articles, like &amp;ldquo;I&amp;rsquo;m&amp;rdquo;, &amp;ldquo;I&amp;rsquo;ve&amp;rdquo;, and &amp;ldquo;mine&amp;rdquo;. That&amp;rsquo;s probably not going to change either. This is just&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; the tonal choice I&amp;rsquo;ve made. I read many blogs that mainly speak in the second person and I don&amp;rsquo;t think it&amp;rsquo;s a style that works for me. Although I consciously know that they&amp;rsquo;re not speaking to me directly, or even to the audience as a whole, I don&amp;rsquo;t want to give that impression myself, unless that&amp;rsquo;s my intention. So it&amp;rsquo;ll be first person for the foreseeable future I&amp;rsquo;m sure.&lt;/li&gt;
&lt;li&gt;Because it&amp;rsquo;s only the first page, many of the more prominent words are from recent posts. So lots about testing, OS/2, and Bundanoon. I would like to cut down on how much I write about testing. A lot of it is little more than venting, which I guess is what one does on their blog, but I don&amp;rsquo;t want to make a habit of it.&lt;/li&gt;
&lt;li&gt;I see the word &amp;ldquo;good&amp;rdquo; is prominent. That&amp;rsquo;s good: not a lot of negative writing (although, &lt;a href=&#34;https://lmika.org/2024/05/12/manuel-moreale-wrote.html&#34;&gt;this is a choice too&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;I see the word &amp;ldquo;video&amp;rdquo; is also prominent. That&amp;rsquo;s probably not as good. Might be a sign I&amp;rsquo;m talking a little too much about the videos I&amp;rsquo;ve been watching.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Anyway, I thought these findings were quite interesting. One day, I&amp;rsquo;ll have to make another word cloud across all the posts on this blog.&lt;/p&gt;
&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-word-cloud.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;One more &amp;ldquo;just&amp;rdquo;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Day Trip to Bundanoon</title>
      <link>https://lmika.org/2024/06/10/day-trip-to-bundanoon.html</link>
      <pubDate>Sun, 09 Jun 2024 23:26:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/06/10/day-trip-to-bundanoon.html</guid>
      <description>&lt;p&gt;Decided to go on a day trip to Bundanoon today. It&amp;rsquo;s been five years since I last visited and I remember liking the town enough that I thought it&amp;rsquo;d be worth visiting again. It&amp;rsquo;s not close, around 1 hour and 40 minutes from Canberra, but it not far either and I thought it would be a nice way to spend the day. Naturally, others agreed, which I guess explains why it was busier than I expected, what with the long weekend and all. Fortunately, it wasn&amp;rsquo;t too crowded, and I still had a wonderful time.&lt;/p&gt;
&lt;p&gt;The goal was to go on a bush-walk first. I chose to do the Erith Coal Mine track, for no particular reason other than it sounded interesting. This circuit track was meant to take you to a waterfall by an old coal mine. However, the track leading to the actual mine was closed, thanks to the recent rain. In fact, if I could describe the bush-walks in one word, it would be &amp;ldquo;wet&amp;rdquo;. The ground was soaked, as were the paths, and although conditions were lovely, the paths were still very slippery.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/pxl-20240609-010511518.jpg&#34;
     
        alt=&#34;Auto-generated description: A narrow, rocky trail winds through thick, bushy vegetation and trees under a clear blue sky.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Eirth Coal Mine track starting off pretty well, if a little wet.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/pxl-20240609-011050785.jpg&#34;
     
        alt=&#34;Auto-generated description: A narrow, winding trail with wooden steps descends through a dense forest of tall trees and thick vegetation.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Starting our desent into the vally.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/pxl-20240609-011536112.jpg&#34;
     
        alt=&#34;Auto-generated description: A metal railing pathway leads through a wooded area with numerous trees and plants under a clear blue sky.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Continuing our desent via these elevated platforms. The stairs here were very narrow.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/pxl-20240609-012326198.jpg&#34;
     
        alt=&#34;Auto-generated description: A fenced-off construction site with Danger Construction Site signs is located next to a flowing waterway amidst surrounding greenery.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  I assume the path is meant to cross this flat stone, which I&amp;#39;m guessing is either dry, or has a small stream. But all the recent rain has turned it into a torrent.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/pxl-20240609-012638908.jpg&#34;
     
        alt=&#34;Auto-generated description: A weathered picnic table and bench set is situated in a natural, wooded area with a signpost nearby.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  My make shift walking staff, which prooved useful getting through the slippery parts of the trail.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/pxl-20240609-012854757.jpg&#34;
     
        alt=&#34;Auto-generated description: A winding gravel road curves through a dense forested area under a clear blue sky.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  View behind me as a start climbing back to road level.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;figure&gt;
&lt;video src=&#34;https://cdn.uploads.micro.blog/25293/2024/bundanoon-walk-1.mov&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;
&lt;figcaption&gt;I assume the mine was across these rocks, but there was no way I was going to cross it.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;After completing that circuit in probably 45 minutes, my appetite for bush-walking was still unsatisfied, so I tried the Fairy Bower Falls walk next. This was not as steep as the first one, but it turned to be a much harder track due to how wet and slippery everything was.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/pxl-20240609-015027509.jpg&#34;
     
        alt=&#34;Auto-generated description: A dirt pathway in a forested area is marked by a sign indicating Fairy Beaver Walk and features another small signpost further along the path.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Enterance to Fairy Bower Falls walk.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/pxl-20240609-015716731.jpg&#34;
     
        alt=&#34;Auto-generated description: A narrow, rocky trail winds through a dense, wooded area with sunlight filtering through the trees.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  This track wet and very slippery. Much of it was barely walkable if you wanted to keep your shoes dry.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/pxl-20240609-020820102.jpg&#34;
     
        alt=&#34;Auto-generated description: A dense thicket of bushes and trees with varying shades of green foliage.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  This was the best shot I have of the waterfall, which is pretty bad, but I can assure you, it was there amongst the trees.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/pxl-20240609-021321005.jpg&#34;
     
        alt=&#34;Auto-generated description: A muddy forest trail is flanked by greenery with wooden planks placed sporadically for stability.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  The walk back to the road. This was much easier, partly because it was uphill but also because I know I already walked this part once.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;I stopped short of the end of this one too, as it seems the path was washed away. But I did manage to get a glimpse of the waterfall, so I&amp;rsquo;m considering that a win.&lt;/p&gt;
&lt;p&gt;After that, I returned to the town for lunch and some train spotting. The train line to Goulburn runs through Bundanoon, and the last time I was there, there was probably a freight train every hour or so. So I was hoping to get a good view of a lot of freight traffic. Maybe shoot a video of a train passing through the station I could share here.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/pxl-20240609-034857614.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A quaint train station platform with a sign that reads Bundanoon is shown, surrounded by trees and blue skies.&#34;&gt;
&lt;figcaption&gt;Bundanoon train station.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I had lunch outside and walked around the town a little, always within sight of the railway line, hoping for at least one train to pass through. But luck wasn&amp;rsquo;t on my side, and it wasn&amp;rsquo;t until I was on my way home that I saw what I think was a grain train passing through Wingello. I pulled over to take a video, and while I miss the locomotive, I got a reasonable enough recording of the wagons.&lt;/p&gt;
&lt;figure&gt;
&lt;video src=&#34;https://cdn.uploads.micro.blog/25293/2024/bundanoon-freight-3.mov&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;
&lt;figcaption&gt;Stopping by the side of the road to film these grain wagons passing by.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Being a little more hopeful, I stopped at Tallong, the next town along the road. I bought a coffee and went to the station to drink it and hopefully see a train pass through. Sadly, it was not to be. So I decided to head back home.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/pxl-20240609-043946516.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A quiet train station platform is shown with tracks stretching into the distance and surrounded by trees.&#34;&gt;
&lt;figcaption&gt;Tallong train station.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;So the train spotting was a bust, and the bush-walks were difficult, but all in all it was quite a nice day. I look forward to my next visit to Bundanoon. Lets hope the trains are running a little more frequently then.&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/day-trip-to-bundanoon.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;&lt;/audio&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>An Unfair Critique Of OS/2 UI Design From 30 Years Ago</title>
      <link>https://lmika.org/2024/06/06/173723.html</link>
      <pubDate>Thu, 06 Jun 2024 17:37:23 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/06/06/173723.html</guid>
      <description>&lt;p&gt;A favourite YouTube channel of mine is &lt;a href=&#34;https://www.youtube.com/@MichaelMJD&#34;&gt;Michael MJD&lt;/a&gt;, who likes to explore retro PC products and software from the 90s and early 2000s. Examples of these include videos on Windows 95, Windows 98, and the various consumer tech products designed to get people online. Can I just say how interesting those times were, where phrases such as &amp;ldquo;surfing the net&amp;rdquo; were thrown about, and where shopping centres were always used to explain visiting websites. I guess it was the best analogy one could use at the time.&lt;/p&gt;
&lt;p&gt;A staple of Michael MJD&amp;rsquo;s channel is when he installs an old operating systems onto old hardware&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. Yesterday, I watched the one where he &lt;a href=&#34;https://www.youtube.com/watch?v=Q6R1DK4BdmQ&#34;&gt;installed OS/2 Warp 4&lt;/a&gt; onto a 98 PC. We were an OS/2 household back when I was growing up, thanks to my dad using it for work, and I can remember using OS/2 2.1 and thinking it was actually pretty good. Better than Windows 95, in fact. I can&amp;rsquo;t remember if I ever used Warp 4, though.&lt;/p&gt;
&lt;p&gt;Anyway, while watching this video, and I was taken aback on how bad the UI design of OS/2 Warp 4 was. And really, I probably shouldn&amp;rsquo;t be throwing stones here: I&amp;rsquo;m not a great UI designer myself. But I guess my exposure to later versions of Windows and macOS matured my tastes somewhat; where I got exposed to the idea of interaction systems and user experience design (and generally just growing up). Obviously given how new the GUI was back then, many of these concepts were still in their infancy, although if you were to compare these UIs to the classic Mac or even Windows 3.1, I do think there was something missing in IBM&amp;rsquo;s design acumen. Was it ability? Interest? Care? Not sure. But given that it&amp;rsquo;s been 30 years, I&amp;rsquo;m not expecting the OS/2 devs  to be able to defend themselves now. That&amp;rsquo;s what makes this critique wholly unfair.&lt;/p&gt;
&lt;p&gt;Anyway, I&amp;rsquo;d thought I share some of the stills from this video that I thought contained some of the more cringeworthy UI designs&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;, along with my remarks. Enjoy.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/michael-mjd-frame-1.jpg&#34;
     
        alt=&#34;Auto-generated description: A computer monitor displays a system configuration screen for hardware and devices, including details such as locale settings, display types, peripheral devices, and the absence of a printer connection.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  First still is not too bad, except for how close the text is to the top of the window. Their vertical alignment by the icons is not great either. Also, what&amp;#39;s with the button placement at the bottom?
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/michael-mjd-frame-2.jpg&#34;
     
        alt=&#34;Auto-generated description: A computer screen displays a software installation menu with options for various components, including Assistance Center, Fonts, System Utilities, and more, with an indication of available and needed disk space.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Ah, okay. So the addition of the Previous button explains why Next is in the middle. I wouldn&amp;#39;t say that&amp;#39;s my preferred styling, but it does make sense in a way.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/michael-mjd-frame-3.jpg&#34;
     
        alt=&#34;Auto-generated description: A computer screen displays the setup and installation process for OS/2 Warp Version 4, prompting the user for personal information.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  When I first saw this still, I thought the labels were missaligned, but it seems to me now that it&amp;#39;s actually the text fields. Looks like they&amp;#39;ve been positioned too far down to the bottom left. Also, is that font different? Not sure I like it.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/michael-mjd-frame-4.jpg&#34;
     
        alt=&#34;Auto-generated description: A computer screen displays an installation window offering additional software options for installation, including Java versions, IBM Web Browser, OS/2 Toolkit, and Macromedia Flash Player, with system information along the bottom.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Spacing and alignment are reoccurring issues with these dialogs. Seeing the left margin of controls and all that wasted space on the bottom is amusing. One thing I also learnt from these videos is that IBM loves to be verbose on their UIs, which I pretty sure people will just skip past.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/michael-mjd-frame-5.jpg&#34;
     
        alt=&#34;Auto-generated description: A computer screen displays a password setup window over a Windows desktop background with various program icons, indicating instructions to enter a password in two fields.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  More bad alignment of labels and controls. A bit more space for that verification password label would&amp;#39;ve been nicer.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/michael-mjd-frame-6.jpg&#34;
     
        alt=&#34;Auto-generated description: A computer screen displays the IBM OS/2 Program Manager version 3.1 running on a system with 65,093 KB of total memory and 832 KB free.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Microsoft is not known for their great UI design skills, but this still just shows that, compared to OS/2, they are SO much better. At least everything is aligned, and buttons always tend to appear on the right (although this one is a bit close to the margin).
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/michael-mjd-frame-7.jpg&#34;
     
        alt=&#34;Auto-generated description: A computer screen displays the Volume - Properties window from an older operating system interface.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  So much empty space; although to be fair to the designs, I wouldn&amp;#39;t know how to lay this dialog out any better.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/michael-mjd-frame-8.jpg&#34;
     
        alt=&#34;Auto-generated description: A computer screen displays an OS/2 Window with a prompt asking if the user wants to close the session without saving data.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  Spacing and alignment, and now we&amp;#39;re mixing center and left justified buttons in our dialog.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2024/michael-mjd-frame-9.jpg&#34;
     
        alt=&#34;A computer screen displays a Netscape profile creation window with a profile named &amp;#39;default&amp;#39; highlighted.&#34; 
     
      /&gt;

  &lt;figcaption&gt;
  
  This dialog&amp;#39;s from Netscape and it&amp;#39;s still not great, but at least it&amp;#39;s better. I&amp;#39;m wondering if the bad layout of the text within the input fields themselves is just a result of the UI toolkit.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/narration-unfair-cretique-os2.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;When I first visited the channel, I said to myself: &amp;ldquo;who would want to watch videos of someone installing old software onto old hardware?&amp;rdquo; Well, apparently I do. 😀&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;All stills taken from the video &lt;a href=&#34;https://www.youtube.com/watch?v=Q6R1DK4BdmQ&#34;&gt;Installing the Last Version of IBM OS/2 on the $5 Windows 98 PC&lt;/a&gt;, by Michael MJD, release 21 Dec 2019.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Some More Thoughts On Unit Testing</title>
      <link>https://lmika.org/2024/06/03/some-more-thoughts.html</link>
      <pubDate>Mon, 03 Jun 2024 12:32:50 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/06/03/some-more-thoughts.html</guid>
      <description>&lt;p&gt;Kinda want to avoid this blog descending into a series of &amp;ldquo;this is wrong with unit testing&amp;rdquo; posts, but something did occur to me this morning. We&amp;rsquo;ve kicked off a new service at work recently. It&amp;rsquo;s just me and this other developer working on it at the moment, and it&amp;rsquo;s given us the opportunity to try out this &amp;ldquo;mockless&amp;rdquo; approach to testing, of which I ranted about a &lt;a href=&#34;https://lmika.org/2024/05/17/had-a-great.html&#34;&gt;couple of weeks ago&lt;/a&gt; (in fact, the other developer is the person I had that discussion with). And it&amp;rsquo;s probably no surprise, but I&amp;rsquo;m finding writing tests this way to be a much nicer experience already.&lt;/p&gt;
&lt;p&gt;And I think I&amp;rsquo;ve come to the realisation that the issue is not so much with mocking itself. Rather, it&amp;rsquo;s the style of testing that it encourages. When you&amp;rsquo;re testing against &amp;ldquo;real&amp;rdquo; services, you&amp;rsquo;ll left with treating them as a black box. There&amp;rsquo;s no real way to verify  your code is working correctly other than letting it interact with these services as it would, and then &amp;ldquo;probing&amp;rdquo; them in some way — running queries, waiting for messages to arrive at topics, etc. — to know whether the interaction worked. You can&amp;rsquo;t just verify this by intercepting the various calls made by the service (well you can, but it would be difficult to do).&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s nothing about mocking that inhibits this style of testing. You can use mocks to simulate a message broker by storing the messages in an in-memory list, for example. What it does do, however, is make it easier to write tests that simply intercept the calls of the service and verify that they were made. It&amp;rsquo;s less upfront work than setting up a real client, or simulating a message broker, but now you&amp;rsquo;ve tied your tests to your implementation. You may feel like you&amp;rsquo;ve saved time and effort now, but really you&amp;rsquo;ve just deferred it for later, when you need to fix your tests when you&amp;rsquo;ve change your implementation.&lt;/p&gt;
&lt;p&gt;I know this is stuff I said before, so I&amp;rsquo;ll just stop here, and end by saying that I&amp;rsquo;m excited to seriously try out this approach to writing unit tests. Is it a better approach than using mocks? I guess time will tell. It&amp;rsquo;s been my experience that it&amp;rsquo;s when you need to refactor things in your service when you find out how good your tests are. So I guess we&amp;rsquo;ll check back in about six months or so.&lt;/p&gt;
&lt;audio controls=&#34;controls&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2024/some-more-thoughts-on-unit-testing.mp3&#34; preload=&#34;metadata&#34; style=&#34;display: none;&#34;&gt;
</description>
    </item>
    
    <item>
      <title>Side Scroller 95</title>
      <link>https://lmika.org/2024/05/26/side-scroller.html</link>
      <pubDate>Sun, 26 May 2024 11:49:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/05/26/side-scroller.html</guid>
      <description>&lt;p&gt;I haven&amp;rsquo;t been doing much work on new projects recently. Mainly, I&amp;rsquo;ve
been perusing my archives looking for interesting things to play around
with. Some of them needed some light work to get working again but
really I just wanted to experience them.&lt;/p&gt;
&lt;p&gt;I did come across one old projects which I&amp;rsquo;ll talk about here: a game I
called Side Scroller 95.  And yes, the &amp;ldquo;95&amp;rdquo; refers to Windows 95.&lt;/p&gt;
&lt;p&gt;This was a remake of &lt;a href=&#34;https://www.f5to.run/apps/sidescr/&#34;&gt;Side Scroller&lt;/a&gt;, a QBasic game I made back in
the day. Having played around with Delphi programming after a while, and
finding a bunch of DirectX 7 components, I set about recreating this
game.  I&amp;rsquo;m not sure why I decided to remake this game vs. making
something new. Was it because it was simple enough to try, or I found
the levels interesting? Maybe? I can&amp;rsquo;t really
say.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/fe083b0141.jpg&#34;&gt;
&lt;figcaption&gt;SS95 showing level E2M2.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Anyway, there wasn&amp;rsquo;t much to this game from the beginning. All the
movement was cell based, with the usual assortment of solid tiles,
hazards, and keys and locks (no pickups though). I eventually added a
simple enemy as well: a robot which just went from left to right.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/1fedd0a116.jpg&#34;&gt;
&lt;figcaption&gt;Level set 2 has the more interesting levels. This is S2E7, showing the player loose breath while underwater.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/d12086fd10.jpg&#34;&gt;
&lt;figcaption&gt;Level S2E8, with a &#34;gold&#34; tile scheme and backdrop.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/b9eb873019.jpg&#34;&gt;
&lt;figcaption&gt;Level S2E10, with a rainbow scheme and lots of invisible laser emitters.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/cc924d067b.jpg&#34;&gt;
&lt;figcaption&gt;Level S2E11, with an &#34;underwater&#34; scheme, which is just blues and greens. Also note the only enemy of the game, which was a robot that moved from left to right.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This project really showcases my crappy art skills at the time (I wish I
could say they improved, but that would be a lie). The tiles and sprites
were creating using MSPaint. This was back in the day when MSPaint was
actually quite usable for pixel art, where drawing a rectangle actually
rendered a rectangle then and there, rather than product an object which
could be moved around (it&amp;rsquo;s just not the same now). The backgrounds were
made by making gradients in Microsoft Word, taking a screenshot, and
cropping them. And the sound effects were taken from the PC version of
Metal Gear Solid (the WAV files were just sitting there on the file
system).&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/63b35d5c5e.jpg&#34;&gt;
&lt;figcaption&gt;The level editor. Unfortunately, it doesn&#39;t render well in Crossover so here&#39;s how it looks in Delphi 7&#39;s form designer.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/70809a6e13.jpg&#34;&gt;
&lt;figcaption&gt;The &#34;Level Properties&#34; form in the level editor, showing how Sprites were configured. Adding sprites were an absolute nightmare.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The game itself is pretty unremarkable, although I would say that one
attribute that I really enjoyed doing is adding level scripts. These
were Pascel scripts — interpreted by a Delphi control I found — that
intercepted events, such as triggers or timeouts, and modified the level
in some way. This was done late in the project, so it wasn&amp;rsquo;t used much,
but it did make for some unique level-specific elements in the later
levels. An example of one of the level scripts is provided below. This
added platforms which ascended one cell at a time when activated. I
forget most of what the built-ins did but I believe the &lt;code&gt;OnActivateX&lt;/code&gt;
hooks fired when a switch was triggered and the &lt;code&gt;OnTimedEventX&lt;/code&gt; fired
when a timer elapsed. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unit LevelScr;

uses SSUTIL;

const
  INT_STX  = 0;
  INT_STY  = 1;
  INT_EDX  = 2;
  INT_EDY  = 3;

  INT_CURX = 4;
  INT_CURY = 5;

procedure LevSetupMove(sx, sy, ex, ey: integer);
begin
  SetStackInteger(INT_STX, sx); 
  SetStackInteger(INT_STY, sy);
  SetStackInteger(INT_EDX, ex);
  SetStackInteger(INT_EDY, ey);

  SetStackInteger(INT_CURX, sx);
  SetStackInteger(INT_CURY, sy);
end;

procedure OnActivate1(tag: integer; ison: boolean);
begin
  case tag of
    1: LevSetupMove(6, 9, 6, 5);
    2: LevSetupMove(18, 9, 18, 4);
    3: begin
     LevSetupMove(20, 19, 20, 16);
     Level.SetTile(21, 18, 52);
       end;
    4: LevSetupMove(11, 23, 11, 19);
    5: LevSetupMove(10, 23, 10, 17);
    6: LevSetupMove(9, 17, 9, 15);
    7: LevSetupMove(9, 15, 9, 13);
    8: LevSetupMove(9, 13, 9, 11);
  end;
  SetupTimer(1, 2, 1, true); 
end;

procedure OnActivate2(tag: integer; ison: boolean);
begin
  case tag of
    1: LevSetupMove(20, 20, 20, 16);
  end;
  SetupTimer(2, 2, 1, true); 
end;

procedure OnTimedEvent1(event: integer);
var cx, cy: integer;
begin
    
  cx := StackInteger(INT_CURX);
  cy := StackInteger(INT_CURY);

  if ((cx = StackInteger(INT_EDX)) and
    (cy = StackInteger(INT_EDY))) then
  begin
    ResetTimer(1);
  end
  else
  begin
    Level.SetTile(cx, cy, 12);
    cy := cy - 1;
    Level.SetTile(cx, cy, 13);

    SetStackInteger(INT_CURX, cx);
    SetStackInteger(INT_CURY, cy);
    PlaySound(11, false);
  end;
end;

procedure OnTimedEvent2(event: integer);
var cx, cy: integer;
begin
    
  cx := StackInteger(INT_CURX);
  cy := StackInteger(INT_CURY);

  if ((cx = StackInteger(INT_EDX)) and
    (cy = StackInteger(INT_EDY))) then
  begin
    ResetTimer(2);
    Explode(cy, cx);
  end
  else
  begin
    cy := cy - 1;
    Level.SetTile(cx, cy, 0);

    SetStackInteger(INT_CURX, cx);
    SetStackInteger(INT_CURY, cy);
    PlaySound(11, false);
  end;
end;

end.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One other hallmark of this project was completely gutting all the
hard-coded logic and moving it into a game definition file. I build a
pretty simple &amp;ldquo;game designer&amp;rdquo; tool which managed all the artwork, tile
and sprite definitions, and also had custom logic implemented using that
same Pascal interpreter I was using for the level scripts. I never used
it for anything than the &amp;ldquo;Side Scroller&amp;rdquo; game, apart from a recreation
of the &lt;a href=&#34;https://www.f5to.run/apps/gamefile/&#34;&gt;File Platform game&lt;/a&gt; I also built way back when.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/e0972f7cc7.jpg&#34;&gt;
&lt;figcaption&gt;The game definition editor, showing the image tab.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/ac26279f9c.jpg&#34;&gt;
&lt;figcaption&gt;How tile logic was configured. This was modelled after the triggers used in Red Alert&#39;s map editor.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/930733faa4.jpg&#34;&gt;
&lt;figcaption&gt;How sprite logic was configured.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/257bb9d18b.jpg&#34;&gt;
&lt;figcaption&gt;Game logic scripts, which was used to extend the built-in actions.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Again, nothing about this was remarkable, and for a long time I had no
way to get this working. But thanks to Whisky I managed to launch this
for the first time in ages and have a play. I don&amp;rsquo;t know when I&amp;rsquo;ll be
able to do so again, nor whether it&amp;rsquo;s a good use of my time to try, so I
recorded some screencasts of the gameplay which you can see here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://youtu.be/wdGYN-lSUTo&#34;&gt;The Test Level Set&lt;/a&gt; — used to test the level script feature.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://youtu.be/ziizuqAdPWY&#34;&gt;The Additional Level Set&lt;/a&gt; — these were the levels that really made
use of level scripts, including the one listed above.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The desire to move away from cell-based movement and physics continued
my drive in making platform based games in Delphi. I eventually managed
to build such a game, which I will talk about once I can get it working
again.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Don&#39;t Leave User Experience For Later</title>
      <link>https://lmika.org/2024/05/22/dont-defer-ui.html</link>
      <pubDate>Wed, 22 May 2024 16:18:22 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/05/22/dont-defer-ui.html</guid>
      <description>&lt;p&gt;DDH &lt;a href=&#34;https://world.hey.com/dhh/beautiful-motivations-6fef7c73&#34;&gt;wrote a post yesterday&lt;/a&gt; that resonates with me. This is how he opens:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Programmers are often skeptical of aesthetics because they frequently associate it with veneering&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I doubt DHH reads this blog, but he could&amp;rsquo;ve address this post directly at me. I&amp;rsquo;m skeptical about aesthetics. Well… maybe not skeptical, but if we&amp;rsquo;re talking about personal projects, I do consider it less important than the functional side of things. Or at least I did.&lt;/p&gt;
&lt;p&gt;He continues:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Primary reason I appreciate aesthetics so much is its power to motivate. And motivation is the rare fuel that powers all the big leaps I&amp;rsquo;ve ever taken in my career and with my projects and products. It&amp;rsquo;s not time, it&amp;rsquo;s even attention. [sic&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;] It&amp;rsquo;s motivation. And I&amp;rsquo;ve found that nothing quite motivates me like using and creating beautiful things.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;He was mainly talking about the code design, but I think this extends to the project&amp;rsquo;s UI and UX. Particularly if you&amp;rsquo;re building it for yourself. Maybe especially if you&amp;rsquo;re building it for yourself.&lt;/p&gt;
&lt;p&gt;And this is where my story begins. From the start I&amp;rsquo;ve been putting off any task that would improve the user experience on &lt;a href=&#34;https://workpad.dev/categories/photo-bucket&#34;&gt;one of my side project&lt;/a&gt;. I considered such tasks unnecessary, or certainly less important than the &amp;ldquo;functional&amp;rdquo; side of things. Whenever faced with a decision on what part to work on next, the user experience work was left undone, usually with a thought along the lines of &amp;ldquo;eh, it&amp;rsquo;s UI stuff, I&amp;rsquo;ll do it later.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;But I think this was a mistake. Since I was actually using this tool, I was exposed to the clunky, unfinished UI whenever I needed to do something with it. And it turns out no matter how often you tell yourself that you&amp;rsquo;ll fix it later, a bad UI is still a bad UI, and it affects how it feels to use it. And let me tell you: it didn&amp;rsquo;t feel good at all. In fact, I detested it so much that I thought about junking it all together.&lt;/p&gt;
&lt;p&gt;It was only when I decided to add a bit of polish did things improved. And it didn&amp;rsquo;t take much: just fixing the highlights of the nav to reflect the current section, for example. But it was enough, and the improved UI made it feel better to use, which motivated me to get back to working on it again.&lt;/p&gt;
&lt;p&gt;So I guess the take away is similar to the point DHH made in his post, which is if something feels good to use, you&amp;rsquo;re more likely to work on it. And sure, you can&amp;rsquo;t expect to have a great user experience out of the box: I&amp;rsquo;ll have to work through some early iterations of it as I build things out. But I shouldn&amp;rsquo;t ignore user experience completely, or defer it until &amp;ldquo;later&amp;rdquo;. If it&amp;rsquo;s something I&amp;rsquo;m using myself, and it doesn&amp;rsquo;t feel good to use, best to spend some time on it now.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Should there be a &amp;ldquo;not&amp;rdquo; here?  &amp;ldquo;It&amp;rsquo;s not even attention?&amp;rdquo;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Writing Good Data Migration Scripts</title>
      <link>https://lmika.org/2024/04/02/writing-good-data.html</link>
      <pubDate>Tue, 14 May 2024 16:20:24 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/04/02/writing-good-data.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m waiting for a data migration to finish, so I&amp;rsquo;ve naturally got migration scripts on my mind.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s an art to writing a good migration script. It may seem that simply throwing together a small Python script would be enough; and for the simpler cases, it very well might be. But it&amp;rsquo;s been my experience that running the script in prod is likely to be very different than doing test runs in dev.&lt;/p&gt;
&lt;p&gt;There are a few reasons for this. For one thing, prod is likely to have &lt;em&gt;way&lt;/em&gt; more data so there will always be more to migrate. And dealing with production data is always going to be a little more stressful than in non-prod, especially when you consider things like restricted access and the impact of things going wrong. So a script with a better &amp;ldquo;user experience&amp;rdquo; is always going to be better than one slapped togeather.&lt;/p&gt;
&lt;p&gt;So without further ado, here&amp;rsquo;s the attributes for what I think makes for a good migration script:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;No migration script&lt;/strong&gt; — If you can get away with not writing a migration script, then this is preferred option. Of course, this will depend on how much data you&amp;rsquo;ll need to migrate, and how complicated keeping support for the previous version in your code-base. If the amount of data is massive (we&amp;rsquo;re talking millions or hundred of millions of rows), then this is probably your only option. On the other hand, if there&amp;rsquo;s a few hundred or a few thousands, then it&amp;rsquo;s probably just worth migrating the data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Always indicate progress&lt;/strong&gt; —  You&amp;rsquo;re likely going to have way more data in prod that your dev environments so consider showing ongoing progress of the running script. If there&amp;rsquo;s multiple stages in the migration process, make sure you log when each stage begins. If you&amp;rsquo;re running a scan or processing records, then give some indication of progress through the collection of rows. A progress bar is nice, but failing that, include a log message say every 1,000 records or so.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Calculate expected migration size if you can&lt;/strong&gt; — If it&amp;rsquo;s relatively cheap to get a count of the number of records that need to be migrated, then it&amp;rsquo;s helpful for the user to report this to the user. Even an estimate would be good just to give a sense of magnitude. If it&amp;rsquo;ll be too expensive to do so, then you can ignore it: better to just get migrating rather than have the user wait for a count.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Silence is golden&lt;/strong&gt; — Keep logging to the screen to a minimum, mainly progress indicators plus and any serious warnings or errors. Avoid bombarding the user with spurious log messages. They want to know when things go wrong, otherwise they just want to know that the script is running properly. That said:&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Log everything to a file&lt;/strong&gt; — If the script is involved with migrating data, but will ignored records that have already been migrated, then log that records will be skipped. What you&amp;rsquo;re looking for is assurance that all records have been dealt with, meaning that any discrepancy with the summary report (such as max records encountered vs. max records migrated) can be reconciled with the log file.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;May your migrations be quite and painless.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Sharing Too Much About Too Little</title>
      <link>https://lmika.org/2024/05/12/manuel-moreale-wrote.html</link>
      <pubDate>Sun, 12 May 2024 08:06:29 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/05/12/manuel-moreale-wrote.html</guid>
      <description>&lt;p&gt;Manuel Moreale wrote an interesting post today about &lt;a href=&#34;https://manuelmoreale.com/sharing-too-much-about-too-little&#34;&gt;sharing stuff online&lt;/a&gt;:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Life can be joyful and wonderful and marvellous. But it can also be a fucking nightmare. And yes, it’s important to celebrate the victories and to immortalise the glorious moment. But it’s also important to document the failures, the shitty moments, the dark places our minds find themselves stuck in. It’s all part of what makes us unique after all.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I can certaintly appreciate those that are willing to share both the ups and downs to others online. But I know for myself that I rather not, for a couple of reasons. First being that there’s already so much dark stuff online already: why add to that? And the second being that, along with being a public journal of most of my day, this site is a bit of an escape as well: a refuge that I can visit when the world gets a little too much for me.&lt;/p&gt;
&lt;p&gt;And it may seem that what is posted here is exclusively what I feel, think, or do during the day, but that cannot be further from the truth. Shitty things happen to me, I have dark thoughts, fits of despair, or just general periods of malaise. Those get documented too, but in a private journal that’s not for public consumption.&lt;/p&gt;
&lt;p&gt;That’s not to say that others should do likewise. I’m not here to tell you what to post, and why: you do you. This is just explaining how and why I post what I post. Maybe one day this will change: but until then that’s how I like to do things.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>As Someone Who Works In Software</title>
      <link>https://lmika.org/2024/05/07/as-someone-who.html</link>
      <pubDate>Tue, 07 May 2024 08:45:14 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/05/07/as-someone-who.html</guid>
      <description>&lt;p&gt;As someone who works in software…&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I cringe every time I see &lt;a href=&#34;https://www.bbc.com/news/uk-england-york-north-yorkshire-68942321&#34;&gt;society bend to the limitations&lt;/a&gt; of the software they use. It shouldn&amp;rsquo;t be this way; the software should serve the user, not the other way around.&lt;/li&gt;
&lt;li&gt;I appreciate a &lt;a href=&#34;https://www.manton.org/2024/05/06/not-sick-of.html&#34;&gt;well designed API&lt;/a&gt;. Much of my job is using APIs built by others, and the good ones always feel natural to use, like water flowing through a creek. Conversely, a badly designed API makes me want to throw may laptop to the ground.&lt;/li&gt;
&lt;li&gt;I think a well designed standard is just as important as a well designed API. Thus, if you&amp;rsquo;re extending the standard in a way that  &lt;a href=&#34;https://developer.chrome.com/blog/masonry#what_do_we_do_about_the_things_that_dont_make_sense_in_each_layout_method&#34;&gt;adds a bunch of exceptions&lt;/a&gt; to something that&amp;rsquo;s already there, you may want to reflect on your priorities and try an approach that doesn&amp;rsquo;t do that.&lt;/li&gt;
&lt;li&gt;I also try to appreciate, to varying levels of success, that there are &lt;a href=&#34;https://webkit.org/blog/15269/help-us-invent-masonry-layouts-for-css-grid-level-3/&#34;&gt;multiple ways to do something&lt;/a&gt; and once all the hard and fast requirements are settled, it usually just comes down to taste. I know what appeals to my taste, but I also (try to) recognise that others have their own taste as well, and what appeals to them may not gel with me. And I just have to deal with it. I may not like it, but sometimes we have to deal with things we don&amp;rsquo;t like.&lt;/li&gt;
&lt;li&gt;I believe a user&amp;rsquo;s home directory is their space, not yours. And you better have a bloody good reason for &lt;a href=&#34;https://512pixels.net/2024/04/ai-overlay-tmp-home-folder-mac-os/&#34;&gt;adding stuff there&lt;/a&gt; that the user can see and didn&amp;rsquo;t ask for.&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>The Perfect Album</title>
      <link>https://lmika.org/2024/05/04/the-perfect-album.html</link>
      <pubDate>Sat, 04 May 2024 09:03:46 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/05/04/the-perfect-album.html</guid>
      <description>&lt;p&gt;The guys on Hemispheric Views have got me blogging once again. &lt;a href=&#34;https://listen.hemisphericviews.com/110&#34;&gt;The latest episode&lt;/a&gt; bought up the topic of the perfect album: an album that you can &amp;ldquo;just start from beginning, let it run all the way through without skipping songs, without moving around, just front to back, and just sit there and do nothing else and just listen to that whole album&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Well, having &lt;a href=&#34;https://lmika.org/2024/04/19/crashing-hemispheric-views.html&#34;&gt;crashed Hemispheric Views once&lt;/a&gt;, I’d thought it’s time once again to give my unsolicited opinion on the matter. But first, some comments on some of the suggestions made on the show.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll start with Martin’s suggestion of the Cat Empire. I feel like I should like Cat Empire more than I currently do. I used to know something who was fanatic about them. He shared a few of their songs when we were jamming out — we were in a band together — and on the whole I thought they were pretty good. They&amp;rsquo;re certainly a talented bunch of individuals. But it&amp;rsquo;s not a style of music that gels with me. I&amp;rsquo;m just not a huge fan of scar, which is funny considering the band we were both in was a scar band.&lt;/p&gt;
&lt;p&gt;I feel like I haven&amp;rsquo;t given Radiohead a fair shake. There were many people that approached me and said something of the lines of “you really should try Radiohead; it’s a style of music you may enjoy,” and I never got around to following their advice. I probably should though, I think they may be right. Similarly for Daft Punk, of which I have heard a few tracks of and thought them to be pretty good. I really should give &lt;em&gt;Random Access Memory&lt;/em&gt; a listen.&lt;/p&gt;
&lt;p&gt;I would certainly agree with Jason’s suggestion of the &lt;em&gt;Dark Side of the Moon&lt;/em&gt;. I count myself a Pink Floyd fan, and although I wouldn&amp;rsquo;t call this my favourite album by them, it&amp;rsquo;s certainly a good album (if you were to ask, my favourite would probably be either &lt;em&gt;The Wall&lt;/em&gt; or &lt;em&gt;Wish You Were Here&lt;/em&gt;, plus side B of &lt;em&gt;Metal&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;As to what my idea of a perfect album would be, my suggestion is pretty simple: it&amp;rsquo;s anything by Mike Oldfield.&lt;/p&gt;
&lt;p&gt;LOL, just kidding!&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; 😄&lt;/p&gt;
&lt;p&gt;No, I’d say a great example of a perfect album is &lt;a href=&#34;https://songwhip.com/jeff-wayne/jeff-waynes-musical-version-of-the-war-of-the-worlds1978&#34;&gt;Jeff Wayne’s musical adaptation of The War Of The Worlds&lt;/a&gt;.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/1400x1400bb.jpg&#34; width=&#34;400&#34; height=&#34;400&#34; alt=&#34;The album cover of Jeff Wayne’s Musical Version of The War Of The Worlds&#34;&gt;
&lt;p&gt;I used to listen to this quite often during my commute, before the pandemic arrived and bought that listen count down to zero. But I&amp;rsquo;ve picked it back up a few weeks ago and it&amp;rsquo;s been a constant earworm since. I think it ticks most of the boxes for a perfect album. It’s a narrative set to music, which makes it quite coherent and naturally discourages skipping tracks. The theming around the various elements of the story are really well done: hearing one introduced near the start of the album come back later is always quite a thrill, and you find yourself picking up more of these as you listen to the album multiple times. It&amp;rsquo;s very much not a recent album but, much like Pink Floyd, there&amp;rsquo;s a certain timelessness that makes it still a great piece of music even now.&lt;/p&gt;
&lt;p&gt;Just don&amp;rsquo;t listen to the recent remakes.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Although not by much.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Favourite Comp. Sci. Textbooks </title>
      <link>https://lmika.org/2024/05/02/favourite-comp-sci.html</link>
      <pubDate>Fri, 03 May 2024 09:33:12 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/05/02/favourite-comp-sci.html</guid>
      <description>&lt;p&gt;John Siracusa talked about his two favourite textbooks on &lt;a href=&#34;https://www.relay.fm/rd/233&#34;&gt;Rec Diffs #233&lt;/a&gt;: &lt;a href=&#34;https://micro.blog/books/9780138134594?title=Modern+Operating+Systems&amp;amp;author=Andrew+S.+Tanenbaum&amp;amp;cover_id=160178&#34;&gt;Modern Operation Systems&lt;/a&gt;, and &lt;a href=&#34;https://micro.blog/books/9780135408001?title=Computer+Networks&amp;amp;author=Andrew+S.+Tanenbaum&amp;amp;cover_id=160200&#34;&gt;Computer Networks&lt;/a&gt;, both by Andrew S. Tanenbaum. I had those textbooks at uni as well. I still do, actually. They&amp;rsquo;re fantastic. If I were to recommend something on either subject, it would be those two.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/pxl-20240502-232502146.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: Two textbooks titled Modern Operating Systems and Computer Networks by Andrew S. Tanenbaum are placed side by side on a surface.&#34;&gt;
&lt;figcaption&gt;The two Tanenbaums.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I will add that my favourite textbook I had during my degree was &lt;a href=&#34;https://micro.blog/books/9780133002140?title=Compilers&amp;amp;author=Alfred+V.+Aho&amp;amp;cover_id=160209&#34;&gt;Compilers: Principal, Techniques and Tools&lt;/a&gt; by Alfred V. Aho, et al. also known as the &amp;ldquo;dragon book.&amp;rdquo; If you&amp;rsquo;re interested in compiler design in any way, I can definitely recommend this book. It&amp;rsquo;s a little old, but really, the principals are more or less the same.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/pxl-20240502-232541743.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Auto-generated description: A book titled Compilers: Principles, Techniques, and Tools by Alfred V. Aho, Ravi Sethi, and Jeffrey D. Ullman, often referred to as the Dragon Book, is lying on a textured surface.&#34;&gt;
&lt;figcaption&gt;And dragon makes three.&lt;/figcaption&gt;
&lt;/figure&gt;
</description>
    </item>
    
    <item>
      <title>Thou Doth Promote Too Much</title>
      <link>https://lmika.org/2024/04/26/thou-doth-promote.html</link>
      <pubDate>Fri, 26 Apr 2024 08:19:30 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/04/26/thou-doth-promote.html</guid>
      <description>&lt;p&gt;Manual Moreale wrote an interesting &lt;a href=&#34;https://manuelmoreale.com/too-little-and-too-much-self-promotion&#34;&gt;post about self promotion&lt;/a&gt;, where he reflects on whether closing out all his People and Blogs post with a line pointing to his Ko-Fi page is too much:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;And so I added that single line. But adding that single line was a struggle. Because in my head, it’s obvious that if you do enjoy something and are willing to support it, you’d probably go look for a way to do it. That’s how my brain works. But unfortunately, that’s not how the internet works. Apparently, the correct approach seems to be the opposite one. You have to constantly remind people to like and subscribe, to support, to contribute, and to share.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I completely understand his feelings about this. I’m pretty sure I’d have just as much trouble adding such a promotion at the bottom of my post. Heck, it’s hard enough to write about what I’m working on here without any expectation from the reader other than to maybe, possibly, read it. They’ve been relegated to a separate blog, so as to not bother anyone.&lt;/p&gt;
&lt;p&gt;But as a reader of P&amp;amp;B, I think the line he added is perfectly fine. I think it’s only fair to ask people to consider supporting something where it’s obvious someone put a lot of effort into it, as he obviously has been doing with P&amp;amp;B.&lt;/p&gt;
&lt;p&gt;As for where to draw the line, I think I agree with Moreale:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;How much self-promotion is too much? Substack interrupting your reading experience to remind you to subscribe feels too much to me. An overlay interrupting your browsing to ask you to subscribe to a newsletter is also too much. Am I wrong? Am I crazy in thinking it’s too much?&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I get the need to “convert readers” but interrupting me to sign up to a newsletter is just annoying. And I’m not sure “annoying” is the feeling you want to imbue in your readers if you want them to do something.&lt;/p&gt;
&lt;p&gt;But a single line at the end of a quality blog post? Absolutely, go for it!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Crashing Hemispheric Views #109: HAZCHEM</title>
      <link>https://lmika.org/2024/04/19/crashing-hemispheric-views.html</link>
      <pubDate>Fri, 19 Apr 2024 10:55:07 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/04/19/crashing-hemispheric-views.html</guid>
      <description>&lt;p&gt;Okay, maybe not &amp;ldquo;crashing&amp;rdquo;, a.la &lt;a href=&#34;https://heydingus.net/&#34;&gt;Hey Dingus&lt;/a&gt;. But some thoughts did come to me while listening to &lt;a href=&#34;https://listen.hemisphericviews.com/109&#34;&gt;Hemispheric Views #109: HAZCHEM&lt;/a&gt; that I&amp;rsquo;d though I share with others.&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Haircuts&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;m sorry but I cannot disagree more. I don&amp;rsquo;t really want to talk while I&amp;rsquo;m getting a haircut. I mean I will if they&amp;rsquo;re striking up a conversation with me, but I&amp;rsquo;m generally not there to make new friends; just to get my hair cut quickly and go about my day.  I feel this way about taxis too.&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;I&amp;rsquo;m Rooted&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I haven&amp;rsquo;t really used &amp;ldquo;rooted&amp;rdquo; or &amp;ldquo;knackered&amp;rdquo; that much. My goto phrase is &amp;ldquo;buggered,&amp;rdquo; as in &amp;ldquo;oh man, I&amp;rsquo;m buggered!&amp;rdquo; or simply just &amp;ldquo;tired&amp;rdquo;. I sometimes used &amp;ldquo;exhausted&amp;rdquo; when I&amp;rsquo;m really tired, but there&amp;rsquo;s just too many syllable in that word for daily use.&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Collecting&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;m the same regarding stickers. I received (although not really sought after) stickers from various podcasts and I didn&amp;rsquo;t know what to do with them. I&amp;rsquo;ve started keeping them in this journal I never used, and apart from my awful handwriting documenting where they&amp;rsquo;re from and when I added them, so far it&amp;rsquo;s been great.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/pxl-20240413-064813292.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Journal opened up to a double page showing stickers from omg.lol and Robb Knight&#34;&gt;
&lt;p&gt;I probably do need to get some HV stickers, though.&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;A Trash Ad for Zachary&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;Should check to see if Johnny Decimal got any conversions from that ad in #106. 😀&lt;/p&gt;
&lt;p&gt;Also, here&amp;rsquo;s a free tag line for your rubbish bags: &lt;em&gt;we put the trash in the bags, not the pods&lt;/em&gt;.&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;🍅⏲️ 00:39:05&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;m going to make the case for Vivaldi. Did you know there&amp;rsquo;s actually a Pomodoro timer built into Vivaldi? Click the clock on the bottom-right of the status bar to bring it up.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/screenshot-2024-04-19-at-10.38.46am.png&#34; width=&#34;230&#34; height=&#34;435&#34; alt=&#34;Screenshot of the Clock panel in Vivaldi, which shows a Countdown and Alarm with a Pomodoro option&#34;&gt;
&lt;p&gt;Never used it myself, since I don&amp;rsquo;t use a Pomodoro timer, but can Firefox do that?!&lt;/p&gt;
&lt;p&gt;Once again, a really great listen, as always.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Micro.blog, Scribbles, And Multi-homing</title>
      <link>https://lmika.org/2024/04/16/yeah-honestly-i.html</link>
      <pubDate>Tue, 16 Apr 2024 19:44:32 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/04/16/yeah-honestly-i.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been ask why I&amp;rsquo;m using Scribbles given that I&amp;rsquo;m here on Micro.blog. Honestly I wish I could say I&amp;rsquo;ve got a great answer. I like both services very much, and I have no plans of abandoning Micro.blog for Scribbles, or visa-versa. But I am planning to use both for writing stuff online, at least for now, and I suppose the best answer I can give is a combination of various emotions and hang-ups I have about what I want to write about, and where it should go.&lt;/p&gt;
&lt;p&gt;I am planning to continue to use Micro.blog pretty much how others would use Mastodon: short-form posts, with the occasional photo, mainly about what I&amp;rsquo;m doing or seeing during my day. I&amp;rsquo;ll continue to write the occasional long-form posts, but it won&amp;rsquo;t be the majority of what I write here.&lt;/p&gt;
&lt;p&gt;My intentions for what I post on Scribbles is more likely to be long-form, which brings me to my first reason: I think I prefer Scribbles editor for long-form posts. Micro.blog works well for micro-blogging but I find any attempt to write something longer a little difficult. I can&amp;rsquo;t really explain it. It just feels like I&amp;rsquo;m spending more effort trying to get the words out on the screen, like they&amp;rsquo;re resisting in some way.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s easier for me to do this using Scribbles editor. I don&amp;rsquo;t know why. Might be a combination of how the compose screen is styled and laid out, plus the use of a WYSIWYG editor&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. But whatever it is, it all combines into an experience where the words flow a little easier for me. That&amp;rsquo;s probably the only way I can describe it. There&amp;rsquo;s nothing really empirical about it all, but maybe that&amp;rsquo;s the point. It&amp;rsquo;s involves the emotional side of writing: the &amp;ldquo;look and feel&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Second, I like that I can keep separate topics separate. I thought I could be someone who can write about any topic in one place, but when I&amp;rsquo;m browsing this site myself, I get a bit put out by all the technical topics mixed in with my day-to-day entries. They feel like they don&amp;rsquo;t belong here. Same with project notes, especially given that they tend to be more long-form anyway.&lt;/p&gt;
&lt;p&gt;This I just attribute to one of my many hang-ups. I never have this issue with other sites I visit. It may be an emotional response from seeing what I wrote about; where reading about my day-to-day induces a different feeling (casual, reflective) than posts about code (thinking about work) or projects (being a little more critical, maybe even a little bored).&lt;/p&gt;
&lt;p&gt;Being about to create multiple blogs in Scribbles, thanks to signing up for the lifetime plan, gives me the opportunity to create separate blogs for separate topics: one for current projects, one for past projects, and one for coding topics. Each of them, along with Micro.blog, can have their own purpose and writing style: more of a public journal for the project sites, more informational or critical on the coding topics, and more day-to-day Mastodon-like posts on Micro.blog (I also have a check-in blog which is purely a this-is-where-I&amp;rsquo;ve-been record).&lt;/p&gt;
&lt;p&gt;Finally, I think it&amp;rsquo;s a bit of that &amp;ldquo;ooh, shiny&amp;rdquo; aspect of trying something new. I definitely got that using Scribbles. I don&amp;rsquo;t think there&amp;rsquo;s much I can do about that (nor, do I want to 😀).&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s probably the best explanation I can give. Arguably it&amp;rsquo;s easier just writing in one place, and to that I say, &amp;ldquo;yeah, it absolutely is.&amp;rdquo; Nothing about any of this is logical at all. I guess I&amp;rsquo;m trying to optimise to posting something without all the various hang-ups I have about posting it at all, and I think having these separate spaces to do so helps.&lt;/p&gt;
&lt;p&gt;Plus, knowing me, it&amp;rsquo;s all likely to change pretty soon, and I&amp;rsquo;ll be back to posting everything here again.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Younger me would be shocked to learn that I&amp;rsquo;d favour a WYSIWYG editor over a text editor with Markdown support&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Moan-routine: Stripe Prices</title>
      <link>https://lmika.org/2024/04/12/moanroutine-stripe-prices.html</link>
      <pubDate>Fri, 12 Apr 2024 07:50:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/04/12/moanroutine-stripe-prices.html</guid>
      <description>&lt;p&gt;I love coding and anything computers. I&amp;rsquo;ve spent, and continue to spend, a significant amount of my life writing code. And on the whole, it&amp;rsquo;s been a magical experience.&lt;/p&gt;
&lt;p&gt;But not always.&lt;/p&gt;
&lt;p&gt;Sometimes I encounter something that makes me wonder why? Why was that designed that way? Why doesn&amp;rsquo;t it work? Why couldn&amp;rsquo;t this be easier? You encounter something that blocks you or puzzles you, maybe even questions how anything in computers can work at all. You&amp;rsquo;ve got things to do, and you try your best to work around the problem. Sometimes you succeed. Most times you get blocked and need to find some other way to fix it. And so the frustration builds, with no easy way to dissipate it.&lt;/p&gt;
&lt;p&gt;Well, this is my attempt to do just that. I need a place to write these somethings down, not least to make me feel better. To air thy grievance is to start the healing process. They say that &amp;ldquo;a moan begun is half done&amp;rdquo; after all. Well, okay, no-one has ever said that. But maybe we should. We may not have the power or energy to change things, or even to find out why things the way they are, but by God, we can make sure others hear about it.&lt;/p&gt;
&lt;p&gt;So enjoy these &amp;ldquo;moan-routines&amp;rdquo;. Or don&amp;rsquo;t. Honestly, it&amp;rsquo;s totally up to you. 🙂  P.S. The name moan-routine is a play on go-routine, a concept in Go.&lt;/p&gt;
&lt;p&gt;So, onto today&amp;rsquo;s moan. We&amp;rsquo;ll start the moans with something that saved my bacon today: which is Stripe prices. Actually, what saved me was something that we didn&amp;rsquo;t do with Stripe prices, which was archive them. For you see, archiving a price in Stripe makes it effectively unusable.&lt;/p&gt;
&lt;p&gt;This is arguably a good thing: you change a price, you don&amp;rsquo;t want anyone using the old one. Well, I say &amp;ldquo;change&amp;rdquo;; what I actually mean is replace. You can&amp;rsquo;t &amp;ldquo;change&amp;rdquo; a price directly in Stripe, say from $10 to $12. Instead, you create a new $12 price which will replace the old $10 one. Any new subscriptions you create will use the $12 one from now on, and ideally you&amp;rsquo;d never use that $10 price ever again.&lt;/p&gt;
&lt;p&gt;So you want to archive the $10 price. But there&amp;rsquo;s a problem: you&amp;rsquo;ve got all these customers with subscriptions still paying the old $10 price. Stripe doesn&amp;rsquo;t provide an easy way to move customers over to the new $12 one: this is something you have to do yourself. And you may not want to do that anyway. You may want to send your customers an email informing them about the price change; letting them know they have 30 days or whatever to be ready for it.&lt;/p&gt;
&lt;p&gt;And until you actually go in and change their subscriptions, they&amp;rsquo;ll be paying this old $10 price. And the minute you archive that $10 price, it becomes radioactive. Any attempt to do anything with it, or any subscription using it, will resolve in an error.&lt;/p&gt;
&lt;p&gt;Which gets to the moan: why do this for archive prices? I would&amp;rsquo;ve thought that Stripe was aware that subscriptions with old price was a thing. Am I to keep this old price active for as long as those accounts are still using it? Even if no-one else will ever get that price again? I wouldn&amp;rsquo;t be able to archive anything at all until them. And it may be a while before accounts do get changed. Think beta users grandfathered in-to a lower rate.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;d like Stripe to change this. Either get rid of the pitfalls around archive prices, or make it easy to port subscriptions over to the new price in some way (subscription schedules are a whole other moan). Instead, I stuck keeping old prices active because I&amp;rsquo;m afraid that archiving them will things. This clutters up the dashboard and introduce traps like creating a subscription with an old price.&lt;/p&gt;
&lt;p&gt;Look, prices change. It&amp;rsquo;s just the way of the world. And if Stripe is going to make prices effectively immutable, it would be helpful for us to make it easy to mark a price as &amp;ldquo;don&amp;rsquo;t use this for new stuff but keep the old staff effectively unchanged.&amp;rdquo; I expected this what archiving a price would be. Turns out I was wrong.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Small Calculator Commands</title>
      <link>https://lmika.org/2024/04/07/small-calculator-commands.html</link>
      <pubDate>Sun, 07 Apr 2024 21:34:18 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/04/07/small-calculator-commands.html</guid>
      <description>&lt;p&gt;This page documents the extra commands from Small Calculator. These were
taken from source code, pretty much as is, but styled to suite the web,
and any spelling mistakes fixed. These were retrievable from the
application itself by typing &amp;ldquo;help&amp;rdquo; follow by the command.&lt;/p&gt;
&lt;h2 id=&#34;available-commands&#34;&gt;Available Commands&lt;/h2&gt;
&lt;p&gt;The list of available commands are as follows&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;BLOCK &amp;lt;statements&amp;gt;    Executes a block of statements
HELP [topic]          Display help on topic
DEFFNC &amp;lt;function&amp;gt;     Defines a new function
ECHO &amp;lt;text&amp;gt;           Displays text on the line
ECHOEXPR &amp;lt;cmd&amp;gt;        Executes a command and displays the result
EXEC &amp;lt;file&amp;gt;           Executes a file of commands
FUNCTIONS             Displays all predefined functions
IF &amp;lt;pred&amp;gt;             Does a command on condition
RETURN &amp;lt;val&amp;gt;          Sets the return value
RETURNEXPR &amp;lt;cmd&amp;gt;      Sets the return value to the result of &amp;lt;cmd&amp;gt;

Type &amp;quot;HELP &amp;lt;command&amp;gt;&amp;quot; to see infomation on a command
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;block&#34;&gt;BLOCK&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;BLOCK {&amp;lt;cmd1&amp;gt;} {&amp;lt;cmd2&amp;gt;} ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Executes a block of commands.  The commands can be any statement
including other block statements.&lt;/p&gt;
&lt;h2 id=&#34;deffnc&#34;&gt;DEFFNC&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;DEFFNC &amp;lt;fnname&amp;gt;(&amp;lt;parameters&amp;gt;) = &amp;lt;command&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Defines a new function.  The function name can only consist of letters
and numbers.  Only a maximum of 4 parameters can be used in the
parameter list.  Parameters are required to be referred to using
$&lt;par&gt; in the function due to the interpretation of parameters.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;deffnc test(x) = $x + 2
      -- Adds two to any number
      
deffnc sign(k) = if ($k &amp;lt; 0} {-1} {if {$k &amp;gt; 0} {1} {0}}
      -- Returns -1 if k is negative, 1 if k is positive and
         0 if k is 0.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Functions can be recursive if using the &amp;ldquo;if&amp;rdquo; command.&lt;/p&gt;
&lt;h2 id=&#34;echo&#34;&gt;ECHO&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;ECHO &amp;lt;string&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Displays a string on the console.&lt;/p&gt;
&lt;h2 id=&#34;echoexpr&#34;&gt;ECHOEXPR&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;ECHOEXPR &amp;lt;command&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Executes a command and displays the result on the console.&lt;/p&gt;
&lt;h2 id=&#34;exec&#34;&gt;EXEC&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;EXEC &amp;lt;filename&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Executes a file of commands.  Lines starting with &amp;ldquo;;&amp;rdquo; are considered
comments.  Lines ending with &amp;quot;&amp;quot; are considered incomplete and the next
line is appended (after trimming) to the end of that line.&lt;/p&gt;
&lt;h2 id=&#34;functions&#34;&gt;FUNCTIONS&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;functions
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Displays all predefined functions.  No user functions are included.&lt;/p&gt;
&lt;h2 id=&#34;if&#34;&gt;IF&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;IF {&amp;lt;cond&amp;gt;} {&amp;lt;truepart&amp;gt;} {&amp;lt;falsepart&amp;gt;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the result of &lt;cond&gt; is not zero, executes &lt;truepart&gt;
which could be any statement, otherwise executes &lt;falsepart&gt;.  If
statements can be nested.&lt;/p&gt;
&lt;h2 id=&#34;help&#34;&gt;HELP&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;HELP [topic]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Displays a help topic on the console window.  Use &amp;ldquo;HELP
&lt;command&gt;&amp;rdquo; to find help on a particular command.&lt;/p&gt;
&lt;h2 id=&#34;return&#34;&gt;RETURN&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;RETURN &amp;lt;val&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sets the return value to &lt;val&gt;.&lt;/p&gt;
&lt;h2 id=&#34;returnexpr&#34;&gt;RETURNEXPR&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;RETURNEXPR &amp;lt;cmd&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sets the return value to the return value of &lt;cmd&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Small Calculator</title>
      <link>https://lmika.org/2024/04/07/small-calculator.html</link>
      <pubDate>Sun, 07 Apr 2024 21:19:01 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/04/07/small-calculator.html</guid>
      <description>&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; Unknown, but probably around 2005&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Retired&lt;/p&gt;
&lt;p&gt;Give me Delphi 7, a terminal control, and an expression parser, and of
course I&amp;rsquo;m going to build a silly little REPL program.&lt;/p&gt;
&lt;p&gt;I can&amp;rsquo;t really remember why I though this was worth spending time on,
but I was always interested in little languages (still am), and I guess
I though having a desk calculator that used one was worth having. I was
using a parser library I found on Torry&amp;rsquo;s Delphi Pages (the best site
at the time to get free controls for Delphi) for something else, and
after getting a control which simulated a terminal, I wrote a very
simple REPL loop which used the two.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/0720335caa.jpg&#34;&gt;
&lt;figcaption&gt;Example of basic usage.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;And credit to the expression parser developer: it was pretty decent. It
supported assignments and quite a number of functions. Very capable for
powering a desk calculator.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/2bd70652b6.jpg&#34;&gt;
&lt;figcaption&gt;List of functions the parser library supported.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;For a while the app was simply that. But, as with most things like this,
I got the itch to extend it a little. I started by added a few extra
commands. Simple things, like one that would echo something to the
screen. All quite innocent, if a little unnecessary. But it soon grew to
things like if statements, blocks using curly brackets, and function
definitions.&lt;/p&gt;
&lt;p&gt;It even extended to small batch scripts, like the one below.  The &lt;a href=&#34;https://folio.red/page/small-calculator-commands&#34;&gt;full
set of commands is listed here&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x := 2
y := 3

if {x = y} {echo 5} \
  {echo 232}

return 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These never went anywhere beyond a few tests. The extra commands was not
really enough to be useful, and they were all pretty awful. I was
already using a parser library so I didn&amp;rsquo;t want to spend any time
extending it. As a result, many of these extensions were little more
than things that scanned and spliced strings together. It was more of a
macro language rather than anything else.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/e47c08b61f.jpg&#34;&gt;
&lt;figcaption&gt;The full set of extensions.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Even with the expression parser the program didn&amp;rsquo;t see a great deal of
use. I was working on the replacement at the time which would eventually
be much more capable, and as soon as that was ready, this program fell
out of use.&lt;/p&gt;
&lt;p&gt;Even so, it was still quite a quirky little program to make a bit of an
impression.&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://media.lmika.org/small-calc-usage.mov&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Self-Driving Bicycle for The Mind</title>
      <link>https://lmika.org/2024/04/05/listening-to-the.html</link>
      <pubDate>Fri, 05 Apr 2024 07:14:18 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/04/05/listening-to-the.html</guid>
      <description>&lt;p&gt;While listening to the Stratchery interview with Hugo Berra, a thought occurred to me. Berra mentioned that Xaomi was building an EV. Not a self-driving one, mind you: this one has a steering wheel and peddles. He made the comment that were Apple to actually go through with releasing a car, it would look a lot like what Xaomi has built. I haven&amp;rsquo;t seen either car project myself so I&amp;rsquo;ll take his word for it.&lt;/p&gt;
&lt;p&gt;This led to the thought that it was well within Apple&amp;rsquo;s existing capability to release a car. They would&amp;rsquo;ve had to skill up in automotive engineering, but they can hire people to do that. What they couldn&amp;rsquo;t do was all the self-driving stuff. No-one can do that yet, and it seems to me that being unable to deliver on this non-negotiable requirement was one of the things that doomed the project. Sure there were others — seems like they were lacking focus in a number of other areas — but this seems like a big one.&lt;/p&gt;
&lt;p&gt;This led to the next thought, which is why Apple thought it was ever a good idea to actually have the car self-driving. What&amp;rsquo;s wrong with having one driven by the user? Seems like this was a very un-Apple-like product decision. Has Apple ever been good a releasing tech that would replace, rather than augment, the user&amp;rsquo;s interaction with the device? Do they have phones that would browse the web for you? Have they replaced ZSH with ChatGPT in MacOS (heaven forbid). Probably the only product that comes close is Siri, and we all know what a roaring success that is.&lt;/p&gt;
&lt;p&gt;Apple&amp;rsquo;s strength is in releasing products that keep human interaction a central pillar of it&amp;rsquo;s design. They should just stick with that, and avoid any of the self-driving traps that come up. It&amp;rsquo;s a &amp;ldquo;bicycle for a mind&amp;rdquo; after all: the human is still the one doing the peddling.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Post Headers </title>
      <link>https://lmika.org/2024/03/27/on-post-headers.html</link>
      <pubDate>Wed, 27 Mar 2024 16:13:17 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/03/27/on-post-headers.html</guid>
      <description>&lt;p&gt;My answer to &lt;a href=&#34;https://micro.blog/mandaris&#34;&gt;@mandaris&lt;/a&gt; question:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;How many of you are using headers in your blogging? Are you using anything that denotes different sections?&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I generally don&amp;rsquo;t use headers, unless the post is so long it needs them to break it up a little. When I do, I tend to start with H2, then step down to H3, H4, etc.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d love to start with H1, but most themes I encounter, including those from software like Confluence, style H1 to be almost the same size as the page title. This kills me as the page title should be separate from any H1s in the body, and styled differently enough that there&amp;rsquo;s no mistaking what level the header&amp;rsquo;s on.&lt;/p&gt;
&lt;p&gt;But, c&amp;rsquo;est la vie.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Sorting And Go Slices</title>
      <link>https://lmika.org/2024/03/26/sorting-and-go.html</link>
      <pubDate>Tue, 26 Mar 2024 08:38:57 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/03/26/sorting-and-go.html</guid>
      <description>&lt;p&gt;Word of caution for anyone passing Go slices to a function which will sort them. Doing so as is will modify the original slice.  If you were to write this, for example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sort&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;printSorted&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ys&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;sort&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Slice&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ys&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; { &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ys&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;] &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ys&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt;] })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ys&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;xs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;printSorted&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;xs&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;xs&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will find, when you run it, that both &lt;code&gt;xs&lt;/code&gt; and &lt;code&gt;ys&lt;/code&gt; will be sorted:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[1,2,3]
[1,2,3]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If this is not desired, the remedy would be make a copy of the slice prior to sorting it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;printSorted&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ys&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;ysDup&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make([]&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, len(&lt;span style=&#34;color:#a6e22e&#34;&gt;ys&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	copy(&lt;span style=&#34;color:#a6e22e&#34;&gt;ysDup&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ys&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;sort&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Slice&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ysDup&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; { &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ys&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;] &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ys&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt;] })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ysDup&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This make sense when you consider that the elements of a slice are more or less stored in a normal array. Slices add things like start and end items, which are stored as values within the slice struct, but the array itself is just a normal reference and it is this that &lt;a href=&#34;https://pkg.go.dev/sort@go1.22.1#Slice&#34;&gt;sort.Slice&lt;/a&gt; will modify.&lt;/p&gt;
&lt;p&gt;On the face of it, this is a pretty trivial thing to find out. But it&amp;rsquo;s worth noting here just so that I don&amp;rsquo;t have to remember it again.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Adding A Sidebar To A Tiny Theme Micro.blog</title>
      <link>https://lmika.org/2024/03/14/adding-a-sidebar.html</link>
      <pubDate>Thu, 14 Mar 2024 07:27:45 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/03/14/adding-a-sidebar.html</guid>
      <description>&lt;p class=&#34;callout&#34;&gt;This is now a standalone Micro.blog Plugin called &lt;a href=&#34;https://sidebar-for-tiny-theme.lmika.dev/&#34;&gt;Sidebar For Tiny Theme&lt;/a&gt; which adds support for this out of the box. The method documented below no longer works, but I&#39;m keeping it here for posterity reason.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d though I&amp;rsquo;d write a little about how I added a sidebar with recommendations to my Tiny Theme&amp;rsquo;ed Micro.blog, for anyone else interested in doing likewise. For an example on how this looks, please &lt;a href=&#34;https://lmika.org/2024/03/13/blogroll-ported-to.html&#34;&gt;see this post&lt;/a&gt;, or just go to the home page of this site.&lt;/p&gt;
&lt;p&gt;I should say that I wrote this in the form of a Micro.blog plugin, just so that I can use a proper text editor. It&amp;rsquo;s not published at the time of this post, but you can find all the code &lt;a href=&#34;https://github.com/lmika/sidebar-for-tinytheme&#34;&gt;on Github&lt;/a&gt;, and although the steps here are slightly different, they should still work using Micro.blog&amp;rsquo;s template designer.&lt;/p&gt;
&lt;p&gt;I started by defining a new Hugo partial for the sidebar. This means that I can choose which page I want it to appear on without any copy-and-paste. You can do so by adding a new template with the name &lt;code&gt;layouts/partials/sidebar.html&lt;/code&gt;, and pasting the following template:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sidebar&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sidebar-cell&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;h1&lt;/span&gt;&amp;gt;Recommendations&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;ul&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;blogroll&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {{ range .Site.Data.blogrolls.recommendations }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;li&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{ .url }}&amp;#34;&lt;/span&gt;&amp;gt;{{ .name }}: &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;span&lt;/span&gt;&amp;gt;{{ (urls.Parse .url).Hostname }}&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;span&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {{ else }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;p&lt;/span&gt;&amp;gt;No recommendations yet.&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;ul&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This creates a sidebar with a single cell containing your Micro.blog recommendations. Down the line I&amp;rsquo;m hoping to add additional cells with things like shoutouts, etc. The styling is not defined for this yet though.&lt;/p&gt;
&lt;p&gt;The sidebar is added to the page using Tiny Theme&amp;rsquo;s &lt;a href=&#34;https://tiny.micro.blog/microhooks/&#34;&gt;microhooks&lt;/a&gt; customisation feature. I set the &lt;code&gt;microhook-after-post-list.html&lt;/code&gt; hook to the following HTML to include the sidebar on the post list:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ partial &amp;#34;sidebar.html&amp;#34; . }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In theory it should be possible to add it to the other pages just by adding the same HTML snippet to the other microhooks (go for the &amp;ldquo;after&amp;rdquo; ones). I haven&amp;rsquo;t tried it myself though so I&amp;rsquo;m not sure how this will look.&lt;/p&gt;
&lt;p&gt;Finally, there&amp;rsquo;s the styling. I added the following CSS which will make the page slightly wider and place the sidebar to the right side of the page:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@&lt;span style=&#34;color:#66d9ef&#34;&gt;media&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;min-width&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;776px&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt;:&lt;span style=&#34;color:#a6e22e&#34;&gt;has&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;sidebar&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;max-width&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;50&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;em&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;wrapper&lt;/span&gt;:&lt;span style=&#34;color:#a6e22e&#34;&gt;has&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;sidebar&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;display&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;grid&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;grid-template-columns&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;minmax&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;em&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;35&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;em&lt;/span&gt;) &lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;em&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;column-gap&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;sidebar&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;font-size&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0.9&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;em&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;line-height&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1.8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@&lt;span style=&#34;color:#66d9ef&#34;&gt;media&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;max-width&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;775px&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;sidebar&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;display&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;none&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;sidebar&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;margin-bottom&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;sidebar&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;header&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;h1&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;font-size&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;em&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;accent1);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;ul&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;blogroll&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;padding-inline&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;ul&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;blogroll&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;li&lt;/span&gt; {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;list-style-type&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;none&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;!important&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;ul&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;blogroll&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;li&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;text-decoration&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;none&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;text&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;ul&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;blogroll&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;li&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;span&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;accent2);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This CSS uses the style variables defined by Tiny Theme so they should match the colour scheme of your blog. A page with a sidebar is also wider than one without it. It doesn&amp;rsquo;t change with width of pages that don&amp;rsquo;t have the sidebar (if this isn&amp;rsquo;t your cup of tea, you can remove the &lt;code&gt;:has(div.sidebar)&lt;/code&gt; selector off the &lt;code&gt;body&lt;/code&gt; tag) and the sidebar will not appear on small screens, like a phone in portrait orientation. I&amp;rsquo;m not entirely sure if I like this, and I may eventually make changes. But it&amp;rsquo;s fine for now.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s how the sidebar was added. More to come as I tinker with this down the line.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Photo Bucket Update: Exporting To Zip</title>
      <link>https://lmika.org/2024/03/09/photo-bucket-update.html</link>
      <pubDate>Sat, 09 Mar 2024 09:33:57 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/03/09/photo-bucket-update.html</guid>
      <description>&lt;p&gt;Worked a little more on Photo Bucket this week. Added the ability to export the contents of an instance to a Zip file. This consist of both images and metadata.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/screenshot-2024-03-09-at-9.58.38-am.png&#34; width=&#34;600&#34; height=&#34;304&#34; alt=&#34;Screenshot of a finder window showing the contents of the exported Zip file&#34;&gt;
&lt;p&gt;I&amp;rsquo;ve went with lines of JSON file for the image metadata. I considered a CSV file briefly, but for optional fields like captions and custom properties, I didn&amp;rsquo;t like the idea of a lot of empty columns. Better to go with a format that&amp;rsquo;s a little more flexible, even if it does mean more text per line.&lt;/p&gt;
&lt;p&gt;As for the images, I&amp;rsquo;m hoping the export to consist of the &amp;ldquo;best quality&amp;rdquo; version. What that means will depend on the instance. The idea is to have three tiers of image quality managed by the store: &amp;ldquo;original&amp;rdquo;, &amp;ldquo;web&amp;rdquo;, and &amp;ldquo;thumbnail&amp;rdquo;. The &amp;ldquo;original&amp;rdquo; version is the untouched version uploaded to the store. The &amp;ldquo;web&amp;rdquo; version is re-encoded from the &amp;ldquo;original&amp;rdquo; and will be slightly compressed with image metadata tags stripped out. The &amp;ldquo;thumbnail&amp;rdquo; version will be a small, highly compressed version suitable for the thumbnail. There is to be a decision algorithm in place to get an image given the desired quality level. For example, if something needed the &amp;ldquo;best quality&amp;rdquo; version of an image, and the &amp;ldquo;original&amp;rdquo; image is not available, the service will default to the &amp;ldquo;web&amp;rdquo; version (the idea is that some of these tiers will be optional depending on the need of the instances).&lt;/p&gt;
&lt;p&gt;This is all partially working at the moment, and I&amp;rsquo;m hoping to rework all this when I replace how stores and images relate to each other (This is what I&amp;rsquo;m starting on now, and why I built export now since this will be backwards incompatible). So for the moment the export simply consists of the &amp;ldquo;web&amp;rdquo; version.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve got unit tests working for this as well. I&amp;rsquo;m trying a new approach for unit testing in this project. Instead of using mocks, the tests are actually running against a fully instantiated versions of the services. There exists a &lt;code&gt;servicestest&lt;/code&gt; package which does all the setup (using temporary directories, etc) and tear down of these services. Each individual unit test gets the services from this package and will run tests against a particular one.&lt;/p&gt;
&lt;p&gt;This does mean all the services are available and exercised within the tests, making them less like unit tests and more like integrations tests. But I think I prefer this approach. The fact that the dependent services are covered gives me greater confidence that they&amp;rsquo;re working. It also means I can move things around without changing mocks or touching the tests.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s not to say that I&amp;rsquo;m not trying to keep each service their own component as much as I can. I&amp;rsquo;m still trying to follow best practice of component design: passing dependencies in explicitly when the services are created, for example. But setting them all up as a whole in the tests means I can exercise them while they serve the component being tested. And the dependencies are explicit anyway (i.e. no interfaces) so it makes sense keeping it that way for the tests as well. And it&amp;rsquo;s just easier anyway. 🤷&lt;/p&gt;
&lt;p&gt;Anyway, starting rework on images and stores now. Will talk more about this once it&amp;rsquo;s done.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Photo Bucket Update: More On Galleries</title>
      <link>https://lmika.org/2024/03/05/photo-bucket-update.html</link>
      <pubDate>Tue, 05 Mar 2024 21:43:05 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/03/05/photo-bucket-update.html</guid>
      <description>&lt;p&gt;Spent a bit more time working on Photo Bucket this last week&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, particularly around galleries. They&amp;rsquo;re progressing quite well. I&amp;rsquo;m made some strides in getting two big parts of the UI working now: adding and removing images to galleries, and re-ordering gallery items via drag and drop.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll talk about re-ordering first. This was when I had to bite the bullet and start coding up some JavaScript. Usually I&amp;rsquo;d turn to &lt;a href=&#34;https://stimulus.hotwired.dev&#34;&gt;Stimulus&lt;/a&gt; for this but I wanted to give &lt;a href=&#34;https://blog.jim-nielsen.com/2023/html-web-components/&#34;&gt;HTML web components&lt;/a&gt; a try. And so far, they&amp;rsquo;ve been working quite well.&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://cdn.uploads.micro.blog/25293/2024/moving-gallery-images.mov&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;The gallery page is generated server-side into the following HTML:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;main&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;pb-draggable-imageset&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/_admin/galleries/1/items&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;image-grid&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;pb-draggable-image&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;position&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;item-id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;7&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/_admin/photos/3&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;img&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/_admin/img/web/3&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;pb-draggable-image&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;pb-draggable-image&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;position&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;item-id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/_admin/photos/4&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;img&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/_admin/img/web/4&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;pb-draggable-image&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;pb-draggable-image&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;position&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;item-id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/_admin/photos/1&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;img&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/_admin/img/web/1&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;pb-draggable-image&lt;/span&gt;&amp;gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;pb-draggable-imageset&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;main&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each &lt;code&gt;&amp;lt;pb-draggable-image&amp;gt;&lt;/code&gt; node is a direct child of an &lt;code&gt;&amp;lt;pb-draggable-imageset&amp;gt;&lt;/code&gt;. The idea is that the user can rearrange any of the &lt;code&gt;&amp;lt;pb-draggable-image&amp;gt;&lt;/code&gt; elements within a single &lt;code&gt;&amp;lt;pb-draggable-imageset&amp;gt;&lt;/code&gt; amongst themselves. Once the user has moved an image onto to another one, the image will signal its new position by firing a custom event. The containing &lt;code&gt;&amp;lt;pb-draggable-imageset&amp;gt;&lt;/code&gt; element is listening to this event and will respond by actually repositioning the child element and sending a JSON message to the backend to perform the move in the database.&lt;/p&gt;
&lt;p&gt;A lot of this was based on the MDN documentation for &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API&#34;&gt;drag and drop&lt;/a&gt; and it follows the examples quite closely. I did find a few interesting things though. My first attempt at this was to put it onto the &lt;code&gt;&amp;lt;pb-draggable-image&amp;gt;&lt;/code&gt; element, but I wasn&amp;rsquo;t able to get any drop events when I did. Moving the &lt;code&gt;draggable&lt;/code&gt; attribute onto the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element seemed to work. I not quite sure why this is. Surely I can&amp;rsquo;t think of any reason as to why it wouldn&amp;rsquo;t work. It may had something else, such as how I was initialising the HTTP components.&lt;/p&gt;
&lt;p&gt;Speaking of HTML components, there was a time where the custom component&amp;rsquo;s &lt;code&gt;connectedCallback&lt;/code&gt; method was being called before the child &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; elements were present in the DOM. This was because I had the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag in the the HTML head and configured to be evaluated during parsing. Moving it to the end of the body and loading it as a module fixed that issue. Also I found that moving elements around using &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/Element/before&#34;&gt;element.before&lt;/a&gt; and &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/Element/after&#34;&gt;element.after&lt;/a&gt; would actually call &lt;code&gt;connectedCallback&lt;/code&gt; and &lt;code&gt;disconnectedCallback&lt;/code&gt; each time, meaning that any event listeners registered within &lt;code&gt;connectedCallback&lt;/code&gt; would need to be de-registered, otherwise events would be handled multiple times. This book-keeping was slightly annoying, but it worked.&lt;/p&gt;
&lt;p&gt;Finally, there was moving the items with the database. I&amp;rsquo;m not sure how best to handle this, but I have that method that seems to work. What I&amp;rsquo;m doing is tracking the position of each &amp;ldquo;gallery item&amp;rdquo; using a &lt;code&gt;position&lt;/code&gt; field. This field would be 1 for the first item, 2 for the next, and so on for each item in the gallery. The result of fetching items would just order using this field, so as long as they&amp;rsquo;re distinct, they don&amp;rsquo;t need to be a sequence incrementing by 1, but I wanted to keep this as much as possible.&lt;/p&gt;
&lt;p&gt;The actual move involves two update queries. The first one will update the positions of all the items that are to shift left or right by one to &amp;ldquo;fill the gap&amp;rdquo;. The way it does this is that when an item is moved from position X to position Y, the value of &lt;code&gt;position&lt;/code&gt; between X and Y would be changed by +1 if X &amp;gt; Y, or by –1 if Y &amp;gt; X. This is effectively the same as setting position X to X + 1, and so on, but done using one &lt;code&gt;UPDATE&lt;/code&gt; statement. The second query just sets the position of item X to Y.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s moving gallery items. I&amp;rsquo;m not sure how confident I am with this approach, but I&amp;rsquo;ve been testing this, both manually and by writing unit tests. It&amp;rsquo;s not quite perfect yet: I&amp;rsquo;m still finding bugs (I found some while coming up with these screencasts). Hopefully, I&amp;rsquo;ll be able to get to the bottom of them soon.&lt;/p&gt;
&lt;p&gt;The second bit of work was to actually add and remove images in the gallery themselves. This, for the moment, is done using a &amp;ldquo;gallery picker&amp;rdquo; which is available in the image details. Clicking &amp;ldquo;Gallery&amp;rdquo; while viewing an image will show the list of galleries in the system, with toggles on the left. The galleries an image already belongs to is enabled, and the user can choose the galleries they want the image to be in by switching the toggles on and off. These translate to &lt;code&gt;inserts&lt;/code&gt; and &lt;code&gt;remove&lt;/code&gt; statements behind the scenes.&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://lmika.org/uploads/2024/adding-removing-images-to-galleries.mov&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;The toggles are essentially just HTML and CSS, and a bulk of the code was &lt;a href=&#34;https://www.w3schools.com/howto/howto_css_switch.asp&#34;&gt;taken from this example&lt;/a&gt;, with some tweaks. They look good, but I think I may need to make them slightly smaller for mouse and keyboard.&lt;/p&gt;
&lt;p&gt;I do see some downside with this interaction. First, it reverses the traditional idea of adding images to a gallery: instead of doing that, your selecting galleries for an image. I&amp;rsquo;m not sure if this would be confusing for others (it is modelled on how Google Photos works). Plus, there&amp;rsquo;s no real way to add images in bulk. Might be that I&amp;rsquo;ll need to add a way to select images from the &amp;ldquo;Photos&amp;rdquo; section and have a dialog like this to add or remove them all from a gallery. I think this would go far in solving both of these issues.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s where things are. Not sure what I&amp;rsquo;ll work on next, but it may actually be import and export, and the only reason for this is that I screwed up the base model and will need to make some breaking changes to the DB schema. And I want to have a version of export that&amp;rsquo;s compatible with the original schema that I can deploy to the one and only production instance of Photo Bucket so that I can port the images and captions over to the new schema. More on this in the future, I&amp;rsquo;m sure.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Apparently I&amp;rsquo;m more than happy to discuss work in progress, yet when it comes to talking about something I&amp;rsquo;ve finished, I freeze up. 🤷&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Complexity Stays At the Office</title>
      <link>https://lmika.org/2024/02/22/complexity-stays-at.html</link>
      <pubDate>Thu, 22 Feb 2024 07:22:29 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/02/22/complexity-stays-at.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s interesting to hear what others like to look at during their spare time, like setting up Temporal clusters or looking at frontend frameworks built atop five other frameworks built on React. I guess the thinking is that since we use it for our jobs, it&amp;rsquo;s helpful to keep abreast of these technologies.&lt;/p&gt;
&lt;p&gt;Not me. Not any more. Back in the day I may have though similar. I may even have had a passing fancy at stuff like this, revelling in its complexity with the misguided assumption that it&amp;rsquo;ll equal power (well, to be fair, it would equal leverage). But I&amp;rsquo;ve been burned by this complexity one to many times. Why just now, I&amp;rsquo;ve spent the last 30 minutes running into problem after problem trying to find a single root cause of something. It&amp;rsquo;s a single user interaction but because it involves 10 different systems, it means looking at 10 different places, each one having their own issues blocking me from forward progress.&lt;/p&gt;
&lt;p&gt;So I am glad to say that those days are behind me. Sure, I&amp;rsquo;ll learn new tech like Temporal if I need to, but I don&amp;rsquo;t go out looking for these anymore. If I want to build something, it would be radically simple: Go, Sqlite or PostgreSQL, server-side rendered HTML with a hint of JavaScript. I may not achieve the leverage these technologies may offer, but by gosh I&amp;rsquo;m not going to put up with the complexity baggage that comes with it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Message Simulator Client</title>
      <link>https://lmika.org/2024/02/17/message-simulator-client.html</link>
      <pubDate>Sat, 17 Feb 2024 19:23:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/02/17/message-simulator-client.html</guid>
      <description>&lt;p&gt;&lt;strong&gt;Years: &lt;/strong&gt;2017 — 2020&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Gone&lt;/p&gt;
&lt;p&gt;I once worked at a company that was responsible for sending SMS messages
via an API. Think one time passwords when you log into websites, before
time-based OTP apps were a thing. And yeah, this did involve some
&amp;ldquo;marketing&amp;rdquo; messages, although we were pretty strict about outright
spam or phishing messages.&lt;/p&gt;
&lt;p&gt;Anyway, since sending messages costed us money, we had a simulator setup
in our non-prod environments which we used for testing. The features
were pretty minimal: basically get the list of messages sent through the
API, send a status message back, and simulating a reply from the
receiver. The messages were kept in memory and there was no UI:
everything was done via a web frontend generated from a Swagger spec.&lt;/p&gt;
&lt;p&gt;Being somewhat bored one day, and getting frustrated with the clunky web
frontend, I&amp;rsquo;d though I had a go at making a MacOS client for this
thing. After finding my way around Xcode and AppKit, I managed to get
something that was usable.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/caf8bdd6d6.jpg&#34;&gt;
&lt;figcaption&gt;The one and only screenshot I have, which is fitting since this is the one and only screen there was.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It was not a sophisticated app in the least. It only consisted of a
toolbar, a client area, and a right sidebar.&lt;/p&gt;
&lt;p&gt;The toolbar allowed switching the environment to connect to, such as Dev
or Test. I believe the environments were hard-coded, and if I wanted to
add a new one, I&amp;rsquo;d had to change the Swift code. There was a button to
clear the messages from the simulator, and one to refresh the list of
messages. There was also a very simple search, which simply did an
in-memory substring match, but was good enough for finding unique IDs.&lt;/p&gt;
&lt;p&gt;The client area consisted of the message ID and message body, and
that&amp;rsquo;s it. Not included were source and target numbers, plus a few
other things. These were occasionally useful to me, but not enough to
justify the effort it would take to add them to the UI (I was more
likely to use the message body anyway).&lt;/p&gt;
&lt;p&gt;The right sidebar consisted of the message &amp;ldquo;details&amp;rdquo;, which was just
the message ID and message content. There were also sections for sending
a particular status message, or sending a reply to the selected message.&lt;/p&gt;
&lt;p&gt;I always had grand plans for more features, but I couldn&amp;rsquo;t justify the
time. And eventually I decided to leave, and the project was wiped once
I returned my laptop.&lt;/p&gt;
&lt;p&gt;Despite how bare-bones it was, it was still useful, and something I used
most days if I had to work with the simulator. And given that it was my
first attempt at a native Mac app, I was reasonably proud of it. So I
think it deserves a place in the archives.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Implicit Imports To Load Go Database Drivers Considered Annoying (By Me)</title>
      <link>https://lmika.org/2024/02/14/implicit-imports-to.html</link>
      <pubDate>Wed, 14 Feb 2024 22:13:04 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/02/14/implicit-imports-to.html</guid>
      <description>&lt;p&gt;I wish Go&amp;rsquo;s approach to loading database drivers didn&amp;rsquo;t involve implicitly importing them as packages. At least that way, package authors would be more likely to get the driver from the caller, rather than load a driver themselves.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been bitten by this recently, twice. I&amp;rsquo;m using a GitHub Linux driver to build an ARM version of something that needs to use SQLite. As far as I can tell, it&amp;rsquo;s not possible to build an ARM binary with CGO enabled with these runners (at-least, not without installing a bunch of dependencies — I&amp;rsquo;m not that desperate yet).&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m currently using &lt;a href=&#34;modernc.org/sqlite&#34;&gt;an SQLite driver&lt;/a&gt; that doesn&amp;rsquo;t require CGO, so all my code builds fine. There also exists a &lt;a href=&#34;github.com/mattn/go-sqlite3&#34;&gt;substantially more popular SQLite&lt;/a&gt; driver that does require CGO, and twice I&amp;rsquo;ve tried importing packages which used this driver, thereby breaking the build. These packages don&amp;rsquo;t allow me pass in a database connection explicitly, and even if they did, I&amp;rsquo;m not sure if would help: they&amp;rsquo;re still importing this SQLite driver that needs CGO.&lt;/p&gt;
&lt;p&gt;So what am I to do? As long as I need to build ARM versions, I can&amp;rsquo;t use these packages (not that I need an ARM version, but it makes testing in a Linux VM running on an M1 Mac easier). I suppose I could roll my own, but it would be nice not to do so. It&amp;rsquo;d  be much better for me to load the driver myself, and pass it to these packages explicitly.&lt;/p&gt;
&lt;p&gt;So yeah, I wish this was better.&lt;/p&gt;
&lt;p&gt;P.S. When you see the error message &amp;ldquo;unable to open database file: out of memory (14)&amp;rdquo; when you try to open an SQLite database, it may just mean the directory it&amp;rsquo;s in doesn&amp;rsquo;t exist.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Rubber-ducking: On Context</title>
      <link>https://lmika.org/2024/02/09/rubberducking-on-context.html</link>
      <pubDate>Fri, 09 Feb 2024 10:28:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/02/09/rubberducking-on-context.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m torn between extracting auth credentials in the handler from a Go Context and passing them as arguments to service methods, or just passing the context and having the service methods get it from the Context themselves.&lt;/p&gt;
&lt;p&gt;Previously, when the auth credentials just had a user ID, we were doing the former. But we&amp;rsquo;re now using more information about what the user has access to and if we were to continue doing this, we&amp;rsquo;ll need to pass more parameters through to the service layer. Not only does this make things a little less neater, it&amp;rsquo;ll mean the next time we do this, we&amp;rsquo;ll have to do the whole thing again.&lt;/p&gt;
&lt;p&gt;But, it means that the service methods would need to get the user IDs themselves, along with this new stuff. Not that that&amp;rsquo;s an issue: there will be providers that are also using the context to get this info. So this is a viable option. And yet, I feel uneasy about using the context for this. &lt;/p&gt;
&lt;p&gt;🦆: So what are your options?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; I guess I could replace the use of the user ID with a structure that holds both the user ID and this extra stuff.&lt;/p&gt;
&lt;p&gt;🦆: Would that work?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; I mean, I guess it would? It would make it clearer as to who&amp;rsquo;s request this. It would also mean that we&amp;rsquo;re being explicit about what the method needs.&lt;/p&gt;
&lt;p&gt;🦆: Do you see any downsides with this approach?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; The only thing I can see is that it would be inconsistent with other parts of the system that are getting it from the context.&lt;/p&gt;
&lt;p&gt;🦆: Your hesitating. You don&amp;rsquo;t seem sure about this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Well, I just don&amp;rsquo;t like the fact that we&amp;rsquo;re passing both the context which holds this auth info and the auth info alongside it. And I know that it&amp;rsquo;s unclear, and would mean that the tests would need to be changed (I mean they&amp;rsquo;ll need to be changed anyway if we went with this &amp;ldquo;principal&amp;rdquo; approach). &lt;/p&gt;
&lt;p&gt;🦆: So what&amp;rsquo;s really going on? Why are you unsure about this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Well, it&amp;rsquo;s just showing this in a review and having people say &amp;ldquo;oh, that&amp;rsquo;s not the right way to write Go.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;🦆: They say that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; We&amp;rsquo;ll, not exactly. But they do have opinions about how best to do this (like pretty much everyone, I guess).&lt;/p&gt;
&lt;p&gt;🦆: Do they have an opinion about this decision?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Well, not really. In fact, I think they&amp;rsquo;re pretty okay with either approach.&lt;/p&gt;
&lt;p&gt;🦆: So if they&amp;rsquo;re okay with either approach, it probably doesn&amp;rsquo;t matter that much. But if it were me, I&amp;rsquo;d probably prefer something a little more readable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Well, yeah. But how can I trust you? You&amp;rsquo;re me.&lt;/p&gt;
&lt;p&gt;🦆: Am I? I&amp;rsquo;m a duck. Are you a duck?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; No, I&amp;rsquo;m not. But you&amp;rsquo;re not a duck either. You don&amp;rsquo;t exist. You&amp;rsquo;re just a figment of my imagination.&lt;/p&gt;
&lt;p&gt;🦆: Really? Then how are you speaking to me?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Because I conjured you up so I can work through this problem I&amp;rsquo;m having.&lt;/p&gt;
&lt;p&gt;🦆: So you&amp;rsquo;re having a go at me because I don&amp;rsquo;t exist, yet you still need me because you&amp;rsquo;re stuck on this decision and you need a resolution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Well, I didn&amp;rsquo;t say I don&amp;rsquo;t need you. It&amp;rsquo;s probably still helpful to me to have this conversation. &lt;/p&gt;
&lt;p&gt;🦆: Ok, I think we&amp;rsquo;ve gone off track a little. What are you going to do about this context decision?&lt;/p&gt;
&lt;p&gt;…&lt;/p&gt;
&lt;p&gt;🦆: Well?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Ok, I&amp;rsquo;m not certain that implicitly including the user ID will work, as the user ID may be different to what is actually in the context. I also don&amp;rsquo;t like how it&amp;rsquo;s implicit in the context, and I think I do prefer something a little more readable. It pains me to think that I&amp;rsquo;ll be effectively duplicating values that are already available to the method. But we&amp;rsquo;re doing that anyway with the user ID.&lt;/p&gt;
&lt;p&gt;So here&amp;rsquo;s what I&amp;rsquo;ll do. I&amp;rsquo;ll replace it with a dedicated type, retrievable from the context and holding all the information that is needed to authorise the user. I&amp;rsquo;ll also retroactively make those changes to other areas of the code that are doing it.&lt;/p&gt;
&lt;p&gt;🦆: Okay. And what of your peers?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; If they ask about it, I&amp;rsquo;ll just tell them that I prefer something a little more explicit. I know it&amp;rsquo;s a departure from how I did things previously. But the benefits outweigh the costs I think in this case.&lt;/p&gt;
&lt;p&gt;🦆: Okay. Sounds like you&amp;rsquo;ve got a way forward now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L:&lt;/strong&gt; Great. This has been helpful. Thanks for that, D.&lt;/p&gt;
&lt;p&gt;🦆: No worries. &lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Rubberducking: On Context</title>
      <link>https://lmika.org/2024/02/09/092800.html</link>
      <pubDate>Fri, 09 Feb 2024 09:28:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/02/09/092800.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m torn between extracting auth credentials in the handler from a Go Context and passing them as arguments to service methods, or just passing the context and having the service methods get it from the Context themselves.&lt;/p&gt;
&lt;p&gt;Previously, when the auth credentials just had a user ID, we were doing the former. But we&amp;rsquo;re now using more information about what the user has access to and if we were to continue doing this, we&amp;rsquo;ll need to pass more parameters through to the service layer. Not only does this make things a little less neater, it&amp;rsquo;ll mean the next time we do this, we&amp;rsquo;ll have to do the whole thing again.&lt;/p&gt;
&lt;p&gt;But, it means that the service methods would need to get the user IDs themselves, along with this new stuff. Not that that&amp;rsquo;s an issue: there will be providers that are also using the context to get this info. So this is a viable option. And yet, I feel uneasy about using the context for this.&lt;/p&gt;
&lt;p&gt;🦆: So what are your options&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: I guess I could replace the use of the user ID with a structure that holds both the user ID and this extra stuff.&lt;/p&gt;
&lt;p&gt;🦆: Would that work?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: I mean, I guess it would? It would make it clearer as to whoes request this. It would also mean that we&amp;rsquo;re being explicit about what the method needs.&lt;/p&gt;
&lt;p&gt;🦆: Do you see any downsides with this approach?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: The only thing I can see is that it would be inconsistent with other parts of the system that are getting it from the context.&lt;/p&gt;
&lt;p&gt;🦆: You&amp;rsquo;re hesitating. You don&amp;rsquo;t seem sure about this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: Well, I just don&amp;rsquo;t like the fact that we&amp;rsquo;re passing both the context which holds this auth info and the auth info alongside it. And I know that it&amp;rsquo;s unclear, and would mean that the tests would need to be changed (I mean they&amp;rsquo;ll need to be changed anyway if we went with this &amp;ldquo;principal&amp;rdquo; approach).&lt;/p&gt;
&lt;p&gt;🦆: So what&amp;rsquo;s really going on? Why are you unsure about this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: Well, it&amp;rsquo;s just showing this in a review and having people say &amp;ldquo;oh, that&amp;rsquo;s not the right way to write Go.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;🦆: They say that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: We&amp;rsquo;ll, not exactly. But they do have opinions about how best to do this (like pretty much everyone, I guess).&lt;/p&gt;
&lt;p&gt;🦆: Do they have an opinion about this decision?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: Well, not really. In fact, I think they&amp;rsquo;re pretty okay with either approach.&lt;/p&gt;
&lt;p&gt;🦆: So if they&amp;rsquo;re okay with either approach, it probably doesn&amp;rsquo;t matter that much. But if it were me, I&amp;rsquo;d probably prefer something a little more readable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: Well, yeah. But how can I trust you? You&amp;rsquo;re me.&lt;/p&gt;
&lt;p&gt;🦆: Am I? I&amp;rsquo;m a duck. Are you a duck?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: No, I&amp;rsquo;m not. But you&amp;rsquo;re not a duck either. You don&amp;rsquo;t exist. You&amp;rsquo;re just a figment of my imagination.&lt;/p&gt;
&lt;p&gt;🦆: Really? Then how are you speaking to me?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: Because I conjured you up so I can work through this problem I&amp;rsquo;m having.&lt;/p&gt;
&lt;p&gt;🦆: So you&amp;rsquo;re having a go at me because I don&amp;rsquo;t exist, yet you still need me because you&amp;rsquo;re stuck on this decision and you need a resolution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: Well, I didn&amp;rsquo;t say I don&amp;rsquo;t need you. It&amp;rsquo;s probably still helpful to me to have this conversation.&lt;/p&gt;
&lt;p&gt;🦆: Ok, I think we&amp;rsquo;ve gone off track a little. What are you going to do about this context decision?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: …&lt;/p&gt;
&lt;p&gt;🦆: Well?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: Ok, I&amp;rsquo;m not certain that implicitly including the user ID will work, as the user ID may be different to what is actually in the context. I also don&amp;rsquo;t like how it&amp;rsquo;s implicit in the context, and I think I do prefer something a little more readable. It pains me to think that I&amp;rsquo;ll be effectively duplicating values that are already available to the method. But we&amp;rsquo;re doing that anyway with the user ID.&lt;/p&gt;
&lt;p&gt;So here&amp;rsquo;s what I&amp;rsquo;ll do. I&amp;rsquo;ll replace it with a dedicated type, retrievable from the context and holding all the information that is needed to authorise the user. I&amp;rsquo;ll also retroactively make those changes to other areas of the code that are doing it.&lt;/p&gt;
&lt;p&gt;🦆: Okay. And what of your peers?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: If they ask about it, I&amp;rsquo;ll just tell them that I prefer something a little more explicit. I know it&amp;rsquo;s a departure from how I did things previously. But the benefits outweigh the costs I think in this case.&lt;/p&gt;
&lt;p&gt;🦆: Okay. Sounds like you&amp;rsquo;ve got a way forward now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt;: Great. This has been helpful. Thanks for that, D.&lt;/p&gt;
&lt;p&gt;🦆: No worries.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Goland Debugger Not Working? Try Upgrading All The Things</title>
      <link>https://lmika.org/2024/02/06/golang-debugger-not.html</link>
      <pubDate>Tue, 06 Feb 2024 10:57:04 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/02/06/golang-debugger-not.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been having occasional trouble with the debugger in Goland. Every attempt to debug a test would just fail with the following error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/usr/local/go/bin/go tool test2json -t /Applications/GoLand.app/…
API server listening at: 127.0.0.1:60732
could not launch process: EOF

Debugger finished with the exit code 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My &lt;a href=&#34;https://lmika.org/2023/10/09/goland-updated-to.html&#34;&gt;previous attempts at fixing this&lt;/a&gt; — upgrading Go and Goland — did get it working for a while, but recently it&amp;rsquo;s been happening to me again. And being at the most recent version of Go and Goland, that avenue was not available to me.&lt;/p&gt;
&lt;p&gt;So I set about looking for other ways to fix this. Poking around the web netted &lt;a href=&#34;https://intellij-support.jetbrains.com/hc/en-us/community/posts/12065481660690-golang-debugger-could-not-launch-process-EOF&#34;&gt;this support post&lt;/a&gt;, which suggested upgrading the Xcode Command Line tools:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo rm -rf /Library/Developer/CommandLineTools
$ xcode-select --install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I ran the command and the tools did upgrade successfully, but I was still encountering the problem. I then wondered if Goland used &lt;a href=&#34;https://github.com/go-delve/delve&#34;&gt;Delve&lt;/a&gt; for debugging, and if that actually need upgrading. I&amp;rsquo;ve got Delve via Homebrew, so I went about upgrading that:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ brew upgrade dlv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And indeed, Homebrew did upgrade Delve from 1.21.0 to 1.22.0. And once that finished, and after restarting Goland&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, I was able to use the debugger again.&lt;/p&gt;
&lt;p&gt;So, if you&amp;rsquo;re encountering this error yourself, try upgrading one or more of these tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Goland&lt;/li&gt;
&lt;li&gt;Go&lt;/li&gt;
&lt;li&gt;Xcode Command Line tools (if on a Mac)&lt;/li&gt;
&lt;li&gt;Delve&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This was the order I tried them in, but you might be lucky by trying Delve first. YMMV&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Not sure that a restart is required, but I just did it anyway, &amp;ldquo;just in case&amp;rdquo;.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>People Are More Interested In What You&#39;re Working On Than You Think</title>
      <link>https://lmika.org/2024/02/03/people-are-more.html</link>
      <pubDate>Sat, 03 Feb 2024 20:47:31 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/02/03/people-are-more.html</guid>
      <description>&lt;img src=&#34;https://cdn.micro.blog/books/9781936891047/cover.jpg&#34; align=&#34;left&#34; class=&#34;microblog_book&#34; style=&#34;max-width: 60px; margin-right: 20px; margin-top: 0px; padding-top: 0px;&#34;&gt;
&lt;p&gt;If anyone else is weary about posting about what projects they&amp;rsquo;re working on, fearing that others would think they&amp;rsquo;re showing off or something, here&amp;rsquo;s two bits of evidence that I hope would allay these fears:&lt;/p&gt;
&lt;p&gt;Exhibit 1: I&amp;rsquo;m a bit of a fan of the &lt;a href=&#34;https://www.youtube.com/channel/UCqJ-Xo29CKyLTjn6z2XwYAw&#34;&gt;GMTK YouTube channel&lt;/a&gt;. Lots of good videos there about game development that, despite not being a game developer myself, I find facinating. But the playlist I enjoy the most is the one where Mark Brown, the series creator, actually goes through the process of building a game himself. Now, you&amp;rsquo;re not going to learn how to use Unity from that series (although he does have a video about that), but it&amp;rsquo;s fun seeing him making design decisions, showing off prototypes, overcoming challenges — both external and self imposed, and seeing it all come together. I&amp;rsquo;m aways excited when he drops one of these videos, and when I learnt today that he&amp;rsquo;s been posting dev logs on his Discord, so interested am I in this topic that I immediately signed up as a Patreon supporter.&lt;/p&gt;
&lt;p&gt;Exhibit 2: I&amp;rsquo;ve been writing about my own projects on a &lt;a href=&#34;https://folio.red&#34;&gt;new Scribbles blog&lt;/a&gt;. This was completely for myself, as a bit of an archive of previous work that would be difficult or impossible to revisit later. I had no expectations of anyone else finding these interesting. Yet, earlier this week, while at lunch, the conversation I was having with work colleagues turned to personal projects. He ask if I was working on anything, and when I told him about this blog, he expressed interest. I gave him the link and that afternoon I saw him taking a look (I not expecting him to be a regular visitor but the fact that he was interested at all was something).&lt;/p&gt;
&lt;p&gt;It turns out that my colleague gets a kick out of seeing others do projects like this on the side. I guess, in retrospect, that this shouldn&amp;rsquo;t be a surprise to me, seeing that I get the same thrill. Heck, that&amp;rsquo;s why I&amp;rsquo;ve subscribed to the tech podcasts like Under the Radar: I haven&amp;rsquo;t written an iOS app in my life, yet it&amp;rsquo;s just fun listing to dev stories like this.&lt;/p&gt;
&lt;p&gt;Yet, when it comes to something that I&amp;rsquo;m working on, for a long time I&amp;rsquo;ve always held back, thinking that talking about it is a form of showing off. I like to think I&amp;rsquo;m getting better here, but much like &lt;a href=&#34;https://micro.blog/books/9781936891047&#34;&gt;the Resistance&lt;/a&gt;, that feeling is still there. Wispering doubt in my ear. Asking who would be interested in these raw, unfinished, things that will never go beyond the four walls of the machine from whence then came? I don&amp;rsquo;t think that feeling will ever go away, but in case I loose my nerve again, I hope to return to the events of this week, just to remind myself that, yeah, people are interested in these stories. I can put money on that assurance. After all, I just did.&lt;/p&gt;
&lt;p&gt;So don&amp;rsquo;t be afraid to publish those blog posts, podcasts, or videos on what you&amp;rsquo;re working on. I can&amp;rsquo;t wait to see them.&lt;/p&gt;
&lt;p&gt;See also, &lt;a href=&#34;https://github.com/readme/guides/publishing-your-work&#34;&gt;this post by Aaron Francis&lt;/a&gt; that touches on the same topic (via &lt;em&gt;The ReadME Project&lt;/em&gt;).&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Github Actions, Default Token Permissions, And Publishing Binaries</title>
      <link>https://lmika.org/2024/02/01/github-actions-default.html</link>
      <pubDate>Thu, 01 Feb 2024 21:23:11 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/02/01/github-actions-default.html</guid>
      <description>&lt;p&gt;Looks like Github&amp;rsquo;s locked down the access rights of the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; recently. This is the token that&amp;rsquo;s available to all Github actions by default.&lt;/p&gt;
&lt;p&gt;After taking a &lt;a href=&#34;https://goreleaser.com&#34;&gt;GoReleaser&lt;/a&gt; config file from an old project and using it in a new one, I encountered this error when GoReleaser tried to publish the binaries as part of a Github Release:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;failed to publish artifacts:
could not release:
PATCH https://api.github.com/repos/lmika/&amp;lt;project&amp;gt;/releases/139475588:
403 Resource not accessible by integration []
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After a quick search, I found this &lt;a href=&#34;https://github.com/ncipollo/release-action/issues/208&#34;&gt;Github issue&lt;/a&gt; which seemed to cover the same problem. It looks like the way to resolve this is to explicitly add the &lt;code&gt;content: write&lt;/code&gt; permission to the Github Actions YAML file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Create Release&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;on&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;push&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;tags&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;v*&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Add this section&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;permissions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;contents&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;write&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;build&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;runs-on&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ubuntu-latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And sure enough, after adding the &lt;code&gt;permissions&lt;/code&gt; section, Goreleaser was able to publish the binaries once again.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs&#34;&gt;There&amp;rsquo;s a bunch of other permissions&lt;/a&gt; that might be helpful for other things, should you need it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Thoughts on The Failure of Microsoft Bob</title>
      <link>https://lmika.org/2024/01/30/thoughts-on-the.html</link>
      <pubDate>Tue, 30 Jan 2024 17:38:09 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/01/30/thoughts-on-the.html</guid>
      <description>&lt;p&gt;Watching a &lt;a href=&#34;https://youtu.be/utjh8R-yedk&#34;&gt;YouTube video about Microsoft Bob&lt;/a&gt; left me wondering if one of the reasons why Bob failed was that it assumed that users, who may have been intimidated by a GUI when they first encountered one, would be intimidated for ever. That their level of skill will always remain one in which the GUI was scary and unusable, and their only success in using a computer is through applications like Bob.&lt;/p&gt;
&lt;p&gt;That might be true for some, but I believe that such cases are a fewer representation of the userbase as a whole. If someone’s serious about getting the most out of their computer, even back then when the GUI was brand new, I can’t see how they wouldn’t naturally skill up, or at least want to.&lt;/p&gt;
&lt;p&gt;I think that’s why I’m bothered by GUIs that sacrifice functionality in leau of “simplicity.” It might be helpful at the start, but pretty soon people would grow comfortable using your UI, and will hit against the artificial capabilities of the application sooner than you expect.&lt;/p&gt;
&lt;p&gt;Not that I’m saying that all UIs should be as complex as Logic Pro for no reason: if the domain is simple, then keep it simple. But when deciding on the balance between simplicity and capability, perhaps have trust in your users’ abilities. If they’re motivated (and your UI design is decent) I’m sure they’ll be able to master something a little more complex.&lt;/p&gt;
&lt;p&gt;At least, that’s what this non-UI designer believes.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Build Indicators</title>
      <link>https://lmika.org/2024/01/29/build-indicators.html</link>
      <pubDate>Mon, 29 Jan 2024 19:31:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/01/29/build-indicators.html</guid>
      <description>&lt;p&gt;&lt;strong&gt;AKA:&lt;/strong&gt; Das Blinkenlights&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; 2017 — now&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Steady Green&lt;/p&gt;
&lt;p&gt;I sometimes envy those that work in hardware. To be able to build
something that one can hold and touch. It&amp;rsquo;s something you really cannot
do with software. And yeah, I dabbled a little with Arduino, setting up
sketches that would run on prebuilt shields, but I never went beyond the
point of building something that, however trivial or crappy, I could
call my own.&lt;/p&gt;
&lt;p&gt;Except for this one time.&lt;/p&gt;
&lt;p&gt;And I admit that this thing is pretty trivial and crappy: little more
than some controllable LEDs. But the ultimate irony is that it turned
out to be quite useful for a bunch of software projects. &lt;/p&gt;
&lt;h3 id=&#34;the-hardware&#34;&gt;The Hardware&lt;/h3&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/a12fd62320.jpg&#34;&gt;
&lt;figcaption&gt;The build indicator light shield, on top of an Arduino.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I built this Arduino shield a while ago, probably something like 2013.
It&amp;rsquo;s really not that complicated: just a bunch of LEDs wired up in
series to a bunch of resistors atop an Arduino prototyping shield. The
LEDs can be divided up into two groups of three, with each group having
a red, amber, and green LED, arrange much like two sets of traffic
lights. I&amp;rsquo;m using the analogue pins of the Arduino, making it possible
to dim the LEDs (well, &amp;ldquo;dimmed&amp;rdquo;: the analogue pins are little more
than a square pulse with an adjustable duty cycle).&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/232a7216e3.jpg&#34;&gt;
&lt;figcaption&gt;Build indicator shield beside a rather dusty Arduino Duemilanove.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/5df18b0f71.jpg&#34;&gt;
&lt;figcaption&gt;The underside of the shield, show the connection between the resistors and the LEDs.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I can&amp;rsquo;t remember why I built this shield originally: it might had
something to do with train signals, or maybe they were intended as
indicators right out of the box. But after briefly using them for their
original purpose, it sat on my desk for a while before I started using
them as indicator lights. Their first use was for some tool that would
monitor the download and transcode of videos. This would take between
45–60 minutes, and it was good to be able to start the job, leave the
room, and get the current progress without having to wake the screen
while I pass by the door. The red LED will slowly pulse while the
download was in progress, then the yellow LED will start flashing when
transcoding begins. Once everything is done, the green LED will be lit
(or the red LED, which will indicate an error). The Arduino sketch had a
bunch of predefined patterns, encoded as strings. Each character would
indicate an intensity, with &amp;ldquo;a&amp;rdquo; being the brightness, and &amp;ldquo;z&amp;rdquo; being
the dimmest (I believe the space or dot meant &amp;ldquo;off&amp;rdquo;). Each LED could
be set to a different pattern, which was done via commands sent over the
RS-232 connection. I think the code driving this connection was baked
into the download-and-transcode tool itself. The Arduino would reset
whenever the RS-232 connection is formed, and just letting it do this
when the tool started up meant that I didn&amp;rsquo;t need to worry about
connection state (it didn&amp;rsquo;t make the code portable though).&lt;/p&gt;
&lt;h3 id=&#34;watching-webpack&#34;&gt;Watching Webpack&lt;/h3&gt;
&lt;p&gt;Eventually this tool fell out of use, and for the long time this board
sat in my drawer. Projects came and went, until one came along with a
problem that was perfect for this device. I was working on a HTML
web-app and I was switching between the code and a web-browser, while
Webpack was watching for changes. Because I only had a single screen,
the terminal was always out of sight — behind either the code editor or
the web-browser — and the version of Webpack I was using would stop
watching when it encountered an error (a Go application was serving the
files, and Webpack was simply deploying the bundle assets to a public
folder, so even though Webpack would stop working, the actual web-server
will continue running). Not seeing these errors, I&amp;rsquo;d fall into the trap
into thinking that I was changing things, and getting confused as to why
I wasn&amp;rsquo;t seeing them in the browser. I could go for a minute or two
like this before I found out that Webpack died because of an earlier
error and my changes were not getting deployed at all. So I dug this
device out, built a very simple Go CLI tool and daemon that would talk
to it, and hacked it into the Webpack config. When a Webpack build
started, it would light up the amber LED. If the build was successful,
the green LED would light up; if not, the red LED would.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/d99bab7023.jpg&#34;&gt;
&lt;figcaption&gt;The green LED lit, indicating that the last build was successful.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/1407daab26.jpg&#34;&gt;
&lt;figcaption&gt;The amber LED, indicating a build in progress.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/68f60dc0c0.jpg&#34;&gt;
&lt;figcaption&gt;The red LED, indicating a failed build.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This proved to be super useful, and took out the guesswork of knowing
when a change was deployed. As long as the green LED was lit, it&amp;rsquo;s good
to go, but as soon as amber becomes red, I know I&amp;rsquo;ll have to check for
errors and get it green once more.&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://media.lmika.org/indicator-lights-720p.mp4&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;The sketch and daemon software is a lot simpler than what this device
used to do. Now, instead of individual patterns of intensity, the daemon
— which is itself controlled by a CLI tool — would communicate to the
device using a very simple protocol that would either turn LEDs on or
off. Some of the protocol details, taken from the Arduino sketch, are
included below:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/*
 * ledstatus - simple led indicators
 * 
 * SERIAL FORMAT
 * 
 * Commands take the form:  &amp;lt;cmd&amp;gt; &amp;lt;pars&amp;gt;... NL.  Any more than
 * 8 bytes (1 command, 7 parameters) will be ignored.
 * 
 * Responses from the device will take the form: &amp;lt;status&amp;gt; &amp;lt;par&amp;gt;... NL
 * 
 */

// Commands
\#define CMD_NOP       0x0
\#define CMD_PING      &#39;p&#39;   // a ping, which should simply respond with RES_OK
\#define CMD_TURN_ON   &#39;o&#39;   // &#39;o&#39; &amp;lt;addr&amp;gt;   :: turn on the leds at these addresses
\#define CMD_TURN_OFF  &#39;f&#39;   // &#39;f&#39; &amp;lt;addr&amp;gt;   :: turn off the leds at these addresses

// Response
\#define RES_OK        &#39;1&#39;

\#define PIN_ADDR_G1   (1 &amp;lt;&amp;lt; 0)
\#define PIN_ADDR_Y1   (1 &amp;lt;&amp;lt; 1)
\#define PIN_ADDR_R1   (1 &amp;lt;&amp;lt; 2)
\#define PIN_ADDR_G2   (1 &amp;lt;&amp;lt; 3)
\#define PIN_ADDR_Y2   (1 &amp;lt;&amp;lt; 4)
\#define PIN_ADDR_R2   (1 &amp;lt;&amp;lt; 5)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But in a way the simplicity actually helps here. Because it&amp;rsquo;s now a
command and daemon, I could use it in anything else where I&amp;rsquo;d like to
show progress without having to see the screen. Just now, for example,
I&amp;rsquo;m working on a Go project that uses &lt;a href=&#34;https://github.com/cosmtrek/air&#34;&gt;Air&lt;/a&gt; to rebuild and restart
whenever I change a template. The cycle is slightly longer than a simple
Webpack run, and I tend to reload the browser window too soon. So
waiting for this device to go  from amber to green cuts down on these
early reloads.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/404c1a3e20.jpg&#34;&gt;
&lt;figcaption&gt;The ledstatus command line tool. It would communicate with the daemon via gRPC bound to a local TCP port.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;So thats the Build Indicators. The project is steady, and I have no
desire to do anything significant, like modify the hardware. But if I
were to work on it again, I think I&amp;rsquo;d like it to add variable intensity
back, and extend the command language to let the user upload customer
patterns. But for the moment, it&amp;rsquo;s doing it&amp;rsquo;s job just fine.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Why I Use a Mac</title>
      <link>https://lmika.org/2024/01/25/why-i-use.html</link>
      <pubDate>Thu, 25 Jan 2024 15:17:30 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/01/25/why-i-use.html</guid>
      <description>&lt;p&gt;Why do I use a Mac?&lt;/p&gt;
&lt;p&gt;Because I can’t get anything I need to get done on an iPad.&lt;/p&gt;
&lt;p&gt;Because I can’t type to save myself on a phone screen.&lt;/p&gt;
&lt;p&gt;Because music software doesn’t exist on Linux.&lt;/p&gt;
&lt;p&gt;Because the Bash shell doesn’t exist on Windows (well, it didn’t when I stopped using it).&lt;/p&gt;
&lt;p&gt;That’s why I use a Mac.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Broadtail</title>
      <link>https://lmika.org/2024/01/24/broadtail.html</link>
      <pubDate>Wed, 24 Jan 2024 02:10:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/01/24/broadtail.html</guid>
      <description>&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; 2021 – 2022&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Paused&lt;/p&gt;
&lt;p&gt;First project I&amp;rsquo;ll talk about is Broadtail. I think I talked about this
one before, or at least I posted screenshot of it. I started work on
this in 2021. The pandemic was still raging, and much of my downtime was
watching YouTube videos. We were coming up to a federal election, and I
was getting frustrated with seeing YouTube ads from political parties
that offend me. This was before YouTube Premium so there was no real way
to avoid these ads. Or was there?&lt;/p&gt;
&lt;h3 id=&#34;a-frontend-for-youtube-dl&#34;&gt;A Frontend For youtube-dl&lt;/h3&gt;
&lt;p&gt;I had some experience with youtube-dl in the past, downloading and
saving videos that I hoped to watch later. I recently discovered that
YouTube also published RSS feeds for channels and playlist. So I was
wondering if it was possible to build something that could use both of
these. My goal was to have something that would allow me to subscribe to
YouTube channels via RSS, download videos using youtube-dl, and watch
them via Plex on my TV. This was to be deployed on a Intel Nuc that I
was using as a home server, and be accessible via the web-browser.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/db017f8413.jpg&#34;&gt;
&lt;figcaption&gt;An early version of Broadtail, with only the download frontend.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I deceided to get the YouTuble downloading feature built first. I
started a new Go project and got something up and running reasonably
quickly. It was a good excuse to get back to vanilla Go web development,
using http.Handle and Go templates, instead of relying on frameworks
like Buffalo (don&amp;rsquo;t get me wrong, I still like Buffalo, but it is quite
heavy handed).&lt;/p&gt;
&lt;p&gt;It was also an excuse to try out StormDB, which is an embedded NoSQL
data store. The technology behind it is quite good — it&amp;rsquo;s used B-Trees
memory mapped files under the cover — and I tend to use it for other
things as well. It proved to be quite usable, apart from not allowing
multiple read/writers at the same time, which made deployments
difficult.&lt;/p&gt;
&lt;p&gt;But the backend code was the easy part. What I lack was any sense of web
design. That&amp;rsquo;s one good thing about a framework like Buffalo: it comes
with a usable style framework out of the box (Bootstrap). If I were to
go my own way, I&amp;rsquo;d have to start from scratch.&lt;/p&gt;
&lt;p&gt;The other side of that coin though, is that it would give me the freedom
to go for something that&amp;rsquo;s slightly off-beat. So I went for an
aesthetics that reminded me of early 2000 web-design: san-serif font,
all grey lines, dull pastels colours, small controls and widgets (I
stopped short at gradients and table-based layouts).&lt;/p&gt;
&lt;p&gt;This version also included a hand-rolled job manager that I used for a
bunch of other things. It&amp;rsquo;s… fine. I wouldn&amp;rsquo;t use it for anything
&amp;ldquo;real&amp;rdquo;, but it had a way of managing job lifecycles, updating
progress, and the ability to cancel a running job. So for that, it was
good enough.&lt;/p&gt;
&lt;p&gt;Finally, it needed a name. At the time, I was giving all projects
bird-like codename, since I can&amp;rsquo;t come up with names that I liked. I
eventually settled on Broadtail, which was a reference to &lt;a href=&#34;https://en.wikipedia.org/wiki/Broad-tailed_parrot&#34;&gt;broadtail
parrots&lt;/a&gt;, like the rosella.&lt;/p&gt;
&lt;h3 id=&#34;rss-subscriptions&#34;&gt;RSS Subscriptions&lt;/h3&gt;
&lt;p&gt;It didn&amp;rsquo;t take long after I got this up and running before I realised I
needed the RSS subscription feature. So that was the next thing I
added.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/02c7ec1b20.jpg&#34;&gt;
&lt;figcaption&gt;Homepage of the most recent version of Broadtail, with an active
download and a couple of favourited items.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;That way it worked was pretty straight forward. One would setup a
subscription to a YouTube channel or playlist. Broadtail will then poll
that RSS subscription every 15 minutes or so, and show new videos on the
homepage. Clicking that video item would bring up details and an option
to download it.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/7811391b7b.jpg&#34;&gt;
&lt;figcaption&gt;Video details. This one is simulated as I couldn&#39;t get youtube-dl working.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Each RSS subscription had an associated target directory. Downloading an
ad-hoc video would just dump it in a configured directory but I wanted
to make it possible to organise downloads from feeds in a more
structured way. This wasn&amp;rsquo;t perfect though: I can&amp;rsquo;t remember the
reason but I had some trouble with this, and most videos just ended up
in the download directory by default (it may have had to do with making
directories or video permissions).&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/7b51bf5401.jpg&#34;&gt;
&lt;figcaption&gt;Click a feed to view the most recent items.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Only the feed polling was automatic at this stage. I was not interested
in having all shows downloaded, as that would eat up on bandwidth and
disk storage. So users still had to choose which videos they wanted to
download. The list of recent feed items were available from the
home-screen so they were able to just do so from there.&lt;/p&gt;
&lt;p&gt;I also wanted to keep abreast with what jobs were currently running, so
the home-screen also had the list of running job.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/6869f68642.jpg&#34;&gt;
&lt;figcaption&gt;Click the job to view it&#39;s details. This one failed.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The progress-bar was powered by a web-socket backed by a goroutine on
the server side, which meant realtime updates. Clicking the job would
also show you the live output of the youtube-dl command, making it easy
to troubleshoot any jobs that failed. Jobs could be cancelled at any
time, but one annoying thing that was missing was the ability to retry
failed job. If a download failed, you had to spin up a new job from
scratch. This meant clearing out the old job from the file-system and
finding the video ID again from wherever you found it.&lt;/p&gt;
&lt;p&gt;If you were interested in a video but were not quite ready to download
it right away, you could &amp;ldquo;favourite&amp;rdquo; it by clicking the star. This was
available in every list that showed a video, and was a nightmare to code
up, since I was keeping references to where the video came from, such as
a feed or a quick look. Keeping atop of all the possible references were
become difficult with the non-relational StormDB, and the code that
handled this became quite dodgy (the biggest issue was dealing with
favourites from feeds that were deleted).&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/c975ebbac0.jpg&#34;&gt;
&lt;figcaption&gt;Clicking &#34;Favourites&#34; would show the items that you starred.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&#34;rules--wwdc-videos&#34;&gt;Rules &amp;amp; WWDC Videos&lt;/h3&gt;
&lt;p&gt;The basics were working out quite well, but it was all so manual. Plus,
going from video publication to having something to watch was not
timely. The RSS feed from YouTube was always several hours out of date,
and downloading whole videos took quite a while (it may not have been
realtime, but it was pretty close).&lt;/p&gt;
&lt;p&gt;So one of the later things I added was a feature I called &amp;ldquo;Rules&amp;rdquo;.
These were automations that would run when the RSS feed was polled, and
would automatically download videos that met certain criteria (you could
also hide them or mark them as downloaded). I quite enjoy building these
sorts of complex features, where the user is able to do configure
sophisticated automatic tasks, so this was a fun thing to code up. And
it worked: video downloads would start when they become available and
would usually be in Plex when I want to watch it (it was also possible
to ping Plex to update the library once the download was finished). It
wasn&amp;rsquo;t perfect though: not retrying failed downloads did plague it a
little. But it was good enough.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/619c14dab2.jpg&#34;&gt;
&lt;figcaption&gt;Editing a Rule.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This was near the end of my use of Broadtail. Soon after adding Rules, I
got onto the YouTube Premium bandwagon, which hid the ads and removed
the need for Broadtail as a whole. It was a good thing too, as the Plex
Android app had this annoying habit of causing the Chrome Cast to hang,
and the only way to recover from this was to reboot the device.&lt;/p&gt;
&lt;p&gt;So I eventually returned to just using YouTube, and Broadtail was
eventually abandoned.&lt;/p&gt;
&lt;p&gt;Although, not completely. One last thing I did was extend Broadtail&amp;rsquo;s
video download capabilities to include Apple WWDC Videos. This was
treated as a special kind of &amp;ldquo;feed&amp;rdquo; which, when polled, would scrap
the WWDC video website. I was a little uncomfortable doing this, and I
knew when videos were published, they wouldn&amp;rsquo;t change. So this &amp;ldquo;feed&amp;rdquo;
was never polled and the user had to refresh it automatically.&lt;/p&gt;
&lt;p&gt;Without the means to stream them using AirPlay, downloading them and
making them available in Plex was the only way I knew of watching them
on my TV, which is how I prefer to watch them.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s what Broadtail is primarily used for now. It&amp;rsquo;s no longer
running as a daemon: I just boot it up when I want to download new
videos. And although it&amp;rsquo;s only a few years old, it&amp;rsquo;s starting to show
signs of decay, with the biggest issue being youtube-dl slowly being
abandoned.&lt;/p&gt;
&lt;p&gt;So it&amp;rsquo;s unlikely that I&amp;rsquo;ll put any serious efforts into this now. But
if I did, there are a few things I&amp;rsquo;d like to see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Authentication added with username/password&lt;/li&gt;
&lt;li&gt;Retry failed video downloads.&lt;/li&gt;
&lt;li&gt;The ability to download YouTube videos in audio only (all these
&amp;ldquo;podcasts&amp;rdquo; that are only available as YouTube videos… 😒)&lt;/li&gt;
&lt;li&gt;The ability to handle the lifecycle of videos a little better than it
does now. It&amp;rsquo;s already doing this for errors: when a download fails,
the video is deleted. But it would be nice if it did things like
automatically delete videos 30 days after downloading them. This would
require more control over the &amp;ldquo;video store&amp;rdquo; though.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, that&amp;rsquo;s Broadtail.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The AWS Generative AI Workshop</title>
      <link>https://lmika.org/2024/01/17/aws-generative-ai.html</link>
      <pubDate>Wed, 17 Jan 2024 21:10:45 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/01/17/aws-generative-ai.html</guid>
      <description>&lt;p&gt;Had an AI workshop today, where we went through some of the generative AI services AWS offers and how they could be used. It was reasonably high level yet I still got something out of it.&lt;/p&gt;
&lt;p&gt;What was striking was just how much of integrating these foundational models (something like an LLM that was pre-trained on the web) involved natural language. Like if you building a chat bot to have a certain personality, you’d start each context with something like:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;You are a friendly life-coach which is trying to be helpful. If you don’t know the answer to a question, you are to say I don’t know. (Question)&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;This would extend to domain knowledge. Now you could fine tune a foundational model with your own data set, but an easier, allbeit slightly less efficient way, would be to do something like hand craft a bunch of questions and answers pairs, and feed that straight into the prompt.&lt;/p&gt;
&lt;p&gt;This may also extend to agents as well (code that the model interacts with). We didn’t cover agents to a significant degree, but after looking at some of the &lt;a href=&#34;https://aws.amazon.com/bedrock/agents/&#34;&gt;marketing materials&lt;/a&gt;, it seems to me that much of the integration is instructing the model to put parameters within XML tags (so that the much “dumber” agent can parse it out), and how to interpret the structured response.&lt;/p&gt;
&lt;p&gt;A lot of boilerplate, written in natural language, in the prompt just to deal with passing information around. I didn’t expect that.&lt;/p&gt;
&lt;p&gt;Nevertheless, it was pretty interesting. And although I haven’t got the drive to look into this much further, I would like to learn more about how one might hook up external data-sources and agents (somthing that involves vector databases that’s available to the model and doesn’t require fine turning. I not sure how to represent these “facts” so that it’s usable by the model, or even if that’s a thing).&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Replacing Ear Cups On JBL E45BT Headphones</title>
      <link>https://lmika.org/2024/01/15/replacing-earcups-on.html</link>
      <pubDate>Tue, 16 Jan 2024 06:57:02 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/01/15/replacing-earcups-on.html</guid>
      <description>&lt;p&gt;As far as wearables go, my daily drivers are a pair of JBL E45BT Bluetooth headphones. They&amp;rsquo;re several years old now and are showing their age: many of the buttons no longer work and it usually takes two attempts for the Bluetooth to connect. But the biggest issue is that the ear cups were no longer staying on. They&amp;rsquo;re fine when I wear them, but as soon as I take them off, the left cup would fall to the ground.&lt;/p&gt;
&lt;p&gt;But they&amp;rsquo;re a decent pair of headphones, and I wasn&amp;rsquo;t keen on throwing them out or shopping for another pair. So I set about looking for a set of new ear cups.&lt;/p&gt;
&lt;p&gt;This is actually the second pair of replacement cups I&amp;rsquo;ve bought for these headphones. The first had a strip of adhesive that stuck the cup straight on to the speaker (it was this adhesive that was starting to fail). I didn&amp;rsquo;t make a note of where I bought them and a quick search didn&amp;rsquo;t turn up anything that looked like them. So in December, I settled for this pair from &lt;a href=&#34;https://www.ebay.com.au/itm/155617238680?mkcid=16&amp;amp;mkevt=1&amp;amp;mkrid=705-154756-20017-0&amp;amp;ssspo=rb-1vurvtek&amp;amp;sssrc=2047675&amp;amp;ssuid=&amp;amp;var=455902772642&amp;amp;widget_ver=artemis&amp;amp;media=COPY&#34;&gt;this eBay seller&lt;/a&gt;. Yesterday, they arrived.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/pxl-20240115-085846870.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;New set of ear-cups for a JBL E-series bluetooth headphones&#34;&gt;
&lt;figcaption&gt;The new set of ear cups.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/pxl-20240115-085909666.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;A black bluetooth headphone on a table, with the left cup fallen off exposing the speaker, and the right cup slightly removed from it&#39;s original position&#34;&gt;
&lt;figcaption&gt;They couldn&#39;t have come sooner.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;First impressions were that they were maybe too big. I also didn&amp;rsquo;t see an adhesive strip to stick them on. Looking at the listing again, I realised that they&amp;rsquo;re actually for a different line of JBL headphones. But I was a little desperate, so I set about trying to get them on.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/pxl-20240115-090938512.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;The headphones in question on an old piece of paper with the left cup replaces with the new ear-cups and the right speaker exposed and bits of old adhestive laying on the paper&#34;&gt;
&lt;figcaption&gt;Removing the old adhesive, with my fingers (yeah, I probably should buy some tools).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It turns out that they&amp;rsquo;re actually still a good fit for my pair.
The aperture is a little smaller than the headphone speaker, but there&amp;rsquo;s a little rim around each one and I found that by slotting one side of the padding over the rim, and then lightly stretching and rolling the aperture around the speaker, it was possible to get them on. It&amp;rsquo;s a tight fit, but that just means they&amp;rsquo;re likely to stay on. And without any adhesive, which is good.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/pxl-20240115-091118672.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;The headphones with the right cup in profile demonstrating the roll of the padding onto the rim&#34;&gt;
&lt;figcaption&gt;It&#39;s a bit hard to see, but if you look at the top of the right cup, you can see how the padding was rolled onto the speaker from the bottom.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;After a quick road test (a walk around the block and washing the dishes), I found the replacement to be a success. So here&amp;rsquo;s to a few more years of this daily driver.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/pxl-20240115-091344682.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;The headphones in profile with the new replacement cups&#34;&gt;
&lt;figcaption&gt;Headphones with the new cups. They look and feel pretty good.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/pxl-20240115-091351284.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;The old replacement cups on a table, with the left cup loosing it&#39;s vinyl skin revealing the actual foam.&#34;&gt;
&lt;figcaption&gt;The old cups, ready for retirement.&lt;/figcaption&gt;
&lt;/figure&gt;
</description>
    </item>
    
    <item>
      <title>Detecting A Point In a Convex Polygon</title>
      <link>https://lmika.org/2024/01/05/for-reasons-that.html</link>
      <pubDate>Tue, 09 Jan 2024 21:35:14 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/01/05/for-reasons-that.html</guid>
      <description>&lt;p&gt;&lt;em&gt;Note: there are some interactive elements and MathML in this post. So for those reading this in RSS, if it looks like some formulas or images are missing, please click through to the post.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For reasons that may or may not be made clear lately, I’ve been working on something involving &lt;a href=&#34;https://youtu.be/thOifuHs6eY?si=9zherPbByKLIKh7g&#34;&gt;bestagons&lt;/a&gt;. I tended to shy away from things like this before, mainly because of the maths involved in tasks like determining whether a point is within a hexagon. But instead of running away once again from things more complex than a grid, I figured it was time to learn this once and for all. So off I went.&lt;/p&gt;
&lt;p&gt;First stop was Stack Overflow, and &lt;a href=&#34;https://stackoverflow.com/questions/1119627/how-to-test-if-a-point-is-inside-of-a-convex-polygon-in-2d-integer-coordinates&#34;&gt;this answer&lt;/a&gt; on how to test if a point is inside a convex polygon:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;You can check that easily with the dot product (as it is proportional to the cosine of the angle formed between the segment and the point, if we calculate it with the normal of the edge, those with positive sign would lay on the right side and those with negative sign on the left side).&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I suppose I could&amp;rsquo;ve taken this answer as it is, but I know if I did, I&amp;rsquo;d have something that&amp;rsquo;ll be little more than magic. It&amp;rsquo;ll do the job but I&amp;rsquo;d have no idea way. Now like many, if I can get away with having something that works without me knowing how, I&amp;rsquo;ll more likely to take it. But when it comes to code, doing this will usually comes back to bite me in the bum. So I&amp;rsquo;m trying to look for opportunities to dig a little deeper than I would in learning how and why it works.&lt;/p&gt;
&lt;p&gt;It took me a while, and a few false starts, but I think I got there in the end. And I&amp;rsquo;d figured it would be helpful for others to know how I came to understand how this worked at all. And yeah, I&amp;rsquo;m sure this is provable with various  theorems and relationships, but that&amp;rsquo;s just a little too abstract for me. No, what got me to the solution in the end was visualising it, along with attempting to explain it below.&lt;/p&gt;
&lt;p&gt;First, let&amp;rsquo;s ignore polygons completely and consider a single line. Here&amp;rsquo;s one, represented as a vector:&lt;/p&gt;
&lt;lmika-polycanvas world=&#34;1&#34;&gt;
  &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-1.png&#34; width=&#34;600&#34; height=&#34;428&#34; alt=&#34;A vector drawn on graph paper pointing to the top-right&#34;&gt;
&lt;/lmika-polycanvas&gt;
&lt;p&gt;Oh, I should point out that I&amp;rsquo;m assuming that you&amp;rsquo;re aware of things like vectors and trigonometric functions, and have heard of things like dot-product before. Hopefully it won&amp;rsquo;t be too involved.&lt;/p&gt;
&lt;p&gt;Anyway, we have this line.  Let&amp;rsquo;s say we want to know if a specific point is to the &amp;ldquo;right&amp;rdquo; of the line. Now, if the line was vertical, this would be trivial to do. But here we&amp;rsquo;ve got a line that&amp;rsquo;s is on an angle. And although a phrase like &amp;ldquo;to the right of&amp;rdquo; is still applicable, it&amp;rsquo;ll only be a matter of time before we have a line where &amp;ldquo;right&amp;rdquo; and &amp;ldquo;left&amp;rdquo; has no meaning to us.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s generalise it and say we&amp;rsquo;re interested in seeing whether a point is on the same side as the line&amp;rsquo;s normal.&lt;/p&gt;
&lt;p&gt;Now, there are actually &lt;em&gt;two&lt;/em&gt; normals available to us, one going out on either side of the line. But let&amp;rsquo;s pick one and say we want the normal that points to the right if the line segment is pointing directly up. We can add that to our diagram as a grey vector:&lt;/p&gt;
&lt;lmika-polycanvas world=&#34;2&#34;&gt;
  &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-2.png&#34; width=&#34;600&#34; height=&#34;428&#34; alt=&#34;That same vector pointing to the top-right, with a normal originating from the same origin pointing to the bottom-right&#34;&gt;
&lt;/lmika-polycanvas&gt;
&lt;p&gt;Now let&amp;rsquo;s consider this point. We can represented as a vector that the shares the same origin as the line segment&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. With this we can do all sorts of things, such as work out the angle between the two (if you&amp;rsquo;re viewing this in a browser, you can tap on the canvas to reposition the green ray):&lt;/p&gt;
&lt;lmika-polycanvas world=&#34;3&#34; class=&#34;interactive&#34;&gt;
  &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-3.png&#34; width=&#34;600&#34; height=&#34;428&#34; alt=&#34;That same vector and normal, now with an additional line coming from the origin drawn rotated 48° clockwise from the original vector&#34;&gt;
&lt;/lmika-polycanvas&gt;
&lt;p&gt;This might give us a useful solution to our problem here; namely, if the angle between the two vectors falls between 0° and 180°, we can assume the point is to the &amp;ldquo;right&amp;rdquo; of the line. But we may be getting ahead of ourselves. We haven&amp;rsquo;t even discussed how we can go about &amp;ldquo;figuring out the angle&amp;rdquo; between these vectors.&lt;/p&gt;
&lt;p&gt;This is where the &lt;a href=&#34;https://en.wikipedia.org/wiki/Dot_product&#34;&gt;dot product&lt;/a&gt; comes in. The dot product is an equation that takes two vectors and produces a scalar value, based on the formula below:&lt;/p&gt;
&lt;math display=&#34;block&#34;&gt;
  &lt;mi&gt;a&lt;/mi&gt;
  &lt;mo&gt;•&lt;/mo&gt;
  &lt;mi&gt;b&lt;/mi&gt;
  &lt;mo&gt;=&lt;/mo&gt;    
  &lt;msub&gt;
    &lt;mi&gt;a&lt;/mi&gt;
    &lt;mi&gt;x&lt;/mi&gt;
  &lt;/msub&gt;
  &lt;msub&gt;
    &lt;mi&gt;b&lt;/mi&gt;
    &lt;mi&gt;x&lt;/mi&gt;
  &lt;/msub&gt;
  &lt;mo&gt;+&lt;/mo&gt;
  &lt;msub&gt;
    &lt;mi&gt;a&lt;/mi&gt;
    &lt;mi&gt;y&lt;/mi&gt;
  &lt;/msub&gt;
  &lt;msub&gt;
    &lt;mi&gt;b&lt;/mi&gt;
    &lt;mi&gt;y&lt;/mi&gt;
  &lt;/msub&gt;
&lt;/math&gt;
&lt;p&gt;One useful relationship of the dot product is that it&amp;rsquo;s proportional to the cosign of the angle between the two vectors:&lt;/p&gt;
&lt;math display=&#34;block&#34;&gt;
  &lt;mi&gt;a&lt;/mi&gt;
  &lt;mo&gt;•&lt;/mo&gt;
  &lt;mi&gt;b&lt;/mi&gt;
  &lt;mo&gt;=&lt;/mo&gt;
  &lt;mrow&gt;
    &lt;mo&gt;|&lt;/mo&gt;
    &lt;mi&gt;a&lt;/mi&gt;
    &lt;mo&gt;|&lt;/mo&gt;
  &lt;/mrow&gt;
  &lt;mrow&gt;
    &lt;mo&gt;|&lt;/mo&gt;
    &lt;mi&gt;b&lt;/mi&gt;
    &lt;mo&gt;|&lt;/mo&gt;
  &lt;/mrow&gt;
  &lt;mi&gt;cos&lt;/mi&gt;
  &lt;mi&gt;θ&lt;/mi&gt;
&lt;/math&gt;
&lt;p&gt;Rewriting this will give us a formula that will return the angle between two vectors:&lt;/p&gt;
&lt;math display=&#34;block&#34;&gt;
  &lt;mi&gt;θ&lt;/mi&gt;
  &lt;mo&gt;=&lt;/mo&gt;
  &lt;msup&gt;
    &lt;mi&gt;cos&lt;/mi&gt;
    &lt;mn&gt;-1&lt;/mn&gt;
  &lt;/msup&gt;
  &lt;mrow&gt;
    &lt;mo&gt;(&lt;/mo&gt;
    &lt;mfrac&gt;
      &lt;mrow&gt;
        &lt;mi&gt;a&lt;/mi&gt;
        &lt;mo&gt;•&lt;/mo&gt;
        &lt;mi&gt;b&lt;/mi&gt;
      &lt;/mrow&gt;
      &lt;mrow&gt;
        &lt;mrow&gt;
          &lt;mo&gt;|&lt;/mo&gt;
          &lt;mi&gt;a&lt;/mi&gt;
          &lt;mo&gt;|&lt;/mo&gt;
        &lt;/mrow&gt;
        &lt;mrow&gt;
          &lt;mo&gt;|&lt;/mo&gt;
          &lt;mi&gt;b&lt;/mi&gt;
          &lt;mo&gt;|&lt;/mo&gt;
        &lt;/mrow&gt;          
      &lt;/mrow&gt;
    &lt;/mfrac&gt;          
    &lt;mo&gt;)&lt;/mo&gt;
  &lt;/mrow&gt;    
&lt;/math&gt;
&lt;p&gt;So a solution here would be to calculate the angle between the line and the point vector, and as long as it falls between 0 and 180°, we can determine that the point is on the &amp;ldquo;right&amp;rdquo; side of the line.&lt;/p&gt;
&lt;p&gt;Now I actually tried this approach in a quick and dirty mockup using JavaScript, but I ran into a bit of an issue. For you see, the available inverse cosign function did not provide a value beyond 180°. When you think about it, this kinda makes sense, as the cosign function starts moving from -1 back to 1 as the angle is greater than 180° (or less than 0°).&lt;/p&gt;
&lt;p&gt;But we have another vector at our disposal, the normal. What if we were to calculate the angle between those two?&lt;/p&gt;
&lt;lmika-polycanvas world=&#34;4&#34; class=&#34;interactive&#34;&gt;
  &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-4.png&#34; width=&#34;600&#34; height=&#34;428&#34; alt=&#34;That same vector and normal, and the additional line coming from the origin drawn 136° anti-clockwise from the normal, with text indicating that the dot product is -47500&#34;&gt;
&lt;/lmika-polycanvas&gt;
&lt;p&gt;Ah, now we have a relationship that&amp;rsquo;s usable. Consider when the point moves to the &amp;ldquo;left&amp;rdquo; of the line. You&amp;rsquo;d notice that the angle is either greater than 90° or less than –90°. These just happen to be angles in which the cosign function will yield a negative result. So a possible solution before is is to work out the angle between the point vector and normal, take the cosign, and if it&amp;rsquo;s positive, the point will be on the &amp;ldquo;right&amp;rdquo; side of the line (and it&amp;rsquo;ll be on the &amp;ldquo;left&amp;rdquo; side if the cosign is negative).&lt;/p&gt;
&lt;p&gt;But we can do better than that. Looking back at the relationship between the dot product and the angle, we can see that the only way this equation could be negative is if the cosign function is negative, since the vector magnitudes will always return a positive value. So we don&amp;rsquo;t even need to work out angles at all. We can just rely on the dot product between the point and the normal.&lt;/p&gt;
&lt;p&gt;And it&amp;rsquo;s here that the solution clicked. A point is to the &amp;ldquo;right&amp;rdquo; of a line if the dot product of the point vector and the &amp;ldquo;right&amp;rdquo;-sided normal is positive. Look back at the original Stack Overflow answer above, and you&amp;rsquo;ll see that&amp;rsquo;s pretty much what was said there as well.&lt;/p&gt;
&lt;p&gt;Now that we&amp;rsquo;ve got this working for a single line, it&amp;rsquo;s trivial to extend this to convex&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; polygons. After including all the line segments, with the normals pointing inwards, calculate the dot product between each of the normals with the point, and check the sign. If they&amp;rsquo;re all positive, the point is within the polygon. If not, it&amp;rsquo;s outside.&lt;/p&gt;
&lt;lmika-polycanvas world=&#34;5&#34; class=&#34;interactive&#34;&gt;
  &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/out-5.png&#34; width=&#34;600&#34; height=&#34;428&#34; alt=&#34;A hexagon drawn in the centre of graph paper with normals and lines originating at each vertex and converting at a single point located in the centre of the hexagon. The lines indicating a positive dot product for each one and that the point is within the hexagon&#34;&gt;
&lt;/lmika-polycanvas&gt;
&lt;p&gt;So here&amp;rsquo;s an approach that&amp;rsquo;ll work for me, and is relatively easy and cheap to work out. And yeah, it&amp;rsquo;s not a groundbreaking approach, and basically involved relearning a bunch of linear algebra I&amp;rsquo;ve forgotten since high school. But hey, better late than never.&lt;/p&gt;
&lt;script src=&#34;https://lmika.org/scripts/lmika-polycanvas.js&#34; type=&#34;module&#34;&gt;&lt;/script&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Well, technically these vectors should be offset from the global origin, but they&amp;rsquo;re translated in these plots for demonstration purposes.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;I&amp;rsquo;m not sure if this is applicable to concave polygons, where the angle between segments can go beyond 180°.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Can a Single Line Or Even a Single Word Be Considered a Legitimate Blog Post?</title>
      <link>https://lmika.org/2024/01/03/can-a-single.html</link>
      <pubDate>Wed, 03 Jan 2024 06:54:49 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/01/03/can-a-single.html</guid>
      <description>&lt;p&gt;Yes.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>2023 Year In Review</title>
      <link>https://lmika.org/2023/12/27/year-in-review.html</link>
      <pubDate>Mon, 01 Jan 2024 19:10:01 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/12/27/year-in-review.html</guid>
      <description>&lt;p&gt;Well, once more around the sun and it&amp;rsquo;s time again to look back on the year that was.&lt;/p&gt;
&lt;h2 id=&#34;career&#34;&gt;Career&lt;/h2&gt;
&lt;p&gt;Reflecting on the work we did this past year, there were a few highlights. We managed to get a few major things released, like the new billing and resource usage tracking system (not super exciting, but it was still fun to work on). And although the crunch period we had was a little hard — not to mention the 3 AM launch time — it was good to see it delivered on time. We&amp;rsquo;re halfway through another large change that I hope to get out before the end of summer, so it&amp;rsquo;ll probably be full steam ahead when I go back to work this week.&lt;/p&gt;
&lt;p&gt;Highlights aside, there&amp;rsquo;s not much more to say here. Still feel like my career is in a bit of a rut. And although I generally still like my job, it&amp;rsquo;s difficult seeing a way forward that doesn&amp;rsquo;t involve moving away from being an &amp;ldquo;individual contributor&amp;rdquo; to a more managerial role. Not sure I like that prospect — leading a squad of 6 devs is probably the maximum number of people I can manage.&lt;/p&gt;
&lt;p&gt;And honestly, I probably need to make thinking of this more of a priority for the new year. I&amp;rsquo;ve been riding in the backseat on this aspect of my life long enough. Might be time to spend a bit more of effort on driving aspects of my career, rather than letting things just happen to me.&lt;/p&gt;
&lt;p&gt;Ok, that&amp;rsquo;s out of the way. Now for the more exciting topics.&lt;/p&gt;
&lt;h2 id=&#34;projects&#34;&gt;Projects&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://dynamobrowse.app/&#34;&gt;Dynamo-browse&lt;/a&gt; is ticking along, which is good. I&amp;rsquo;ve added a few features here and there, but there&amp;rsquo;s nothing huge that needs doing to it right now. It&amp;rsquo;s received some traction from others, especially people from work. But I gotta be honest, I was hoping that it would be better received than it did. Oh yes, the project website gets visitors, but I just get the sense it hasn&amp;rsquo;t seen as many takers as I had hoped (I don&amp;rsquo;t collect app metrics so don&amp;rsquo;t know for certain). I&amp;rsquo;d like to say this it doesn&amp;rsquo;t matter: so long as I find it useful (and I do), that&amp;rsquo;s all that counts. And yeah, that&amp;rsquo;s true. But I&amp;rsquo;d be lying if I said that I wished others would find it useful as well. Ah well.&lt;/p&gt;
&lt;p&gt;One new &amp;ldquo;major&amp;rdquo; project released this past year was &lt;a href=&#34;http://f5to.run/&#34;&gt;F5 To Run&lt;/a&gt;. Now this, I&amp;rsquo;m not expecting anyone else to be interested in other than myself. This project to preserve the silly little games I made when I was a kid was quite a success. And now that they&amp;rsquo;re ensconced in the software equivalent of amber (i.e. web technologies) I hope they can live on for as long as I&amp;rsquo;m paying for the domain. Much credit goes to those that ported DosBox to a JavaScript library. They were reasonably easy to port over (it was just making the images), and it&amp;rsquo;s a testament to their work seeing stuff built on primitive 90&amp;rsquo;s IBM PC technologies actually being the easiest things to run this way. I just need to find a way to do this to my Windows projects next.&lt;/p&gt;
&lt;p&gt;Another &amp;ldquo;major&amp;rdquo; project that I&amp;rsquo;m happy to have released was &lt;a href=&#34;http://mainboard.lmika.dev/&#34;&gt;Mainboard Mayhem&lt;/a&gt;, a Chips Challenge clone. This was one of those projects that I&amp;rsquo;ve been building for myself over the last ten years, and debating with myself whether it was worth releasing or not. I&amp;rsquo;ve always sided on not releasing it for a number of reasons. But this past year, I figured it was time to either finish and release it, or stop work on it all together. I&amp;rsquo;m happy with the choice I made. And funnily enough, now that it&amp;rsquo;s finished, I haven&amp;rsquo;t had a need to tinker with it since (well, apart from that &lt;a href=&#34;https://lmika.org/2023/11/19/sort-of-inbetween.html&#34;&gt;one time&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;ve been a few other things I worked on this past year, many which didn&amp;rsquo;t really go anywhere. The largest abandoned project was probably those &lt;a href=&#34;https://lmika.org/2023/02/05/mahjong-score-card.html&#34;&gt;reclaimed devices built for schools&lt;/a&gt; I was planning to flash to be scoring devices. The biggest hurdle is connecting to the device. The loader PCB that was shipped to me didn&amp;rsquo;t quite work as the pins weren&amp;rsquo;t making good contact (plus, I broke one of them, which didn&amp;rsquo;t improve things). The custom board I built to do the same thing didn&amp;rsquo;t work either, the pins were too short and uneven. So I never got to do anything with them. They&amp;rsquo;re currently sitting in my cupboard in their box, gathering dust. I guess I could unscrew the back and hook wires up to the appropriate solder points, but that&amp;rsquo;s a time investment I&amp;rsquo;m not interested in taking at the moment.&lt;/p&gt;
&lt;p&gt;This project may rise again with hardware that&amp;rsquo;s a little easier for me to work with. I have my eye on the &lt;a href=&#34;https://arstechnica.com/gadgets/2023/05/its-a-raspberry-pi-a-blackberry-keyboard-and-a-battery-its-the-beepberry/&#34;&gt;BeepBerry&lt;/a&gt;, which looks to be easier to work with, at least with my skills. I added my name to the wait-list, but  hearing from others, it might be some time before I can get my hands on one (maybe if the person running the project spent less time &lt;a href=&#34;https://daringfireball.net/2023/12/beep_beep&#34;&gt;fighting with Apple&lt;/a&gt;, he can start going through the wait-list).&lt;/p&gt;
&lt;p&gt;So yeah, got a few things finished this year. On the whole, I would like to get better at getting projects out there. Seeing people like &lt;a href=&#34;http://rknight.me/&#34;&gt;Robb Knight&lt;/a&gt; who seem to just be continuously releasing things has been inspiring. And it probably doesn&amp;rsquo;t need to be all code either. Maybe other things, like music, video, and written prose. Throw a bit of colour into the mix.&lt;/p&gt;
&lt;p&gt;Speaking of written prose…&lt;/p&gt;
&lt;h2 id=&#34;writing-and-online-presence&#34;&gt;Writing And Online Presence&lt;/h2&gt;
&lt;p&gt;The domain reduction goal continues. I&amp;rsquo;m honestly not sure if it&amp;rsquo;s better or worse than last year. I didn&amp;rsquo;t record the number of registered domains I had at the start of 2023, but as of 27 December 2023, the domain count is at 25, of which 16 have auto-renew turned on.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Domains&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Count&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Registered&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;25&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;With auto-renew turned on&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;16&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Currently used for something&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;13&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Not currently for something but worth keeping&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;3&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Want to remove but stuck with it because it&amp;rsquo;s been shared by others&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;1&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Ultimately I&amp;rsquo;d like to continue cutting down the number of new domains I register. It&amp;rsquo;s getting to be an expensive hobby. I&amp;rsquo;ve started to &lt;a href=&#34;https://blog.jim-nielsen.com/2023/domain-sins-of-my-youth/&#34;&gt;switch to sub-domains&lt;/a&gt; for new things, so I shouldn&amp;rsquo;t be short of possible URLs for projects.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m still trying to write here once a day, mainly to keep me from falling out of the habit. But I think I&amp;rsquo;m at the point where I can start thinking less about the need for a daily post, and focus more towards &amp;ldquo;better&amp;rdquo; posts as a whole. What does &amp;ldquo;better&amp;rdquo; mean? 🤷 Ultimately it&amp;rsquo;s in the eye of the beholder, but publishing less posts that I find &amp;ldquo;cringeworthy&amp;rdquo; is a start. And maybe having less posts that are just me complaining about something that happened that day. Maybe more things about what I&amp;rsquo;m working on, or interesting things I encounter. Of course this is all just a matter of balance: I do enjoy reading (and writing) the occasional rant, and writing about things that frustrate me is cathartic. Maybe just less of that in the new year.&lt;/p&gt;
&lt;p&gt;I did shut down the other blog I was using for tech and project posts. It&amp;rsquo;s now a &lt;a href=&#34;https://workingset.net&#34;&gt;digital garden and knowledge base&lt;/a&gt;, and so far working quite well. I&amp;rsquo;m so glad I did this in retrospect. I was paying unnecessary cognitive overhead deciding which blog a post should go. They all just go here now.&lt;/p&gt;
&lt;h2 id=&#34;travel&#34;&gt;Travel&lt;/h2&gt;
&lt;p&gt;Oof, it was a big year of travel this past year. The amount of time I&amp;rsquo;ve spent away from home comes to 10 weeks in total, a full 20% of the year. This might actually be a record.&lt;/p&gt;
&lt;p&gt;The highlight of the past year was my five-week trip to Europe. Despite it being my third visit to Europe (forth if you include the UK), I consider this to be what could only be described as my &amp;ldquo;Europe trip&amp;rdquo;. I had a lot of great memories there, and stacks of photos and journal entries that I&amp;rsquo;ve yet to go through. I&amp;rsquo;m please that it seemed to have bought my friends and I closer. These sorts of trips can make or break friendships, and I think we left Europe with tighter bonds than we arrived.&lt;/p&gt;
&lt;p&gt;One other notable trip was a week in Singapore. This &lt;em&gt;was&lt;/em&gt; for work, and much like my previous work trips, mainly consisted of being in offices. But we did get a chance to do some site-seeing and it was a pleasure to be able to work with those in Singapore.&lt;/p&gt;
&lt;p&gt;And of course, there was another trip to Canberra to look after my sisters cockatiels, which was always a pleasure.&lt;/p&gt;
&lt;p&gt;Not sure what this new year will bring in terms of travel. I&amp;rsquo;m predicting a relatively quiet one, but who knows.&lt;/p&gt;
&lt;h2 id=&#34;books-and-media&#34;&gt;Books And Media&lt;/h2&gt;
&lt;p&gt;This is the first year I setup a reading goal. I wanted to get out of the habit of starting books and not finishing them (nothing wrong with not finishing books; I was just getting distracted). This past year&amp;rsquo;s goal was quite modest — only 5 books — but it&amp;rsquo;s pleasing to see that I managed to surpass that and actually finish 7 books&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;div class=&#34;bookgoals&#34;&gt;&lt;a href=&#34;https://micro.blog/books/9781523506644&#34;&gt;&lt;img src=&#34;https://cdn.micro.blog/photos/300x/https%3A%2F%2Fbooks.google.com%2Fbooks%2Fcontent%3Fid%3DPNh7DwAAQBAJ%26printsec%3Dfrontcover%26img%3D1%26zoom%3D5%26source%3Dgbs_api&#34; alt=&#34;Keep Going: 10 Ways to Stay Creative in Good Times and Bad&#34; width=&#34;100&#34; height=&#34;120&#34; class=&#34;cover&#34;&gt;&lt;/a&gt;&lt;a href=&#34;https://micro.blog/books/9781936719310&#34;&gt;&lt;img src=&#34;https://cdn.micro.blog/photos/300x/https%3A%2F%2Fbooks.google.com%2Fbooks%2Fcontent%3Fid%3DpV45tAEACAAJ%26printsec%3Dfrontcover%26img%3D1%26zoom%3D5%26source%3Dgbs_api&#34; alt=&#34;What to Do When It&amp;#39;s Your Turn&#34; width=&#34;100&#34; height=&#34;120&#34; class=&#34;cover&#34;&gt;&lt;/a&gt;&lt;a href=&#34;https://micro.blog/books/9781591848264&#34;&gt;&lt;img src=&#34;https://cdn.micro.blog/photos/300x/https%3A%2F%2Fbooks.google.com%2Fbooks%2Fcontent%3Fid%3DAmkBDAAAQBAJ%26printsec%3Dfrontcover%26img%3D1%26zoom%3D5%26source%3Dgbs_api&#34; alt=&#34;Anything You Want&#34; width=&#34;100&#34; height=&#34;120&#34; class=&#34;cover&#34;&gt;&lt;/a&gt;&lt;a href=&#34;https://micro.blog/books/9781936891320&#34;&gt;&lt;img src=&#34;https://cdn.micro.blog/photos/300x/https%3A%2F%2Fbooks.google.com%2Fbooks%2Fcontent%3Fid%3D5HMYBQAAQBAJ%26printsec%3Dfrontcover%26img%3D1%26zoom%3D5%26source%3Dgbs_api&#34; alt=&#34;Do The Work!&#34; width=&#34;100&#34; height=&#34;120&#34; class=&#34;cover&#34;&gt;&lt;/a&gt;&lt;a href=&#34;https://micro.blog/books/9781936891054&#34;&gt;&lt;img src=&#34;https://cdn.micro.blog/photos/300x/http%3A%2F%2Fbooks.google.com%2Fbooks%2Fcontent%3Fid%3DFR7hAAAAQBAJ%26printsec%3Dfrontcover%26img%3D1%26zoom%3D5%26source%3Dgbs_api&#34; alt=&#34;Turning Pro&#34; width=&#34;100&#34; height=&#34;120&#34; class=&#34;cover&#34;&gt;&lt;/a&gt;&lt;a href=&#34;https://micro.blog/books/9780593715543&#34;&gt;&lt;img src=&#34;https://cdn.micro.blog/photos/300x/https%3A%2F%2Fbooks.google.com%2Fbooks%2Fcontent%3Fid%3DRHGoEAAAQBAJ%26printsec%3Dfrontcover%26img%3D1%26zoom%3D5%26source%3Dgbs_api&#34; alt=&#34;The Song of Significance&#34; width=&#34;100&#34; height=&#34;120&#34; class=&#34;cover&#34;&gt;&lt;/a&gt;&lt;a href=&#34;https://micro.blog/books/9781591843849&#34;&gt;&lt;img src=&#34;https://cdn.micro.blog/photos/300x/https%3A%2F%2Fbooks.google.com%2Fbooks%2Fcontent%3Fid%3DGv27DAEACAAJ%26printsec%3Dfrontcover%26img%3D1%26zoom%3D5%26source%3Dgbs_api&#34; alt=&#34;Evil Plans: Having Fun on the Road to World Domination&#34; width=&#34;100&#34; height=&#34;120&#34; class=&#34;cover&#34;&gt;&lt;/a&gt;&lt;a href=&#34;https://micro.blog/books/9781988575971&#34;&gt;&lt;img src=&#34;https://cdn.micro.blog/photos/300x/https%3A%2F%2Fbooks.google.com%2Fbooks%2Fcontent%3Fid%3DfeWfzgEACAAJ%26printsec%3Dfrontcover%26img%3D1%26zoom%3D5%26source%3Dgbs_api&#34; alt=&#34;Hell Yeah or No&#34; width=&#34;100&#34; height=&#34;120&#34; class=&#34;cover&#34;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;As for visual media, well, there&amp;rsquo;s nothing really worth commenting about here. I did have a go at watching &lt;a href=&#34;https://en.wikipedia.org/wiki/BoJack_Horseman&#34;&gt;Bojack Horseman&lt;/a&gt; earlier in the year, and while the first few series were good, I bounced after starting the forth. I&amp;rsquo;ve also gave &lt;a href=&#34;https://en.wikipedia.org/wiki/Mad_Men&#34;&gt;Mad Men&lt;/a&gt; a try, after hearing how well it was received by others, but I couldn&amp;rsquo;t get through the first series. I found the whole look-at-how-people-lived-in-the-&amp;rsquo;60s trope a bit too much after a first few episodes.&lt;/p&gt;
&lt;p&gt;In general, I&amp;rsquo;ve found my viewing habits drift away from scripted shows over this past year. I&amp;rsquo;m more than happy to just watch things on YouTube; or more accurately, rewatch things, as I tend to stick with video&amp;rsquo;s I&amp;rsquo;ve seen before. And although I&amp;rsquo;ve got no plans to write a whole post about my subscriptions just yet (the sand just feels too nice around my face), I did get around to cancelling my Netflix subscription, seeing how little I used it this past year.&lt;/p&gt;
&lt;p&gt;As for podcasts, not much change here. With a few exceptions, the shows I was listening to at the end of the year are pretty close to what I was listening to at the start. But I did find myself enjoying these new shows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://ruminatepodcast.com&#34;&gt;Ruminate Podcast&lt;/a&gt;, with Robb Knight and John Voorhees&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://shoptalkshow.com&#34;&gt;Shop Talk Show&lt;/a&gt;, with Dave Rupert and Chris Coyier&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://play.acast.com/s/the-rest-is-history-podcast&#34;&gt;The Rest is History Podcast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.flophousepodcast.com/&#34;&gt;The Flop House&lt;/a&gt;, with Dan McCoy, Stuart Wellington, Elliott Kalan&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are now in my regular rotation.&lt;/p&gt;
&lt;h2 id=&#34;the-2023-word&#34;&gt;The 2023 Word&lt;/h2&gt;
&lt;p&gt;My 2023 word for the year was &lt;strong&gt;generous&lt;/strong&gt;, trying to be better at sharing things. And I like to think I&amp;rsquo;ve made some improvements here. It may not have come across in a summary post like this, but I&amp;rsquo;ve tried to keep it front of mind in most things I work on. I probably can do a little better here in my personal life. But hey, like most themes, it&amp;rsquo;s always a constant cycle of improvement.&lt;/p&gt;
&lt;p&gt;I must say, this last year has been pretty good. Not all aspects of it — there will always be peeks and valleys — but thinking back on it now, I&amp;rsquo;ve felt that it&amp;rsquo;s been one of the better ones recently. And as for this review, I&amp;rsquo;ll just close by saying, here&amp;rsquo;s to a good 2024.&lt;/p&gt;
&lt;p&gt;Happy New Year. 🥂&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;It&amp;rsquo;s a good thing I was tracking them as I though I&amp;rsquo;d only get to 6 this year.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Day One Waffling</title>
      <link>https://lmika.org/2023/12/30/thinking-about-my.html</link>
      <pubDate>Sat, 30 Dec 2023 07:06:15 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/12/30/thinking-about-my.html</guid>
      <description>&lt;p&gt;Thinking about my journalling in Day One recently and I’m wondering if it’s time to move it off to something else, maybe Markdown files in a Git repository. Still mulling it over but every time I weigh the two options in my mind, the simpler Markdown approach always wins out.&lt;/p&gt;
&lt;p&gt;Plain old Markdown files are just way more versatile and portable than what Day One offers. I can put them in a private Hugo (or Eleventy) site and browse them in a web browser, with the backing of a full HTML renderer that offers, amongst other things, figures with captions (yes, I want them that badly). Making them into a book will be more involved than what Day One offers, but I’ve been a little unhappy with how books from Day One are laid out anyway. Doing it from Markdown files will be pricier and more involved, but at least I’ll have a bit more control over how it looks.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I’ll miss the writing experience from Day One though, especially things like recording the current location and weather for each entry. I’m still wondering what the best substitute for it will be.&lt;/p&gt;
&lt;p&gt;I’m toying around with a web-app I whipped up yesterday. A web-app will be fine for those times I’m online, but how would it work if I’m in an aeroplane? I’m also a little worried about trying it for a while, then abandoning it and leaving it to rot. I guess one thing going for it is that at-least it won’t lock me out of any entries, since they’ll just be Markdown files in Git.&lt;/p&gt;
&lt;p&gt;I’m also considering iA Writer. I haven’t tried it yet but from my understanding, it’ll just write Markdown files to a directory, which is the goal. But I’m not sure how I can get the posts and media from there to a Git repo.&lt;/p&gt;
&lt;p&gt;Anyway, that’s the current thinking. Will keep you posted on what happens.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Of course, the challenge there will be to overcome the friction involved in doing this work to actually get the book made.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>First Impressions of Eleventy</title>
      <link>https://lmika.org/2023/12/29/im-a-hugo.html</link>
      <pubDate>Fri, 29 Dec 2023 07:23:59 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/12/29/im-a-hugo.html</guid>
      <description>&lt;p&gt;I tend to use Hugo whenever I need a static site. But my &lt;a href=&#34;https://lmika.org/2023/12/15/looking-at-the.html&#34;&gt;magpie tendencies&lt;/a&gt; have driven me to take a look at &lt;a href=&#34;http://11ty.dev/&#34;&gt;Eleventy&lt;/a&gt;, and I can definitely see the appeal.&lt;/p&gt;
&lt;p&gt;Going through the Eleventy quick-start guide, I’m quite impressed with how easy it was to setup a bespoke layout for a single site. I’ve done similar things in a few Hugo sites and while I wouldn’t describe it as “hard”, it’s certainly more involved. Hugo’s decent, but it feels quite… engineered. That’s not necessarily a bad thing: putting together something using one of the &lt;a href=&#34;https://themes.gohugo.io&#34;&gt;pre-built themes&lt;/a&gt; is quite straightforward. But going beyond a few theme customisations involves a fair bit of work compare to Eleventy.&lt;/p&gt;
&lt;p&gt;There’s still much more I’ve got to learn, like how Eleventy handles resource bundling (I like how Hugo handles this directly in the template) and configuration (how Eleventy does this is very Node-esk, which is not my preferred approach). But it’s definitely something I’ll keep in my toolkit.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>2023 Song of The Year</title>
      <link>https://lmika.org/2023/12/13/song-of-the.html</link>
      <pubDate>Sun, 24 Dec 2023 17:33:52 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/12/13/song-of-the.html</guid>
      <description>&lt;p&gt;Well, believe it or not, my standing Christmas Eve Mass organ gig has come around once more&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, so it&amp;rsquo;s time to decide on this year&amp;rsquo;s Song of The Year. This is the second post in this series, so please see &lt;a href=&#34;https://lmika.org/2022/12/24/song-of-the.html&#34;&gt;last year&amp;rsquo;s post&lt;/a&gt; on what this nonsense is all about.&lt;/p&gt;
&lt;p&gt;This year&amp;rsquo;s nominees are (not too many this year):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://song.link/s/4DHw4DFUx8StHiCuk961CQ&#34;&gt;Wooden Ship&lt;/a&gt;, from &lt;em&gt;Antarctica — Suit for guitar and orchestra&lt;/em&gt; by Nigel Westlake.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://song.link/s/2W5wldFS2L7fYodRcjXdIf&#34;&gt;Penguin Ballet&lt;/a&gt;, from &lt;em&gt;Antarctica — Suit for guitar and orchestra&lt;/em&gt; by Nigel Westlake. Not really a new track for me, but I&amp;rsquo;m including it here anyway as it&amp;rsquo;s been many years since I last heard it&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the winner is: &lt;strong&gt;Wooden Ship&lt;/strong&gt; by Nigel Westlake 👏&lt;/p&gt;
&lt;img class=&#34;block-center max-half-width&#34; src=&#34;https://cdn.uploads.micro.blog/25293/2023/antarctica-album-cover.jpeg&#34; width=&#34;300&#34; height=&#34;300&#34; alt=&#34;Album cover of the Australian Composers Series, &#39;Out of the Blue&#39;, by Nigel Westlake, performed by the Tasmanian Symphony Orchestra. Copyright the Australian Broadcasting Corporation&#34;&gt;
&lt;p&gt;Specifically, the version played by the Tasmanian Symphony Orchestra and released by the ABC. This has been quite a special song for me this year and was pretty certain to be the winner for most of the year. Well, since first hearing it in May, there hasn&amp;rsquo;t been another one to top it. So bravo!&lt;/p&gt;
&lt;p&gt;But that&amp;rsquo;s not to say there weren&amp;rsquo;t other tracks discovered this year. The honourable mentions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://song.link/s/2C6vglDWy3E2sc0D5BhSLY&#34;&gt;The Last Place on Earth&lt;/a&gt;, from &lt;em&gt;Antarctica — Suit for guitar and orchestra&lt;/em&gt; by Nigel Westlake. A good song, but a little too complex for me.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Extremes&amp;rdquo;, from &lt;em&gt;Music From the Private Life of Plants&lt;/em&gt; by Richard Grassby Lewis. Really wish I had a recent link to this (the only one I know of that works is one to a defunct music store, &lt;a href=&#34;https://web.archive.org/web/20060104190436/http://www.2ndsight.co.uk/records/2NDCD003/2NDCD003.html&#34;&gt;picked up by the Wayback Machine&lt;/a&gt;, that previously sold this album).&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://song.link/s/2ZTYjO9SWLIkaWaIBB0nqJ&#34;&gt;The Knight&lt;/a&gt;, from the &lt;em&gt;Tunic OST&lt;/em&gt; by Lifeformed &amp;amp; Janice Kwan. Not a completely new album to me, but until now, I tended to skip this track.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://song.link/i/1547105129&#34;&gt;Epic Grandpa&lt;/a&gt;, by Izioq.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;It&amp;rsquo;s the only gig on my calendar actually.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;And to have at least one other nominee with what was ultimately going to be the winning song this whole year.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Test Creek: A Test Story With Evergreen.ink</title>
      <link>https://lmika.org/2023/12/07/test-creek-a.html</link>
      <pubDate>Thu, 07 Dec 2023 21:34:57 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/12/07/test-creek-a.html</guid>
      <description>&lt;p&gt;Had a play with &lt;a href=&#34;https://jonathanhays.me/2023/12/06/evergreen-ink/&#34;&gt;Evergreen.ink&lt;/a&gt; this afternoon. It was pretty fun. Made myself a test story called &lt;a href=&#34;https://testcreek.lmika.dev/&#34;&gt;Test Creek&lt;/a&gt; which you can try out (the story was written by me but all the images were done using DALL-E).&lt;/p&gt;
&lt;p&gt;The experience was quite intuitive. I&amp;rsquo;ve yet to try out the advanced features, like the Sapling scripting engine, but the basics are really approachable for anyone not interested with any of that.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/screenshot-of-evergreen-ink.png&#34; width=&#34;600&#34; height=&#34;394&#34; alt=&#34;A screenshot of the Evergreen.ink editor, showing the contents of a card with two options and a preview on the right&#34;&gt;
&lt;p&gt;I would recommend not writing too much on a single card. Keep it to maybe two or three paragraphs. Otherwise the text will start to flow over the image, like it does on one of the cards in this story. Evergreen.ink does keep the text legible with a translucent background. But still, it&amp;rsquo;s just too much text.&lt;/p&gt;
&lt;p&gt;I should also say that the preview, to the right of the editor, is interactive, meaning that you can use it to jump to cards backed by the options. While I was playing around, I was wondering why there wasn&amp;rsquo;t a quick way to do this. It wasn&amp;rsquo;t until I started writing this post that I actually tried the option in the preview, and it worked.&lt;/p&gt;
&lt;p&gt;As for the app itself, if I could make one improvement, it would be something like an image picker which would allow me to reuse images already attached to other cards. I&amp;rsquo;m not sure how best to use images in these types of stories, but the way I was going for was more to accent the story instead of simply illustrating what&amp;rsquo;s going on in the prose. So I wanted to reuse images over a series of related cards, and in order to do that I had to upload duplicates.&lt;/p&gt;
&lt;p&gt;But really, this is quite a minor quibble. On the whole I was quite impress by how well the experience was. It&amp;rsquo;s not easy trying to express something as complex as an interactive story, and I think Evergreen.ink did a pretty decent job.&lt;/p&gt;
&lt;p&gt;So yeah, give it a try. It was quite fun putting this interactive story together. I haven&amp;rsquo;t got any other ideas lined up, but it would be good to make another one down the line.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; One other thing to be aware of is that the link given to you when you try to share a story requires a login. So if you want to avoid that, you&amp;rsquo;ll need to choose the Zip option, which basically bundles the story as a static website. You can deploy it to Netlify quite easily (just check the permissions of the files first, I had to &lt;code&gt;chmod 666&lt;/code&gt; them).  Thank-you &lt;a href=&#34;https://rknight.me/&#34;&gt;Robb&lt;/a&gt; for letting me know.&lt;/p&gt;
&lt;p&gt;Also, thank-you to omg.lol for the Switchboard feature. It saved my tale dealing with the new redirect.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Best, First, Favourite</title>
      <link>https://lmika.org/2023/11/24/best-first-favourite.html</link>
      <pubDate>Sat, 25 Nov 2023 08:17:43 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/11/24/best-first-favourite.html</guid>
      <description>&lt;p&gt;On &lt;a href=&#34;https://www.relay.fm/rd/221&#34;&gt;Reconcilable Difference #221&lt;/a&gt;, Merlin and John introduced the concept of “Best, First, Favourite”. For a particular category, which would you consider the best (i.e. closest to a perfect representation of that category, in however you define it), which would you recommend someone who’s interested in starting should experience first, and which one is your favourite.&lt;/p&gt;
&lt;p&gt;I thought it was a fun idea, so I’ve put together a few of my own.&lt;/p&gt;
&lt;p&gt;It was hard coming up with categories for this one, particularly when considering “best” and “favourite”. You need to have had enough experience to know what makes a good &amp;ldquo;thing&amp;rdquo;, in order to  judge it against all the others and come up with a &amp;ldquo;best&amp;rdquo; one. It also helps to have enough experience to avoid picking your favourite as the best as well. I tried picking categories in which my favourite is different than what I consider the “best”. And it might be that I lack variety in my life, but the list of categories that I managed to come up with was relatively short.&lt;/p&gt;
&lt;p&gt;Nonetheless, here they are:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Mike Oldfield music&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Best:&lt;/strong&gt; &lt;a href=&#34;https://album.link/au/i/79324466&#34;&gt;Tubular Bells 3&lt;/a&gt;. Oldfield was in his element here. A balanced helping of both accoustic and electronic, slow and moving, and very consistent in it&amp;rsquo;s theming.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First:&lt;/strong&gt; &lt;a href=&#34;https://album.link/au/i/358349175&#34;&gt;Tubular Bells 2&lt;/a&gt;. It may seem that &lt;a href=&#34;https://album.link/au/i/1440852773&#34;&gt;Tubular Bells&lt;/a&gt; should be the album to goto for a taste of Mike Oldfield, and it certainty has the Oldfield signature sound. But I&amp;rsquo;d suggest considering going with this album first, as it&amp;rsquo;s a bit more refined while having the same basic structure. It&amp;rsquo;s also the album that grabbed me.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Favourite:&lt;/strong&gt; &lt;a href=&#34;https://album.link/au/i/1445842752&#34;&gt;Crises&lt;/a&gt;; &lt;a href=&#34;https://album.link/au/i/267551838&#34;&gt;The Songs of Distant Earth&lt;/a&gt;. I&amp;rsquo;d probably put TB3 here as well, but in lieu of choosing something that I also consider the best, these two are probably my next favourite. Or it could just be the positive associations I have of them: Songs of Distant Earth reminding me of faraway places, Crises reminding me of home.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Episodes of Seinfield&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Best:&lt;/strong&gt; &lt;em&gt;The Parking Garage.&lt;/em&gt; A refinement of The Chinese Resturant, which was groundbreaking in it’s own right. Honourable mention: &lt;em&gt;The Parking Space&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First:&lt;/strong&gt; &lt;em&gt;The Conversion.&lt;/em&gt; I think anything in Season 5 or Season 6 would work here. I&amp;rsquo;ve chosen this one as I wanted an episode which showcases all the character&amp;rsquo;s traits without having too many supporting character (that also features George&amp;rsquo;s parents). Honourable mention: &lt;em&gt;The Bris&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Favourite:&lt;/strong&gt; &lt;em&gt;The Busboy.&lt;/em&gt; This is a series 2 episode, while they were still finding their feet. But it’s one of the first where the writers manage to have multiple plot threads all wrapped up together in a cohesive whole by the end, an attribute of the writing that I absolutely love. Honourable mention: &lt;em&gt;The Dinner Party&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Programming text editors (for MacOS)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Best:&lt;/strong&gt; &lt;a href=&#34;https://code.visualstudio.com&#34;&gt;VS Code&lt;/a&gt;. I&amp;rsquo;m not a user of this myself but I can&amp;rsquo;t deny the amount of effort (and that sweet, sweet Microsoft cash) that&amp;rsquo;s going to this project. Certainly it&amp;rsquo;s the most capable out there for pretty much any language you need to work in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First:&lt;/strong&gt; &lt;a href=&#34;http://nova.app&#34;&gt;Nova&lt;/a&gt;, depending on which language you&amp;rsquo;re working in. Obviously if you&amp;rsquo;re doing anything Apple related, it&amp;rsquo;s probably best to go with XCode or something. But I think for anything else, Nova is a pretty decent text editor, and definitely one worth trying for anyone starting out.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Favourite:&lt;/strong&gt; Anything from &lt;a href=&#34;http://jetbrains.com&#34;&gt;JetBrains&lt;/a&gt;. When you feel like moving into something a little more integrated, especially for languages considered &amp;ldquo;complicated&amp;rdquo;, I can definitely recommend the IDEs from JetBrains. I use Goland in my day-to-day, with the occaional WebStorm for anything frontend that is considered large. Others include IntelliJ and Android Studios.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Apple-related Tech Podcasts for anyone that has never heard a podcast before&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Best:&lt;/strong&gt; &lt;a href=&#34;https://relay.fm/upgrade&#34;&gt;Upgrade&lt;/a&gt;. I&amp;rsquo;m not much of a listener of this one anymore, but it&amp;rsquo;s still a very well-produced show, and Jason really knows his stuff.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First:&lt;/strong&gt; &lt;a href=&#34;https://daringfireball.net/thetalkshow/&#34;&gt;The Talk Show&lt;/a&gt;. I think having something a little more off-the-cuff is the way to get into the medium. You have to warm yourself into it, like you&amp;rsquo;re having a conversation with friends, and starting with something a little “produced” can leave you feeling as if you&amp;rsquo;re just another listener (which, I guess, you are but you shouldn&amp;rsquo;t want that feeling). I think the Talk Show fits the mould here. It did for me.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Favourite:&lt;/strong&gt; &lt;a href=&#34;https://atp.fm&#34;&gt;Accidental Tech Podcast&lt;/a&gt;. Hands down. Informative and enjoyable to listen to. This is one that I do my best to catch every episode they release.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Walks in and around greater Melbourne&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Best:&lt;/strong&gt; &lt;em&gt;Sherbrooke Falls, Mt. Dandenong&lt;/em&gt;. This is not the longest, nor the most challenging, but it’s by far the prettiest. Walking amongst the great Mountain Ash is quite a moving experience. Be sure to have the soundtrack of the Atterborough documentary series &lt;em&gt;The Private Life of Plants&lt;/em&gt; playing while you do.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First:&lt;/strong&gt; &lt;em&gt;The Domino Trail, Trentham&lt;/em&gt; This is about an 1.5 hours out of Melbourne but a nice easy rail-trail going through the lovely forest around the Domino Creek.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Favourite:&lt;/strong&gt; &lt;em&gt;Bushrangers Bay to Cape Shank Lighthouse:&lt;/em&gt; A two hour return walk that is moderately challenging with lovely scenes of Bass Strait. Don’t be surprised to run into a kangaroo or two (plus the occasional snake; look out for those).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Pubs in and around greater Melbourne that make a decent &lt;a href=&#34;https://en.wikipedia.org/wiki/Chicken_parmesan&#34;&gt;parma&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Best:&lt;/strong&gt; &lt;em&gt;The Panton Hill Pub, Panton Hill&lt;/em&gt;. This is a good 30 km out of Melboune area, in the green-wedge in a little town called Panton Hill. There&amp;rsquo;s not much there: a few houses, maybe a shop or two, and this pub. But they do a pretty solid parma there. Good fillet, decent balance of cheese and ham, and a good portion of chips and salid. It’s been a while since I&amp;rsquo;ve been there, so things may have change.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First:&lt;/strong&gt; &lt;em&gt;The Turf Bar, Queens St&lt;/em&gt;. If you&amp;rsquo;re visiting Melbourne for the first time, and you&amp;rsquo;d like to try a pretty decent parma, then I&amp;rsquo;d probably suggest trying out the Turf. It&amp;rsquo;s probably not the best pub in town: it&amp;rsquo;s more of a sports bar and can be pretty load when city-workers go there for Friday lunch or drinks. But I&amp;rsquo;ve been pretty impressed by the quality of their parmas. Be prepared to wait a little while for them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Favourite:&lt;/strong&gt; &lt;em&gt;The Old England Hotel, Heidelberg&lt;/em&gt;. This is not the best parma out there, but they’re pretty consistent. One thing going for this place is that it&amp;rsquo;s easy to get to.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Idea For Mainboard Mayhem: A Remote Pickup</title>
      <link>https://lmika.org/2023/11/19/sort-of-inbetween.html</link>
      <pubDate>Sun, 19 Nov 2023 20:21:56 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/11/19/sort-of-inbetween.html</guid>
      <description>&lt;p&gt;Sort of in-between projects at the moment so I&amp;rsquo;m doing a bit of light stuff on Mainboard Mayhem. I had an idea for a new element: a remote control which, when picked up, will allow the player to toggle walls and tanks using the keyboard, much like the green and blue buttons.&lt;/p&gt;
&lt;p&gt;I used ChatGGT to come up with some artwork, and it produced something that was pretty decent.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/red-pixel-button-generated-by-dalle.png&#34; width=&#34;600&#34; height=&#34;600&#34; alt=&#34;Image generated from DALL-E with the prompt: pixel art of a remote control with a single red button styled like the tiles found in Chips Challange, rotated 45 degrees to the right.&#34;&gt;
&lt;figcaption&gt;Prompt: pixel art of a remote control with a single red button styled like the tiles found in Chips Challange, rotated 45 degrees to the right.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Only issue was that the image was huge — 1024 x 1024 — and the tiles in Mainboard Mayhem were only 32 x 32.&lt;/p&gt;
&lt;p&gt;I tried shrinking it down in Acorn, using various scaling algorithms. The closest that worked was bringing it down slowly to about 128 x 128 using Nearest Neighbour, than trying to go all the way down  to 32 x 32 using Lanczos. That worked, but it required true 32 bit colour to be recognisable, and I wanted to preserve the 16 colour palette used by the original Chips Challenge.&lt;/p&gt;
&lt;p&gt;So using the original image as a reference, I bit the bullet and drew my own in Acorn. You can see it here in this test level:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/mainboard-mayhem-example-levem.png&#34; width=&#34;600&#34; height=&#34;315&#34; alt=&#34;Example Mainboard Mayhem level showing the green and blue remote controls. The controls have 4 small buttons and one large bulbous button that is either blue or green, with a bit of phong and shadow applied&#34;&gt;
&lt;figcaption&gt;They&#39;re the elements that look like remote controls.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It turn out okay. At least it&amp;rsquo;s recognisable. Anyway, I coded it up and gave it a bit of a try:&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://cdn.uploads.micro.blog/25293/2023/mainboard-mayhem-demo.mov&#34; poster=&#34;https://lmika.org/uploads/2023/mainboard-mayhem-demo-poster.png&#34; controls=&#34;controls&#34; playsinline=&#34;playsinline&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Yeah, it works well. When the player has the appropriate colour remote, they can hit either &lt;kbd&gt;Z&lt;/kbd&gt; or &lt;kbd&gt;X&lt;/kbd&gt; to toggle the green walls or blue tanks respectively. I really should add some indicators in the status bar to show which button to press.&lt;/p&gt;
&lt;p&gt;Not sure what I&amp;rsquo;ll do after this. The fun part was coming up with the element. But I guess I&amp;rsquo;ll have to come up with a few puzzles that use it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Few Thoughts On Using iA Presenter</title>
      <link>https://lmika.org/2023/11/16/a-few-thoughts.html</link>
      <pubDate>Thu, 16 Nov 2023 20:51:48 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/11/16/a-few-thoughts.html</guid>
      <description>&lt;p&gt;Well the “big presentation” was today, the one I thought would be a good canditate for &lt;a href=&#34;https://lmika.org/2023/10/31/ive-been-asked.html&#34;&gt;trying out iA Presenter&lt;/a&gt;. And after spending the last couple of weeks preparing for it, I’d thought it would be good time to give my thoughts on how it worked for me.&lt;/p&gt;
&lt;p&gt;First, I must say that I can appreciate using an app that is opinionated. This is not a drop-in replacement for Keynote&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;: the app really does try and steer you towards a particular presenting style. They’re quite upfront with this: the example shown on first launch outlines how to prepare the slides and why writing out the entire presentation in full, while leaving slides as the role of accenting your points, makes for better presentation.&lt;/p&gt;
&lt;p&gt;I knew this going in, so it wasn’t a big shock to me. Plus it’s easy to like an opinionated piece of software when you share that opinion yourself.&lt;/p&gt;
&lt;p&gt;This flows naturally into the second thing that I like about iA Presentation, which is the Markdown support. Using Markdown to prepare the slides is wonderful, especially when you compare it to the point-and-click content-by-bullet-point interaction style you’d find in Keynote. That WYSIWYG styles is not for me, particularly when it comes to correcting style and alignment issues that only affects one slide that sticks out like a sore thumb when giving the presentation.  Markdown only means iA Presenter is left to handle the layout, and that&amp;rsquo;s fine with me.&lt;/p&gt;
&lt;p&gt;Now, it would be nice to have even more control over the slide layout and styling. iA Presentation has very limited support for this, and while I was preparing the slides, I kept finding myself wishing that I could do more of the finer things, like adjust the font size of a code block to avoid line-wraps.&lt;/p&gt;
&lt;p&gt;There are some things you can do, such as choose whether elements should appear below or beside each other. This is done through the use of new-lines — or lack there-of — which is a style that didn&amp;rsquo;t really gel with me. It seemed like a concept that was a little undercooked. It also didn’t help that there wa no way to actually force a new line, to do something like space out content vertically.&lt;/p&gt;
&lt;p&gt;I don’t know how this could be improved. Maybe having a way to specify a layout or styling that is separate from the implicit styling from the Markdown, sort of like slide-specific front-matter, maybe? If done in such a way as to avoid complicating things too much, it’ll probably be welcomed.&lt;/p&gt;
&lt;p&gt;One other thing that would be good is to have more control over separating the layout of the slides from that of the exported speaker notes. You’re essentially writing long-form content, complete with headers that’ll appear on the slide. But putting a H2 header over a H1 header to start a section would look strange in a PDF export. It’ll look fine on the slide, but that’s becasue you’re stuck using header levels (h1, h2) to control text size. Because the content on the slide is interleaved with the speech itself, the order of elements that make sense on the presentation may note for the exported PDFs.&lt;/p&gt;
&lt;p&gt;Although I guess the solution there would be just to open the presentation in a regular Markdown editor and export to PDF. But having a one-stop solution to that would be nice. So, I don’t know: having a way to separate the symantics of the header from the size they appear on the slide would work? (Maybe all I want is just HTML, 🤷)&lt;/p&gt;
&lt;p&gt;So, what’s the verdect? Would I use iA Presenter again? Hmm, maybe. If I’m working on a presentation with a small number of slides containing simple visual elements designed to emphasise something, such things you’d find at TED talks or an Apple keynote, then yeah, I’d probably use it again. It’s a style of presentation that the app is clearly optimised for, and it does a good job for that. If I needed slides that were a little more informative in their own right, I&amp;rsquo;d probably consider something else. Probably not Keynote, but I’d consider one of the JS+HTML options.&lt;/p&gt;
&lt;p&gt;But it’s a really nice app&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; and a pleasure to use, so it&amp;rsquo;s probably worth checking out for your next presentation.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Not to single out Keynote here. You can easily use Powerpoint or Google Slides as a drop-in replacement for this post.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;I didn’t talk about the UI as I wanted to focus on the preparation aspects, but the UI is delightful. They put a lot of care into it, and despite being “just a text editor”, seeing the little things like having the highlight or carrat colour match the slide background colour is a really nice touch. Dare I say, almost whimsical.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Resurrecting Untraveller And Finishing The RA-V Mission Posts</title>
      <link>https://lmika.org/2023/11/12/resurrecting-untraveller-and.html</link>
      <pubDate>Sun, 12 Nov 2023 15:30:30 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/11/12/resurrecting-untraveller-and.html</guid>
      <description>&lt;p&gt;It’s been 10 years to the day when I had the opportunity to tour the Pacific as part of my job at the Bureau of Meteorology, the so call “RA-V Missions”. This last month or so, I’ve been writing about them in my journal, trying to get it all down before I forget. I had grand plans of publishing them on a travel blog, which I shelved a couple of months ago.&lt;/p&gt;
&lt;p&gt;But while I was updating my journal, I was wondering if anyone else would find it interesting. Probably not, really: I don’t know if people enjoy reading about other peoples work trips (I can go either way, myself).&lt;/p&gt;
&lt;p&gt;But in the off-chance that someone out there will find this story intriguing, I decided to &lt;a href=&#34;https://untraveller.org/trips/ra-v-missions/&#34;&gt;resurrect my travel blog&lt;/a&gt; and publish these (moderately edited) journal entries.&lt;/p&gt;
&lt;p&gt;If this ends up being your cup of tea, I hope you enjoy it. I may add some other trips to the site down the line (including the ones that were on the old one). I’ll let you know here if I do.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Defaults</title>
      <link>https://lmika.org/2023/11/04/defaults.html</link>
      <pubDate>Sat, 04 Nov 2023 06:53:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/11/04/defaults.html</guid>
      <description>&lt;p&gt;I see that &lt;a href=&#34;https://gabz.blog/2023/11/03/my-defaults.html&#34;&gt;Gabz&lt;/a&gt;, &lt;a href=&#34;https://rknight.me/app-defaults/&#34;&gt;Robb&lt;/a&gt;, and &lt;a href=&#34;https://maique.eu/2023/11/03/defaults.html&#34;&gt;Manique&lt;/a&gt;  — along with &lt;a href=&#34;https://defaults.rknight.me/&#34;&gt;many others&lt;/a&gt; — have posted their defaults after listening to &lt;a href=&#34;https://listen.hemisphericviews.com/097&#34;&gt;Hemispheric Views 097 - Duel of the Defaults!&lt;/a&gt;, which was a really fun episode. I thought I’d do the same.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mail Client&lt;/strong&gt;: Fastmail. Web-app on the desktop and app on mobile&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mail Server&lt;/strong&gt;: Fastmail&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notes&lt;/strong&gt;: Obsidian for work. It was Obsidian for personal use but I’m trying out Notion at the moment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;To-Do&lt;/strong&gt;: Obsidian/Notion (todos go in as notes)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Photo Shooting&lt;/strong&gt;: Android camera app&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Photo Management&lt;/strong&gt;: Google Photos&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Calendar&lt;/strong&gt;: Google Calendar&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloud file storage&lt;/strong&gt;: Google Drive&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RSS&lt;/strong&gt;: Feedbin. I mainly read it with NetNewsWire but I also use the web-app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contacts&lt;/strong&gt;: Android contacts app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Browser&lt;/strong&gt;: Safari, Vivaldi&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chat&lt;/strong&gt;: Mainly still use Android Messanger for SMS but started using WhatsApp more&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bookmarks&lt;/strong&gt;: Linkding running on Pikapods.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Read It Later&lt;/strong&gt;: None, but if I were to start, I’d probably try out Feedbin’s RIL service.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Word Processing&lt;/strong&gt;: n/a&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spreadsheets&lt;/strong&gt;: Google Sheets, Numbers (I don’t do a lot of spreadsheeting)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Presentations&lt;/strong&gt;: Keynote, but giving iA Presenter a try at the moment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shopping Lists&lt;/strong&gt;: Google Keep&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Meal Planning&lt;/strong&gt;: n/a&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Budgeting &amp;amp; Personal Finance&lt;/strong&gt;: n/a&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;News&lt;/strong&gt;: ABC&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; News, in a web-browser&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Music&lt;/strong&gt;: &lt;a href=&#34;https://lmika.org/2024/03/11/alto.html&#34;&gt;Alto&lt;/a&gt; (my own music app), Spotify&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Podcasts&lt;/strong&gt;: Pocketcasts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Password Management&lt;/strong&gt;: 1Password&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Photo Editing&lt;/strong&gt;: Google Photo&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Weather:&lt;/strong&gt; Bureau of Meterology website.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Social Clients:&lt;/strong&gt; Tusky (Mastodon)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code Editor:&lt;/strong&gt; GoLand (Jetbrains in general), Android Studios, or XCode&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Text Editor:&lt;/strong&gt; Nova&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hard Quiz Expert Subject:&lt;/strong&gt; Probably the music of Mike Oldfield.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Scored myself based on the rules of the game and came up with 44 points. It was a little tricky as I’ve got both feet in separate ecosystems.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Australian Broadcasting Cooperation&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Why I Like Go</title>
      <link>https://lmika.org/2023/10/30/why-i-like.html</link>
      <pubDate>Mon, 30 Oct 2023 12:59:56 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/10/30/why-i-like.html</guid>
      <description>&lt;p&gt;This question was posed to me in the Hemispheric Views Discord the other day. It&amp;rsquo;s a bit notable that I didn&amp;rsquo;t have an answer written down for this already, seeing that I do have pretty concrete reasons for why I really like Go. So I figured it was time to write them out.&lt;/p&gt;
&lt;p&gt;I should preface this by saying that by liking Go it doesn&amp;rsquo;t mean I don&amp;rsquo;t use or like any other languages.  I don&amp;rsquo;t fully understand those that need to dislike other languages like they&amp;rsquo;re football teams. &amp;ldquo;Right tool for the job&amp;rdquo; and all that. But I do have a soft-spot for Go and it tends to be my go-to language for any new projects or scripting tasks.&lt;/p&gt;
&lt;p&gt;So here it is: the reasons why I like Go.&lt;/p&gt;
&lt;p&gt;First, it&amp;rsquo;s simplicity. Go is not a large language, and a large majority of it you can keep in your working memory. This makes Go easy to write and, more importantly, easy to read.&lt;/p&gt;
&lt;p&gt;It might seam that a small feature set makes the language quite limiting. Well, it does, to a degree, but I&amp;rsquo;d argue that&amp;rsquo;s not a bad thing. If there are only a couple of ways to do something, it makes it way easier to predict what code you&amp;rsquo;re expecting to see. It&amp;rsquo;s sort of like knowing what the next sentence will be in a novel: you know a piece of logic will require some dependant behaviour, and you start thinking to yourself &amp;ldquo;how would &lt;em&gt;I&lt;/em&gt; do that?&amp;rdquo; If the answer space is small, you&amp;rsquo;re more likely to see what you expect in the actual code.&lt;/p&gt;
&lt;p&gt;But just because Go is a deliberately small language doesn&amp;rsquo;t mean that it&amp;rsquo;s a stagnant language. There have been some pretty significant features added to it recently, and there are more plans for smoothing out the remaining rough edges. It&amp;rsquo;s just that the dev team are very deliberate in how they approach these additions. They consider forward compatibility as much as they do about backwards compatibility, being careful not to paint themselves into a corner with every new feature.&lt;/p&gt;
&lt;p&gt;Type parameters are a great example of this. There were calls for type parameters since Go 1.0, but the dev team pushed back until they came up with a design that worked well for the language. It wasn&amp;rsquo;t the first design either. I remember one featuring some new constraint-based constructs that did about 80% what interfaces were doing already. If that were shipped it would&amp;rsquo;ve meant a lot of extra complexity just for type parameters. What was shipped isn&amp;rsquo;t perfect, and it doesn&amp;rsquo;t cover every use case type parameters could theoretically support. But it made sense: type parameters built on interfaces, a construct that already existed and was understood.&lt;/p&gt;
&lt;p&gt;This, I think, is where C++ fails to a huge degree. The language is massive. It was massive 30 years ago, and they&amp;rsquo;ve been adding features to the language every few year since, making it larger still. I see Apple doing something similar with Swift, and I&amp;rsquo;m not sure that&amp;rsquo;s good for the language. It&amp;rsquo;s already quite a bit larger than Go, and I think Apple really should curb their desire for adding features to the language unless there&amp;rsquo;s a good reason for doing so.&lt;/p&gt;
&lt;p&gt;The drive for simplicity also extends to deployments. Go is compiled to a static binary, one that is extremely portable and could be easily deployed or package as you so desire. No need to fluff about with dependencies. This is where I found Go having a leg-up over scripting languages, like Python and Ruby. Not because Go is compiled (although that helps) but that you have less need to think about packaging dependencies during a deploy. I wrote a lot of Ruby before looking at Go, and dealing with gems and Bundle was a pain. I&amp;rsquo;m not a huge Python expert to comment on the various ways that language deals with dependencies, but hearing about the various ways to setup virtual environments doesn&amp;rsquo;t fill me with confidence that it&amp;rsquo;s simple.&lt;/p&gt;
&lt;p&gt;And I will admit that Go&amp;rsquo;s approach to this isn&amp;rsquo;t perfect either. For a long while Go didn&amp;rsquo;t even have a way to manage versioned dependencies: they were all lumped into a single repository. The approach with modules is better, but not without some annoyances themselves. Any dependency that goes beyond version 1 requires you to change the import statement to include a &lt;code&gt;vX&lt;/code&gt;, an unnecessary measure if the version change is backwards compatible. That&amp;rsquo;s not even considering packages that avoid this, and are forever on version 1 (or 0).&lt;/p&gt;
&lt;p&gt;But since moving to modules my encounters with package dependencies issues remains quite rare, and once you&amp;rsquo;ve got the build sorted out, that&amp;rsquo;s it. No need to deal with packaging after that.&lt;/p&gt;
&lt;p&gt;And I&amp;rsquo;d argue that Go rides that sweet-spot between a scripting language like Python and Ruby, and a compiled language like C (maybe Rust, but I know very little of that language to comment on it). It&amp;rsquo;s type safe, but type inferences make it easy to write concise code without excessive annotations everywhere. It&amp;rsquo;s compiled to an executable, yet memory is managed for you. I won&amp;rsquo;t talk about how Go does concurrency, but after dealing with Java threads for several years, using them are a joy.&lt;/p&gt;
&lt;p&gt;I should probably balance the scale a bit and talk about areas where I think Go could be made better. The big one is error handling. While I do like the principals — that errors are values and can be handled as such — it does mean a lot of boilerplate like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;foo&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;doThis&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bar&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;doThat&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;foo&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;baz&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;doAnotherThing&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;bar&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To the Go teams credit, the are looking at improving this. And I think there&amp;rsquo;s enough prior art out there for a solution that&amp;rsquo;ll look pretty nice without having to resort to exceptions. Maybe something like Swift&amp;rsquo;s &lt;code&gt;guard&lt;/code&gt; statement, that can be used in an expression.&lt;/p&gt;
&lt;p&gt;And yeah, other areas of Go, like it&amp;rsquo;s support for mobile or GUI-style programming and lacking a bit. That could probably be plugged with third-party modules to a degree, although I think because Go is not an object-orientated language, the seals won&amp;rsquo;t be perfect (take a look at &lt;a href=&#34;https://github.com/therecipe/qt&#34;&gt;Go&amp;rsquo;s implementation of QT&lt;/a&gt; to see how imperfect Go maps to a toolkit that assumes objects). And some gaps need to be plugged by Google themselves, like with mobile support (they do have something here, but I&amp;rsquo;m not sure to what degree they&amp;rsquo;re being maintained).&lt;/p&gt;
&lt;p&gt;But I&amp;rsquo;m sure most of these issues are surmountable. And no language is perfect. If Go doesn&amp;rsquo;t work for a situation, I&amp;rsquo;ll use Java or Swift or something else. &amp;ldquo;Right tool for the job&amp;rdquo; and all that.&lt;/p&gt;
&lt;p&gt;So these are the reasons why I like Go. And I think it all boils down to trying to keep the language simple while still being useful. And as far as my experience with Go is concerned, those maintaining it are doing a pretty stellar job.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Work Email Spam</title>
      <link>https://lmika.org/2023/10/24/work-email-spam.html</link>
      <pubDate>Tue, 24 Oct 2023 06:49:40 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/10/24/work-email-spam.html</guid>
      <description>&lt;p&gt;Opened my work email this morning and received a greeting from the following spam messages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Webinar to &amp;ldquo;overcome the fear of public speaking&amp;rdquo; from some HR Training mob&lt;/li&gt;
&lt;li&gt;A training course on &amp;ldquo;accelerating innovation in data science an ML&amp;rdquo; (there&amp;rsquo;re a few emails about AI here)&lt;/li&gt;
&lt;li&gt;Webinars from Stripe, Slack, and Cloudflare about how other companies are using them&lt;/li&gt;
&lt;li&gt;Weekly updates about what&amp;rsquo;s happening on our Confluence wiki (this probably could be useful… maybe?  But our wiki is so large that most updates are about things other teams are working on)&lt;/li&gt;
&lt;li&gt;A training course on some legal mandates about hiring (honestly, my email must&amp;rsquo;ve appeared on some mailing list for HR professionals)&lt;/li&gt;
&lt;li&gt;Another webinar from the first training mob about dealing with &amp;ldquo;employees from hell&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Marked all as read, closed email, and opened Slack.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Links About Blogging</title>
      <link>https://lmika.org/2023/10/17/links-about-blogging.html</link>
      <pubDate>Tue, 17 Oct 2023 09:16:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/10/17/links-about-blogging.html</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s a collection of links to posts about the &amp;ldquo;practice&amp;rdquo; of blogging or writing that I&amp;rsquo;ve found useful:&lt;/p&gt;
&lt;h2 id=&#34;about-blog&#34;&gt;About Blog&lt;/h2&gt;
&lt;p&gt;About blogs, starting a blog, keeping a blog, and others who blog.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.preetamnath.com/blog/why-you-should-write&#34;&gt;Why You Should Write&lt;/a&gt;, by Preetam Nath.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cagrimmett.com/thoughts/2022/04/26/why-blog/&#34;&gt;Why Blog&lt;/a&gt;, by Chuck Grimmett.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://tomcritchlow.com/2018/02/23/small-b-blogging/&#34;&gt;Small b blogging&lt;/a&gt;, by Tom Critchlow.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://tracydurnell.com/2023/01/15/understanding-blogs/&#34;&gt;Understanding blogs&lt;/a&gt;, by Tracy Durnell.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://skoo.bz/2023/07/27/on-having-a.html&#34;&gt;On Having a Blog&lt;/a&gt;, by Skoobs.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.gr36.com/2023/07/26/more-personal-blogging.html&#34;&gt;More Personal Blogging&lt;/a&gt;, by Greg Morris.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.robinsloan.com/lab/new-avenues/&#34;&gt;A Year Of New Avenues&lt;/a&gt;, by Robin Sloan&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://matthiasott.com/notes/the-year-of-the-personal-website&#34;&gt;The Year of the Personal Website&lt;/a&gt;, by Matthias Ott&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-practice&#34;&gt;The Practice&lt;/h2&gt;
&lt;p&gt;How and why to blog, and what to write.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://seths.blog/2018/10/the-first-1000-are-the-most-difficult/&#34;&gt;The first 1,000 are the most difficult&lt;/a&gt;, by Seth Godin. Via &lt;a href=&#34;https://www.manton.org/2018/10/31/seth-godin-blogs.html&#34;&gt;Manton Reece&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.cjchilvers.com/blog/practical-ways-to-post-something-every-day&#34;&gt;Practical Ways to Post Something Every Day&lt;/a&gt;, by CJ Chilvers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://aliabdaal.com/prolific-beats-perfect/&#34;&gt;Prolific Beats Perfect&lt;/a&gt;, by Ali Abdaal. The section &amp;ldquo;Prolific Beats Perfect&amp;rdquo; and &amp;ldquo;Content is Time Travel&amp;rdquo; are good ones to revisit occasionally.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://critter.blog/2020/10/02/write-5x-more-but-write-5x-less/&#34;&gt;Write 5x more but write 5x less&lt;/a&gt;, Mike Crittenden.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://werd.io/2022/what-to-blog&#34;&gt;What to blog&lt;/a&gt;, by Ben Werdmuller.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.robinrendle.com/notes/take-care-of-your-blog-/&#34;&gt;Take Care of Your Blog&lt;/a&gt;, by Robin Rendle.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.alexmolas.com/2023/07/15/nobody-cares-about-your-blog.html&#34;&gt;Nobody cares about your blog&lt;/a&gt;, by Alex Molas. Don&amp;rsquo;t let the title dissuade you, this is actually encouragement.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/readme/guides/publishing-your-work&#34;&gt;Publishing your work increases your luck&lt;/a&gt;, by Aaron Francis, via Github&amp;rsquo;s &lt;em&gt;The ReadME Project&lt;/em&gt;. A specific use case for your blog: to publish stuff you&amp;rsquo;re working on (yes, people would read it).&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://css-irl.info/what-to-blog-about-when-you-dont-know-what-to-blog-about/&#34;&gt;What to Blog About When You Don’t Know What to Blog About&lt;/a&gt;, by Michelle Barker. Via &lt;a href=&#34;https://buttondown.email/ownyourweb/archive/issue-04/&#34;&gt;Own Your Web: Issue 4&lt;/a&gt; by Matthias Ott.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://micro.fromjason.xyz/2024/01/06/blogging-platforms.html&#34;&gt;Blogging Platforms&lt;/a&gt;, by Jason Velazquez. A list of blogging CMS and writing environments. Most options there are pretty good.  &lt;a href=&#34;https://kangminsuk.com/blog/blogging/&#34;&gt;Here&amp;rsquo;s another list.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://havn.blog/2024/04/16/advice-for-how.html&#34;&gt;Advice for How To Make Sure You Never Create Anything&lt;/a&gt;, by Erlend. I won&amp;rsquo;t spoil the theme here.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://thedent.net/post/blog-like-no-one-is-watching-qz6s2wfq&#34;&gt;Blog like no one is watching&lt;/a&gt;, by Andy Nicolaides. This one&amp;rsquo;s a little personal, but the lessons are still worth taking away.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;blogging-aint-dead-yet&#34;&gt;Blogging Ain&amp;rsquo;t Dead Yet&lt;/h2&gt;
&lt;p&gt;Your blog is not alone.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://cdevroe.com/2023/01/11/blogging-is-alive&#34;&gt;Blogging is alive and well&lt;/a&gt;, by Colin
Devroe.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://brandontreb.com/2023/01/26/u722z0gbkf24kybk7dykn&#34;&gt;(Another) Blogging Isn’t Dead Post&lt;/a&gt;, by Brandon Trebitowski.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;about-work-in-general&#34;&gt;About Work In General&lt;/h2&gt;
&lt;p&gt;Not about blogging, but about working in public.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.manton.org/2015/10/21/a-great-developer.html&#34;&gt;A great developer can come from anywhere&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://tomcritchlow.com/2017/01/26/f-yeah-side-projects/&#34;&gt;The Importance of Launching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://alearningaday.blog/2022/03/26/stay-on-the-bus/&#34;&gt;Stay on the bus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;to-share&#34;&gt;To Share&lt;/h2&gt;
&lt;p&gt;Links to share with others to encourage them to start blogging.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://getblogging.org/&#34;&gt;Get Blogging!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://startafuckingblog.com&#34;&gt;Start a Fucking Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;metalinks&#34;&gt;Metalinks&lt;/h2&gt;
&lt;p&gt;Links to others who have also collected links about blogging.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://frankmeeuwsen.com/2023/12/10/how-to-write.html&#34;&gt;How to write about blogs&lt;/a&gt;, by Frank Meeuwsen. Via &lt;a href=&#34;https://buttondown.email/ownyourweb/archive/issue-05/&#34;&gt;Own Your Web: Issue 5&lt;/a&gt;, by Matthias Ott.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Pixel Phones Are Not Dog-food, and That&#39;s a Problem</title>
      <link>https://lmika.org/2023/10/06/about-the-pixel.html</link>
      <pubDate>Fri, 06 Oct 2023 12:50:39 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/10/06/about-the-pixel.html</guid>
      <description>&lt;p&gt;John Gruber on the &lt;a href=&#34;https://daringfireball.net/2023/10/googles_pixel_8_event&#34;&gt;Pixel 8 launch event&lt;/a&gt;:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;It’s also impossible not to comment on just how much less interest there is in Google’s Pixel ecosystem. […] On the one hand I’m tempted to say the difference is just commensurate with how much better at hardware Apple is than Google. But I think there’s more to it than that. There’s something ineffable about it. There are aspects of marketshare traction — in any market — that can’t be explained by side-by-side product comparisons alone.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;Can&amp;rsquo;t speak for the market but as a Pixel 6 Pro owner I can give you my opinion. You don&amp;rsquo;t need to watch the keynote to get that sense of disinterest. You can get it just by using the phone.&lt;/p&gt;
&lt;p&gt;For the last few months&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; I&amp;rsquo;ve been experiencing a bug with the calendar widget. If you have nothing on your calendar for the next two weeks, it completely blanks out:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/screenshot-20230908-070337.png&#34; width=&#34;300&#34; height=&#34;650&#34; alt=&#34;An Android phone screen with the calendar widget on the right that is completely white except for a blue plus button&#34;&gt;
&lt;p&gt;I doubt that this is intentional as the plus button doesn&amp;rsquo;t work either. Tapping it does nothing at all.&lt;/p&gt;
&lt;p&gt;For comparison, here&amp;rsquo;s how it&amp;rsquo;s meant to look:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/screenshot-20231006-121118.png&#34; width=&#34;300&#34; height=&#34;650&#34; alt=&#34;An Android phone screen with the same calendar widget functioning normally: it has the current date, a message saying &#39;Nothing scheduled&#39;, and two entries in blue for dates in the future&#34;&gt;
&lt;p&gt;Now, bugs in software happen — they certainly happen in mine — and there&amp;rsquo;s no reason why Google would be immune to this, so I can forgive them for this bug showing up in a shipped version of Android. My problem is that it&amp;rsquo;s been like this for &lt;em&gt;months&lt;/em&gt; now. This is a widget built by Google, included in Google&amp;rsquo;s Calendar app running on Google&amp;rsquo;s OS and Google&amp;rsquo;s hardware, and it&amp;rsquo;s been broken for this long. I would&amp;rsquo;ve expected this to be fixed in a few weeks, but for it to take this long?&lt;/p&gt;
&lt;p&gt;I can&amp;rsquo;t see how anyone with an Android phone using this widget would not notice this. And the only reason I can come up with is that no-one in Google &lt;em&gt;has&lt;/em&gt; noticed this. They simply don&amp;rsquo;t use Android, the OS that they build, in their day-to-day. Maybe some of them do, but obviously not enough of them to drive change. If there was, they would&amp;rsquo;ve found this problem and fix it by now. To &lt;a href=&#34;https://en.wikipedia.org/wiki/Linus%27s_law&#34;&gt;quote Linus&lt;/a&gt;, &amp;ldquo;given enough eyeballs, all bugs are shallow,&amp;rdquo; and those eyeballs are obviously looking elsewhere.&lt;/p&gt;
&lt;p&gt;Now this theory may be far fetched, but after reading Gruber&amp;rsquo;s piece, it seems like I&amp;rsquo;m not alone in thinking this. As he says later in the same article:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;I’d wager that more Google employees carry an iPhone than carry a Pixel.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;It shows.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I can&amp;rsquo;t remember when I first saw this, but I think it was in July.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Your Dev Environment is Not Your Production Environment</title>
      <link>https://lmika.org/2023/10/03/your-dev-environment.html</link>
      <pubDate>Tue, 03 Oct 2023 13:42:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/10/03/your-dev-environment.html</guid>
      <description>&lt;p&gt;There will be certain things you&amp;rsquo;re going to need to do in your development environments that you should never do in production. That&amp;rsquo;s pretty much a given: playing around with user&amp;rsquo;s data or potentially doing something that will cause an incident is generally not a good idea.&lt;/p&gt;
&lt;p&gt;But there are things you shouldn&amp;rsquo;t do in prod that you may need to do in dev. And make no mistake, there may be a legitimate need to do these things. Using Auth0 and only have a limited number of emails available for your test environment? You may need a way to quickly reset a user. Support billing in multiple countries and need to do a test in one of them? You&amp;rsquo;ll need a way to change the user&amp;rsquo;s countries.&lt;/p&gt;
&lt;p&gt;And I think that&amp;rsquo;s fine. Not every environment needs to be a reflection of production. As long you&amp;rsquo;ve got a staging or pre-prod environment where you can do things like rehearse deployments. But everything else should be skewed towards ease of development, which will mean making these drastic options available and easy to use.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Electrification of Melbourne Suburban Railways Plaque</title>
      <link>https://lmika.org/2023/09/27/electrification-of-melbourne.html</link>
      <pubDate>Wed, 27 Sep 2023 07:59:22 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/09/27/electrification-of-melbourne.html</guid>
      <description>&lt;p&gt;Found this plaque while passing through Southern Cross station this morning.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/pxl-20230926-210950093.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;Plaque about the Electrification of Melbourne Suburban Railways&#34;&gt;
&lt;p&gt;I didn&amp;rsquo;t have time to read it, and the subject matter looks really interesting to me (Trains? Power Lines? What&amp;rsquo;s not to love? 😀). I also don&amp;rsquo;t know how long it&amp;rsquo;ll be up for, and I&amp;rsquo;ve been burned in the past of not capturing something when I had the chance.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;m posting photos of it here for posterity reasons.  Enjoy.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2023/pxl-20230926-210956908.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2023/pxl-20230926-211000620.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2023/pxl-20230926-211005031.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Alternative Day Four Photo</title>
      <link>https://lmika.org/2023/09/04/alternative-day-four.html</link>
      <pubDate>Mon, 04 Sep 2023 08:16:03 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/09/04/alternative-day-four.html</guid>
      <description>&lt;p&gt;I had an alternative idea for today&amp;rsquo;s photo challenge, which is &amp;ldquo;orange&amp;rdquo;. I was hoping to post a photo of something related to Melbourne&amp;rsquo;s busses.&lt;/p&gt;
&lt;p&gt;You see, PTV has designated different colour for different modes of transport. Blue for metro trains, purple for regional trains, green for trams, and orange for busses. And from my experience using the service, they&amp;rsquo;re pretty consistent with adhering to this design language:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/pxl-20230831-213325532.jpg&#34; width=&#34;600&#34; height=&#34;451&#34; alt=&#34;A bus in orange livery at a bus-stop with an orange sign and trim&#34;&gt;
&lt;p&gt;Anyway, they&amp;rsquo;re doing train works along my rail line over the past few weeks and this morning I noticed this sign (forgive the lighting, it was before dawn):&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/pxl-20230903-202209065.jpg&#34; width=&#34;600&#34; height=&#34;600&#34; alt=&#34;A large orange sign that reads &#39;Buses replace trains&#39; and then below an exclamation icon reads &#39;Plan ahead at ptv.vic.gov.au&#39;&#34;&gt;
&lt;p&gt;It&amp;rsquo;s not the first time I saw this sign, but I had orange on my mind and the fact that it mentioned busses got me thinking, &amp;ldquo;how cleaver, they&amp;rsquo;re maintaining the design language through and through, using an orange sign to reference the bus service that would be replacing the trains.&amp;rdquo;  Or so I thought, until I saw this sign:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/pxl-20230903-202525244.jpeg&#34; width=&#34;600&#34; height=&#34;599&#34; alt=&#34;A large orange sign that reads &#39;Car space closures&#39;, along with details of when the car park will be closed and how many spaces would no longer be available&#34;&gt;
&lt;p&gt;Ah, that blew that theory out of the water.  And also the opportunity to use it as today&amp;rsquo;s photo. I mean, I could&amp;rsquo;ve still used it — it&amp;rsquo;s still orange after all — but it doesn&amp;rsquo;t have the neat adherence to the design language that I was hoping it did.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Mainboard Mayhem</title>
      <link>https://lmika.org/2023/08/30/project-update-on.html</link>
      <pubDate>Wed, 30 Aug 2023 22:27:50 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/08/30/project-update-on.html</guid>
      <description>&lt;p&gt;Project update on Mainboard Mayhem, my Chip&amp;rsquo;s Challenge fan game. I didn&amp;rsquo;t get it finished in time for the release deadline, which was last weekend. I blame work for that. We&amp;rsquo;re going through a bit of a crunch at the moment, and there was a need to work on the weekend.&lt;/p&gt;
&lt;p&gt;The good news is that there wasn&amp;rsquo;t much left to do, and after a few more evenings, I&amp;rsquo;m please to say that it&amp;rsquo;s done. The game is finish, and ready for release.&lt;/p&gt;
&lt;p&gt;So here it is: &lt;a href=&#34;https://mainboard.lmika.dev&#34;&gt;Mainboard Mayhem: A Chip&amp;rsquo;s Challenge fan game&lt;/a&gt; (and yes, that&amp;rsquo;s its full title).&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/screenshot-2.png&#34; width=&#34;600&#34; height=&#34;455&#34; alt=&#34;Screenshot of Mainboard Mayhem&#34;&gt;
&lt;p&gt;At the moment it&amp;rsquo;s only available for MacOS. It should work on both Intel and Apple Silicon Macs, although I&amp;rsquo;ve only tested on my M2 Mac Mini running Ventura.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s good to finally see this project done. It&amp;rsquo;s been in development for about last ten years, and I spent half of that time wondering whether it was worth getting it finished it at all. Not committing to anything meant any work I did do on it was pretty aimless, and I always felt like I was wasting my time. Giving myself three weeks to either kill it, or release it helped a lot. I&amp;rsquo;ll start making deadlines for all the other unfinished projects I&amp;rsquo;m working on.&lt;/p&gt;
&lt;p&gt;As to what that next project will be, I not sure at this stage. Part of me wants to wait until this crunch time ends, but I suspect I&amp;rsquo;ll get antsy before then and start work on something else. I&amp;rsquo;ll keep you posted one way or the other.&lt;/p&gt;
&lt;p&gt;But for now, if you happen to give it a try, thank you and I hope you enjoy it.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/avatar.png&#34; width=&#34;100&#34; height=&#34;100&#34; alt=&#34;The app icon of Mainboard Mayhem&#34; class=&#34;block-center&#34;&gt;
</description>
    </item>
    
    <item>
      <title>Early Version of This Blog</title>
      <link>https://lmika.org/2023/08/12/i-was-looking.html</link>
      <pubDate>Sat, 12 Aug 2023 12:42:38 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/08/12/i-was-looking.html</guid>
      <description>&lt;p&gt;I was looking for something in GitHub the other day when I found the repository for the first iteration of this blog. I was curious as to how it looked and I&amp;rsquo;d thought that I&amp;rsquo;d boot it up and post a few screenshots of it.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;It started life as a Hugo site. There a two reasons for that, with the first being that I didn&amp;rsquo;t have the patients to style a website from scratch, and Hugo came with some pretty nice templates. I chose the &lt;a href=&#34;https://github.com/keichi/vienna/tree/master&#34;&gt;Vienna&lt;/a&gt; template, which seems to have fallen out date: many of the template variables no longer work with a modern version of Hugo. I&amp;rsquo;m also please to see that I did end up customising the header image — a photo taken in Macedon of the train line to Bendigo — although that&amp;rsquo;s pretty much all I customised.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v1-1.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Home-page of lmika.org circa May 2020.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v1-2.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    A post that I actually published (my second one, actually).
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v1-4.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    A draft post that was hanging around. I didn&amp;#39;t get beyond the introduction (and I didn&amp;#39;t proof read it either).
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v1-3.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Another draft post.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Believe it or not, I feel a little nostelgic for it. Such simple innocence in trying to summon up the courage to write stuff on the internet. Although don&amp;rsquo;t let the article count fool you: I think there were a total of 10 posts, with half of those being unfinished drafts. I was still trying to work out whether I&amp;rsquo;d like to write mainly about software technology, or simply talk about my day. But one thing&amp;rsquo;s for sure, I was under the impression that &amp;ldquo;real&amp;rdquo; blogs required posts with a title and at-least 300 words of content. That&amp;rsquo;s probably why I only had 5 posts finished in 8 months.&lt;/p&gt;
&lt;p&gt;The second reason why I went with Hugo was that I&amp;rsquo;d have no excuse to tinker with a CMS. I&amp;rsquo;d figure that, given that I wasn&amp;rsquo;t using one, I&amp;rsquo;d be force to focus on the content. Well, that level of self-discipline didn&amp;rsquo;t last long. About in the middle of 2020, I started building a CMS for the blog using &lt;a href=&#34;https://gobuffalo.io&#34;&gt;Buffalo&lt;/a&gt;. I was thinking of launching it with the name &amp;ldquo;72k&amp;rdquo; (72k.co), named after the milepost the header photo was taken at.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v2-1.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    The home-page of &amp;#39;72k&amp;#39; (cool name, huh 😏).
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v2-12.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Each post has a particular type, and an optional &amp;#39;Relevant link&amp;#39; for link-posts. I staged this screenshot to see if titleless posts were supported. Looks like they were.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v2-2.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    A post body.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v2-11.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    I even styled the error pages (only the 404 and the generic &amp;#39;Something bad&amp;#39; happened).
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v2-8.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    The admin console, showing the list of posts. A titleless post will just show up in the table as a blank entry.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v2-4.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Editing a post.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v2-7.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Each post had a customisable slug; a summary, for long-form posts; and a relevant URL, for link-posts.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v2-6.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Configuring the stream, post type, published state and date. In retrospect, this was a pretty crappy layout of these fields. The tab-bar really made this part of editing posts annoying.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v2-9.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Post types determine how a post appears on the home-page: what icon to use in the sidebar, and whether to show the summary or full body.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/blog-v2-10.png&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    I can&amp;#39;t remember what streams were meant to be. I think they were meant to be categories, and each would have their own list page and RSS feed. It never really went beyond just having a &amp;#39;default&amp;#39; feed.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;I got reasonably far with building this CMS but it still lacked a lot, like uploads and an RSS feed. It also involved a really annoying workflow: in order to publish something, you needed to choose a &amp;ldquo;post type&amp;rdquo; (whether it&amp;rsquo;s a long-form post; a link post; or a note), the &amp;ldquo;stream&amp;rdquo; the post will appear in, write a summary, and then &amp;ldquo;review&amp;rdquo; it. Once all that&amp;rsquo;s good, you&amp;rsquo;re free to publish it. This was in service of building this up into a popular, wizz-bang blog with a well-engineered navigation and category-specific feeds (I think that&amp;rsquo;s what &amp;ldquo;streams&amp;rdquo; were). Yeah, these grand plans got the better of me and really crippled the usability of the CMS&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. I never launched it, opting instead to move to Micro.blog.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s what this blog looked like, back in the day. I probably won&amp;rsquo;t look at these projects again. It&amp;rsquo;s only been four years and already bit-rot is settling in: it took me all morning trying to hack these into a state where I can open them in a browser. But it&amp;rsquo;s good to look back at what it was.&lt;/p&gt;
&lt;p&gt;Still really happy I moved it over to Micro.blog.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I don&amp;rsquo;t deny that part of this is procrastination of &lt;a href=&#34;https://lmika.org/2023/08/05/working-on-my.html&#34;&gt;other things&lt;/a&gt; I should be finishing.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;To be honest, I think part of this lengthy workflow was to satisfy the &amp;ldquo;resistance&amp;rdquo;: self-imposed roadblocks to stop me from publishing anything at all.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>On Tools and Automation </title>
      <link>https://lmika.org/2023/08/02/on-tools-and.html</link>
      <pubDate>Wed, 02 Aug 2023 07:16:04 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/08/02/on-tools-and.html</guid>
      <description>&lt;p&gt;The thing about building tools to automate your work is that it&amp;rsquo;s hard to justify doing so when you&amp;rsquo;re in the thick of it. Easy to see all the time you save in the aggregate, but when you&amp;rsquo;re faced with the task in your day to day, you&amp;rsquo;re just as likely to say &amp;ldquo;I can build a tool which will let me do this task in a couple of seconds, but it&amp;rsquo;ll take me an hour to build it verses the 5 minutes it&amp;rsquo;ll take for me to just do the task.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;So you just &amp;ldquo;do the task.&amp;rdquo; And the next time you get that task, you face the same dilemma.&lt;/p&gt;
&lt;p&gt;Of course the alternative is spending the hour to automate it, and then never running that tool again (or investing more time than you save keeping it up to date l).&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not sure what the best answer is. Maybe tracking times where you wish you had that tool you didn&amp;rsquo;t build somewhere? Then, when you&amp;rsquo;ve done it at least 3 times for the same thing, you have supporting evidence that it&amp;rsquo;s worth automating. Maybe include the time it took to do it manually as well, so you can compare it to how long it might take to build the automation.&lt;/p&gt;
&lt;p&gt;Might be worth a try.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2023/07/15/xml-is-the.html</link>
      <pubDate>Sat, 15 Jul 2023 09:24:13 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/07/15/xml-is-the.html</guid>
      <description>&lt;p&gt;🔗 &lt;a href=&#34;https://www.bitecode.dev/p/hype-cycles&#34;&gt;XML is the future - Bite code!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I wanted to write something about fads in the software development industry when the post about Amazon Prime Video moving away from micro-services back to monoliths was making the rounds. A lot of the motivation towards micro-services can be traced back to Amazon&amp;rsquo;s preaching about them being the best way to architect scalable software. Having a team from Amazon saying &amp;ldquo;micro-services didn&amp;rsquo;t work; we went back to a monolith and it was more scalable and cheaper to run&amp;rdquo; is, frankly, a bit like the Pope renouncing his Catholic faith.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t say anything at the time as doing so seemed like jumping on the fad wagon along with everyone else, but I have to agree with this article that this following along with the crowd is quite pervasive in the circuits I travel in. I did witness the tail end of the XML fad when I first started working. My first job had all the good stuff: XML for data and configuration, XSLT to render HTML and to ingest HL7&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, XForms for customisable forms. We may have used XSD somewhere as well. Good thing we stopped short of SOAP.&lt;/p&gt;
&lt;p&gt;The whole feeling that XML was the answer to any problem was quite pervasive, and with only a few evangelists, it was enough to drive the team in a particular direction. And I wish I could say that I was above it all, but that would be a lie. I drank the cool-aid like many others about the virtues of XML.&lt;/p&gt;
&lt;p&gt;But here lies the seductive thing about these technology fads: they&amp;rsquo;re not without their merits. There &lt;em&gt;were&lt;/em&gt; cases where XML was the answer, just like there are cases where micro-services are. The trap is assuming that just because it worked before, it would work again, 100% of the time in fact, even if the problem is different. After all, Amazon or whatever is using it, and &lt;em&gt;they&amp;rsquo;re&lt;/em&gt; successful. And you do want to see this project succeed, right? Especially when we&amp;rsquo;re pouring all this money into it and your job is on the line, hmm?&lt;/p&gt;
&lt;p&gt;Thus, teams are using micro-services, Kubernetes, 50 different middleware and sidecar containers, and pages and pages of configuration to build a service where the total amount of data can be loaded into an SQLite3 database&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. And so it goes.&lt;/p&gt;
&lt;p&gt;So we&amp;rsquo;ll see what would come of it all. I hope there is a move away from micro-services back to simpler forms of software designs; one where the architecture can fit entirely in one&amp;rsquo;s head. Of course, just as this article says, they&amp;rsquo;ll probably be an overcorrection, and a whole set of new problems arise when micro-services are ditched in favour of monoliths. I only hope that, should teams decide to do this, they do so with both eyes open and avoid the pitfalls these fads can lay for them.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;HL7 is a non-XML format used in the medical industry. We mapped it to XML and passed it through an XSLT to extract patient information. Yes, we really use XSLT to do this!&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Ok, this is a bit of an exaggeration, but not by much.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Code First, Tests After</title>
      <link>https://lmika.org/2023/07/07/im-still-doing.html</link>
      <pubDate>Fri, 07 Jul 2023 11:38:13 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/07/07/im-still-doing.html</guid>
      <description>&lt;p&gt;Still doing the &lt;a href=&#34;https://lmika.org/2023/05/10/ive-been-finding.html&#34;&gt;code first, tests after&lt;/a&gt; at work and I&amp;rsquo;m really starting to see the benefits from it. Test driven development is fine, but most of our recent issues — excess logging or errors that are false positives — have nothing to do with buggy business logic. It&amp;rsquo;s true that you can catch these in unit tests (although I find them to be the worst possible tests to write) but I think you gain a lot more just from launching the application and seeing it run.&lt;/p&gt;
&lt;p&gt;Now granted, it&amp;rsquo;s not always possible to do this with micro-services. There&amp;rsquo;s always some dependency you need, and setting all these up is a bit of a pain. That&amp;rsquo;s probably why I deferred all my manual testing to the end, when I&amp;rsquo;ve pushed my changes to get them reviewed and deployed it to the environment. Do a quick cursory test from the frontend just to make sure it hasn&amp;rsquo;t broken anything, then move on to the next task.&lt;/p&gt;
&lt;p&gt;I think this way of working was a mistake. This is something frontend developments get right: you need to run your software while you&amp;rsquo;re working on it. It&amp;rsquo;s so important to see not just how well it works, but how it feels to work&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;: what goes to the log, how fast it performs, etc. You don&amp;rsquo;t get this feeling from just depending on unit tests.&lt;/p&gt;
&lt;p&gt;Plus, there&amp;rsquo;s always a nice buzz to see the thing you&amp;rsquo;re working on run for the first time. That magic seems to decay the further you are from where it&amp;rsquo;s running. It just becomes another cog in the system. And maybe that&amp;rsquo;s what it&amp;rsquo;s destined to be, but it doesn&amp;rsquo;t need to be this way while you&amp;rsquo;re working on it.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I don&amp;rsquo;t know of a better way to say this other than &amp;ldquo;how it feels to work&amp;rdquo;. I suppose I could use boring words like &amp;ldquo;tight iteration loop&amp;rdquo; but there are too many boring words on the blog already.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>On The Reddit Strike </title>
      <link>https://lmika.org/2023/06/15/on-the-reddit.html</link>
      <pubDate>Thu, 15 Jun 2023 16:49:35 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/06/15/on-the-reddit.html</guid>
      <description>&lt;p&gt;Ben Thompson has been writing about the Reddit strike in his daily updates. I like this excerpt from &lt;a href=&#34;https://stratechery.com/2023/reddit-rage-the-tadpole-shrimp-of-the-internet-judge-pauses-microsoft-activision-deal/&#34;&gt;the one he wrote yesterday&lt;/a&gt;:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Reddit is miffed that Google and OpenAI are taking &lt;em&gt;its&lt;/em&gt; data, but Huffman and team didn’t create that data: Reddit’s users did, under the watchful eyes of Reddit’s unpaid mod workforce. In other words, my strong suspicion is that what undergirds everything that is happening this week is widespread angst and irritation that everything that was supposed to be special about the web, particularly the bit where it gives everyone a voice, has turned out to be nothing more than grist to be fought over by millionaires and billionaires.&lt;/p&gt;
&lt;p&gt;That, though, takes me back to Bier’s tweet; the crazy thing about the Internet is that said grist is in fact worth fighting over.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;It&amp;rsquo;s easy for me to say this, as I&amp;rsquo;m not a user of Reddit, but I have full sympathy for the striking moderators.&lt;/p&gt;
&lt;p&gt;You spend much of your free time volunteering to keep a community on a site, producing value for it&amp;rsquo;s users and owner, with the expectation that the site would recognise your efforts and reciprocate by serving your needs with, say, an API. I can understand how enraging that would feel when they turn around and &amp;ldquo;alter the deal&amp;rdquo; while expecting the mods to continue as if nothing has changed.&lt;/p&gt;
&lt;p&gt;So good on the moderators showing that they too have leverage.&lt;/p&gt;
&lt;p&gt;And as to OpenAI using the API to train its model: well yeah I can understand the CEO of Reddit feeling shitty about that, but I would’ve hope he would have the ingenuity to solve that while maintaining the needs of those that &lt;em&gt;actually provide value to the site&lt;/em&gt;. Either he doesn&amp;rsquo;t which, given that he’s one of the founders, I find hard to believe; or he just doesn&amp;rsquo;t want to.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Truthful Travel Talk</title>
      <link>https://lmika.org/2023/06/11/truthful-travel-talk.html</link>
      <pubDate>Mon, 12 Jun 2023 02:42:20 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/06/11/truthful-travel-talk.html</guid>
      <description>&lt;p&gt;It’s time to be honest: I think overseas travel is wasted on me.&lt;/p&gt;
&lt;p&gt;We were driving down from Antibes to Genova today. It was a nice trip, complete with picturesque towns passing us by as we drove along the motorway. My friend was oohing and ahhing at each one: remaking about how nice it would be to see them, stay in them for a while. He was also remarking on what we would do when we arrived at our destination. There was just this air of enthusiasm about the whole thing.&lt;/p&gt;
&lt;p&gt;I didn’t feel that enthausiasm. We heard some news that another friend of ours had their luggage stolen, and I just couldn’t stop thinking about it. I spent a fair bit of last night going through possible ways on how I could avoid it happening to me, and how I would handle it if it did, and just the whole hassle of dealing with that possibility.&lt;/p&gt;
&lt;p&gt;This, mixed with the inevitable task of finding my barring in an unfamiliar place, stressing about how I would interact with the locals in their non-native language, the ongoing recovery from Covid-19, and a bit of home-sickness, and you can probably guess that I’m just not feeling the vibe of adventure at the moment.&lt;/p&gt;
&lt;p&gt;And yeah, I might have done this to myself, particularly since I haven’t had much to do with this part of the trip.Happy to “go with the flow” of what others are doing. And people might ask me “oh, wouldn’t it be good to see this?” or “wouldn’t it be fun to experience that?” Yeah, maybe? I might get some enjoyment out of it, but I’m not sure if it’ll offset the stress I feel with the logistics of it all.&lt;/p&gt;
&lt;p&gt;So that’s where I am at the moment. It’s got to the point where I’m contemplating coming home early. It would actually simplify my itinerary quite a bit, and I won’t be leaving my friends in the lurch: it would be a portion of the trip where I would be travelling by myself. Even with a week less, that’s still about 4 weeks in total, which I think it’s plenty, or at least plenty for me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 30 June:&lt;/strong&gt; Apart from taking a slightly earlier flight home, I ended up staying the the full 5 weeks. And in retrospect, I&amp;rsquo;m really glad I did. I figured that I would regret not visiting the places I&amp;rsquo;ve would&amp;rsquo;ve cut out if I were to go home early; and after visiting them, I know now that I would&amp;rsquo;ve missed out on some of the most memorable parts of the trip.&lt;/p&gt;
&lt;p&gt;I think what sparked this post was a mixture of anxiety of travelling alone and little bit of home sickness. But nothing beats anxiety like working through the problem (if you can call travelling solo a &amp;ldquo;problem&amp;rdquo;). And as for home sickness: well, I&amp;rsquo;m not sure there&amp;rsquo;s much I can do about that apart from remembering that home will always be there.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Where Have I Been</title>
      <link>https://lmika.org/2023/05/28/inspired-by-manton.html</link>
      <pubDate>Mon, 29 May 2023 03:31:41 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/05/28/inspired-by-manton.html</guid>
      <description>&lt;p&gt;Inspired by &lt;a href=&#34;https://www.manton.org/2023/05/28/where-have-i.html&#34;&gt;Manton&lt;/a&gt; and &lt;a href=&#34;https://maique.eu/2023/05/28/where-have-i.html&#34;&gt;Maique&lt;/a&gt;, I thought I’d document the places I’ve visited as well. I’d had to refer to this list a few times in the past so having a record like this is helpful.&lt;/p&gt;
&lt;p&gt;Transfers are not included here. In order for a place to be listed here, I’ve have had to have landed there. Also, I&amp;rsquo;ve excluded Victoria, Australia, as this is where I live.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🇦🇺Australia (home)
&lt;ul&gt;
&lt;li&gt;New South Wales&lt;/li&gt;
&lt;li&gt;South Australia&lt;/li&gt;
&lt;li&gt;ACT&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;🇧🇷Brazil&lt;/li&gt;
&lt;li&gt;🇨🇰Cook Islands&lt;/li&gt;
&lt;li&gt;🇫🇯Fiji&lt;/li&gt;
&lt;li&gt;🇫🇷France&lt;/li&gt;
&lt;li&gt;🇮🇹Italy&lt;/li&gt;
&lt;li&gt;🇮🇩Indonesia&lt;/li&gt;
&lt;li&gt;🇯🇵Japan&lt;/li&gt;
&lt;li&gt;🇰🇮Kiribati&lt;/li&gt;
&lt;li&gt;🇳🇿New Zealand&lt;/li&gt;
&lt;li&gt;🇳🇺Niue&lt;/li&gt;
&lt;li&gt;🇵🇬Papua New Guinea&lt;/li&gt;
&lt;li&gt;🇼🇸Samoa&lt;/li&gt;
&lt;li&gt;🇸🇬Singapore&lt;/li&gt;
&lt;li&gt;🇸🇧Solomon Islands&lt;/li&gt;
&lt;li&gt;🇪🇸Spain&lt;/li&gt;
&lt;li&gt;🇨🇭Switzerland&lt;/li&gt;
&lt;li&gt;🇹🇴Tonga&lt;/li&gt;
&lt;li&gt;🇹🇻Tuvalu&lt;/li&gt;
&lt;li&gt;🇦🇪United Arab Emirates&lt;/li&gt;
&lt;li&gt;🇬🇧United Kingdom
&lt;ul&gt;
&lt;li&gt;Devon, England&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;🇺🇸United States of America
&lt;ul&gt;
&lt;li&gt;District of Columbia&lt;/li&gt;
&lt;li&gt;Maryland&lt;/li&gt;
&lt;li&gt;Nevada&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;🇻🇺Vanuatu&lt;/li&gt;
&lt;li&gt;🇻🇳Vietnam&lt;/li&gt;
&lt;/ul&gt;
&lt;details&gt;
&lt;summary&gt;Last Updated 12 Nov 2023&lt;/summary&gt;
&lt;ul&gt;
&lt;li&gt;12 Nov 2023: Added Singapore and Indonesia&lt;/li&gt;
&lt;li&gt;28 May 2023: First version.  Note: I’m writing this while on an overseas trip, so I’ll also be including the countries that I’ll be visiting over the next few weeks.&lt;/li&gt;
&lt;/ul&gt;
&lt;/details&gt;
</description>
    </item>
    
    <item>
      <title>Full Width Notes In Obsidian</title>
      <link>https://lmika.org/2023/05/18/full-with-notes.html</link>
      <pubDate>Thu, 18 May 2023 09:49:21 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/05/18/full-with-notes.html</guid>
      <description>&lt;p&gt;More custom styling of Obsidian today. This snippet turns off fixed-width display of notes, so that they can span the entire window. Useful if you&amp;rsquo;re dealing with a bunch of wide tables, as I am right now.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;body {
    --file-line-width: 100%;
}

div.cm-sizer {
    margin-left: 0 !important;
    margin-right: 0 !important;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I wish I could say credit goes to ChatGPT, but the answer it gave wasn&amp;rsquo;t completely correct (although it was close). The way I got this was by enabling the developer tools — which you can do from the View menu — and just going through the HTML DOM to find the relevant CSS class. I guess this means that this&amp;rsquo;ll break the minute Obsidian decides to change their class names, but I guess we&amp;rsquo;ll cross that bridge when we come to it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>F5 To Run</title>
      <link>https://lmika.org/2023/05/07/f-to-run.html</link>
      <pubDate>Tue, 09 May 2023 21:26:37 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/05/07/f-to-run.html</guid>
      <description>&lt;p&gt;While going through my archive about a month ago, I found all my old Basic programs I wrote when I was going through school. I had a lot of fun working on them back in the day, and I though it would be nice to preserve them in some way. Maybe even make them runnable in the browser, much like what the Wayback Machine did with the more well-known DOS programs.&lt;/p&gt;
&lt;p&gt;So I set about doing just that, and today the site is live: &lt;a href=&#34;https://www.f5to.run/&#34;&gt;F5 To Run&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And yeah, it&amp;rsquo;s likely that I&amp;rsquo;m the only one interested in this. No matter. I&amp;rsquo;m glad they&amp;rsquo;re off my dying portable drive and preserved on the web in some fashion.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Twitter, Public Alerts, And Federated Protocols </title>
      <link>https://lmika.org/2023/05/04/so-apparently-twitters.html</link>
      <pubDate>Thu, 04 May 2023 08:49:49 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/05/04/so-apparently-twitters.html</guid>
      <description>&lt;p&gt;So apparently Twitter&amp;rsquo;s leadership team &lt;a href=&#34;https://arstechnica.com/tech-policy/2023/05/twitter-just-realized-it-was-dumb-to-cut-off-automated-public-service-tweets/&#34;&gt;has discovered the value&lt;/a&gt; it has for public alerts:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Of all the changes Elon Musk has made to Twitter, blocking emergency and public transit services from tweeting automated alerts might have been his least popular. User backlash roared, as National Weather Service accounts got suspended. Then, one of the country&amp;rsquo;s largest public transit services, Metropolitan Transportation Authority (MTA), had so much trouble tweeting, it decided to quit posting updates to Twitter.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;It always seemed a little off that these organisations were using Twitter for this. Not everyone is on Twitter, and those that were had to agree to the terms of a private company which could, at any time, do… well, what it&amp;rsquo;s doing now. Should public alerts for weather and transportation really rely on such private entities?&lt;/p&gt;
&lt;p&gt;I can see why these companies were used back in the late 2000&amp;rsquo;s, when they first came onto the scene. They had apps with push-based notification with a good (enough) user experience. They were also investing in the backend, setting up services that can scale. So organisations palming off dissemination of these alerts to Twitter made sense.&lt;/p&gt;
&lt;p&gt;But I don&amp;rsquo;t think it makes sense anymore. With ActivityPub and (in theory) whatever BlueSky is cooking up, you now have open, federated protocols, and a bunch of apps people are building which use them. You also have public clouds which provide an easier way to scale a service. With these two now available, it seems clear to me that these organisations should deploy their own service for sending out these alerts using any or all of these open protocols.&lt;/p&gt;
&lt;p&gt;Then, the public can come to them on their terms. Those using Mastodon or BlueSky can get the alerts in their app of choice. Those that aren&amp;rsquo;t interested in either can still use any mobile apps these&amp;rsquo;s organisations have released, and these protocols can be used there as well. One can imagine a very simple ActivityPub &amp;ldquo;receiver&amp;rdquo; app, stripped of all the social features apart from receiving notifications, that can be used for organisations that don&amp;rsquo;t or can&amp;rsquo;t release a mobile app.  Plus, having a service that they run themselves could also make it possible to setup more esoteric notification channels, like &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/Push_API&#34;&gt;web push-notifications through the browser&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And yeah, it&amp;rsquo;ll cost money and will require some operational expertise. But I&amp;rsquo;d argue that serving the public in this way is their perdure, and the reason why tax dollars go in their direction.&lt;/p&gt;
&lt;p&gt;So now&amp;rsquo;s a great time for these organisations to step away from relying on these private companies for disseminating alerts and embrace the new federated protocols coming onto the scene. Who knows, maybe they&amp;rsquo;ll also embrace RSS. That would be nice.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Content Warning: About A Spider</title>
      <link>https://lmika.org/2023/04/30/114757.html</link>
      <pubDate>Sun, 30 Apr 2023 11:47:57 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/04/30/114757.html</guid>
      <description>&lt;p&gt;This spider was hanging around my garage door opening button for a few weeks now. I didn&amp;rsquo;t think much of it until today, when I noticed that it was actually a &lt;a href=&#34;https://en.wikipedia.org/wiki/Redback_spider&#34;&gt;redback&lt;/a&gt;.  Not the largest redback I&amp;rsquo;ve seen, but one located pretty close to a button I push quite frequently.&lt;/p&gt;
&lt;details&gt;
    &lt;summary&gt;Photo of said redback (it&#39;s small, but the photo is a close-up)&lt;/summary&gt;
    &lt;figure&gt;
       &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/57890c0ec9.jpg&#34; width=&#34;600&#34; height=&#34;600&#34; alt=&#34;Photo of a redback spider beside a garage door opener, with another spider on the left.&#34; /&gt;
      &lt;figcaption&gt;If you look closely you can see a bit of the classic red stripe on the spider&#39;s abdomen.&lt;/figcaption&gt;
&lt;/details&gt;
&lt;p&gt;I don&amp;rsquo;t know about other Australians, but I&amp;rsquo;ve got a &amp;ldquo;kill on sight&amp;rdquo;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; policy with redbacks, so it had to go.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Of course I say that, but I&amp;rsquo;ve seen redbacks on shed doors that I haven&amp;rsquo;t done anything about. Though I wouldn&amp;rsquo;t call them harmless, there were out of the way enough for me to disregard them.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>About Those Checkmarks</title>
      <link>https://lmika.org/2023/04/24/this-posts-going.html</link>
      <pubDate>Mon, 24 Apr 2023 09:26:18 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/04/24/this-posts-going.html</guid>
      <description>&lt;p&gt;This posts going to be about Twitter. Yes, I know; another one out there. It&amp;rsquo;s also going to be a bit speculative in nature, so feel free to skip it if you like.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been reading the coverage over the &amp;ldquo;retirement&amp;rdquo; of the legacy verification system, both in &lt;a href=&#34;https://slate.com/technology/2023/04/elon-musk-twitter-blue-checkmarks-verification-lebron-james.html&#34;&gt;the news&lt;/a&gt; and on &lt;a href=&#34;https://danielpunkass.micro.blog/2023/04/23/all-the-dingaling.html&#34;&gt;the socials&lt;/a&gt;. And what I find interesting about this whole affair is all the new Twitter Blue subscribers complaining about people that had the checkmark choosing not to sign up.&lt;/p&gt;
&lt;p&gt;Their displeasure comes through in their tweets on why they think these people choose not to subscribe. Many tout money (these people are too stingy) or logistics (write it off as a business expense). But they don&amp;rsquo;t give a reason as to why they care. Surely money or logistics is their problem to sort through. Why should you be unhappy that they chose not to join Twitter Blue? I haven&amp;rsquo;t seen any tweets answering this question.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not surprised by that. I wonder if the reason is that many of those that have acquired a checkmark  saw the those with a verified Twitter handle as being part of the in-group; members of an elite club that you cannot get a membership for&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. Naturally they wanted to be part of this in-group, and when this new Twitter Blue subscription offer rolled out, they saw an easy opportunity to gain entry.&lt;/p&gt;
&lt;p&gt;But the thing about status symbols is that they&amp;rsquo;re only valuable if the in-group chooses to keep them. When all these formally verified people refuse to sign up to Twitter Blue, and their checkmarks were removed from their handle, so too did the checkmark loose it&amp;rsquo;s value as an indicator of worth. The checkmark no longer a signals status.&lt;/p&gt;
&lt;p&gt;Even worse is this in-group has changed their position to one where &lt;em&gt;not&lt;/em&gt; having the checkmark is the sign of status. Suddenly, those that have signed up to Twitter Blue found that their attempts to buy their way in was for naught. And that&amp;rsquo;s what I think they&amp;rsquo;re angry about. Their new checkmark doesn&amp;rsquo;t impart status anymore, since those that had it don&amp;rsquo;t want it. Now it&amp;rsquo;s just an indicator that you&amp;rsquo;ve paid $8 a month, with maybe a hint that you found the symbol important in the first place.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s also probably why Musk saw it fit to &amp;ldquo;pay&amp;rdquo; for Twitter Blue for accounts with more than a million followers, trying to prop up any remaining status this indicator once had. This raises more questions though. Surely he would have seen that allowing anyone to verify their account would dilute the intrinsic status that came with it. I guess he thought that those with the checkmark felt it important enough to keep it, and it will retain its value as a status indicator.&lt;/p&gt;
&lt;p&gt;Anyway, this could be all pretty obvious to a first year psychology student, but I found it all very revealing. It&amp;rsquo;s certainly interesting seeing this play out over the last couple of days.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I know that&amp;rsquo;s not the point of this verification status, but it does seem like many saw it as an &amp;ldquo;I&amp;rsquo;m an important person&amp;rdquo; signal.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Day One and Project Jurassic</title>
      <link>https://lmika.org/2023/04/22/so-day-one.html</link>
      <pubDate>Sat, 22 Apr 2023 09:44:29 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/04/22/so-day-one.html</guid>
      <description>&lt;p&gt;So, Day One is in danger of being sherlocked by rumor’s of Apple’s upcoming &lt;a href=&#34;https://arstechnica.com/gadgets/2023/04/apple-plans-mental-health-focused-journaling-app-for-ios-17/&#34;&gt;journaling app&lt;/a&gt;:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Mayne echoes the sentiment of several app developers who have been frustrated when Apple launched in-house competitors to the apps they have introduced to the ecosystem, often copying features those apps innovated and adding functionality that only Apple can offer, per the iPhone&amp;rsquo;s privacy and security policies and APIs.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I’m a user of Day One and I have my doubts that Apple’s app would be a drop-in replacement for my journaling needs. And I think the reasons why Day One works for me — and could be made to work better — are also opportunities for Auttomatic to differentiate Day One from project Jurassic.&lt;/p&gt;
&lt;p&gt;The first is access to user&amp;rsquo;s data. If Apple’s going to leverage the data it has access to on the phone, then Auttomatic should go the other way, making it dead easy for services outside Apples ecosystem to add stuff to people’s journal. Have a blog? Post photos to Flickr? Track movies in Letterboxd? Wouldn’t it be nice to get this into your Day One journal, safe and secure? A public API that these services can use to add posts to user&amp;rsquo;s journal would go a long way here. These services can offer an export option straight from the app, and Day One can be the private collection of all things a user does on the web, sort of like a private blog.&lt;/p&gt;
&lt;p&gt;And yes, I know there’s that IFTTT integration, but I found it to be pretty crummy (all the post formatting was stripped and images were not uploaded). And it would be a pretty ordinary user experience to have these services say to their users &amp;ldquo;hey, if you want the stuff you track here in your journal, you have to create an account at this other service.&amp;rdquo;  I guess all these services could publish this information as RSS feeds, and I would settle for that, if the IFTTT integration is actually working.&lt;/p&gt;
&lt;p&gt;But arguments about IFTTT aside, the point is that Day One should fully embrace other services getting user&amp;rsquo;s data into their journal, and the best way to do this is with a public API. I know it won&amp;rsquo;t work for all their journals (one&amp;rsquo;s encrypted E2E should remain so) but the user&amp;rsquo;s should have that option, and services should be empowered to allow this.&lt;/p&gt;
&lt;p&gt;And let’s not forget the largest trump card Automattic has over Apple: an Android app and web app. I haven&amp;rsquo;t used the web app but I use the Android app all the time. I can&amp;rsquo;t imagine Apple releasing an Android version of their journalling app, particularly if they&amp;rsquo;re gearing it towards health and leveraging all the private data people have on their iPhones.  Automattic should keep working on both the Android and web app, so that users not completely in Apple&amp;rsquo;s ecosystem can keep journals.&lt;/p&gt;
&lt;p&gt;So I don&amp;rsquo;t think Auttomatic has much to fear about project Jurassic. But they can&amp;rsquo;t rest on their laurels. They should embrace the platforms outside of Apple and iOS to really differentiate Day One, and keep it a favourite of mine for journaling.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Nerd Counterflex</title>
      <link>https://lmika.org/2023/04/22/nerd-counterflex.html</link>
      <pubDate>Sat, 22 Apr 2023 08:41:47 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/04/22/nerd-counterflex.html</guid>
      <description>&lt;p&gt;You know that &lt;a href=&#34;https://www.washingtonpost.com/technology/interactive/2023/ai-chatbot-learning/&#34;&gt;Washington Post article&lt;/a&gt; that has the list of websites Google used to train Bard? I been seeing people post screenshots of their sites in the training set on their blogs and Mastodon. This morning I read a post from &lt;a href=&#34;https://chriscoyier.net/2023/04/21/the-secret-list-of-websites/&#34;&gt;Chris Coyier&lt;/a&gt; about it:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;My largest corpus of writing to date is on the web at css-tricks.com (along with many other writers), so naturally, I’m interested in seeing if it was used. (Plus, I’ve been seeing people post their rank as a weird nerd flex, so I’m following suit.)&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I’d suggest reading it. The post is more than just him flexing his ranking in the training set.&lt;/p&gt;
&lt;p&gt;Well, I got curious to see if any of my writing was there. Here’s the result:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/e68ed62897.png&#34; alt=&#34;Screenshot of Washington Post article with Bard training set with ‘no results’ showing up for lmika.org&#34; /&gt;
&lt;p&gt;I guess you can call this a form of a nerd counterflex?&lt;/p&gt;
&lt;p&gt;None of my other sites were there either. There was “lmika.com”, but that’s not me. Maybe having that was good enough for Google.&lt;/p&gt;
&lt;p&gt;So yeah, you won’t be seeing Bard sharing any of my… “insightful” thoughts about code reviews anytime soon. 😄&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>First Posts Of The Day</title>
      <link>https://lmika.org/2023/04/01/first-posts-of.html</link>
      <pubDate>Sat, 01 Apr 2023 14:06:38 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/04/01/first-posts-of.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s bit strange how the first post of the day can always feel like the hardest to get out. Every one after it is so much easier to write.&lt;/p&gt;
&lt;p&gt;I wonder if it&amp;rsquo;s because when faced with an empty text-box, there are these grand plans about what I&amp;rsquo;m going to write, as if everyone reading this is hanging on my every word: &lt;em&gt;it&amp;rsquo;ll be my masterpiece of wit, inspiration, and insightfulness that will spread far and wide and blow the minds of everrryyywoonnneee&lt;/em&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.  Then I write something, and naturally it falls &lt;em&gt;far&lt;/em&gt; short of these expectations: mundane, unimportant, already said before&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Then I say to myself, &amp;ldquo;ah well, at least it&amp;rsquo;s written down.&amp;rdquo; And with that, the expected level of quality for anything else that day has been set.&lt;/p&gt;
&lt;p&gt;So, this is today&amp;rsquo;s first post. More might come, probably along the same level of importance as this one. At least until the tomorrow, when the cycle starts again.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Another possibility is that I feel I need to write something at the same level of quality of those that I read. That&amp;rsquo;s probably not a bad feeling to have; but, at least for me, it can get in the way of writing anything at all that day.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;And lets not forget the bad spelling and grammar I failed to catch.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Reheating Chicken Schnitzel in a Microwave</title>
      <link>https://lmika.org/2023/03/20/reheating-chicken-schnitzel.html</link>
      <pubDate>Mon, 20 Mar 2023 10:49:32 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/03/20/reheating-chicken-schnitzel.html</guid>
      <description>&lt;p&gt;Some tips for heating up chicken schnitzel that you had for dinner in a 1.1 kW microwave for lunch the next day. This is something I occasionally do, and today I found a process that works that I’d like to document for the future.&lt;/p&gt;
&lt;p&gt;First, don’t use the high setting on the microwave. A minute at high will heat the schnitzel up, but would also harden the crumbling, making it rubbery and unpleasent to eat. Even worst is using a plate instead of a container. That would ruin the meat even more and make a mess of your microwave.&lt;/p&gt;
&lt;p&gt;Instead, put the schnitzel in a microwave-safe container and heat it up twice, one minute each time, at medium. This will heat it up without making it rubbery. If still not warm enough, do it a third time for about 30 seconds (this I haven’t tried, but it seems like a good approach to getting the meat slightly warm while giving you time to make sure it’s still nice to eat).&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Photos Of Churchill Island</title>
      <link>https://lmika.org/2023/03/18/yesterday-my-parents.html</link>
      <pubDate>Sat, 18 Mar 2023 12:30:08 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/03/18/yesterday-my-parents.html</guid>
      <description>&lt;p&gt;Yesterday, my parents and I went to Churchill Island for afternoon tea and a walk around the homestead. Here are a few photos of that outing. Apologies that some of them are not great — they were taken in a bit of a hurry.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/ded063486e.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    View from the table
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/2f29737a99.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    The peacock that was moving from table to table. Didn’t get my camera out in time for a good photo of him.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/a516ac7b33.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    The seagull that was interested in what we were eating. We were not allowed to feed the birds but as soon as we left the table, she took full advantage.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/cf4f0d45b3.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/59c9cf5789.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Phillip and Churchill Island are full of these Cape Barren Geese.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/fd14f246db.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Approaching the homestead
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/99fbd7c77f.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/4c6ea9b496.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/bd498ce1b1.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/2054d15587.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    A gig. Perhaps owned by Dan, who is a man. (Anyone who has read the ‘The Victorian Readers - Book 1’ would get that reference).
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/3a21cfdb19.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    A Furphy horse-drawn water tank
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/68b3c1ea98.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Good, Better, Best…
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/2b82c302d9.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/ffeb5f8785.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/6e35db76bd.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2023/7e92a5d07c.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Hiding Your Attachment Folder In Obsidian&#39;s Outline</title>
      <link>https://lmika.org/2023/03/09/hiding-your-attachment.html</link>
      <pubDate>Thu, 09 Mar 2023 13:04:46 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/03/09/hiding-your-attachment.html</guid>
      <description>&lt;p&gt;A useful little CSS snippet for anyone using Obsidian that wants to hide their attachment folder from their outline.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.nav-folder.mod-root&amp;gt;.nav-folder-children .nav-folder&amp;gt;.nav-folder-title[data-path^=&amp;quot;Attachments&amp;quot;],
.nav-folder.mod-root&amp;gt;.nav-folder-children .nav-folder&amp;gt;.nav-folder-title[data-path^=&amp;quot;Attachments&amp;quot;] + .nav-folder-children {
        display: none;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to the directory &lt;code&gt;$VAULT/.obsidian/snippets&lt;/code&gt; where &lt;code&gt;$VAULT&lt;/code&gt; is the directory of you vault. If the &lt;code&gt;snippets&lt;/code&gt; directory doesn&amp;rsquo;t exist, create it.&lt;/li&gt;
&lt;li&gt;Copy the CSS snippet into a new CSS file.&lt;/li&gt;
&lt;li&gt;Open you vault settings and go to &lt;em&gt;Appearance&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Scroll to the bottom to where you see &lt;em&gt;CSS snippets&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Click the reload button. You should see the CSS file you&amp;rsquo;ve just created appear in the list. Turn it on to apply it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This&amp;rsquo;ll work if you&amp;rsquo;ve configured Obsidian to store attachments in a folder called &amp;ldquo;Attachments&amp;rdquo; located at the root of your vault, like I do. But I suspect the &lt;code&gt;data-path&lt;/code&gt; attribute holds the folder&amp;rsquo;s path so you could use whatever CSS attribute selector you need based on how you&amp;rsquo;ve configured attachments.  For example, &lt;code&gt;[data-path*=&amp;quot;/Files&amp;quot;]&lt;/code&gt; selector will probably work if you&amp;rsquo;ve configured attachments to be in folders called &amp;ldquo;Files&amp;rdquo; that sits alongside your notes (I haven&amp;rsquo;t tested this so YMMV).&lt;/p&gt;
&lt;p&gt;Source: &lt;a href=&#34;https://www.reddit.com/r/ObsidianMD/comments/qyl4jd/comment/hllu3v8/?utm_source=share&amp;amp;utm_medium=web2x&amp;amp;context=3&#34;&gt;Scribbles_some_words on this Reddit response&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>To Wordpress Or Not To Wordpress</title>
      <link>https://lmika.org/2023/03/04/im-facing-a.html</link>
      <pubDate>Sat, 04 Mar 2023 09:52:55 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/03/04/im-facing-a.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m facing a bit of a dilemma.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been asked to setup a new website for someone who wants to stand up a new business. In therory this is something that I can do quite easily. I know HTML and CSS. I&amp;rsquo;ve made a living building backends for web-apps. I do have an undeveloped eye for design, but I like to think I have an idea of the principal of good website usability; and as long as I&amp;rsquo;m not too ambitious, and aim for a &lt;a href=&#34;https://www.patrickrhone.net/how-i-build-websites-a-loose-manifesto/&#34;&gt;minimal usable site&lt;/a&gt;, I can probably put together a simple static website.&lt;/p&gt;
&lt;p&gt;The only problem is that this may not work for the person that I&amp;rsquo;m building a site for. This is someone that has no experience with putting together websites, and if I were to go down the static HTML road, I&amp;rsquo;d probably be on the hook to make changes going forward.&lt;/p&gt;
&lt;p&gt;So the alternative is to use a CRM like Wordpress. That way, once I hand ownership of the site to the client, he could either contract someone else to maintain it going forward or even learn to do it himself.&lt;/p&gt;
&lt;p&gt;Only problem with that is that my experience with Wordpress is quite minimal. I can get around the dashboard no problem, but when it comes to designing or customising themes or (sigh) using the Block editor, I&amp;rsquo;m just as much as a novice as he is. And I&amp;rsquo;m not sure to what degree I can leverage my HTML and CSS skills to style the site. I may be able to change a few things but I&amp;rsquo;d have to do so within the confines of the block templating system.&lt;/p&gt;
&lt;p&gt;So, what to do?&lt;/p&gt;
&lt;p&gt;Maybe the best way forward is to get a sense of how often this person would need the site changed. That&amp;rsquo;s by far the biggest variable here. I only know what he wants at a very superficial sense at this moment. I don&amp;rsquo;t believe it&amp;rsquo;ll need any sort of blog or product catalogue; just a simple landing page with contact details.&lt;/p&gt;
&lt;p&gt;In that case, I&amp;rsquo;m wondering if a static site with just plain HTML and CSS would be enough. That&amp;rsquo;ll be easy enough to put together. It can probably scale with some basic dynamic aspects as well, maybe powered with a simple backend that can regenerate the site.  Maybe something like &lt;a href=&#34;https://carrd.co&#34;&gt;Carrd&lt;/a&gt; could work here as well.&lt;/p&gt;
&lt;p&gt;But the danger is that he&amp;rsquo;ll be locked into using a static site. Any changes would require someone who&amp;rsquo;s versed in HTML and CSS.  Even worse would be a static site with a bit of backend &amp;ldquo;sprinkled in&amp;rdquo;. Then he&amp;rsquo;d be locked into using me. Not sure I like that for his sake or for mine. You read about those developers in &lt;a href=&#34;https://thedailywtf.com&#34;&gt;The Daily WTF&lt;/a&gt; who&amp;rsquo;ve put together a custom backend for a &amp;ldquo;simple website&amp;rdquo; that has grown unwieldily and become a huge mess that someone who inherits it needs to cleanup or take responsibility for. The prospect of being such a developer is not a great one.&lt;/p&gt;
&lt;p&gt;Which is why I&amp;rsquo;m looking at Wordpress, and wondering the pain of learning how to work with it is worth it. I guess it&amp;rsquo;s offsetting the potential future pain (and embarrassment) of transitioning a static site to a proper CRM later.&lt;/p&gt;
&lt;p&gt;So, Leon, which pain is worse?&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Quotes Around Names In Error Messages</title>
      <link>https://lmika.org/2023/03/01/putting-quotes-around.html</link>
      <pubDate>Wed, 01 Mar 2023 08:42:43 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/03/01/putting-quotes-around.html</guid>
      <description>&lt;p&gt;I saw this error a few minutes ago:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;failed to process input: RUNTIME ERROR: function has no parameter stack
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This threw me for a minute as I was trying to work out which parameter stack went missing, what I did to cause it to go missing, and what the heck a parameter stack actually is anyway.&lt;/p&gt;
&lt;p&gt;But it had nothing to do with any sort of stack. The error message was showing up because a function call was expecting a parameter with name &amp;ldquo;stack&amp;rdquo; which was missing from the function definition.&lt;/p&gt;
&lt;p&gt;This is why I always like putting quotes around names in logs or error messages. It removes any ambiguity about what the message is referring to. If the message was:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;failed to process input: RUNTIME ERROR: function has no parameter &amp;quot;stack&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;then you&amp;rsquo;re more likely to infer that the thing missing was a parameter with the name &lt;code&gt;stack&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Consider doing this in the error messages you write.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Web Search Works With Blogs Too</title>
      <link>https://lmika.org/2023/02/27/web-search-works.html</link>
      <pubDate>Mon, 27 Feb 2023 06:57:42 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/02/27/web-search-works.html</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s one more reason to write (or syndicate) to your blog instead of post directly to social media: you can use web search engines to find what you need.&lt;/p&gt;
&lt;p&gt;I hear a lot of people complain about the crappy search in Twitter or the lack of search in Mastodon, but this won&amp;rsquo;t be a problem if you post to your site and let public search engines crawl it. They&amp;rsquo;re incentivised to make sure their search is good, so you&amp;rsquo;re more likely to get better results more quickly.&lt;/p&gt;
&lt;p&gt;Honestly, it works. I used it today to find a post from a fellow Micro.blogger that I wanted to reread. A &lt;code&gt;site:&amp;lt;url&amp;gt;&lt;/code&gt; query with a few keywords. Found it in 5 seconds. I can&amp;rsquo;t imagine how long it would&amp;rsquo;ve taken if I had to track it down in Mastodon.&lt;/p&gt;
&lt;p&gt;Obviously this won&amp;rsquo;t work for posts from others, unless they too write to their blog. But it&amp;rsquo;s probably still worth doing for others that enjoy your work. And who knows? It might be useful to yourself one day. I know it has been for me.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Less Consuming, More Creating</title>
      <link>https://lmika.org/2023/02/19/less-consuming-more.html</link>
      <pubDate>Sun, 19 Feb 2023 09:29:11 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/02/19/less-consuming-more.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://critter.blog/2023/02/17/consume-a-little-less-create-a-little-more/&#34;&gt;Mike Crittenden&lt;/a&gt; posted a good quote from a &lt;a href=&#34;https://news.ycombinator.com/item?id=16044527&#34;&gt;random Hacker News commenter&lt;/a&gt;:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Less consuming, more creating.&lt;br&gt;
Doesn’t matter what it is, doesn’t matter if it’s bad.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;This quote actually sums up this blog quite nicely. The first line explains why it came to exist. The second line describes how it continues to exist.&lt;/p&gt;
&lt;p&gt;Happy 1,000th post.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Ballarat Beer Festival 2023</title>
      <link>https://lmika.org/2023/02/18/my-frends-and.html</link>
      <pubDate>Sat, 18 Feb 2023 21:27:57 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/02/18/my-frends-and.html</guid>
      <description>&lt;p&gt;My friends and I returned to Ballarat today for the Beer Festival. It was another stunning day for it: sunny, mild, not too hot.  Much like last year I took an earlier train to walk around Ballarat a little. Not much to report here: very little has changed. But I never see Ballarat so it&amp;rsquo;s good to walk around a little. My friends were on the train behind mine and I caught up with them when I boarded at Ballarat. We then made our way to Wendouree park for the festival.&lt;/p&gt;
&lt;p&gt;We were given a plastic pot when we entered and brewers typically offered either a tasting size, a half pot, or a full pot of a particular drink. Half pots are usually the best value for money: you get a decent amount to enjoy but you pace yourself and avoid reaching your limit before you tasted everything you wanted to. I made that mistake last year: buying too many full-sized pot servings. I went half pots this year.&lt;/p&gt;
&lt;p&gt;I also made the mistake of not making notes of the beers I tried last year. I made sure to record them this year. Given the occasion, I decided to go for drinks that I wouldn&amp;rsquo;t normally go for. In other words: lots of sours today. I&amp;rsquo;m not a sour drinker and I think I eventually reached my enjoyment limit of them today. But that&amp;rsquo;s fine: I guess it&amp;rsquo;s good finding my limits this way.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a quick rundown of the drinks I tried:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fox Friday &amp;ldquo;Feeling Peachy&amp;rdquo; Fruited Sour&lt;/strong&gt;: This was less sour and more on the bitter sweet side. The peach flavour came through strong, which took the edge off and made it quite refreshing. Made for a nice starter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dollar Bill &amp;ldquo;Australian Wild Ale&amp;rdquo;&lt;/strong&gt;: This was a regular ale and a little more bitter than I was expecting (although that could&amp;rsquo;ve been because of the peach sour). It felt like a heavy sort of ale. Maybe a little too heavy for me. Not sure it&amp;rsquo;ll be something I go for again.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mountain Culture &amp;ldquo;MS DOS&amp;rdquo; West Coast IPA&lt;/strong&gt;: Nothing too remarkable about this. Just an IPA. But a decent IPA. Will definitely have again.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wild Life Citrus Sour&lt;/strong&gt;: I can&amp;rsquo;t quite remember the make up of this sour. I think it was lemon and blood-orange. Definitely something and blood-orange, as the blood-orange taste really came through. I didn&amp;rsquo;t think much of this but that&amp;rsquo;s probably because I reached my limit for sours at this point. This brewery was also advertising a &amp;ldquo;pineapple sour,&amp;rdquo; which would&amp;rsquo;ve been amazing, but sadly they weren&amp;rsquo;t pouring it when we arrived. Might have affected how I thought about the blood-orange drink: feeling that it was playing second fiddle to the pineapple one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prancing Pony &amp;ldquo;10 Year&amp;rdquo; Beer:&lt;/strong&gt; This was one my friend got but he offered me a taste of it. It was a pilsner IPA but more on the pilsner side. It&amp;rsquo;s probably one I can see myself drinking if it wasn&amp;rsquo;t for it&amp;rsquo;s alcoholic content. It was quite high: 7.5%, or 3 standard drinks in a 500 ml can. That&amp;rsquo;s just a little too high for me. The can was quite something though.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Molly Rose &amp;ldquo;Strawberry Sublime&amp;rdquo;:&lt;/strong&gt; This was a low alcholoic strawberry and lime gose. A bit of a mix of sweet and sour. It was nice, but it really wasn&amp;rsquo;t doing it for me, and frankly I&amp;rsquo;m not really sure why I went for it at this point in the day.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The event was back in Wendouree park, and was &lt;a href=&#34;https://lmika.org/2022/02/20/scenes-of-ballarat.html&#34;&gt;pretty much like last year&lt;/a&gt;. Which is good: they did a good job last year.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2023/0b713016dd.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Food and breweries to the left, and ables to the right where the shady trees are.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2023/d803fee77a.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2023/4c6de42893.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The trees in Wendouree park are lovely. Many overseas species providing a lot of shade.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2023/d0a9d47d12.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2023/c45c1d2d7f.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2023/c58cd8d890.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;But there were a few small changes this year. For one, more tables were placed under the trees, which was a good move. There were more tables in the sun last year, which never had anyone on it for long as people preferred the ones in the shade.  I think there were fewer brewers this year too. It might have been because of the layout change but it felt a little smaller this year, and some brewers from last year didn&amp;rsquo;t make an appearance.&lt;/p&gt;
&lt;p&gt;Also, no finska this year.&lt;/p&gt;
&lt;p&gt;But all in all, it was a good day. Good excuse to get on a regional train out to the country for a change.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Ignoring Bard to Speak to Paulie</title>
      <link>https://lmika.org/2023/02/15/so-this-happened.html</link>
      <pubDate>Wed, 15 Feb 2023 15:15:59 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/02/15/so-this-happened.html</guid>
      <description>&lt;p&gt;So this happened today.&lt;/p&gt;
&lt;p&gt;Our team was testing the integration between two systems. The first system — let&amp;rsquo;s call it Bard — can be configured to make API calls directly to Stripe, or be configured to use the second system — let&amp;rsquo;s call it Paulie — to call Stripe on it&amp;rsquo;s behalf. Bard has a REST API that is used by the HTML front-end to handle  user requests. Paulie is designed to be completely isolated from the front-end and has a simple gRPC API that Bard calls. Whether or not it Bard calls Paulie at all is determined by the value of an SSM parameter.&lt;/p&gt;
&lt;p&gt;The test was setup with Bard configured to bypass Paulie and make calls directly to Stripe. The way we were to verify this was to tail the logs of both Bard and Paulie, make a REST-API call, and confirm that logs showed up in Bard but not Paulie.&lt;/p&gt;
&lt;p&gt;I got called by those running the test to help, as they were seeing something unusual: when the test was performed, logs were showing up in Paulie. The system was configured for Bard to ignore Paulie and go directly to Stripe, and yet Paulie was being spoken to.&lt;/p&gt;
&lt;p&gt;So we started going through the motions. We checked to make sure we had the correct version of Bard deployed, checked the SSM parameter, traced through the code, restarted Bard a couple of times to make sure it was configured correctly. And after every check we tried the test again, to nothing changing: logs will still coming through from Paulie.&lt;/p&gt;
&lt;p&gt;We were at it for about 15 minutes. I was staring to go through the more esoteric explanations for why this was happening, like whether we were using SSM parameters incorrectly and we may have been using an old configuration or something. Then as I was going through the traces one last time before giving up, I noticed something: there were no traces from Bard. This REST-API it had did all sorts of things like contact the database before going to Paulie or Stripe so I was expecting something like that to show up. Yet there was no evidence of any of that happening.&lt;/p&gt;
&lt;p&gt;I then asked how this was actually being tested. And you can probably guess what the response was. Turns out the person running the test wasn&amp;rsquo;t using Bard&amp;rsquo;s REST-API at all, and was making gRPC calls directly to Paulie.&lt;/p&gt;
&lt;p&gt;Well, naturally, if you called Paulie directly without calling Bard, it doesn&amp;rsquo;t matter what Bard is configured to do. 🤪&lt;/p&gt;
&lt;p&gt;Now, I don&amp;rsquo;t write this because I&amp;rsquo;m angry or annoyed. In fact, I came away from this feeling very zen about the whole thing. Mistakes like this happen all the time, it&amp;rsquo;s fine.&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s a perfect opportunity remind myself that working on tech can sometimes give you tunnel vision, and that sometimes the explanation isn&amp;rsquo;t technical at all. Sometimes the answer is much simpler than you think.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>New Stuff Setup Weekend</title>
      <link>https://lmika.org/2023/02/10/a-bunch-of.html</link>
      <pubDate>Sun, 12 Feb 2023 10:48:28 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/02/10/a-bunch-of.html</guid>
      <description>&lt;p&gt;A bunch of new stuff I&amp;rsquo;ve bought has arrived recently and this is the weekend I finally get around to setting it up.&lt;/p&gt;
&lt;h2 id=&#34;new-furniture&#34;&gt;New Furniture&lt;/h2&gt;
&lt;p&gt;The largest one is a new couch. I&amp;rsquo;ve been sitting on a second-hand two seater that my parents gave me when I&amp;rsquo;ve moved out. It did the job but it was getting quite old and saggy, and I&amp;rsquo;ve been finding myself wanting to have something larger that I can lie across. So about six months ago, I bought a new couch. It was meant to come last December, but got delayed thanks to Covid-19 supply chain issues. But it finally arrived this Saturday.&lt;/p&gt;
&lt;p&gt;But before it can be delivered, I had to &lt;a href=&#34;https://www.relay.fm/rd/102&#34;&gt;prepare the way&lt;/a&gt;, as they said. I did that on Friday, moving the old couch around to make space for the new one and also taking the opportunity to clean up a little.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/ff522ed000.jpg&#34; alt=&#34;An empty space where the new couch will go, with the old couch to the right, beside the bookcase&#34; width=&#34;600&#34; height=&#34;452&#34; /&gt;
  &lt;figcaption&gt;Preparing the way for the new couch. The old couch is temporarily beside the book shelf.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The placement of the old couch is a little awkward, but it will only be for about a week and a half before it leaves my house.&lt;/p&gt;
&lt;p&gt;Delivery time was between 10 and 12 on Saturday morning, and it was near the end of the delivery window when the movers eventually arrived. There was some concerns about getting the couch through the door but they managed to do so by standing it up on its side and sliding it through.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/47073d5bf2.jpg&#34; alt=&#34;The new couch placed in the living room. The couch is a maroone reclying three seater&#34; width=&#34;600&#34; height=&#34;452&#34; /&gt;
  &lt;figcaption&gt;The new couch.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The delivery went smoothly, but I wouldn&amp;rsquo;t call it a great &amp;ldquo;first launch&amp;rdquo; experience. Apparently it&amp;rsquo;s policy not to take all the packing material, which means it&amp;rsquo;s something I have to deal with. It&amp;rsquo;s not a huge problem, and it&amp;rsquo;s not the first time I had to get rid of waste over several weeks, but it did take the shine out of enjoying a new piece of furniture.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/d3024abb7e.jpg&#34; alt=&#34;A pile of packing material waste in the living room&#34; width=&#34;600&#34; height=&#34;452&#34; /&gt;
  &lt;figcaption&gt;All the waste I need to sort and throw out.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/bfbf3ed7ab.jpg&#34; alt=&#34;A wooden board propped up against a wall&#34; width=&#34;600&#34; height=&#34;452&#34; /&gt;
  &lt;figcaption&gt;Most of the waste is plastic and cardboard, but there&#39;s also this wooden board. I&#39;ll probably keep this. Might be useful in the future.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;But that aside, it&amp;rsquo;s great having a new couch. One interesting thing about the long time gap from purchase and arrival is that I forget how it felt trying it out in the store. It&amp;rsquo;s firmer than I remember and the height of the seats are a little short. It&amp;rsquo;s feels like a whole new experience from scratch. But I expect I&amp;rsquo;ll get use to it over time. And it&amp;rsquo;s not like I didn&amp;rsquo;t realise these properties when I actually tried it out during the shopping phase.&lt;/p&gt;
&lt;p&gt;All in all, I&amp;rsquo;m really happy with it.&lt;/p&gt;
&lt;h2 id=&#34;new-electronic-devices&#34;&gt;New Electronic Devices&lt;/h2&gt;
&lt;p&gt;Today (Sunday) was all out electronic devices, starting with a new M2 Mac mini.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/a317bcf4d5.jpg&#34; alt=&#34;A M2 Mac mini in its box&#34; width=&#34;600&#34; height=&#34;452&#34; /&gt;
  &lt;figcaption&gt;The new M2 Mac Mini&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This will replace my 2018 Intel Mac mini, which will become a home server. This is actually the first Apple Silicon computer I own. I&amp;rsquo;ve been using a M1 MacBook Pro for work for a year and a half, and I&amp;rsquo;m reasonably confident that the M1 chips will handle the type of things I&amp;rsquo;d like to run.&lt;/p&gt;
&lt;p&gt;Going through the setup was pretty seamless. I tend to start all new machines from scratch, meaning I don&amp;rsquo;t migrate anything over. Since the old Mac mini will be still around, I&amp;rsquo;ll move projects and documents over to the new Mac over time.&lt;/p&gt;
&lt;p&gt;The one thing that didn&amp;rsquo;t work out as well as I expected was my USB audio interface. I&amp;rsquo;ve been using a &lt;a href=&#34;https://www.roland.com/global/products/quad-capture/&#34;&gt;Roland Quad-Capture&lt;/a&gt; as my audio interface and while I was getting ready to move to the M2 chip, I did a search to see whether Roland had drivers that worked with Apple Silicon. At the time I thought they did, but when I tried installing them they didn&amp;rsquo;t work at all. Another look today confirmed that there was no driver support the M2 chip.&lt;/p&gt;
&lt;p&gt;This was a bit of a setback. I think next time I make sure to actually do a search for &amp;ldquo;thing M2 support&amp;rdquo; instead of just browse the driver download page and infer support for something when it doesn&amp;rsquo;t explicitly say &amp;ldquo;does not support M1 Macs&amp;rdquo;. It would also be helpful to remember that MacOS 10.X does not equal MacOS X. 🤦&lt;/p&gt;
&lt;p&gt;Anyway I&amp;rsquo;ve got another audio interface on the way. It&amp;rsquo;s another Roland product, since I think something designed for music production means the device will be able to handle low latency audio. I also need something with MIDI since I do occasionally use it for music production. This new one uses the built-in MacOS audio drivers so hopefully I don&amp;rsquo;t need to worry about driver support going forward.&lt;/p&gt;
&lt;p&gt;Apart from that, I&amp;rsquo;m still in the process of setting the Mac up. It always feels a little strange moving to a brand new machine. It&amp;rsquo;s like moving house or going to a holiday let: everything its new to you, you need to find out where things are, and none of your old things are there. But it&amp;rsquo;s also a good opportunity to form a few new habits. For example, I may try using Safari as my browser instead of Vivaldi, and start using &lt;a href=&#34;https://daringfireball.net/linked/2020/03/20/mac-assed-mac-apps&#34;&gt;Mac-Assed Mac Apps&lt;/a&gt; like NetNewsWire in lead of web-apps. I might be a little more judicious about keeping my Download folder cleen as well. I saw a &lt;a href=&#34;https://critter.blog/2023/02/10/clean-up-your-downloads-folder-with-cron/&#34;&gt;cronjob&lt;/a&gt; that will remove things in that folder after a week. I&amp;rsquo;ll give this a try and see if a clean Downloads folder works for me.&lt;/p&gt;
&lt;p&gt;The last bit of kit is a new Smart Keyboard Folio case for my iPad.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/6519674128.jpg&#34; alt=&#34;An iPad Smart Keyboard Folio in its box&#34; width=&#34;600&#34; height=&#34;452&#34; /&gt;
  &lt;figcaption&gt;The new Smart Keyboard Folio&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I finally bit the bullet and replaced my &lt;a href=&#34;https://lmika.org/2023/01/19/im-cautiously-optimistic.html&#34;&gt;old keyboard folio&lt;/a&gt; with a new one. The keyboard was completely non functional in the end and the lining was starting to peel off, so it was probably time for a replacement anyway.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/4248419c01.jpg&#34; alt=&#34;An old Smart Keyboard Folio, with the top lining starting to peel off&#34; width=&#34;600&#34; height=&#34;452&#34; /&gt;
  &lt;figcaption&gt;The &#34;retired&#34; Smart Keyboard Folio.  Notice the lining above the keyboard starting to peel off.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I&amp;rsquo;ve only tried the new one for a few minutes as I was looking up passwords and setup instructions for the new Mac. So far it&amp;rsquo;s working well. The only concern I have is that I&amp;rsquo;ll have to go through this again in three years time.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s all the new stuff I got setup this weekend. Most of the setup will continue over the next few weeks, especially the new Mac, but I&amp;rsquo;m happy I got the most of it done.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Spotify Video Follow-up</title>
      <link>https://lmika.org/2023/02/12/spotify-video-followup.html</link>
      <pubDate>Sun, 12 Feb 2023 06:27:37 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/02/12/spotify-video-followup.html</guid>
      <description>&lt;p&gt;Some follow-up from my &lt;a href=&#34;https://lmika.org/2023/02/10/toying-with-the.html&#34;&gt;post about Spotify videos&lt;/a&gt;. I looked into this a little and from what I understand they&amp;rsquo;re not full videos but &amp;ldquo;short looping video clips that play during certain songs,&amp;rdquo; at least according to &lt;a href=&#34;https://www.wikihow.com/Watch-a-Music-Video-on-Spotify&#34;&gt;this website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So I guess my initial belief is incorrect. Spotify might have music videos (they&amp;rsquo;re a bunch of articles about them thinking about it in 2020-21) but this looks to be completely different.&lt;/p&gt;
&lt;p&gt;Furthermore, you can turn them off. They&amp;rsquo;re called &amp;ldquo;Canvas Video Clips&amp;rdquo;, and if you go into the preferences of the Android mobile app, sure enough there&amp;rsquo;s a switch for them.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/209b9da92e.png&#34; width=&#34;600&#34; height=&#34;600&#34; alt=&#34;Spotify preference screen with the toggle for Canvas visualisations enabled&#34; /&gt;
&lt;p&gt;Not sure why I missed that when I was looking for that option earlier. I guess because I was looking for a preference with the name &amp;ldquo;video&amp;rdquo; in the label. But this switch seems to work and after I turned it off, this visuals stopped.&lt;/p&gt;
&lt;p&gt;Still toying with the idea of cancelling my subscription for other reasons but at least this is one less concern I have for using the service.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Higher Order Functions In Go</title>
      <link>https://lmika.org/2023/02/09/im-a-bit.html</link>
      <pubDate>Thu, 09 Feb 2023 08:53:17 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/02/09/im-a-bit.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s a bit surprising that higher-order functions like map and filter have not caught on in Go.&lt;/p&gt;
&lt;p&gt;They seemed to have caught on quickly when they were added to Java. One of the long standing issues back then was the clunky and verbose approach to writing closures. Java 8 fixed this with the introduction of the lambda (the &lt;code&gt;-&amp;gt;&lt;/code&gt; operator). Suddenly, what once took multiple lines of boilerplate could be done in a single expression. The underlying mechanism was still the same but the new syntax was enough to get people to use it (amongst other things, read on).&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t see that in Go. With generics in Go 1.18 reducing the need for &lt;code&gt;interface{}&lt;/code&gt; and type assertions, I would have expected the tide to turn a little: more maps and filter functions, and way less &lt;code&gt;for&lt;/code&gt; loops.  But it hasn&amp;rsquo;t seem to happen yet. I still see those same &lt;code&gt;for&lt;/code&gt; loops that I&amp;rsquo;ve been seeing over the last eight years.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not sure of the reason but I can guess I could be explained by two things.&lt;/p&gt;
&lt;p&gt;The first is Go&amp;rsquo;s culture. And yeah, you could describe Go as having a culture&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. It&amp;rsquo;s one that&amp;rsquo;s quite conservative and methodical. Fancy ways of doing things that sacrifice readability in favour of terseness is usually frowned upon. It&amp;rsquo;s proper to make sure the code is clear, even if it takes more room on the screen.&lt;/p&gt;
&lt;p&gt;The culture comes through in the design of the Go language itself. A classic example is the use of the &lt;code&gt;error&lt;/code&gt; type rather than exceptions. And I think it partly explains why higher-order functions have not caught on. It&amp;rsquo;s not because you can&amp;rsquo;t do it. At least you had proper closures in the language, which is something you couldn&amp;rsquo;t say about Java, back in the pre-1.8 days.&lt;/p&gt;
&lt;p&gt;But I don&amp;rsquo;t think culture is enough. You couldn&amp;rsquo;t say that using higher-order functions in Java 1.6 was a big thing back then either&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. What got them moving so quickly?&lt;/p&gt;
&lt;p&gt;This is where I think reason number two comes in, which is the lack of standard library support. When Java 8 came out, every collection type was retrofitted with a bunch of higher-order methods which made it trivial to map, filter or reduce anything you need. There was event a new &lt;a href=&#34;https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html&#34;&gt;streams&lt;/a&gt; package, allowing you to build pipelines that are nothing but higher-order methods. All of this was useful and fun to work with, and people naturally wanted to use them.&lt;/p&gt;
&lt;p&gt;Nothing like this existed when Go 1.18 was release. Nothing like this is in the upcoming release of Go 1.20.&lt;/p&gt;
&lt;p&gt;Now, to be fair, this is very characteristic of how Go maintainers add features. They take their time, making sure not to break backwards compatibility or locking themselves into a design that is difficult to evolve. And I understand the reasons for why they want to go slow here. But that means that an &amp;ldquo;official&amp;rdquo; package of higher-order functions will take time to be ready. And no such package exists now. Sure, there are open-source and &lt;a href=&#34;https://pkg.go.dev/golang.org/x/exp/slices&#34;&gt;experimental ones&lt;/a&gt; out there, but would you be using those for any production level code? Maybe adding one more &lt;code&gt;for&lt;/code&gt; isn&amp;rsquo;t exciting, but at least it doesn&amp;rsquo;t involve another dependency (and you&amp;rsquo;re already using &lt;code&gt;for&lt;/code&gt; loops in several of your other functions anyway).&lt;/p&gt;
&lt;p&gt;So I guess I&amp;rsquo;ll need to wait a bit longer for higher-order functions to be more of a thing. I can&amp;rsquo;t say I&amp;rsquo;m not disappointed: one of nice things about working in a language like Ruby, JavaScript, or even Java itself, is all the higher-order functions they have.  I&amp;rsquo;m still hopeful that they will come eventually. After all, generics are only a year old. And Go as a language may move slowly, but at least it&amp;rsquo;s still moving.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Maybe another way to put it is a &amp;ldquo;way that things are done.&amp;rdquo;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Unless you&amp;rsquo;re writing Android apps, in which case you&amp;rsquo;re forced into a culture of anonymous classes for all the callbacks you need to write.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Making A Long Form Posts Category In Micro.blog</title>
      <link>https://lmika.org/2023/02/07/making-the-long.html</link>
      <pubDate>Tue, 07 Feb 2023 12:07:57 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/02/07/making-the-long.html</guid>
      <description>&lt;p&gt;I use the Categories feature of Micro.blog to organise the types of posts I make on this site. One of the categories I have on this blog is called &lt;a href=&#34;https://lmika.org/categories/long-form-posts&#34;&gt;Long Form Posts&lt;/a&gt;, which I use to file all the posts I have that have titles. This is done automatically, such that I don&amp;rsquo;t have to think about adding a post to this category once I&amp;rsquo;ve written it&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a little hard to find the relevant features in Micro.blog to do this, but they&amp;rsquo;re there. Here&amp;rsquo;s how you can use them to make such a category on your Micro.blog blog.&lt;/p&gt;
&lt;h2 id=&#34;creating-the-category&#34;&gt;Creating The Category&lt;/h2&gt;
&lt;figure&gt;
   &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/db35c734d8.png&#34; width=&#34;579&#34; height=&#34;195&#34; alt=&#34;The new category edit box with the name Long Form Post&#34; /&gt;
   &lt;figcaption&gt;The New Category form.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The first thing you need to do is create the category:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click &amp;ldquo;Categories&amp;rdquo; in the sidebar. You should be presented with a list of categories on your blog. You can add a new one by clicking &amp;ldquo;New Category&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Give your category a name.  I chose the name &amp;ldquo;Long Form Posts&amp;rdquo; but it can be anything you want: Titled Posts, Essays, etc.&lt;/li&gt;
&lt;li&gt;Click &amp;ldquo;Create Category&amp;rdquo;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The new category should show up in the list of categories on Micro.blog.  You should also see the category appear on your blog as well.  If you were go to the archive page, the list of categories should appear, along with all the posts on your blog. Clicking it will show only the posts that have that category.&lt;/p&gt;
&lt;p&gt;The new category should also have an RSS feed, which you can use in any standard feed reader.  You can get to it by clicking the category on your blog, and adding &lt;code&gt;feed.xml&lt;/code&gt; to the URL. For example: the URL &lt;a href=&#34;https://lmika.org/categories/long-form-posts/feed.xml&#34;&gt;https://lmika.org/categories/long-form-posts/feed.xml&lt;/a&gt; is the RSS feed of my Long Form Post category.&lt;/p&gt;
&lt;h2 id=&#34;creating-the-filter&#34;&gt;Creating The Filter&lt;/h2&gt;
&lt;figure&gt;
    &lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/9c6375fbaa.png&#34; width=&#34;600&#34; height=&#34;214&#34; alt=&#34;The new filter form configured for putting titled posts in the Long Form Posts category&#34; /&gt;
   &lt;figcaption&gt;The new filter form configured for filing titled posts in the Long Form Posts category.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The Long Form Post category should exist now, but you may notice that it&amp;rsquo;s empty. At this point you need to manually add the Long Form Post category to each post you want in this category by selecting the checkbox in the Edit Post window. In you want Micro.blog to do this automatically for each category that has a title, you will need to create a Filter:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Within the &amp;ldquo;Categories&amp;rdquo; section, click &amp;ldquo;Edit Filters&amp;rdquo;, then click &amp;ldquo;New Filter&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;For a filter that will select all blog posts with a title; in the &amp;ldquo;Post length&amp;rdquo; picker, choose &amp;ldquo;Only long posts with a title&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Select the category you want these posts to have, then click &amp;ldquo;Add Filter&amp;rdquo;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now any post with a title will automatically be given the Long Form Post category. You can try this out by writing a post, giving a title, then saving it as a draft. When you go back to edit the post, the Long Form Post category checkbox should be checked.&lt;/p&gt;
&lt;p&gt;Finally, to apply the new filter for any existing post, click &amp;ldquo;Run Filter&amp;rdquo;.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I haven&amp;rsquo;t managed to get automatic category selection working for blogging apps like MarsEdit. There might be a way to do this, but I haven&amp;rsquo;t really looked.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>I, Developer</title>
      <link>https://lmika.org/2023/02/06/i-developer.html</link>
      <pubDate>Mon, 06 Feb 2023 15:32:07 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/02/06/i-developer.html</guid>
      <description>&lt;p&gt;There was a bit of a discussion &lt;a href=&#34;https://mas.to/@lindadong/109804045011393063&#34;&gt;on Mastodon&lt;/a&gt; and &lt;a href=&#34;http://scripting.com/2023/02/04/181624.html&#34;&gt;various blogs&lt;/a&gt; about how best to call someone who writes code for fun or profit. I&amp;rsquo;ll spare you the prologue of how this discussion that has been going on since the start of the profession itself: I&amp;rsquo;m sure you&amp;rsquo;ve heard it all before. But hearing one of these terms today got me thinking about this, and I thought I&amp;rsquo;d say what my preferences are.&lt;/p&gt;
&lt;p&gt;As someone who writes software for my job and hobby, I personally prefer the term &amp;ldquo;developer&amp;rdquo;. I usually call myself a &amp;ldquo;developer&amp;rdquo; or &amp;ldquo;dev&amp;rdquo; when I&amp;rsquo;m around a group of my peers. When I&amp;rsquo;m with lay people, I usually say that I&amp;rsquo;m a &amp;ldquo;software developer&amp;rdquo; as people can associate a developer as one who&amp;rsquo;s involved with building houses (this has happened to me once). I don&amp;rsquo;t mind the term &amp;ldquo;coder&amp;rdquo; or &amp;ldquo;programmer&amp;rdquo; either, but I don&amp;rsquo;t feel like it fully describes what I actually do, given that about half my job involves things other than code (as much as I dislike that fact).&lt;/p&gt;
&lt;p&gt;Officially my role is &amp;ldquo;engineer&amp;rdquo; but I don&amp;rsquo;t really care for the term. The reasons are the same as anyone else that&amp;rsquo;s got a problem with it, namely the fact that we&amp;rsquo;re not bound to the same level of accreditation that &amp;ldquo;real&amp;rdquo; engineers are (civil, electrical, etc.). But I think my dislike for it also has to do with the fact that the job of a &amp;ldquo;software engineer&amp;rdquo; usually involves more than just the &amp;ldquo;engineering&amp;rdquo; side of things. There&amp;rsquo;s design work, planning work, operations, etc. that feel beyond the scope of what could simply be called engineering. I guess one could say that an engineer is required to consider maintenance when they&amp;rsquo;re designing a structure or electrical circuit, but I feel like us software developers are more involved in the day-to-day operations of things than our &amp;ldquo;real&amp;rdquo; engineer counterparts. I could be completely wrong here though: I don&amp;rsquo;t know a thing about what &amp;ldquo;real&amp;rdquo; engineers really get up to, so I probably can&amp;rsquo;t say.&lt;/p&gt;
&lt;p&gt;One term I&amp;rsquo;ve recently started hearing more is &amp;ldquo;individual contributor&amp;rdquo;, and I must say I don&amp;rsquo;t care for the term. It&amp;rsquo;s feels so abstract and wishy-washy; so divorced from the actual act of working with the code which, arguably, is a pretty important part of delivering value for a project. I don&amp;rsquo;t know how this term got so widespread. Maybe is a way of grouping all the activities involved in software development into one noun-phrase. I guess if I&amp;rsquo;m being charitable, I can see it that way. After all, the existing terms don&amp;rsquo;t really work as well for doing this (I&amp;rsquo;m guess that&amp;rsquo;s why the question was posted on Mastodon in the first place). And yet, I still get this feeling that the existence of this term is to deliberately reduce the importance of value these people deliver, as if we&amp;rsquo;re interchangeable cogs. It might just be where I see this term, so I could be completely unfair. But that&amp;rsquo;s how I feel, and it&amp;rsquo;s for that reason I don&amp;rsquo;t like using this term.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s pretty much it. All in all I&amp;rsquo;m generally okay with being called what you&amp;rsquo;d want me to call, and I won&amp;rsquo;t call you out if you called me something else (except &amp;ldquo;Java monkey&amp;rdquo;, especially since I haven&amp;rsquo;t work in Java for a few years now).  But if I had the choice: call me a &amp;ldquo;dev&amp;rdquo;, &amp;ldquo;developer&amp;rdquo; or &amp;ldquo;programmer&amp;rdquo;; try not to call me a &amp;ldquo;engineer&amp;rdquo;; and please don&amp;rsquo;t call me an &amp;ldquo;individual contributor&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;And please don&amp;rsquo;t call me at home. 😛&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Rambling Thought About The App-Only Social Networks</title>
      <link>https://lmika.org/2023/01/31/a-rambling-thought.html</link>
      <pubDate>Tue, 31 Jan 2023 13:36:52 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/01/31/a-rambling-thought.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://www.jwz.org/blog/2022/11/psa-do-not-use-services-that-hate-the-internet/&#34;&gt;Re-reading this post&lt;/a&gt; got me wondering how much traction Hive and Post are getting from the Twitter exodus. I am aware that Hive had to deal with a vulnerability and had to shut down while they fixed it. I don&amp;rsquo;t know much about Post apart it being another VC backed social network. But unless you&amp;rsquo;re a gamer attracted to Hive, and… 🤷&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; heading to Post, is there anyone else using them?&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m wondering how much traction these app-only services will actually be able to get in this day and age. One huge advantage that Mastodon has is that it&amp;rsquo;s a web service first, and doesn&amp;rsquo;t require an app to use. This makes sharing things outside the network quite easy. Don&amp;rsquo;t have the app? Just open this link up in your web browser.&lt;/p&gt;
&lt;p&gt;If Hive and Post cannot do this, I don&amp;rsquo;t see how you can get people unaware or uninterested in the service to sign up. You might be able to share a link which will prompt people to download and sign in the app. But would they actually do this? I feel that we&amp;rsquo;re beyond the days of just trying out new services unless you know for sure you&amp;rsquo;ll get value for it, and you probably won&amp;rsquo;t know this unless you can see what&amp;rsquo;s being shared without having the app.&lt;/p&gt;
&lt;p&gt;While we&amp;rsquo;re on the subject: my curiosity got the better of me a few minutes ago, so I took a quick look a Post.news to see what it&amp;rsquo;s like. It’s backed by Andreessen Horowitz which means that I was expecting to see a few things that I&amp;rsquo;d find disagreeable. I was not disappointed.&lt;/p&gt;
&lt;p&gt;There was a website — styled by someone the same level of design skills that I could muster (that&amp;rsquo;s not a compliment). And it wasn&amp;rsquo;t just a sign-up page either: there was a &amp;ldquo;discover&amp;rdquo; feed of sorts. Lots of US news, politics, and screenshots of posts from other social platforms (and not just the major ones). I don&amp;rsquo;t know if/how they curate the posts that appear there but the ones I saw did not entice me to sign up (not that I have any interest in signing up anyway).&lt;/p&gt;
&lt;p&gt;I hope A18Z feels like got their money&amp;rsquo;s worth for this. Not sure that I would if I was backing them.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Not sure who would sign-up to Post other than those that know/like the VC backers themselves.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>My 2023 Word</title>
      <link>https://lmika.org/2023/01/21/word-of.html</link>
      <pubDate>Sat, 21 Jan 2023 09:06:40 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/01/21/word-of.html</guid>
      <description>&lt;p&gt;I think I&amp;rsquo;ve settled on my 2023 word of the year: &lt;em&gt;generous&lt;/em&gt;. Specifically (although not exclusively) generous in the projects I work on. I&amp;rsquo;m always working on some form of software in my spare time, but most of the time I keep this software just for myself. I want to do less of this, and start sharing it with others. You could say that I want to get better at &lt;em&gt;shipping&lt;/em&gt;, but shipping to me is making the software usable for what it&amp;rsquo;s designed for, and for many of the projects I build, it&amp;rsquo;s only designed for me and my needs. Shipping&amp;rsquo;s for myself is no longer enough, I want to start shipping for others.&lt;/p&gt;
&lt;p&gt;This word ties in nicely with the words over the last couple of years. Last year my word was &lt;em&gt;finishing&lt;/em&gt;: following through on delivering something that goes beyond just the merely usable. The year before it was &lt;em&gt;sharing&lt;/em&gt;: not being afraid to talk about it.  Both of these desired qualities are still a work in progress, but I feel like I&amp;rsquo;m getting better at these. But the focus has been on solving my own needs. I think now&amp;rsquo;s the time to start looking at the needs of others.&lt;/p&gt;
&lt;p&gt;Like last year, this word is not one from &lt;a href=&#34;https://blog.strategicedge.co.uk/2022/12/your-2023-word.html&#34;&gt;Nicholas Bate&amp;rsquo;s list of words&lt;/a&gt;, although if it were to be closest to anything, it will probably be &lt;em&gt;entrepreneur&lt;/em&gt;.  In fact, my 2023 word was originally going to be &lt;em&gt;entrepreneur&lt;/em&gt;, but I wasn&amp;rsquo;t fully onboard with this. Someone approaches me and says &amp;ldquo;I want to be an entrepreneur,&amp;rdquo; I immediately think &lt;em&gt;that person wants to start a business, get VC funding, go for growth, etc.&lt;/em&gt; and that&amp;rsquo;s not something I&amp;rsquo;d like to do at this stage (maybe at any stage, but I don&amp;rsquo;t want to speak for future me). And maybe those feelings are unfair. Maybe a better way to look at it is thinking of terms of &lt;em&gt;entrepreneur&lt;/em&gt; as someone who solves the problems of others. You read the works of Seth Goden or Nicholas Bates and you&amp;rsquo;re more likely to associate those qualities with that word.&lt;/p&gt;
&lt;p&gt;But, whatever. I&amp;rsquo;ll start with &lt;em&gt;generous&lt;/em&gt; for the moment and we&amp;rsquo;ll see how we go.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/f1f1907fe5.png&#34; width=&#34;450&#34; height=&#34;121&#34; alt=&#34;The word &#39;generous&#39; at the bottom of an Android lock screen&#34; /&gt;
</description>
    </item>
    
    <item>
      <title>Updates To My Online Presence</title>
      <link>https://lmika.org/2023/01/19/updates-to-my.html</link>
      <pubDate>Fri, 20 Jan 2023 10:04:37 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/01/19/updates-to-my.html</guid>
      <description>&lt;p&gt;Making some changes to my online presence.&lt;/p&gt;
&lt;p&gt;The first is moving my knowledge base site from a set of HTML pages generated from a bespoke tool to one managed by Hugo. I &lt;a href=&#34;https://lmika.org/2023/01/09/going-through-my.html&#34;&gt;wrote about that already&lt;/a&gt; so there&amp;rsquo;s nothing new to report here, apart from changing the domain name: I guess I finally fell out of love for &amp;ldquo;tecknow.space&amp;rdquo;. The new domain is simply &lt;a href=&#34;https://technote.wiki&#34;&gt;technote.wiki&lt;/a&gt;. I originally wanted &amp;ldquo;technotes.wiki&amp;rdquo; — note the S — but I ran into a few problems trying to set this up in  Netlify. While waiting for help on this, I gradually grew to like &amp;ldquo;technote.wiki&amp;rdquo; as a domain: not only does it contain notes about technology and development, it alludes to the phrase &amp;ldquo;take note&amp;rdquo;, which I find cute (although part of me is wondering if it&amp;rsquo;s time to stop looking for &amp;ldquo;cute&amp;rdquo; domain names).&lt;/p&gt;
&lt;p&gt;I also tried setting up a site to track Go packages I find useful, or may find useful in the future. This was only up for about a day, mainly because I fell into the same trap as I did before.  I went down the bespoke tool path again, building a web-app that will read these packages from an OPML file and produce a single page site that will present them as cards. It was mildly interesting working on this but as soon as I put it up, I felt nothing. No real sense of accomplishment or feeling that I&amp;rsquo;ve delivered value to others or myself. Worst than that, I couldn&amp;rsquo;t find a way to write about it without feeling like I&amp;rsquo;ve just wasted my time. In hindsight I probably should have taken that &amp;ldquo;mild interest&amp;rdquo; during development as the escape from boredom that it was. I guess I can count myself lucky that it was only a few hours, and not being able to write about it was good sign that I&amp;rsquo;ve made a mistake of sorts.  All this building just for myself is something I&amp;rsquo;ll probably write more about in day or so.&lt;/p&gt;
&lt;p&gt;Anyway, those packages are now on the knowledge base site as well. Not as cards at the moment: just a plain old table. But the foundations are there, and I&amp;rsquo;m getting quite comfortable in using Hugo to do all this now, meaning that there are even fewer reasons to build something bespoke for static web pages.&lt;/p&gt;
&lt;p&gt;Another thing I&amp;rsquo;ve been doing online is putting together a travel blog. I&amp;rsquo;ve been debating with myself on how best to write in detail about the trips I&amp;rsquo;ve taken in the past. I wasn&amp;rsquo;t comfortable posting them here. This blog is more for the up-to-the-minute events that are happening, and many of the trips I hope to write about happened a long while ago. I guess I could&amp;rsquo;ve written something like &amp;ldquo;nine years ago, I went to…&amp;rdquo;, but then should the date stamp be today, or the date of the trip?  If it should be the date of the trip, why would I say &amp;ldquo;nine years ago?&amp;rdquo; Better to just keep them separate, at least for the moment.&lt;/p&gt;
&lt;p&gt;Anyway, I&amp;rsquo;ve published the blog, which I&amp;rsquo;ve called &lt;a href=&#34;https://untraveller.org&#34;&gt;Untraveller&lt;/a&gt;. There&amp;rsquo;s only one trip on there at the moment: my recent trip to Las Vegas. I&amp;rsquo;ll be adding more over time. It&amp;rsquo;s also built as a Hugo site hosted in Netlify. The pictures are hosted in R2, Cloudflares new object store. This is my first use of this, and so far it&amp;rsquo;s been fine. Serving the images are a little slow: maybe I should stick a CDN in-front of them.&lt;/p&gt;
&lt;p&gt;These updates have now been reflected in my &lt;a href=&#34;https://lmika.omg.lol&#34;&gt;omg.lol&lt;/a&gt; page as well.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Hand-made, Home-cooked</title>
      <link>https://lmika.org/2023/01/15/handmade-homecooked.html</link>
      <pubDate>Sun, 15 Jan 2023 11:14:41 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/01/15/handmade-homecooked.html</guid>
      <description>&lt;p&gt;&amp;ldquo;Here, buy this sandwich. It&amp;rsquo;s hand-made.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Well, it&amp;rsquo;s machine made.  But hands made the machines.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Well, hands made the machines that made the machines.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;But it&amp;rsquo;s a home-cooked receipt.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Well, it&amp;rsquo;s a home-cooked &lt;em&gt;inspired&lt;/em&gt; recipe. We did have to get some input from nutritionists and focus groups. And a few of our stakeholder had to approve the list of ingredients we used. But we think it&amp;rsquo;s close enough.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Anyway, enjoy.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;(There&amp;rsquo;s no real point to this. This just came to me while I was at the supermarket.)&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Hustle Writing</title>
      <link>https://lmika.org/2023/01/12/hustle-writing.html</link>
      <pubDate>Thu, 12 Jan 2023 15:09:19 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/01/12/hustle-writing.html</guid>
      <description>&lt;p&gt;There was one other thing that was a bit distasteful about those posts on how you can further your career by being a technical writer, and it had to do with how they formatted their writing.&lt;/p&gt;
&lt;p&gt;Many of them were not afraid to include a lot of emphasis. And when I say a lot, I mean a &lt;em&gt;lot&lt;/em&gt;.  As it &lt;em&gt;whole phrases&lt;/em&gt; or even &lt;em&gt;entire sentences&lt;/em&gt;. &lt;em&gt;They did it quite often.&lt;/em&gt;  Sometimes in &lt;strong&gt;bold&lt;/strong&gt;.  Actually, &lt;strong&gt;quite often in bold.&lt;/strong&gt; And once or twice, they &lt;em&gt;even&lt;/em&gt; used both &lt;strong&gt;bold &lt;em&gt;and italics.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This was mixed in with prose that included a lot of tweets, liberal use of emojis 🧑‍💻, and block quotes as call-outs to something that was just said a few sentences ago.&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Because block quotes &lt;strong&gt;stand out&lt;/strong&gt; from the body text, and are &lt;em&gt;easy to do&lt;/em&gt; in Markdown. 🔥&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I see a lot of this writing in tech newsletters or blog aggregators, and I&amp;rsquo;m always a little suspicious of them. I doubt it&amp;rsquo;s mealy to highlight a point, like a typical &lt;a href=&#34;https://blog.codinghorror.com&#34;&gt;Coding Horror&lt;/a&gt; blog post. I think the motivation for all this emphasis is different. They could be just trying to make the article look fun and approachable, or trying to make it stand out. Does it work though? I suspect it&amp;rsquo;s harder to stand out if &lt;em&gt;every other&lt;/em&gt; post on the site looks like this. You&amp;rsquo;re more likely to stand out if you &lt;em&gt;don&amp;rsquo;t&lt;/em&gt; include any formatting or emoji at all.&lt;/p&gt;
&lt;p&gt;I wouldn&amp;rsquo;t be to fazed by this if was a personal blog and that was just the author&amp;rsquo;s style.  But these posts are trying to inform or persuade, and it all feels a little like the hustle equivalent to writing — &amp;ldquo;hustle writing&amp;rdquo; if you like — as if it&amp;rsquo;s trying to get me jazzed on a subject that I &lt;em&gt;totally&lt;/em&gt; should be on board for. But if the content is good enough, or your argument compelling enough, does it need all this embellishment?&lt;/p&gt;
&lt;p&gt;Maybe I&amp;rsquo;m just a cynic — when all the web3 stuff was blowing up this time last year, I saw many a Substack newsletter touting NFTs or d-apps (remember those?) written in this style. Or it could be that I just don&amp;rsquo;t like fun, and that I&amp;rsquo;m just a stuffy old man.  And yeah, there might be something to that.  I took a look at my archives this morning and many of my older posts were… well, I wouldn&amp;rsquo;t say &amp;ldquo;bad&amp;rdquo;, but they were pretty dry and boring. So it might simply be that my personality just doesn&amp;rsquo;t jell with this style of writing.&lt;/p&gt;
&lt;p&gt;Even so, I wish there was less of this.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Froth and Bubble</title>
      <link>https://lmika.org/2023/01/12/froth-and-bubble.html</link>
      <pubDate>Thu, 12 Jan 2023 07:27:44 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/01/12/froth-and-bubble.html</guid>
      <description>&lt;p&gt;Woke up in the early morning with this poem in my head:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;In this world of froth and bubble,&lt;br&gt;
Two things stand like stone;&lt;br&gt;
Kindness in other peoples&amp;rsquo; trouble,&lt;br&gt;
Courage in your own.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I first read this in a young adults novel some good 25 years ago, and over the years it&amp;rsquo;s come back to me several times.  I guess you can say it resonates.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Hammers, Nails, and Hugo</title>
      <link>https://lmika.org/2023/01/09/going-through-my.html</link>
      <pubDate>Mon, 09 Jan 2023 08:04:33 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/01/09/going-through-my.html</guid>
      <description>&lt;p&gt;Going through my &lt;a href=&#34;https://en.wiktionary.org/wiki/if_all_you_have_is_a_hammer,_everything_looks_like_a_nail&#34;&gt;hammer and nail&lt;/a&gt; phase with Hugo.  Trying it out on my &lt;a href=&#34;https://lmika.org/2022/10/18/technical-knowledge-management.html&#34;&gt;personal knowledge base&lt;/a&gt; to see if it could replace the tool I wrote to generate the site from a set of Markdown files.&lt;/p&gt;
&lt;p&gt;Hey, if you were to squint, that tool kinda looks like a pale imitation of Hugo. How about that.&lt;/p&gt;
&lt;p&gt;Such as it is with things like this. I first tried out Hugo a few years ago and did the bare minimum to get a few sites off the ground. Then I coasted on that knowledge for a while, using Hugo&amp;rsquo;s basic features, and doing only cursory explorations of the more advance stuff like layouts, short-codes, and taxonomies.  When it came to the personal knowledge base, I knew in principal that I could use Hugo, but since I didn&amp;rsquo;t have a lot of experience in these advanced features, I decided to just hack this tool up.&lt;/p&gt;
&lt;p&gt;I guess that occasional explorations worked eventually, since I came to a point where everything &amp;ldquo;click&amp;rdquo; together. That happened last Saturday as I was trying out the largest amount layout changes I&amp;rsquo;ve attempted so far. And now, I can see how Hugo can be used several other things as well.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;If I had all this knowledge before, I probably wouldn&amp;rsquo;t have hacked together that static site generation tool.  I probably would have made it work with Hugo, seeing that they are so similar.&lt;/p&gt;
&lt;p&gt;Now I&amp;rsquo;m not going to beat myself up too badly over this.  One characteristic I&amp;rsquo;ve noticed about myself is the need to go from an idea to something that works as quickly as I  possibly can. If I don&amp;rsquo;t, the idea will die on the vine (I&amp;rsquo;ve lost many draft blog posts this way).  I guess the trick is trying to balance that against that other characteristic I have, which is rushing to a solution using the knowledge I already have, before spending a bit of time looking at the alternatives.&lt;/p&gt;
&lt;p&gt;Hey, if you were to squint, the &lt;a href=&#34;https://en.wikipedia.org/wiki/Law_of_the_instrument&#34;&gt;cognitive bias&lt;/a&gt; of that characteristic might the same one that I&amp;rsquo;m worthing through now.  How about that.&lt;/p&gt;
&lt;p&gt;P.S. This is the second time in three days that I work up at 5 AM with the need to do something. This doesn&amp;rsquo;t normally happen to me, and I&amp;rsquo;m not sure how keen I am for this to become a habit, but I guess sometimes you just gotta feed that beast.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;One other thing favouring Hugo here was that I was facing some largish changes to this hacked up tool which I wasn&amp;rsquo;t too keen on doing.  And yeah, these are changes I can theoretically do using Hugo layouts.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>On Posting Here Daily</title>
      <link>https://lmika.org/2023/01/02/on-posting-here.html</link>
      <pubDate>Mon, 02 Jan 2023 07:48:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2023/01/02/on-posting-here.html</guid>
      <description>&lt;p&gt;I sometimes struggle with the idea of trying to post here at least once a day.  While perusing my archive I find days where my posts are cringeworthy or just not good, and part of me wonders whether it&amp;rsquo;s better to wait for a post to meet a certain level of quality before publishing it.&lt;/p&gt;
&lt;p&gt;I have also seen this argument from other bloggers as well. They post the rules they have that include things like “it should start a conversation” or it should be “distinctive”. I remember reading tweets from one who shuns the idea of posting on a schedule in favour of only publishing something that’s “good”. From looking at their site, there’s probably only a single new post every two years on average&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;But reflecting on it now, I don&amp;rsquo;t think this works for me. Maybe it could if I was a journalist or a professional writer, but for me and this blog, I don&amp;rsquo;t see how holding back could help.&lt;/p&gt;
&lt;p&gt;For one thing, it will mean a lot less posts. Of course the response to this is that’s the whole point: “quality over quantity” after all.  But I think if I did this, the post frequency will probably drop to the point where I&amp;rsquo;d be in danger of abandoning this blog together.  I tried the minimum-level-of-quality approach when I first started this blog, and I think I got a total of 5 or 6 posts in the first 8 months.  And they weren&amp;rsquo;t good posts anyway: the minimum level of quality I was shooting for was just getting something out there at all.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the reason why I joined Micro.blog&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;: writing &lt;a href=&#34;https://critter.blog/2020/10/02/write-5x-more-but-write-5x-less/&#34;&gt;smaller things more often&lt;/a&gt;.  If I were to abandon this, I&amp;rsquo;d just be falling into old habits.&lt;/p&gt;
&lt;p&gt;And if that isn&amp;rsquo;t enough, there are plenty of &lt;a href=&#34;https://austinkleon.com/2020/12/10/quantity-leads-to-quality-the-origin-of-a-parable/&#34;&gt;anecdotes&lt;/a&gt; in how quantity leads to quality.  You can spend a day, week or month trying to come up with the perfect blog post and not publishing anything at all, or publishing something that is mediocre at best.  Publishing regularly forces you to practice: sure what you write today may not be considered &amp;ldquo;good&amp;rdquo;, but you&amp;rsquo;d be force to write it, publish it, and judge it with a critical eye. That can only force you to write better, even if the improvements are small like checking your spelling, or reading it once through before publishing. I can tell by personal experience that this practice has helped me.&lt;/p&gt;
&lt;p&gt;And let&amp;rsquo;s not even discuss the feeling of being accountable from publishing frequently.  You know how often I read the blog of that person I mentioned earlier?  Never.  Why would I if I know there won&amp;rsquo;t be any new content for a year?  When they do post something, it&amp;rsquo;s usually tens of thousands of words that feels so heavy to read. I set it aside for &amp;ldquo;later&amp;rdquo;, which usually means never.&lt;/p&gt;
&lt;p&gt;The blogs I do read regularly? The ones that post daily, or weekly, or even a few times a month, with updates that range from a few sentences to several paragraphs in length.  And the quality of the writing or the topic really doesn&amp;rsquo;t matter to me. It was good just to get an update on what they&amp;rsquo;re thinking.&lt;/p&gt;
&lt;p&gt;And if this piece hasn&amp;rsquo;t convinced me yet, I&amp;rsquo;ll end it this way.  If you want to keep a record of your days, or improve the clarity of your thinking, you&amp;rsquo;ll need to write.  There aren&amp;rsquo;t many ways around that.  And if you want to improve you&amp;rsquo;re writing, you need to practice.  And to keep you honest, you need accountability, even if it&amp;rsquo;s just being accountable to yourself, and the best way to be accountable is to write in public.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s why I&amp;rsquo;m sticking with writing daily.  If that&amp;rsquo;s not enough of a reason to maintain this goal, I don&amp;rsquo;t know what is.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;No, it&amp;rsquo;s not &lt;a href=&#34;https://hypercritical.co&#34;&gt;hypercritical.co&lt;/a&gt;.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Well that, and the social aspect.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>2022 Year In Review</title>
      <link>https://lmika.org/2022/12/31/year-in-review.html</link>
      <pubDate>Sat, 31 Dec 2022 14:17:21 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/12/31/year-in-review.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ll be honest: these year in review posts feel like going to the dentist.  I generally hate doing them, but I know that it can be good exercise to reflect on the past year.  I think one thing in my favour is that I&amp;rsquo;ve actually kept my blogging — and to a lesser extent, my journalling — up to date so I&amp;rsquo;ve actually got something that I can refer back to.&lt;/p&gt;
&lt;p&gt;So here&amp;rsquo;s a brief summary of how my year went.&lt;/p&gt;
&lt;h2 id=&#34;career&#34;&gt;Career&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m a little bit disappointed on this front.  It feels like I&amp;rsquo;m in a bit of a rut, and lately things have been a little boring.  I have been promoted to a squad lead, which I guess is some form of progression.  But just like other times I&amp;rsquo;ve been asked to lead a team, it&amp;rsquo;s not something I feel I&amp;rsquo;m good at or like doing.  And yet, I really cannot see any progression here other than leading bigger teams (apart from changing jobs).&lt;/p&gt;
&lt;p&gt;That said, a few good things have happened.  The project I was working on went live earlier in the year, and while it was a stressful couple of weeks (around Easter time as well), it was generally well received and no major issues came up.  Plus, I got to learn a lot about Stripe, which has been on my goals list for a while.&lt;/p&gt;
&lt;h2 id=&#34;family-and-friends&#34;&gt;Family And Friends&lt;/h2&gt;
&lt;p&gt;Sadly, there&amp;rsquo;ve been a couple of deaths this year.  My grandfather passed away in March, after suffering from a spate of aliments like stomach cancer and emphysema. This was obviously quite sad, but I take solace in the feeling that he&amp;rsquo;s finally found some peace and relief from his suffering. The second was in February, when a friend of the family that my Mum was very close to passed away. Neither of these felt like they came too soon, which is some consolidation, but it was not a great start to the year.&lt;/p&gt;
&lt;p&gt;Oh, and in December, after &lt;a href=&#34;https://lmika.org/2020/03/22/dont-get-it.html&#34;&gt;2 years and 9 months&lt;/a&gt; since March 2020, I got Covid-19 for the first time.  I just glad that I was up to date with my vaccination: I couldn&amp;rsquo;t imagine how worse it could have been if I wasn&amp;rsquo;t.&lt;/p&gt;
&lt;h2 id=&#34;projects&#34;&gt;Projects&lt;/h2&gt;
&lt;p&gt;One new project this year that I actually managed to release: &lt;a href=&#34;https://dynamobrowse.app&#34;&gt;Dynamo-Browse&lt;/a&gt;.  I&amp;rsquo;m actually quite happy that this tool exists.  It&amp;rsquo;s been on my wish-list for a couple of years and the moment came around to finally bite the bullet and work on it.  I&amp;rsquo;m also quite please that I put some effort into the finish of it, so that I wouldn&amp;rsquo;t be completely embarrassed to share it with others.&lt;/p&gt;
&lt;p&gt;For a while I was working on another project called &lt;a href=&#34;https://lmika.org/2022/02/15/my-youtube-watching.html&#34;&gt;Broadtail&lt;/a&gt; which downloaded YouTube videos and made them available in my Plex server.  This was before I got YouTube premium, and as soon as I did, this project fell to the wayside.  It&amp;rsquo;s still around and I&amp;rsquo;ve modified it to download WWDC videos, so I may dust it off come next June.&lt;/p&gt;
&lt;p&gt;There were various other things here or there that aren&amp;rsquo;t really worth any comment.  Again, I&amp;rsquo;m wondering if I&amp;rsquo;m focusing on too much and only half-finishing things.&lt;/p&gt;
&lt;h2 id=&#34;travel&#34;&gt;Travel&lt;/h2&gt;
&lt;p&gt;Wow, after a few years of not travelling at all (this is not just because of the pandemic), there was a fair bit of it this year.&lt;/p&gt;
&lt;p&gt;If there was one destination that was top of list this year, it was Canberra.  We went once as a family during Easter to see my sister&amp;rsquo;s house, and her new cockatiels.  I&amp;rsquo;ve returned to Canberra &lt;a href=&#34;https://lmika.org/2022/06/01/back-in-canberra.html&#34;&gt;three&lt;/a&gt; &lt;a href=&#34;https://lmika.org/2022/09/25/back-in-canberra.html&#34;&gt;other&lt;/a&gt; &lt;a href=&#34;https://lmika.org/2022/11/08/the-consensus-seems.html&#34;&gt;times&lt;/a&gt; this year to look after them while she was overseas for work.  The whole work from home revolution has made this possible, and I&amp;rsquo;m glad I was able to do this.&lt;/p&gt;
&lt;p&gt;The other location of note was travelling to Las Vegas to attend AWS re:Invent, and although I became ill during the trip, and was generally overwhelmed by the size of the conference, it was still good to be able to travel overseas again.  Good thing I &lt;a href=&#34;https://lmika.org/2022/04/11/passport-renewal-forms.html&#34;&gt;got my passport renewed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By the end of the year, the amount of travelling I was doing was exhausting.  2023 is shaping up to be a big travel year as well, and I&amp;rsquo;m a little concerned it may be too much for me.  I guess we&amp;rsquo;ll see this time next year.&lt;/p&gt;
&lt;h2 id=&#34;apps&#34;&gt;Apps&lt;/h2&gt;
&lt;p&gt;Not much on this front.&lt;/p&gt;
&lt;p&gt;This was the year I really got into &lt;a href=&#34;https://lmika.org/2022/10/07/ok-im-all.html&#34;&gt;Obsidian&lt;/a&gt;.  I started the year trying to carry around a paper notebook, and although I used it a few times to write notes, it was a little uncomfortable in my pocket.  Later in the year, I gave Obsidian another try and since they&amp;rsquo;ve now got mobile apps, it&amp;rsquo;s become my go-to place for all my notes.&lt;/p&gt;
&lt;p&gt;Another good app discovered this year was &lt;a href=&#34;https://lmika.org/2022/09/13/saw-someone-at.html&#34;&gt;Numi&lt;/a&gt;.  This has been very useful during sprint planning sessions, when I need to calculate velocity and projected capacity.  If there is one feature I wish I could add to this, it&amp;rsquo;s the ability to turn off iCloud syncing.  I don&amp;rsquo;t like seeing my work stuff showing up on my personal desktop.&lt;/p&gt;
&lt;h2 id=&#34;writing-and-online-presence&#34;&gt;Writing And Online Presence&lt;/h2&gt;
&lt;p&gt;I think this is the first year where I end up with less domains than I started with.  There were a bunch that I bought in 2020 and 2021 which I never used for anything, and they were just sitting there, taunting me.  It&amp;rsquo;s good to see them expire out.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also the year when I fell into a bit of a writing streak.  This was a good one to have happened, and it drove me to write at least one blog post or journal entry every day.  I like to continue this, and maybe refine it further by attempting to write at-least one blog post per day here.&lt;/p&gt;
&lt;p&gt;One thing that didn&amp;rsquo;t work for me was banking posts: writing posts days earlier in anticipation of days where I couldn&amp;rsquo;t think of anything to say.  I got the idea from Seth Godin, and I tried it for a bit, but the Drafts section just piled up with half-finished posts that eventually grew too stale to publish.  I guess the need to publish things as soon as I&amp;rsquo;ve started work on them is something I&amp;rsquo;ve learnt about myself.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also settled on a CMS for my &lt;a href=&#34;https://workingset.net&#34;&gt;side-project work journal&lt;/a&gt; and have started a &lt;a href=&#34;https://lmika.day&#34;&gt;check-in blog&lt;/a&gt;.  Both of them are hosted on Micro.blog and although I&amp;rsquo;m still working on the writing workflow, it seems to be working well for me so far.&lt;/p&gt;
&lt;p&gt;The not-using-Twitter streak has continued, and given the current direction of the platform, it&amp;rsquo;s very unlikely that I will return.  I have started browsing around Mastodon a lot more, especially since Adam has launched &lt;a href=&#34;https://social.lol&#34;&gt;social.lol&lt;/a&gt;.  Mastodon felt like a bit of a ghost-town at first, but things are improving after many of those I used to follow on Twitter started posting there.  I&amp;rsquo;m trying to avoid making the same mistake that drove me away from Twitter, so I continue to be very careful about who I follow and will not hesitate to hide boost from those that make me anxious.  I&amp;rsquo;m also adhering to the idea of &lt;a href=&#34;https://indieweb.org/POSSE&#34;&gt;POSSE&lt;/a&gt; so most of my writing will continue to originate here.&lt;/p&gt;
&lt;h2 id=&#34;books-and-media&#34;&gt;Books And Media&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m combining them in a single section because I really did not get a lot of reading done.  I&amp;rsquo;d like to say that I&amp;rsquo;d like to change this, but I&amp;rsquo;m honestly not sure if I would be serious.  Anyway, here are some moments:&lt;/p&gt;
&lt;p&gt;A lot of reading about creativity and self improvement this year.  This includes &lt;a href=&#34;https://lmika.org/2022/01/26/finished-reading-the.html&#34;&gt;The Dip&lt;/a&gt; by Seth Goden, and &lt;a href=&#34;https://lmika.org/2022/03/13/finished-reading-ignore.html&#34;&gt;Ignore Everybody: and 39 Other Keys to Creativity&lt;/a&gt; by Hugh MacLeod.  Both of these I discovered after reading &lt;a href=&#34;https://lmika.org/2022/04/10/finished-reading-indie.html&#34;&gt;Indie Microblogging&lt;/a&gt; by Manton Reece.&lt;/p&gt;
&lt;p&gt;The books that I&amp;rsquo;ve started reading, but didn&amp;rsquo;t finished, were &lt;a href=&#34;https://lmika.org/2022/04/23/started-reading-the.html&#34;&gt;The Kaiju Preservation Society&lt;/a&gt; by John Scalzi, &lt;a href=&#34;https://lmika.org/2022/05/16/currently-reading-build.html&#34;&gt;Build: An Unorthodox Guide to Making Things Worth&lt;/a&gt; by Tony Fadell, and &lt;em&gt;4000 Weeks&lt;/em&gt; by Oliver Burkeman. There&amp;rsquo;s a bit of irony in not finishing the last one.&lt;/p&gt;
&lt;p&gt;Things I&amp;rsquo;ve watched this year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2022/04/13/started-watching-severance.html&#34;&gt;Severance&lt;/a&gt;: I didn&amp;rsquo;t watch this when this first came out but I eventually got to it and enjoyed this quite a lot.  It was good to finally get all the Lumon references.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2022/06/20/been-watching-the.html&#34;&gt;The Orville&lt;/a&gt;: I was expecting this to be a bit more satirical than it actually was, but in the end I found it to be quite a good watch.  I haven&amp;rsquo;t got around to S3 yet.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lmika.org/2022/07/10/went-to-see.html&#34;&gt;Hamilton&lt;/a&gt;: I finally got to see this in person.  I&amp;rsquo;ve watched the Broadway performance before, and I was familiar with the soundtrack, so I wasn&amp;rsquo;t going into it cold.  But it&amp;rsquo;s a much different experience watching this on the real stage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also made my &lt;a href=&#34;https://lmika.org/2022/09/19/really-specific-stories.html&#34;&gt;podcast debut&lt;/a&gt; by being a guest on Martin&amp;rsquo;s Feld excellent podcast series &lt;a href=&#34;https://www.rsspod.net/&#34;&gt;Really Specific Stories&lt;/a&gt;.  Honestly, if you&amp;rsquo;re interested in tech podcasts at all, you should absolutely be listening to this show.  And I&amp;rsquo;m not just saying that because I was a guest.  Feel free to skip the episode I was on, but make sure you listen to all the others.&lt;/p&gt;
&lt;h2 id=&#34;the-2022-word&#34;&gt;The 2022 Word&lt;/h2&gt;
&lt;p&gt;The word for 2022 was &lt;a href=&#34;https://lmika.org/2022/01/08/i-think-ive.html&#34;&gt;finisher&lt;/a&gt;.  The general goal was to stop splitting my focus and start following through on things all the way to the end. As I mentioned when I was talking about Dynamo-Browse, I think I&amp;rsquo;m improving on that front. It will be something that I&amp;rsquo;ll need to work on going forward, but the improvements are there.&lt;/p&gt;
&lt;p&gt;So overall, a pretty decent year.  Probably one of the more eventful ones than the last few, but I&amp;rsquo;m pretty happy with this one.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>2022 Song Of The Year</title>
      <link>https://lmika.org/2022/12/24/song-of-the.html</link>
      <pubDate>Sat, 24 Dec 2022 17:33:30 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/12/24/song-of-the.html</guid>
      <description>&lt;p&gt;For the past twelve years or so, I&amp;rsquo;ve been invited to &lt;a href=&#34;https://lmika.org/2021/12/24/i-not-someone.html&#34;&gt;play the organ&lt;/a&gt; at the children&amp;rsquo;s Christmas Eve mass at a local(ish) primary school.  During the collection, while people are getting wallets or purses out, I usually play some soft, nondescript  music on a muted organ with only a few soft pipes opened. It doesn&amp;rsquo;t matter what I play during this time so I usually take this opportunity to play a song that I felt was a favourite of mine throughout the year.  I unofficially consider this my song of the year.&lt;/p&gt;
&lt;p&gt;For a song to be considered, it needs to meet these criteria:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It must be a song that I&amp;rsquo;ve discovered during the year. A song that I&amp;rsquo;ve been listening to before Christmas Day the previous cannot be considered.  This forces me to keep discovering new music, instead of falling into the rut of listening to the same thing over and over again.  This doesn&amp;rsquo;t mean it needs to be released during the year.  In fact, many of the songs I grow to like have been released decades ago.&lt;/li&gt;
&lt;li&gt;It must be a song that&amp;rsquo;s found it&amp;rsquo;s way into my general rotation.  I need to have listened to it more than a few times, which generally means I need to like it enough to listen to it regularly.&lt;/li&gt;
&lt;li&gt;It must be a song that I can play softly on an organ.  I can usually slow things down so this doesn&amp;rsquo;t mean that fast songs or songs with vocals are out of the running.  But it needs to sounds good slowed down on a muted organ, which doesn&amp;rsquo;t apply to all songs I listen to.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;this-years-nominees-and-winner&#34;&gt;This Year&amp;rsquo;s Nominees and Winner&lt;/h2&gt;
&lt;p&gt;Here are this years nominees.  You will be surprised to know that not all of them are from Mike Oldfield (well, one isn&amp;rsquo;t at least… which is not all of them 😉).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://album.link/au/i/1615165935&#34;&gt;The Tunic Soundtrack&lt;/a&gt; by Lifeformed × Janice Kwan.  This was an early discovery and is now positively associated with  Canberra, a place I&amp;rsquo;ve been to more than a few times this year. Favourite tracks are To Far Shores, Ocean Glaze and Mirror Moon.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://album.link/au/i/1443215911&#34;&gt;Hergest Ridge&lt;/a&gt; by Mike Oldfield.  Discovered after watching &lt;a href=&#34;https://www.youtube.com/watch?v=NgO7ITHQQPE&#34;&gt;this video&lt;/a&gt; on progressive rock albums. I linked to the 2010 one but I must note that I prefer the original 1974 release, so if you can find a version of that, listen to that one.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://album.link/au/i/79031427&#34;&gt;Voyager&lt;/a&gt; by Mike Oldfield.  I&amp;rsquo;ve been listening to a few tracks of this before 2022, but this year I actually started liking the rest of the album.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the winner for this year is: &lt;em&gt;Part One&lt;/em&gt;, from &lt;em&gt;Hergest Ridge&lt;/em&gt; by Mike Oldfield. 👏&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2022/40471f94a8.jpg&#34; width=&#34;300&#34; height=&#34;300&#34; alt=&#34;Hergest Ridge 1974 album cover. Copyright owned by Virgin records&#34; /&gt;
&lt;p&gt;Well, I should say the first 10 minutes of Part One.  Things seem to turn a bit near the end in the original 1974 release, where it becomes a bit of a rock song, then immediately turns on a dime back into the traditional progressive soft stuff with tubular bells (or maybe that&amp;rsquo;s just the version I&amp;rsquo;m listening on).  But even so, Bravo! You have won the coveted privilege of being peformed on a small pipe organ in a Melbourne suburban church.&lt;/p&gt;
&lt;h2 id=&#34;honourable-mentions&#34;&gt;Honourable Mentions&lt;/h2&gt;
&lt;p&gt;These are the songs that I consider worthy of the title, but didn&amp;rsquo;t meet all the criteria (usually point 3).  For this year, they are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://album.link/au/i/1447170446&#34;&gt;PPPPPP&lt;/a&gt; by Magnus Pålsson.  The soundtrack to VVVVVV. An enjoyable listen, especially if you like the type of soundtracks play on the audio chips of retro 80&amp;rsquo;s hardware.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://album.link/au/i/1612484050&#34;&gt;Retro Grooves Vol. 2&lt;/a&gt; and &lt;a href=&#34;https://album.link/au/i/1441793796&#34;&gt;Vol. 3&lt;/a&gt; by Anders Enger Jensen.  Much like Voyager, I&amp;rsquo;ve been listening to DiscoVision before 2022 but I started poking through the back catalogue of these two albums and they&amp;rsquo;ve been really good.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;past-winners&#34;&gt;Past Winners&lt;/h2&gt;
&lt;p&gt;This might be the first year I&amp;rsquo;ve written about this little tradition but it isn&amp;rsquo;t the first year I&amp;rsquo;ve selected a song.  The past winners, with the associated years if I could remember them, are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://album.link/i/1027735025&#34;&gt;Rendez-Vous 4&lt;/a&gt; by Jean Michel Jarre (2021 winner)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Silhouette&lt;/em&gt; from the &lt;a href=&#34;https://album.link/au/i/1452246793&#34;&gt;Music Of The Spheres&lt;/a&gt; by Mike Oldfield&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Warsaw In The Sun&lt;/em&gt; from the &lt;a href=&#34;https://album.link/au/i/628682062&#34;&gt;Zeitgeist Concert&lt;/a&gt; at the Royal Albert Hall by Tangerine Dream&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://album.link/au/i/1440893253&#34;&gt;Return to Ommadawn&lt;/a&gt;, both &lt;em&gt;Part 1&lt;/em&gt; (2019 winner) and &lt;em&gt;Part 2&lt;/em&gt; (2020 winner), by Mike Oldfield&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>RSS And Tumblr&#39;s Quote-Style Posts</title>
      <link>https://lmika.org/2022/12/13/rss-and-tumblrs.html</link>
      <pubDate>Tue, 13 Dec 2022 12:46:30 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/12/13/rss-and-tumblrs.html</guid>
      <description>&lt;p&gt;Tumblr needs to improve how they generate RSS items. Quote-style posts — in which the post consists of a quote from someone else, followed by a reply by the blog author — show up in my RSS reader with titles consisting of the &amp;ldquo;quote part&amp;rdquo; of the post.  If the quote is more than just a handful of words, the title dominates the actual body of the item. An example:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2022/d61e9504c0.png&#34; width=&#34;600&#34; height=&#34;418&#34; alt=&#34;How quote-style posts look like in Feedbin&#34; /&gt;
&lt;p&gt;I don&amp;rsquo;t know why Tumblr is generating RSS items this way. I can only imagine that it&amp;rsquo;s something to do with the mistaken belief that &lt;a href=&#34;http://scripting.com/2022/12/08/141610.html&#34;&gt;RSS items require titles&lt;/a&gt;.  But even if that&amp;rsquo;s the case, has it not cross their minds just how ugly these posts would look in a feed-reader if the quote is more than a sentence long?  Could they have done something like truncate the title?  Or is it not a priority to them?&lt;/p&gt;
&lt;p&gt;In any case, if they do decided to fix this, may I suggest simply adding the quote part within a &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt; at the start of the RSS item, while leaving the title unset.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2022/ab3c5b3a9d.png&#34; width=&#34;600&#34; height=&#34;362&#34; alt=&#34;An example of how these quote-style posts can be improved&#34; /&gt;
&lt;p&gt;An arguably better reading experience for your RSS audience.  Or at the very least, it would look closer to what the post would look like in Tumblr itself.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Half Measures</title>
      <link>https://lmika.org/2022/11/17/half-measures.html</link>
      <pubDate>Thu, 17 Nov 2022 13:49:14 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/11/17/half-measures.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m coming to realise that one of my shortcomings is not completely following through on a task.  I&amp;rsquo;ve got a habit of only doing enough to get it done quickly, knowing that the work has cracks in it and just hoping that things won&amp;rsquo;t fall through them.  There are a few reasons for this and there the one&amp;rsquo;s that you expect: laziness, boredom, pressure to get something finished, wanting to move onto something else, etc.&lt;/p&gt;
&lt;p&gt;As you can expect, I get burned by this.  And over the last several months it got to the point where it was starting to becoming a noticeable problem.  So, I adopted the following rule: &amp;ldquo;no more half measures&amp;rdquo;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. If I&amp;rsquo;ve got a task to do, the I do the whole task.&lt;/p&gt;
&lt;p&gt;I broke that rule a few days ago.  With the pressure to get something out the door, I finished the work knowing that there existed a case where it wouldn&amp;rsquo;t work properly.  I was hoping to address this over the next week or so, and was not expecting (or hoping, to be more accurate) that this shortcoming will show up.&lt;/p&gt;
&lt;p&gt;Well, today it did.  Fortunately it was just in testing but sure enough the shortcut came back to bite me, and now I&amp;rsquo;ll need to fix it.&lt;/p&gt;
&lt;p&gt;I guess it&amp;rsquo;s a good opportunity to reset and take this rule seriously once again.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;HT to Hank in Breaking Bad.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Day Trip: Macedon And Trentham</title>
      <link>https://lmika.org/2022/10/31/i-had-the.html</link>
      <pubDate>Mon, 31 Oct 2022 21:31:28 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/10/31/i-had-the.html</guid>
      <description>&lt;p&gt;I had the pleasure of taking the day off today and going for a few walks around Macedon and Trentham.  Being someone that&amp;rsquo;s really into keeping with a routine, I try to do these walks at least once a year. It&amp;rsquo;s been somewhat delayed this year, due to work commitments, but with the public holiday tomorrow, I thought I was a perfect time to get outside and do them before summer rolls around.&lt;/p&gt;
&lt;p&gt;Below are some photos of each of the walk.&lt;/p&gt;
&lt;h2 id=&#34;macedon&#34;&gt;Macedon&lt;/h2&gt;
&lt;p&gt;The first walk was along the borders of the Macedon Regional Park, following a self-plotted course, more-or-less, along the Bendigo railway line.  It&amp;rsquo;s a little difficult at times, maybe bordering on dangerous (and possibly not super legal either), so I&amp;rsquo;d probably wouldn&amp;rsquo;t recommend this.  But since it follows the rail line pretty much the entire time, it&amp;rsquo;s a good opportunity to catch up on some train spotting.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/ddc04f9429.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  First landmark of the walk, the Scout Camp Road bridge at the 72 km milepost.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/f01e1c2b64.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/a6956da04d.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  One of a handful of tunnels going underneath the railway embankment. It&amp;#39;s possible to walk through them, but given all the rain we&amp;#39;ve had, you&amp;#39;ll probably get wet in some way.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/34a03ffe06.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Frank Mann Reservoir.  Full to the brim.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/2d46501335.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/eba4f4ab44.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Another tunnel under the rail line. This time the walking track goes right the portal, which is a little dangerous…
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/2c64a457ec.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  …but if you do venture this way, you&amp;#39;re greeted with this lovely glade. Nice place to stay for a minute and listen to the forest sounds.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/cd30c13d46.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Travelling underneath the Caulder Freeway. The path gets a little difficult from this point.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/e7e24a2abc.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/b41b7448d4.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The turnaround point, at the Quarry Road rail bride in Woodend.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;I realised today that it&amp;rsquo;s been 10 years since I first walk this particular trail. I&amp;rsquo;m wondering if it might be time to retire it. As nice as it is, there are certain aspects of it that are getting a little tiresome. Plus it&amp;rsquo;s always boggy, even during the height of summer, meaning that you&amp;rsquo;ll usually get your socks wet and your pants dirty when you walk it. Even so, walking it is always a pleasure.&lt;/p&gt;
&lt;h2 id=&#34;trentham&#34;&gt;Trentham&lt;/h2&gt;
&lt;p&gt;Following a brief lunch in Kynteon, it was time for the second walk: The Domino Trail in Trentham. This is a rail trail that travels through some really nice forest.  Last time I did this, more than a year ago, the path was closed as a severe storm brought down a number of trees and I was unable to do the entire path. Fortunately the trees were cleared and the path reopened.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/1657f79dcc.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/a86c71853e.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The old path, now blocked with fallen trees. I don&amp;#39;t think this path will be opened anytime soon.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/94f8f0f214.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/7b749cdbc0.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Some of the scars of that storm still remain.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/2b65a6b519.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  One of a couple of old, decaying rail bridges. Usually the walking path travels beside them on relatively new bridges.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/70a3d60189.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Approaching the turnaround point.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/43b6e1ba3f.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Another indication that this use to be a railway: an old telegraph pole.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/3651341d4e.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  On the journey back.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/9596519845.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/000999a3d2.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Arriving at the starting point.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;I caught the rain a few times and much of the track was quite boggy given the decent amount of rain we&amp;rsquo;ve received, but overall, it was a nice day out.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Opinionated Tips for New Micro Bloggers Coming From Twitter</title>
      <link>https://lmika.org/2022/10/30/opinionated-tips-for.html</link>
      <pubDate>Sun, 30 Oct 2022 09:19:59 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/10/30/opinionated-tips-for.html</guid>
      <description>&lt;p&gt;&lt;em&gt;or, How I Use Micro.blog&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To all new-comers from Twitter, welcome to Micro.blog!&lt;/p&gt;
&lt;p&gt;No doubt you&amp;rsquo;ve received the welcome message from Jean with links on how Micro.blog is different from Twitter, but you&amp;rsquo;re probably still wondering how to get the most out of Micro.blog. And while I&amp;rsquo;m not claiming to have all the answers, I&amp;rsquo;ve put together a few tips for how I get value from writing here.&lt;/p&gt;
&lt;p&gt;First, the thing that took me a while to appreciate is that Micro.blog&amp;rsquo;s not so much a social media platform, at least not in the traditional sense. I mean, it certainly can be described as one, and if your goal is to connect with others online, it works just as well as any other. But in essence, it&amp;rsquo;s closer to a blogging platform, albeit one with social aspects tied to it. When you write a post, not only would it appear in the timeline of those that follow you, it will also appear on your own blog. So an option before you is to lean into this. Treat your blog as your own space on the web. Get a domain name and share it with others. Style your blog as much or as little as you want. Take a look at the plugins to see what you can add to your site. You don&amp;rsquo;t have to do this right away, but it&amp;rsquo;s well worth considering if you hope to get the most out of writing here.&lt;/p&gt;
&lt;p&gt;Second, write naturally. You&amp;rsquo;re not feeding an algorithm here. There&amp;rsquo;s nothing like trending topics or recommendations that takes what you write and throws it around the network. Instead, you’ll get something better: real humans reading and replying to your post. So write for humans. If a post needs to be longer than 280 characters, then it can be: no need for threads here. Also, adding hashes in front of words does nothing other than make what you write harder for others to read.&lt;/p&gt;
&lt;p&gt;Finally, write for yourself. The cheap endorphin rush you use to get from likes and retweets will never come, so you’ll need another way to get pleasure from writing here. What works for me is to write for myself. If I write something, I do so with the expectation that no-one else will see it. Of course, you&amp;rsquo;re writing on the open web so others can certainly see it: try not to be too much of a jerk. But even if no-one else does, as long as I get something out of what I write, that&amp;rsquo;s all I ever need.&lt;/p&gt;
&lt;p&gt;Of course, how and why you use Micro.blog is ultimately up to you. After all, you are the one paying $5.00 to use the service (and yes, in this case, &lt;em&gt;you&lt;/em&gt; are the customer here, not the product). So make sure you use it in a way that works for you. And it may take a while before you find the utility you&amp;rsquo;re looking for. I&amp;rsquo;d advise patience here. You will not find the short-term rush you&amp;rsquo;ll get from Twitter. Before you is a slower path. But it&amp;rsquo;s one that can lead you towards a much better and fulfilling experience of writing online.&lt;/p&gt;
&lt;p&gt;Happy blogging.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Technical Knowledge Management Update</title>
      <link>https://lmika.org/2022/10/18/technical-knowledge-management.html</link>
      <pubDate>Tue, 18 Oct 2022 10:50:11 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/10/18/technical-knowledge-management.html</guid>
      <description>&lt;p&gt;Finished the first pass of &lt;a href=&#34;https://lmika.org/2022/10/12/might-plan-another.html&#34;&gt;moving all my technical knowledge&lt;/a&gt; into static Markdown files. I&amp;rsquo;ve got all the files now in a Git repository hosted on &lt;a href=&#34;https://github.com/lmika/knowledge&#34;&gt;Github&lt;/a&gt;. They&amp;rsquo;re also published as a website called &lt;a href=&#34;https://tecknow.space&#34;&gt;TecKnow Space&lt;/a&gt; (pronounced &amp;ldquo;techno space&amp;rdquo;)&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The way I&amp;rsquo;ve done this is by writing a tool I which will checkout the source Git repository, iterate over all the source Markdown files, render them as HTML, and push them to another Git repository which is being served using GitHub pages. The tool, which is currently not open-source, was written in Go and uses &lt;a href=&#34;github.com/go-git/go-git&#34;&gt;go-git&lt;/a&gt; for the Git client, and &lt;a href=&#34;github.com/russross/blackfriday&#34;&gt;BlackFriday&lt;/a&gt; as the Markdown renderer.&lt;/p&gt;
&lt;p&gt;The output is just a directory tree of HTML files.
I did consider Hugo for a brief moment, but I wanted to avoid the complexity of including a base Hugo site here, especially given that the main reason for using Hugo is for the themes. That did mean that I had to write a tool to do this, but my thinking is that if I decided to move to Hugo, I still need something to iterate over all the Markdown files anyway, since they&amp;rsquo;re stored in a different structure than the content of a Hugo site. And some small modifications to the tool would could be made to make that happen.&lt;/p&gt;
&lt;p&gt;At the moment, I need to run the tool manually to regenerate the site.  But, ultimately, I&amp;rsquo;d like to setup Github&amp;rsquo;s CI/CD to re-render the source files when I push changes to main.  That, plus styling and optimising how I organise this information, will probably be the next step I tackle.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;One of those names that worked off the bat, and I grin whenever I say it.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Option Currency Symbol Reference</title>
      <link>https://lmika.org/2022/10/12/option-currency-symbol-reference.html</link>
      <pubDate>Wed, 12 Oct 2022 12:06:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/10/12/option-currency-symbol-reference.html</guid>
      <description>&lt;p&gt;A small, incomplete reference of the various currency symbols that can be produced using the &lt;kbd&gt;Option&lt;/kbd&gt; key.  Here because I always forget these, and I&amp;rsquo;ve been needing to produce these quite often lately.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Currency&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Symbol&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Key&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Dollar&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;$&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;$&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Cent&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;¢&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;Opt&lt;/kbd&gt;&lt;kbd&gt;4&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Pound&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;£&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;Opt&lt;/kbd&gt;&lt;kbd&gt;3&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Euro&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;€&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;Shift&lt;/kbd&gt;&lt;kbd&gt;Opt&lt;/kbd&gt;&lt;kbd&gt;2&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Yen&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;¥&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;Opt&lt;/kbd&gt;&lt;kbd&gt;y&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
    </item>
    
    <item>
      <title>An Alternative To The Reply All Idea For Micro.blog</title>
      <link>https://lmika.org/2022/10/06/an-alternative-to.html</link>
      <pubDate>Thu, 06 Oct 2022 06:15:56 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/10/06/an-alternative-to.html</guid>
      <description>&lt;p&gt;Just thinking about Micro.blog conversations and the discussion about having a way to reply all. I wonder if a better alternative is to be able to “follow” conversations, with new replies from anyone showing up in the timeline. This can be completely opt-in per conversation — including for posts that are made by you — so that those that want the old way to continue working as is don’t loose anything.&lt;/p&gt;
&lt;p&gt;It has one other advantage: I’ve seen conversations in Micro.blog that I had no real interest in participating in (usually because I have nothing to add) but I was interested in following along.  Usually these are people asking for recommendations, and others post theirs as replies.  Being able to “subscribe to new replies” would allow me to get updates to these as they come in, rather than have me check-in on the conversation thread every hour or so.&lt;/p&gt;
&lt;p&gt;Anyway, that’s the idea.  Let me know what you think.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The (Annoying) Way To Get the Current MacOS Appearance Scheme From the Command Line</title>
      <link>https://lmika.org/2022/10/04/the-annoying-way.html</link>
      <pubDate>Tue, 04 Oct 2022 08:28:41 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/10/04/the-annoying-way.html</guid>
      <description>&lt;p&gt;Ok, here&amp;rsquo;s something bizarre.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m trying to get the current MacOS appearance scheme — either light or dark mode — from the terminal. The way to do this is by running this command (&lt;a href=&#34;https://stackoverflow.com/questions/25207077/how-to-detect-if-os-x-is-in-dark-mode&#34;&gt;source&lt;/a&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;defaults read -g AppleInterfaceStyle
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If MacOS is in dark mode, this will print &lt;code&gt;Dark&lt;/code&gt;.  But if MacOS is in light mode, the command will print… an error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2022-10-04 09:15:18.058 defaults[35844:466643] 
The domain/default pair of (kCFPreferencesAnyApplication, AppleInterfaceStyle) does not exist
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running &lt;code&gt;defaults read -g&lt;/code&gt; confirms what the error message says: the &lt;code&gt;AppleInterfaceStyle&lt;/code&gt; key is not set when MacOS is in light mode.&lt;/p&gt;
&lt;p&gt;Why was &lt;em&gt;this&lt;/em&gt; chosen as the way to do things?  Now I need to capture and parse stderr just confirm that the reason an error occurred was because MacOS is in light mode; as oppose to some other, possibly legitimate, reason.&lt;/p&gt;
&lt;p&gt;A tad annoying I must say.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Photos of Lake Tuggeranong</title>
      <link>https://lmika.org/2022/10/01/photos-of-lake.html</link>
      <pubDate>Sat, 01 Oct 2022 21:33:30 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/10/01/photos-of-lake.html</guid>
      <description>&lt;p&gt;This morning I went to Tuggeranong, south of Canberra.  After a cafe breakfast I took a walk around the lake.  It was a lovely spring morning for it: cloudy, mild but slightly on the cool side.  It was also quite a decent walk: probably took an hour and 20 minutes, and I didn&amp;rsquo;t even cover the entire lake. All in all, a nice way to begin the day.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/8b1feb2121.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The starting point, facing north. Notice that bridge in the back.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/e0cd4ed8be.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Honestly, I didn&amp;#39;t expect the lake to be this large.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/ad20ca4833.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  A distance shot of one of several people fishing.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/9b061e181b.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  These signs were put up in a few locations. Although I didn&amp;#39;t get swooped myself, I know of people who did, so they were not entirely unnecessary.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/adfa50ed80.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  One of a few islands within the lake.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/92325cd678.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/ca957ef4b5.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/23c37feee6.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/56a3003ccc.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/05a2f7cca0.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Photo of the southern part of the lake, taken from the bridge at the start. This was part of the lake I did not get to walk.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>The Australian Republic Question</title>
      <link>https://lmika.org/2022/09/09/the-australian-republic.html</link>
      <pubDate>Fri, 09 Sep 2022 13:11:20 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/09/09/the-australian-republic.html</guid>
      <description>&lt;p&gt;With the passing of Queen Elisabeth II, the talk of whether Australia should become a republic will probably start making the rounds once more. I don&amp;rsquo;t consider myself a royalist, and when the last referendum on the issue came around, I voted in favour of becoming a republic. The idea of having the British Royal Family as the head of state of a country halfway around the world seem anachronistic to me, and I was disappointed when the referendum failed.&lt;/p&gt;
&lt;p&gt;Since then, my position has been become slightly more nuanced.  I still believe in the ideals of becoming a republic — in being a country that is more-or-less completely self governing. But after reading &lt;a href=&#34;https://www.vox.com/2014/9/23/6831777/new-zealand-electoral-system-constitution-mixed-member-unicameral&#34;&gt;this article from Vox.com&lt;/a&gt;, I&amp;rsquo;ve come to see some benefits of having a head of state that is removed from the day-to-day politics of government. Sure, the stability from such a figurehead &lt;a href=&#34;http://en.wikipedia.org/wiki/1975_Australian_constitutional_crisis&#34;&gt;may not have been wholly constant&lt;/a&gt;, but that &amp;ldquo;lack [of] semblance of legitimacy&amp;rdquo; that comes from the royals being the head of state does provide some reassurance.  One less divisive thing for people to think about when that position changes hands.&lt;/p&gt;
&lt;p&gt;So if the referendum was held today, which way would I go?  I&amp;rsquo;d probably still vote &amp;ldquo;yes&amp;rdquo;, but it would have a &amp;ldquo;can we make it such that the office is not in any way marred in the politics of the day?&amp;rdquo; qualifier attached to it.&lt;/p&gt;
&lt;p&gt;Then again, I talking about the terms of a theoretical referendum where the proposed system is more than just replace-the-monarchy-with-a-president. Such concerns regarding the division of power might already be settled within the constitution. I really don&amp;rsquo;t know: might be worth looking up if the question were to come up again.&lt;/p&gt;
&lt;p&gt;Either way, we&amp;rsquo;ll see which way the winds blow.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Detecting When GetItem On DynamoDB Returns Nothing</title>
      <link>https://lmika.org/2022/09/05/detecting-when-getitem.html</link>
      <pubDate>Mon, 05 Sep 2022 15:18:18 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/09/05/detecting-when-getitem.html</guid>
      <description>&lt;p&gt;I was trying to remember how best to detect when a GetItem call to DynamoDB returns no values.  That is, when there&amp;rsquo;s no item with that key in the table.  This is in a project that is using v2 of the Go AWS SDK.&lt;/p&gt;
&lt;p&gt;After poking through some old code that did this, it looks like the way to do so is to check that the returned &lt;code&gt;Item&lt;/code&gt; field is &lt;code&gt;nil&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;out, err := p.client.GetItem(ctx, &amp;amp;dynamodb.GetItemInput{
    Key:       key,
    TableName: tableName,
})
if err != nil {
    // Error getting the item
    return nil, err
} else if out.Item == nil {
    // No item found
    return nil, nil
}

// Do the thing with out.Item
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, I can live with this approach. I&amp;rsquo;m kinda happy that I don&amp;rsquo;t need to check the error message, since doing so using the Go AWS SDK is a bit of a pain.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some Photos of The Yarra Trail</title>
      <link>https://lmika.org/2022/09/05/some-photos-of.html</link>
      <pubDate>Mon, 05 Sep 2022 08:55:02 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/09/05/some-photos-of.html</guid>
      <description>&lt;p&gt;Went for a very short walk of the Yarra Trail around Heidelberg on Saturday.  The evening light was really lovely so I though I&amp;rsquo;d take some photos.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/e9b05af2aa.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/5e8a5aa439.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/7e6bfa3145.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/1bbae83e1b.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/01d8a0c82e.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/67850f6d41.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Milestone</title>
      <link>https://lmika.org/2022/09/04/milestone.html</link>
      <pubDate>Sun, 04 Sep 2022 09:20:18 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/09/04/milestone.html</guid>
      <description>&lt;p&gt;For a while, I&amp;rsquo;ve been trying to maintain a writing streak.  I need to write at least one blog post or journal entry a day.  Today that streak has been maintained for a full year.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2022/1f30f00198.jpg&#34; width=&#34;600&#34; height=&#34;600&#34; alt=&#34;&#34; /&gt;
&lt;p&gt;I will admit that the streak was not completely continuous: I had to go back a few times and retroactively add a post.  But even so, I&amp;rsquo;m quite please with reaching this milestone.  Onward to the next one.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some Things I Found Out While Browsing a Substack Newsletter in The Wayback Machine</title>
      <link>https://lmika.org/2022/08/30/some-things-i.html</link>
      <pubDate>Tue, 30 Aug 2022 16:21:21 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/08/30/some-things-i.html</guid>
      <description>&lt;p&gt;I did a quick search for &lt;a href=&#34;https://lmika.org/2022/08/30/over-the-weekend.html&#34;&gt;that blog post&lt;/a&gt; in the the Wayback Machine. I couldn&amp;rsquo;t find the post but the Substack newsletter was there. I guess Substack does allows archiving of newsletters with the &amp;ldquo;substack.com&amp;rdquo; domain after all (if it&amp;rsquo;s something that they can even control).&lt;/p&gt;
&lt;p&gt;Anyway, here are a few things I&amp;rsquo;ve found out while browsing through a Substack newsletter in the Wayback Machine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clicking &amp;ldquo;Let me read it first&amp;rdquo; works: it slides away and the most recent posts show up. Guess it&amp;rsquo;s just a simple HTML overlay blocking the home page.&lt;/li&gt;
&lt;li&gt;Open all links in a new tab. Just clicking them will run some JavaScript which, I guess, tries to load the post directly from Substack, resulting in an error if the newsletter is taken offline. Opening the link in a new tab will get the post directly from the Wayback Machine.&lt;/li&gt;
&lt;li&gt;Clicking the archive tab seems to bring up the blog archive briefly, but then some JavaScript — which I guess is trying to load the archive from Substack? — replaces it with an error (why does everything need to be JavaScript?!) I&amp;rsquo;m guessing that the actual HTML is still there so it &lt;em&gt;might&lt;/em&gt; be possible to get it if you disable JavaScript.  I haven&amp;rsquo;t tried this though, so this is only a hypothesis.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As for the post itself, it turns out that it was in my Feedbin archive all along, so the search in Wayback Machine wasn&amp;rsquo;t actually necessary. Now the trick is to find a way to prevent Feedbin from purging old posts. 😳&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Rebinding Keys For Quickly Resolving Conflicts in GoLand</title>
      <link>https://lmika.org/2022/08/26/heres-the-keymap.html</link>
      <pubDate>Fri, 26 Aug 2022 15:05:51 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/08/26/heres-the-keymap.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m dealing with a lot of conflicts today as I try to clear a backlog of Git rebases in my to-do pile.  I&amp;rsquo;ve been using GoLand to do this, as my current Git &amp;ldquo;mergetool&amp;rdquo; is configured to Vimdiff for some reason&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; and I&amp;rsquo;m not really bothered to find some other tool, at least not yet.  GoLand does a pretty good job.&lt;/p&gt;
&lt;p&gt;Unfortunately, the default key-bindings for resolving conflicts in GoLand is far from good.  About the quarter of actions are bound to keys that make sense.  Half the actions, like ignoring hunks, are not bound to anything, while the last quarter are bound to keys that result in a suboptimal experience.  For example, the key to accept changes for a delta use the arrow keys, whereas the key to go to the next delta is F7.  What a strange default.  I&amp;rsquo;d have to constantly move my hand off the arrow keys over to the Fn row if I want to go through and fix each delta.  This, plus moving over to the mouse to select an action not bound to a key, makes resolving conflicts really slow.&lt;/p&gt;
&lt;p&gt;Fortunately GoLand allows remapping keys to pretty much anything, so I&amp;rsquo;d figured it&amp;rsquo;s time to fix this problem.  I set about coming up with a nicer key-mapping scheme for resolving conflicts.  I wanted to come up with one that adhered to these principals as best it can:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;My hands must not move from where they rest on the keyboard for almost the entire time a file is being worked upon.  The only exception are actions that are invoked less frequently and have a large radius of change — such as the accepting all changes from the left or the right.  I&amp;rsquo;d figured it&amp;rsquo;s still okay to use the mouse for those.&lt;/li&gt;
&lt;li&gt;The arrow keys should be used for navigation (go here) and for addressing (this one).&lt;/li&gt;
&lt;li&gt;The modifier keys should be used for choosing the operation, such as going to the next delta, or choosing or ignoring a change.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&amp;rsquo;s the key-map I&amp;rsquo;ve come up with.  The actions marked with an asterisk were the one&amp;rsquo;s that I needed to change.  All these actions are within the &amp;ldquo;Version Control Systems &amp;gt; Diff &amp;amp; Merge&amp;rdquo; group within the &amp;ldquo;Keymap&amp;rdquo; section in preferences.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Action&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Key Binding&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Previous Difference *&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;^&lt;/kbd&gt;&lt;kbd&gt;⌘&lt;/kbd&gt;&lt;kbd&gt;↑&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Next Difference *&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;^&lt;/kbd&gt;&lt;kbd&gt;⌘&lt;/kbd&gt;&lt;kbd&gt;↓&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Previous Conflict *&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;^&lt;/kbd&gt;&lt;kbd&gt;⌥&lt;/kbd&gt;&lt;kbd&gt;⌘&lt;/kbd&gt;&lt;kbd&gt;↓&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Next Conflict *&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;^&lt;/kbd&gt;&lt;kbd&gt;⌥&lt;/kbd&gt;&lt;kbd&gt;⌘&lt;/kbd&gt;&lt;kbd&gt;↓&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Accept Left Side&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;^&lt;/kbd&gt;&lt;kbd&gt;⌘&lt;/kbd&gt;&lt;kbd&gt;→&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Accept Right Side&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;^&lt;/kbd&gt;&lt;kbd&gt;⌘&lt;/kbd&gt;&lt;kbd&gt;←&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Ignore Left Side *&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;⇧&lt;/kbd&gt;&lt;kbd&gt;⌘&lt;/kbd&gt;&lt;kbd&gt;←&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Ignore Right Side *&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;⇧&lt;/kbd&gt;&lt;kbd&gt;⌘&lt;/kbd&gt;&lt;kbd&gt;→&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Confirm Merge&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;kbd&gt;⌘&lt;/kbd&gt;&lt;kbd&gt;Enter&lt;/kbd&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I&amp;rsquo;ve only tried this out as part of a single rebasing session, but already it feels &lt;em&gt;so&lt;/em&gt; much faster.  It&amp;rsquo;s taken me probably half the time to go through each file now, maybe even better.  I also like the fact that most of the time I only need to hold down Ctrl and Cmd key, and use the arrows to move to and accept changes.  Ignoring a change involves moving my left pinky to the Shift key, adding a bit of resistance to indicate an action that requires just that little bit more care.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll see how this goes as I deal with future conflicts, and I may make a change or to.  But this is looking really promising.&lt;/p&gt;
&lt;p&gt;One last note: this was done in GoLand but I&amp;rsquo;d imagine it&amp;rsquo;s possible to set this key-mapping up in most, if not all, of JetBrain&amp;rsquo;s other IDEs.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;It was previously something else, and I have no idea how it got changed.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Turning Off Shared Command History in Oh My Zsh</title>
      <link>https://lmika.org/2022/08/18/ive-been-using.html</link>
      <pubDate>Fri, 19 Aug 2022 11:39:26 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/08/18/ive-been-using.html</guid>
      <description>&lt;p&gt;&lt;em&gt;TL,DR: add &amp;ldquo;unsetopt share_history&amp;rdquo; to your .zshrc file&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://ohmyz.sh&#34;&gt;Oh My Zsh&lt;/a&gt; at work for a few months.  As far as terminal config managers go, this one works pretty well.  But the default configuration does include something which I found quite annoying.&lt;/p&gt;
&lt;p&gt;First, a few words on how I use the terminal.  I&amp;rsquo;m in the terminal constantly in my day to day.  At the start of the day, I&amp;rsquo;m creating terminal tabs and running commands to do things like build the project I&amp;rsquo;m working on, start a testing session, etc.  This all starts very ad-hoc, but over time these tabs take on a particular role.  I may run &lt;code&gt;make&lt;/code&gt; and &lt;code&gt;git&lt;/code&gt; in the left-most tab, run tests in the tab next to that, have logs tailing in the one after that, etc.  As I start and stop commands in each tab, they all build up a different history, which is usually visible in the terminal output itself.  Eventually it comes to the point where in order to run a particular tool, I simply need to switch to the particular tab and press &lt;kbd&gt;↑&lt;/kbd&gt; one or two times to get to it.&lt;/p&gt;
&lt;p&gt;Installing &lt;em&gt;Oh My Zsh&lt;/em&gt; broke this workflow.  Out of the box &lt;em&gt;Oh My Zsh&lt;/em&gt; enables shared command history across each session, meaning that the commands recalled when pressing &lt;kbd&gt;↑&lt;/kbd&gt; are the last commands executed in &lt;em&gt;any&lt;/em&gt; tab.  This meant that when going through the history, the recalled command would be different from the ones that I&amp;rsquo;ve entered in the tab I&amp;rsquo;m in.  In addition to this resulting in pressing &lt;kbd&gt;↑&lt;/kbd&gt; way more times than I used to — sometimes to the point where I gave up and simply typed the command out again — it also meant that the recalled commands  differ from the ones I saw in the tab output itself.  This was very jarring.&lt;/p&gt;
&lt;p&gt;Fortunately, this feature can be turned off simply by adding the following to your .zshrc file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unsetopt share_history
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I did just this and my old workflow was restored.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t know why shared command history was chosen as the default.  Am I in the minority using the terminal this way?  Maybe.  But I know for certain that this group is larger than a single person.  In either case, I&amp;rsquo;m glad that I was able to disable this &amp;ldquo;feature&amp;rdquo;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Kyneton Botanical Gardens</title>
      <link>https://lmika.org/2022/08/06/kyneton-botanical-gardens.html</link>
      <pubDate>Sat, 06 Aug 2022 17:07:54 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/08/06/kyneton-botanical-gardens.html</guid>
      <description>&lt;p&gt;Went to Kyneton with Mum and Dad today.  While they went off for a bike ride, I had the opportunity to
go for a walk around the botanical gardens.  This was my first time there, and although the gardens
themselves were not very big, it was still a pleasant experience.  Here are some photos I took of that visit.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/ad86b4f7f3.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/42ef8c5cbc.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/bd578a3ac3.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/5284aeb00d.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/ae7f3b6673.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/f9a1fbfcbf.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  The Campaspe River.  I&amp;#39;ll be honest: I had no idea this river flowed through Kyneton until today.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/d2b08f758b.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Walking along the river now, through a grove of oak trees.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/1a2c2c10b0.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  I&amp;#39;d imagine this would really nice in Autumn time.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/8785bd5602.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/2f01eb1b3b.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/e9a14f53e6.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  A weir, and what I believe is the remnants of a footbridge.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/27ba592f3b.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/4ab6fb502f.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  What use to be the public swimming pools.  I believe this was the reason for the weir.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/7c35f6513e.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/7fed5f916b.jpg&#34;
     
      /&gt;

&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://lmika.org/uploads/2022/154b9f3596.jpg&#34;
     
      /&gt;

  &lt;figcaption&gt;
  
  Mollison St. bridge over the river, built in the 1880&amp;#39;s.  This is apparently the second bridge, which replaced a lower one that was built in the 1850&amp;#39;s.
  &lt;/figcaption&gt;

&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;This was followed by lunch at Little Swallow Cafe.  The place was quite busy —
I suspect that their reputation is such that it would be busy most of the time — but the food was very nice.
We then went for a short walk around Kyneton and then drove to Malmsbury for a coffee and Devonshire tea.&lt;/p&gt;
&lt;p&gt;It was a little cold but the rain held off.  All in all, a good day for it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>DynamoDB JSON Attribute Types Quick Reference</title>
      <link>https://lmika.org/2022/08/04/a-quick-reference.html</link>
      <pubDate>Thu, 04 Aug 2022 10:33:34 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/08/04/a-quick-reference.html</guid>
      <description>&lt;p&gt;Because apparently it&amp;rsquo;s too difficult for AWS to provide an easy way to find this information.&lt;/p&gt;
&lt;h2 id=&#34;atomic-types&#34;&gt;Atomic Types&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;min-width:80px&#34;&gt;Type&lt;/th&gt;
&lt;th style=&#34;min-width:80px&#34;&gt;JSON&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Binary&lt;/td&gt;
&lt;td&gt;&lt;code&gt;B&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String value containing the Base64-encoded binary data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BOOL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Either &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;&lt;code&gt;S&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;&lt;code&gt;N&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String with the numerical value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Null&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Should always be &lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;collection-types&#34;&gt;Collection Types&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;min-width:80px&#34;&gt;Type&lt;/th&gt;
&lt;th style=&#34;min-width:80px&#34;&gt;JSON&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;List&lt;/td&gt;
&lt;td&gt;&lt;code&gt;L&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A JSON array, with each element being an attribute with a type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Map&lt;/td&gt;
&lt;td&gt;&lt;code&gt;M&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A JSON object, with the keys being the map keys, and the values being an attribute with a type.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;set-types&#34;&gt;Set Types&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;JSON&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Binary Set&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A JSON array of Base-64-encoded binary data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Number Set&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A JSON array of string with the numerical values.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;String Set&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A JSON array of strings.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Sources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_AttributeValue.html&#34;&gt;AttributeValue API documentation&lt;/a&gt;: this the definitive guide from AWS.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CapacityUnitCalculations.html&#34;&gt;DynamoDB Item size and format&lt;/a&gt;: more info about the particulars of each attribute type.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>More Complaining About Autocorrect on MacOS</title>
      <link>https://lmika.org/2022/07/23/more-complaining-about.html</link>
      <pubDate>Sat, 23 Jul 2022 11:42:57 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/07/23/more-complaining-about.html</guid>
      <description>&lt;p&gt;Earlier this morning:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Me: (writing in my journal) &lt;em&gt;Nonna, my 91 year-old grandmother&amp;hellip;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Autocorrect: Did you me &amp;ldquo;Donna&amp;rdquo;?&lt;/p&gt;
&lt;p&gt;Me: No, undo change.  (continue writing) &lt;em&gt;good news is that Nonna&amp;hellip;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Autocorrect: Did you me &amp;ldquo;Gonna&amp;rdquo;?&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I can forgive MacOS for considering &lt;a href=&#34;https://en.wiktionary.org/wiki/nonna#Italian&#34;&gt;nonna&lt;/a&gt; a spelling error, since it&amp;rsquo;s not an English word.&lt;/p&gt;
&lt;p&gt;But I do see why auto-correct on MacOS can be frustrating.  Apart from the two completely random corrections it made for the same word, it also doesn&amp;rsquo;t seem to get the hint when I undo the change.  I would have thought that action is a pretty strong signal from the user to just leave the word alone, at least for the moment.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Write It Down</title>
      <link>https://lmika.org/2022/07/20/write-it-down.html</link>
      <pubDate>Wed, 20 Jul 2022 13:28:45 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/07/20/write-it-down.html</guid>
      <description>&lt;p&gt;I am feeling some very minor after-effects from the booster I took yesterday (nothing serious, just the expected cold-like symptoms). I was curious as to whether it was anything like I experienced in January, when I got my last booster.  I went to my journal to see what I wrote about it.  Unfortunately for me, there was nothing there.&lt;/p&gt;
&lt;p&gt;To be fair to my past self, there were some other events going on at the same time which I did write about.  But I was left pondering this morning about why I didn&amp;rsquo;t write anything about how I was feeling back then.  My guess is that I probably didn&amp;rsquo;t think it was worth writing about at the time.  &amp;ldquo;Feeling a little off&amp;rdquo; was probably something that I thought was quite trivial, and wouldn&amp;rsquo;t be relevant later on.&lt;/p&gt;
&lt;p&gt;I was wrong: it was relevant.  Otherwise I wouldn&amp;rsquo;t be going back in time to find out.&lt;/p&gt;
&lt;p&gt;I guess there&amp;rsquo;s a lesson there: write it down.  Write everything down, even if you think it might not be relevant at the time.  The simple fact is you don&amp;rsquo;t know.  It may very well be relevant months or years from now.  Better to written down too much than not have anything written at all.&lt;/p&gt;
&lt;p&gt;Of course the trick is working out what &amp;ldquo;everything&amp;rdquo; consists of.  If you&amp;rsquo;re writing down what constitutes your daily routine each day, I&amp;rsquo;d imagine you&amp;rsquo;ll be spending most of your time documenting the same thing over and over again.
I guess a good decision process is if you&amp;rsquo;re unsure about particular thing, then it&amp;rsquo;s probably a good idea to just write it down anyway.  You may think it&amp;rsquo;s unnecessary at the time, but again, you don&amp;rsquo;t really know.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll try to get better at this myself.  In fact, part of this post was taken from this morning&amp;rsquo;s journal entry, just below a description on how I was feeling today.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Newsletter Reminder Emails</title>
      <link>https://lmika.org/2022/07/17/newsletter-reminder-emails.html</link>
      <pubDate>Sun, 17 Jul 2022 09:03:59 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/07/17/newsletter-reminder-emails.html</guid>
      <description>&lt;p&gt;I subscribe to a newsletter that sends “reminder” emails if I skip an issue.  If I don’t open one of the email newsletters I receive, then a few days later, a copy will be sent with a forward of the form “looks like you skipped an issue. Here what you missed.”&lt;/p&gt;
&lt;p&gt;These reminder emails are bad, and here’s why:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;It gives the impression of hustling me. I appreicate the time you take to publish something that I see value in, but sending these reminders feels like your forcing your content onto me.  Like I &lt;em&gt;just got to read this content. Really, you must read it! And, oh! You forgot this one day? Well I’ll make sure you don’t forget it (and me) again.&lt;/em&gt; Please, back off! I’ve received your content and I’ll get to it when I get to it, if I feel like it, after I’ve read all the other newsletters I received. Please don’t push me to read it on your schedule.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It just confirms that they’re tracking what I open. I mean, I know this aready, but it does bring it front of mind.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you’ve got an email newsletter, please don’t do this. It could just be me, but I don’t read every issue of every newsletter I receive, apart from a few exceptions. If I’ve subscribed to yours, then know that I get value from your content.  Really, I do; I wouldn’t have subscribed otherwise. But sending these reminder emails out do not do your content any favours.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Feature Epic (Featuring the Epic Feature Branch)</title>
      <link>https://lmika.org/2022/07/07/i-should-not.html</link>
      <pubDate>Thu, 07 Jul 2022 13:06:05 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/07/07/i-should-not.html</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s what&amp;rsquo;s been happening at work with me recently.  I write it here as an exercise in how I can learn from this.  They say that writing can help in this respect so I&amp;rsquo;m going to put that logic to the test (in either case, just having this documented somewhere could prove useful).&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re working on a pretty large change to the billing service powering the SaaS product sold by the company I work at.  Along with our team, there are two other teams working on the same service at the same time, making any changes they need to release the product stuff they&amp;rsquo;re working on.  All our teams had our own deadlines — which are pretty pressing — to get stuff delivered either last month or sometime this month.&lt;/p&gt;
&lt;p&gt;Knowing that this was a change that could impact these other teams, I came up with the idea of using an epic feature branch, which will be used to track our changes.  This will leave the main branch relatively free for the other teams, who&amp;rsquo;s changes would not be as invasive as ours, to proceed with their plans and release when they need to without us blocking them.  Great idea — everyone can work at their pace.&lt;/p&gt;
&lt;p&gt;Of course, if it was such a great idea, this blog post would be a lot shorter. 😉&lt;/p&gt;
&lt;p&gt;This started out well.  We were doing our thing, making our changes and committing them to this epic branch, occasionally pulling updates from main when we started to fall behind.  The other teams were merging their changes on main, and the CI/CD pipeline was dutifully deploying what they merged into the dev environment.&lt;/p&gt;
&lt;p&gt;Then, after a couple of weeks, things started to go a little wrong.  The changes from the devs started to pile up in the &amp;ldquo;Ready for QA&amp;rdquo; column of our Jira board.  The team was a little concerned that the changes we were making couldn&amp;rsquo;t be ready for testing until they were all finished and merged.  Given the way the tickets were written, this seemed like a fair enough argument (I was the one that wrote the tickets BTW), but this delayed testing of the changes to the point when we we only had a few days to complete our testing and push it out to production before we hit our deadline.&lt;/p&gt;
&lt;p&gt;Once the QA team was ready to proceed, disaster.  Many of our changes had bugs or issues that we didn&amp;rsquo;t foresee, and had to be fixed or new tickets had to be spun out.  We had to delay rollout by more than a week (at the time of this post), which made the business quite unhappy.  Making things worse was the use of the epic feature branch and the automated deployment of main to Dev.  The other teams were still doing their thing on main, and when they merge a change, it blew away our changes.  This resulted in the QA team coming to the dev team with issues which, after investigation, were largely because the our changes were not even there.&lt;/p&gt;
&lt;p&gt;One other thing I didn&amp;rsquo;t forsee was that when we get to the point of merging our epic feature branch into main, I had little confidence that it would be integrated correctly.  The sole reason for this is that the test team hasn&amp;rsquo;t been testing our changes from main.  They&amp;rsquo;ve been testing the epic branch just fine, but all those conflicts that were resolved during resyncs from main, plus the actual large-scale merge of our branch: what&amp;rsquo;s to say that we didn&amp;rsquo;t miss something?  We&amp;rsquo;re just right back to delaying our testing near the due date.&lt;/p&gt;
&lt;p&gt;So this is where we are now.  All the bugs (that we know of) have been addressed and I&amp;rsquo;m hoping to merge our changes into main today in preparation for what I hope to be one last test before we roll it out.&lt;/p&gt;
&lt;p&gt;So, what did I learn from this?  I can think of a few takeaways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Continuous integration — not the automated build kind, but the consistently merging into main kind — is not only important, it&amp;rsquo;s bloody vital.  Not doing this means that you&amp;rsquo;re left with little confidence in what the testing team is actually testing is what would be pushed out to production.  Using the  epic feature branch was a mistake.  Always merge to main, and test from main as often as you can.  You may need to push out changes that are turned off, but as long as you design for this, this should be fine (feature flags FTW).&lt;/li&gt;
&lt;li&gt;Letting tickets pile up like they did was another mistake.  Ticket flow is important: not only for the team, who&amp;rsquo;s morale is tied to whether we will pass the sprint or not, but also in finding any problems early enough that you have enough time to react to them.&lt;/li&gt;
&lt;li&gt;This is probably something on my head: don&amp;rsquo;t spend too long doing a design.  I spent a week on one when one probably could have been written up in a few days (to be fair: the scope of the work has not quite been locked down when I was doing the design work, which did delay things a little). A quick design also means getting feedback sooner.&lt;/li&gt;
&lt;li&gt;I think the largest takeaway is trying to check the businesses expectations on what can be delivered and when.  This I find the hardest thing to do.  I&amp;rsquo;m a bit of a &amp;ldquo;aim to please&amp;rdquo; type of person, so it&amp;rsquo;s not easy for me to say something like &amp;ldquo;we can&amp;rsquo;t do that in that time.&amp;rdquo;  Instead I tend to be quite optimistic about what we can deliver.  I have to get better with this.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The saga is not quite finished yet: you may see another blog post on this subject soon enough.  But hopefully things will settle down after this.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Arriving Late</title>
      <link>https://lmika.org/2022/06/29/arriving-late.html</link>
      <pubDate>Wed, 29 Jun 2022 07:29:26 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/06/29/arriving-late.html</guid>
      <description>&lt;p&gt;I’m going to have to tell my boss today that the stuff my squad has been working on is going to arrive late. To much needs to be fixed or reworked, and there is one or two things that have been missed alltogeather.&lt;/p&gt;
&lt;p&gt;I think the biggest problem is that the thing we’ve been working on got into testing far too late — only a few days before the deadline — meaning that there was no time left for fixing things. Really, you can draft all the plans and designs you want but you really don’t know how well it will perform until the “working” code has been handed to someone else.&lt;/p&gt;
&lt;p&gt;Another problem might have been that I didn’t push for more time upfront. It’s been difficult to do this in the place I’m working now. It’s almost like they’re on to me (or at least have their own ideas of when things should be delivered). But I’m guessing it’s still heaps better to shatter expectations by saying something will take longer upfront, then get it delivered early, rather than be optimistic about it and come up against the deadline. “Underpromise and overdeliver,” they say.&lt;/p&gt;
&lt;p&gt;I wonder if there’s a way to have two deadlines: one the business knows about, and one for the squad. The first one is quoted to be a respectable amount of time that is significantly larger than what you &lt;em&gt;really&lt;/em&gt; it would take to deliver the feature. That would check expectations, and leave enough time for any rework. The second is a more optimistic deadline for the squad to work towards. I think having this second deadline is important, otherwise all the work will pile up near the end of the first one and you’ll deliver late again. It’s all so deceptive though, and the thing is that you almost need to deceive yourself: you know that the second deadline is not the “real” deadline after-all.&lt;/p&gt;
&lt;p&gt;I don’t know. I find all this estimation and managing expectations quite difficult and it’s generally not something I like doing.&lt;/p&gt;
&lt;p&gt;In any case, we’re going to need more time. I guess I can take solice in the fact that we &lt;em&gt;almost&lt;/em&gt; got there, maybe 70-80%. We just need to get that other 80% over the line.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Wrong Number</title>
      <link>https://lmika.org/2022/06/27/wrong-number.html</link>
      <pubDate>Mon, 27 Jun 2022 13:39:57 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/06/27/wrong-number.html</guid>
      <description>&lt;p&gt;Got called three times this morning by mistake from an old woman in NSW trying to contact her son who had a very similar phone number to mine.&lt;/p&gt;
&lt;p&gt;First time I ignored it as I didn&amp;rsquo;t recognised the number and thought it was spam.&lt;/p&gt;
&lt;p&gt;Second time I answered and after trying to understand what she was trying to say, I simply said &amp;ldquo;I think you got the wrong number, sorry&amp;rdquo; and hung up.&lt;/p&gt;
&lt;p&gt;Third time I answered and after recognising that it was the same person, I figured it was right to work through the problem with her.  So I spend some time with her, going through each number slowly, trying to make sure that we understood where the mistake was made.  I didn&amp;rsquo;t hang up until I got the sense that she understood what number she needed to dial.  I was trying to be helpful, but I&amp;rsquo;d be lying if I said that self-interest was not involved.  After all, I wasn&amp;rsquo;t too keen on getting any more wrong numbers.&lt;/p&gt;
&lt;p&gt;Four hours have passed and I yet to hear from her again.&lt;/p&gt;
&lt;p&gt;Is there a lesson involved?  I don&amp;rsquo;t know: this could have happened to anyone.  I guess if there is  one, it&amp;rsquo;s that sometimes you don&amp;rsquo;t get to choose the types of problems you need to work on, and you just got to do what you can to, if not solve them, help in making them less of a problem than they were before.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Honour, Democracy, and Galati: A Day in Canberra</title>
      <link>https://lmika.org/2022/06/13/honour-democracy-and.html</link>
      <pubDate>Mon, 13 Jun 2022 21:05:44 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/06/13/honour-democracy-and.html</guid>
      <description>&lt;p&gt;Since being in Canberra, I haven&amp;rsquo;t really done anything &amp;ldquo;touristy&amp;rdquo;.  Given that today was a public holiday, I figured it was as good a time as ever to do so.  So I decided to spend the day visiting a couple of national landmarks, plus something I&amp;rsquo;ve been planning to do since returning to Canberra.&lt;/p&gt;
&lt;h2 id=&#34;the-war-memorial&#34;&gt;The War Memorial&lt;/h2&gt;
&lt;p&gt;The first time I&amp;rsquo;ve ever been in Canberra was during Christmas holidays in 2007 my family.  During that time, Mum and Dad and my two sisters went to the War Memorial and Parliament House, while I stayed in our rented town-house.  The reason why I stayed back was a little embarrassing: I claimed that I was tired, but this was during a weird period where I didn&amp;rsquo;t really want to be seen doing something touristy (I&amp;rsquo;ve mostly got over this feeling).  Not going when I had the chance was something I&amp;rsquo;ve regretted since that day.  Well, today I make amends with at least one of these, with a visit to the War Memorial.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/e6dec5e6b0.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The entrance.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/2409a44d11.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/8685256e8f.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The Hall of Valour.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/a998271bb0.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The WW1 exhibit.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/bea916d927.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      One of the boats from the Gallipoli landing.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/4933488557.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The WW2 exhibit.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/911d559613.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/bb2375b356.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/e284e817f2.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Area of reflection.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/97aec84942.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Looking down Anzac Parade towards Parliament House.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt; /&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/d4886992dd.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The Honour Roll.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/2f46ff9d23.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The Unknown Solder.  By far the quietest and most sombre part of the experience, and a fitting end to the visit.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/568afb2bc3.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/93a6e928e0.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
&lt;/div&gt;
&lt;h2 id=&#34;old-parliament-house&#34;&gt;Old Parliament House&lt;/h2&gt;
&lt;p&gt;Following my visit to the War Memorial and a brief lunch at the Poppy Cafe, it was time to visit Old Parliament House, and the Museum of Australian Democracy.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/4da3a23ba6.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Outside Old Parliament House.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/aa140e9a04.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The exhibit on public media and press freedom.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/e9cab01ac2.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Replication of the ABC broadcasting offices of the early 80.  Much of the exhibits seem to be snapshots in time when this building stopped being the seat of government.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/7882012863.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/5657e91161.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/9ea37aa159.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/bb87571d06.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      An exhibit of political cartoons commenting on recent events like Covid-19 and climate change, amongst others topcis.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/f199134b88.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The governing party offices.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/214809abb5.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The Prime Minister&amp;#39;s office.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/8f896ac139.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/142247bcd0.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The cabinet office.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/adf33d3544.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The cabinet ante room.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/6073491bd4.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The chair of the Speaker.  We were permitted to sit in it.  I did not take up the offer.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/d10815decb.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/d51afc219e.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Office of the Speaker of the House.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/b832ce0c5b.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/ccbe1666d8.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The ceremonial mace of the Speaker.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/4cec5d0cab.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The John Howard Library.  I would not call myself a Howard fan - far from it - but this was one good policy he threw his political weight behind.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/c7a93dde62.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The podium from which Howard made is consolidation speech from when he lost the election.  The only reason I took a photo of this was that I knew a Wentworth in school, and seeing this podium during that speech reminded me of him.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/733142abca.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The House of Representatives - from the press gallery.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/a07cbc8192.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      On the floor of the The House of Representatives itself.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/a05bc0abff.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/ade68643bb.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/328ee2fb53.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The President of the Senate office.  Unfortunately the Senate was closed for what I assume is restoration works.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/1a333fcc98.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Replication of Queen Elisabeth&amp;#39;s symbols of reign.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/aa5ab8fabe.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/b52473b366.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Replication of head gear from other monarchs.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/8201e4bc38.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The exhibit dedicated to democracy itself.  Around the perimeter were entries for each Australian prime minister.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/822f805fca.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      I was actually surprised on how quickly they updated this.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/d76a7d3413.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Paintings of Jack Green on Aboriginal representation and justice.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/0a78941db3.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      On the terrace.  To the left is the Aboriginal Tent Embassy.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Following this was a brief walk around the gardens.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/357d315ef7.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Out in the gardens.  Walking this reminded me a lot of the Mall in Washington, D.C.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/e02079d009.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/ea4d1f0be2.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      On Reconciliation Hill, bisecting the parliamentary triangle, looking towards Parliament House.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/1836655ff8.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Turn 180 degrees to face the War Memorial.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/ed591ceec5.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      On Lake Burley Griffin.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/a1cb564cf8.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The High Court with (half) the flags of the countries of the world.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/6057667f26.jpg&#34;
         
          /&gt;
    
    &lt;/figure&gt;
&lt;/div&gt;
&lt;h2 id=&#34;the-city-and-galati&#34;&gt;The City, And Galati&lt;/h2&gt;
&lt;p&gt;My final stop was the city.  Why?  Well, during my visit in April, we found a really nice galati place where we stayed, and since knowing that I would be back in June, I made it a point, in half jest, to return.  When talking to Mum and Dad on the phone, they asked if I have fulfilled this promise.  So since I was nearby, it felt like the perfect time to do so.&lt;/p&gt;
&lt;p&gt;Despite how sunny it was, it was still quite cold.  But even so, that galati hit the spot.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/364f71040d.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The galati I enjoyed.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/d26297d608.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Where I enjoyed it.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Afternoon Walk Around Lake Ginninderra</title>
      <link>https://lmika.org/2022/06/11/went-for-an.html</link>
      <pubDate>Sat, 11 Jun 2022 22:13:51 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/06/11/went-for-an.html</guid>
      <description>&lt;p&gt;Went for an walk around Lake Ginninderra this afternoon.  Well, not &amp;ldquo;around&amp;rdquo; the lake: that walk would have taken a while.  But I did walk along the path that would take me around the lake for about 30 minutes, then walked back again.  Below are a few photos I took.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/5adb563d5e.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/354bf29c4c.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/77573d75c2.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/3b5bbed5a5.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    It&amp;#39;s not super clear, but there&amp;#39;s actually a frisbee golf basket in this photo.  There were quite a few frisbee golfers out at this time.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/a2ac9d285e.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/c5794c7f04.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/6fdbf8b3fc.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/33f2b5d0a0.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    On the bank of the lake.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/631e2bb346.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Those white specks in the trees are actually corellas, a specie of parrot not unlike a cockatoo.  These flocks can get quite large and can cause quite a loud ruckus when they take flight.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/74c98f9588.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Bridge under Ginniderra Drive.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/e2bb366538.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    I did take a deviation to a substation that caught my eye (I get attracted to such things). Evening was closing in so the kangaroos were starting to appear.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/f5ff828147.jpg&#34;
       
        /&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/c5d1bdbd88.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    On the way back.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>My Evening</title>
      <link>https://lmika.org/2022/06/09/my-evening.html</link>
      <pubDate>Thu, 09 Jun 2022 21:36:25 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/06/09/my-evening.html</guid>
      <description>&lt;p&gt;So here’s how I spent my evening:&lt;/p&gt;
&lt;p&gt;Watching the WWDC state of the union until the DNS resolver konked out in the WiFi router, causing the Chromecast to get into a state in which it could no longer connect to the network, resulting in about 10 minutes of troubleshooting before deceiding to clean up, not go to the gym, spend another 10 minutes trying to troubleshoot the issue, then stared at my laptop for about half an hour wondering whether to go back to troubleshooting the Chromecast, or doing something else with the hope that it would eventually work itself out.&lt;/p&gt;
&lt;p&gt;Eventually, after another 5 minutes of fruitless troubleshooting, I finally got the Chromecast fixed by doing a factory reset and connected it to the 2.4 GHz band.&lt;/p&gt;
&lt;p&gt;Anyway, I hope your evening was more productive than mine.&lt;/p&gt;
&lt;p&gt;(And I was worried I would have nothing to write about today.)&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Powerline Track Walk</title>
      <link>https://lmika.org/2022/06/04/the-powerline-track.html</link>
      <pubDate>Sat, 04 Jun 2022 20:10:46 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/06/04/the-powerline-track.html</guid>
      <description>&lt;p&gt;Went on a walk of the Powerline Track, which I was personally calling the &amp;ldquo;powerline walk&amp;rdquo; (yes, I&amp;rsquo;m impressed at how close I was).  I saw this trail when I was in Canberra earlier this year, and knowing that I would be back, I made a note to actually walk it, which I did today.   This track follows the powerlines just south of Aranda Bushland Nature Reserve, then goes under Gungahlin Drive and into the Black Mountain Nature Reserve.  The weather was cold but pleasant, at least at the start of the track.  It eventually got quite dark and a little wet near the end, but that did result in some nice winter lighting over the landscape.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a gallery of some of the photos I took.  Note that there&amp;rsquo;s a fair few of powerlines, which is something I&amp;rsquo;ve been drawn to ever since I was a little kid.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/cb503f7312.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The start of the Powerline Track.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/cba552314f.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      At the gate looking back along the transmission lines.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/247ba825f4.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Atop of the first hill.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/3336fc8d37.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Looking back down the hill towards the road.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/eb8fb48a2a.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Turning to the right towards Black Mountain.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/46843e566a.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Close-up of the Telstra Tower atop Black Mountain.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/f39fcaf3a5.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Cresting the hill and going down the other end, approaching Gungahlin Drive from the west.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/6013a4eb5a.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Track going under Gungahlin Drive.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/8df589d64c.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Under the bridge.  Noticed the two styles of bridges here, with one I&amp;#39;m guessing being newer than the other, built in order to deal with more traffic.  Only question is, which one&amp;#39;s the original one?
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/1d4b7655eb.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      The track levelled out from here, and went through some lovely forest.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/e23b374e57.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Eventually made it back to the powerlines, and the turnaround point just ahead.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/10a0b9cd71.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      A wide shot looking over Gungahlin Drive and the Telstra Tower on the way back, now covered in low cloud.  It was here when the weather began to turn.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/60ad23cffd.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Atop the first hill once again.  The low clouds were really starting to roll in.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/037d7d28ad.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Looking out towards Mount Painter in the dying light.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img src=&#34;https://lmika.org/uploads/2022/cc25e1cb97.jpg&#34;
         
          /&gt;
    
      &lt;figcaption&gt;
      
      Finally, passed a family of kangaroos on my way back to the road, and the end of the walk.
      &lt;/figcaption&gt;
    
    &lt;/figure&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Humour In Conference Videos — Less Is More</title>
      <link>https://lmika.org/2022/05/29/humour-in-conference.html</link>
      <pubDate>Sun, 29 May 2022 12:23:54 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/05/29/humour-in-conference.html</guid>
      <description>&lt;p&gt;It might be just me but I get a little put off with over-the-top attempts at humour in developer conference videos.&lt;/p&gt;
&lt;p&gt;I’m four minutes into a conference video which has already included some slap-stick humour (with cheesy CGI), and someone trying to pitch to me on why what they’re talking about is worth listening to.  This was done in such a way that it actually distracted me from the content, a.k.a. the reason why I’m watching it.&lt;/p&gt;
&lt;p&gt;This sort of thing is really a turn-off, almost to the point where I feel like turning it off.  I also don’t think it helps you that much either.  If you open your talk by pretending to get zapped by a piece of lab equiptment, I’m probably not going to assume the same level of sincerity in your presentation as I would for someone who is just trying to get their message across.&lt;/p&gt;
&lt;p&gt;I like a joke as much as the next person, and one or two small, well contained jokes like substituted words in the slide pack is fine.  But it really needs to be dished out in small doses, and it really shouldn’t distract from the content.  Less (and much less than you think) is more in my opinion.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Cloud Formation &#34;ValidationError at typeNameList&#34; Errors</title>
      <link>https://lmika.org/2022/05/12/cloud-formation-validationerror.html</link>
      <pubDate>Thu, 12 May 2022 15:16:34 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/05/12/cloud-formation-validationerror.html</guid>
      <description>&lt;p&gt;I was editing some Cloud Formation today and when I tried to deploy it, I was getting this lengthy, unhelpful error message:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;An error occurred (ValidationError) when calling the CreateChangeSet operation: 1 validation error detected: Value &amp;lsquo;[AWS:SSM::Parameter, AWS::SNS::Topic]&amp;rsquo; at &amp;rsquo;typeNameList&amp;rsquo; failed to satisfy constraint: Member must satisfy constraint: [Member must have length less than or equal to 204, Member must have length greater than or equal to 10, Member must satisfy regular expression pattern: [A-Za-z0-9]{2,64}::[A-Za-z0-9]{2,64}::[A-Za-z0-9]{2,64}(::MODULE){0,1}]&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;It was only showing up when I was tried adding a new SSM parameter resource to the template, so I first thought it was some weird problem with the parameter name or value.  But after changing both to something that would work, I was still seeing this.&lt;/p&gt;
&lt;p&gt;Turns out the problem was that I was missing a colon in the resource type.  Instead of using &lt;code&gt;AWS::SSM::Parameter&lt;/code&gt;, I was using &lt;code&gt;AWS:SSM::Parameter&lt;/code&gt; (note the single colon just after &amp;ldquo;AWS&amp;rdquo;).   Just looking at the error message again, I notice that this was actually being hinted to me, both in the regular expression and the &amp;ldquo;Value&amp;rdquo; list.&lt;/p&gt;
&lt;p&gt;I know making good error messages take effort, and for most developers this tend to be an afterthought.  I&amp;rsquo;m just as guilty of this as anyone else.  But if I could make just one suggestion on how this message could be improved, it would be to get rid of the list in &amp;ldquo;Value&amp;rdquo; and replace it with the resource type that was actually failing validation.  It would still be a relatively unhelpful error message but at least it will indicate what part of the template was actually falling over.&lt;/p&gt;
&lt;p&gt;In any case, if anyone else is seeing an error message like this when you&amp;rsquo;re trying to roll out Cloud Formation changes, check for missing colons in your resource types.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>GitLab Search Subscriptions with NetNewsWire</title>
      <link>https://lmika.org/2022/04/29/im-working-on.html</link>
      <pubDate>Fri, 29 Apr 2022 12:47:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/04/29/im-working-on.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m working (with others) on a project that&amp;rsquo;s using GitLab to host the code, and I&amp;rsquo;m looking for a better way to be notified of new merge requests that I need to review.  I cannot rely on the emails from GitLab as they tend to be sent for every little thing that happens on any of the merge requests I am reviewing.  For this reason, any notifications sent by email will probably get missed by me.  People do post new merge requests in a shared Slack channel, but a majority of them are for repos that don&amp;rsquo;t need my review.  They&amp;rsquo;ve also been days where a lot of people are making a lot of changes at the same time, and any new messages for the repos I&amp;rsquo;m interesting would get pushed away.&lt;/p&gt;
&lt;p&gt;Today I learnt that it&amp;rsquo;s possible to subscribe to searches in GitLab using RSS.  So I&amp;rsquo;m trying something with NetNewsWire where I can subscribe to a search for open merge requests for the repos I&amp;rsquo;m interested in.  I assume the way this works is that any new merge requests would result in a new RSS item on this feed, which will show up as an update in NetNewsWire.  In theory, all I have to do is monitor NetNewsWire, and simply keep items unread until they&amp;rsquo;ve been merged or no longer need my attention.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll see this approach helps.  The only down side is that there&amp;rsquo;s no way to get updates for a single merge request as an RSS feed, which would have been nice.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>What Would Get Me Back to Using Twitter Again</title>
      <link>https://lmika.org/2022/04/26/what-would-get.html</link>
      <pubDate>Tue, 26 Apr 2022 09:21:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/04/26/what-would-get.html</guid>
      <description>&lt;p&gt;Congratulations, Elon Musk, on your purchase of Twitter.  I&amp;rsquo;m sure you&amp;rsquo;ve got a bunch of ideas of how you want to move the company forward.  I was once a user of Twitter myself — albeit not a massive one — and I&amp;rsquo;m sure you would just love to know what it would take for me to be a user once more.  Well, here&amp;rsquo;s some advice on how you can improve the platform in ways that would make me consider going back.&lt;/p&gt;
&lt;p&gt;First, you gotta work out the business model.  This is number one as it touches on all the product decisions made to date.  I think it&amp;rsquo;s clear that when it comes to Twitter, the advertising model is suboptimal.  It just don&amp;rsquo;t have the scale, and the insatiable need for engagement is arguable one of the key reasons behind the product decisions that fuel the anxiety and outrage on the platform.  I think the best thing you could do is drop ads completely and move to a different model.  I don&amp;rsquo;t care what that model is.  Subscription tiers; maybe a credit base system where you have a prepaid account and it costs you money to send Tweets based on their virality.  Heck, you can fund it from your personal wealth for the rest of your life if you want.  Just get rid of the ads.&lt;/p&gt;
&lt;p&gt;Next, make it easy to know which actions result in a broadcast of intent.  The big one I have in mind is unfollowing someone.  I use to follow people that I work with simply because I worked with them.  But after a while I found that what they were tweeting was anxiety inducing.  So I don&amp;rsquo;t want to follow them any more, but I don&amp;rsquo;t know what happens if I choose to unfollow them.  Do they get a notification?  They got one when I started following them — I know that because I got one when they started follow me.  So in lieu of any documentation (there might be documentation about this, I haven&amp;rsquo;t checked), I&amp;rsquo;d like to be able to stop following them without them being made aware of that fact.  Note that this is not the same as muting them or blocking them: they&amp;rsquo;re not being nasty or breaking any policies of what they post.  I just want to stop seeing what they post.&lt;/p&gt;
&lt;p&gt;Third, about open sourcing that algorithm.  By all means, do so if you think that would help, but I think that&amp;rsquo;s only half the moderation story.  The other half is removing all the attempts to drive up engagement, or at least having a way to turn them off.  Examples include making it easier to turn off the algorithmic timeline, getting rid of or hiding the &amp;ldquo;Trending Topics&amp;rdquo;, and no longer sticking news items in the notification section (seriously, adding this crap to the notification section has completely removed its utility to me).  If I want the results to simply be a reverse chronological timeline of tweets from people I&amp;rsquo;m following, and notifications only being events of people engaging with what I post, then please make it easy for me to have this.  This might means my usage may move from being less about quantity and more about quality, but remember that you no longer need all that engagement.  You changed the business model, remember?&lt;/p&gt;
&lt;p&gt;Finally, let&amp;rsquo;s talk about all the features that drum up engagement.  If it was up to me, I&amp;rsquo;d probably remove them completely, but I know that some people might find them useful, and it&amp;rsquo;s arguably a way for Twitter (now under your control) to, let&amp;rsquo;s say, &amp;ldquo;steer the direction of the conversation.&amp;rdquo;  So if you must, keep these discovery features, but isolate them to a specific area of the app, maybe called &amp;ldquo;Discovery&amp;rdquo;.  Put whatever you want in there — trending topics, promoted tweets, tweets made within a specific location, whatever you want — but keep them in that section, and only that section.  My timeline must be completely void of this if I choose it to be.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m sure there are others that I can think of, but I think all this is a good first step.  I look forward to taking this onboard, and I thank you for your consideration.  Honestly, it might not be enough for me to go back.  I wasn&amp;rsquo;t a big user before, and I&amp;rsquo;ve since moved to &lt;a href=&#34;https://micro.blog&#34;&gt;greener pastures&lt;/a&gt;.  But who knows, maybe it will.  In any case, I believe, with these changes, that Twitter as a platform would be more valuable, both with you at the helm, and with back there with my 10 or so followers and my posting rate of 25 tweets or so in the last eight years. 😉 &lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;This wink is doing a lot of work.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Showing A File At a Specific Git Revision</title>
      <link>https://lmika.org/2022/03/21/showing-a-file.html</link>
      <pubDate>Mon, 21 Mar 2022 10:00:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/03/21/showing-a-file.html</guid>
      <description>&lt;p&gt;To display the contents of a file at a given revision in Git, run the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git show &amp;lt;revision&amp;gt;:&amp;lt;filename&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, to view the version of &amp;ldquo;README.md” on the &lt;code&gt;dev&lt;/code&gt; branch:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git show dev:README.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is an alternative form of this command that will show the changes applied to that file as part of the commit:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git show &amp;lt;revision&amp;gt; -- &amp;lt;filename&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This can be used alongside the log command to work out what happened to a file that was deleted.&lt;/p&gt;
&lt;p&gt;First, view the history of the file.  You are interested in the ID before the commit that deleted the file: attempting to run &lt;code&gt;git show&lt;/code&gt; using the deletion commit ID it will result in nothing being shown.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git log -- file/that/was/deleted
commit abc123
Author: The Deleter &amp;lt;deleter@example.com&amp;gt;
Date:   XXX

    Deleted this file.  Ha ha ha!

commit beforeCommit
Author: File Changer &amp;lt;changer@example.com&amp;gt;
Date:   XXX

    Added a new file at file/that/was/deleted
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, use &lt;code&gt;git show&lt;/code&gt; to view the version before it was deleted:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git show beforeCommit:file/that/was/deleted
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Code Review Software Sucks. Here&#39;s How I Would Improve It</title>
      <link>https://lmika.org/2022/03/18/214200.html</link>
      <pubDate>Fri, 18 Mar 2022 21:42:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/03/18/214200.html</guid>
      <description>&lt;p&gt;This post is about code reviews, and the software that facilitates them.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll be honest: I&amp;rsquo;m not a huge fan of code reviews, so a lot of what I speak of below can probably be dismissed as that from someone who blames their tools. Be that as it may, I do think there is room for improvements in the tooling used to review code, and this post touches on a few additional features which would help.&lt;/p&gt;
&lt;p&gt;First I should say that I have no experience with dedicated code review tools.  I&amp;rsquo;m mainly talking about code review tools that are part of hosted source code repository systems, like GitHub, GitLab, and Bitbucket.  And since these are quite large and comprehensive systems, it might be that the priorities are different compared to a dedicated code review tool with a narrower focus.  Pull requests and code reviews are just one of the many tasks that these systems need to handle, along with browsing code repositories, managing CI/CD runs, hosting binary releases, etc.&lt;/p&gt;
&lt;p&gt;So I think it&amp;rsquo;s fair to say that such tools may not have the depth that a dedicated code review tool would have.  After all, GitHub Actions does not have the same level of sophistication as something like Jenkins or BuildKite, either.&lt;/p&gt;
&lt;p&gt;But even so, I&amp;rsquo;d say that there&amp;rsquo;s still room for improvement in the code review facilities that these systems do offer.  Improvements that could be tailored more to the code review workflow.  It&amp;rsquo;s a bit like using a text editor to manage your reminders.  Yeah, you can use a text editor, but most of the features related specifically to reminders will not be available to you, and you&amp;rsquo;ll have to plug the feature gap yourself.  Compare this to a dedicated reminder app, which would do a lot of the work for you, such as notifying you of upcoming reminders or having the means to marking an item as done.&lt;/p&gt;
&lt;p&gt;So, what should be improved in the software that is used to review code?  I can think of a few things:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Inbox:&lt;/strong&gt; When you think about it, code reviews are a bit like emails and Jira tickets: they come to you and require you to action them in some way in order to get the code merged.  But the level of attention you need to give them changes over time.  If you&amp;rsquo;ve made comments on a review, there&amp;rsquo;s really no need to look at it again until the code has been updated or those the author has replied.&lt;/p&gt;
&lt;p&gt;But this dynamic aspect of code reviews is not well reflected in most of these systems.  Usually what I see is simply a list of pull requests that have not yet been approved or merged, and I have to keep track of the reviews that need my attention now myself, verses those that I can probably wait for action from others.&lt;/p&gt;
&lt;p&gt;I think what would be better than a simple list would be something more of an inbox: a subset of the open reviews that are important to me now.  As I action them, they&amp;rsquo;ll drop off the list and won&amp;rsquo;t come back until I need to action them again.&lt;/p&gt;
&lt;p&gt;The types of reviews that I&amp;rsquo;d like to appear in the inbox, in the ordered listed below, would be the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ones that have been opened in which I&amp;rsquo;ve made comments that have been responded to — either by a code change or a reply — that I need to look at. The ones with more responses would appear higher in the list than the ones with less.&lt;/li&gt;
&lt;li&gt;Reviews that are brand new that I haven&amp;rsquo;t looked at yet, but others have.&lt;/li&gt;
&lt;li&gt;Brand new reviews that haven&amp;rsquo;t been looked at by anyone.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In fact, the list can be extended to filter out reviews that I don&amp;rsquo;t need to worry about, such as:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reviews that I&amp;rsquo;ve made comments on that have not been responded to yet. This indicates either that the author has not gotten around to it yet, in which case looking at the pull request again serves no purpose.&lt;/li&gt;
&lt;li&gt;Reviews that have enough approvals by others and do not necessarily need mine.&lt;/li&gt;
&lt;li&gt;Reviews that I&amp;rsquo;ve approved.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This doesn&amp;rsquo;t necessarily need to replace the list of open reviews: that might still be useful.  But it will no longer be the primary list of reviews I need to work with during the day to day.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Approval pending resolution of comments:&lt;/strong&gt; One thing I always find myself indecisive about is when I should hit that Approve button.  Let&amp;rsquo;s say I&amp;rsquo;ve gone through the code, made some comments that I&amp;rsquo;d like the submitter to look at, but the rest of the code looks good.  When should I approve the pull request?  If I do it now, then the author may not have seen the comments or have indication that I&amp;rsquo;d like them to make changes, and will go ahead and merge.&lt;/p&gt;
&lt;p&gt;I guess then the best time to approve it is when the changes are made.  But that means the onerous is on me to remember to review the changes again.  If the requests are trivial — such as renaming things — and I&amp;rsquo;d trust the person to make the changes and going through to review them once again is a waste of time.&lt;/p&gt;
&lt;p&gt;This is where “Approval pending resolution of comments” would come in handy.  Selecting this approval mode would mean that my approval would be granted once the author has resolve the outstanding review comments.  This would not replace the regular approval mode: if there are changes which do require a re-review, I&amp;rsquo;d just approve it normally once I&amp;rsquo;ve gone through it again.  But it&amp;rsquo;s one more way to let the workflow of code reviews work in my favour.&lt;/p&gt;
&lt;p&gt;Speaking of review comments…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Review comment types:&lt;/strong&gt;  I think it&amp;rsquo;s a mistake to assume that all review comments are equal.  Certainly in my experience I find myself unable to read the urgency of comments on the reviews I submit.  I also find it difficult to telegraph in the comments that I make on code reviews of others.  This usually results in longer comments with  phrases such as “you don&amp;rsquo;t have to do this now, but&amp;hellip;”, or “something to consider in the future&amp;hellip;”&lt;/p&gt;
&lt;p&gt;Some indication of the urgency of the comment alongside the comment itself would be nice.  I can think of a system that has at least three levels:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Request for change:&lt;/strong&gt; this is the highest level.  It&amp;rsquo;s an indication that you see something wrong with the code that must be changed.  In these cases, these comments would need to be resolved with either a change to the code, or a discussion of some sort, but they need to be resolved before the code is merged.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request for improvement:&lt;/strong&gt; This is a level lower, and indicates that there is something in the code that may need to be change, but not doing so would not block the code review.  This can be used to suggest improvements to how things were done, or maybe to suggest an alternative approach to solving the problem.  All those nitpicking comments can go here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Comments:&lt;/strong&gt; This is the lowest level.  It provides the way to make remarks about the code, but that require no further action from the author.  Uses for this might be praise for doing something a certain way, or FYI type comments that the submitter may need to be aware of for future changes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Notes to self:&lt;/strong&gt; Finally, one thing that is on way too few systems that deal with shared data is the ability to annotate pull requests or the commented files with private notes.  These won&amp;rsquo;t be seen by the author or any of the other reviewers, and are only there to facilitate making notes to self, such as things like “Looked at it, waiting for comments to be addressed”, or “review no longer pending”.  This is probably the minimum, and would be less important if the other things are not addressed.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s how I&amp;rsquo;d improve code review software.  It may be that I&amp;rsquo;m the only one with this problem, and that others are perfectively able to review code effectively without these features.  But I know they would work for me, and if I start seeing these in services like GitHub or GitLab, I probably would start using them.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Broadtail 0.0.7</title>
      <link>https://lmika.org/2022/03/15/broadtail.html</link>
      <pubDate>Tue, 15 Mar 2022 19:54:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/03/15/broadtail.html</guid>
      <description>&lt;p&gt;Released Broadtail 0.0.7 about a week ago.  This included some restyling of the job list on the home page, which now includes a progress bar updated using web-sockets (no need for page refreshes anymore).&lt;/p&gt;
&lt;p&gt;For the frontend, the Websocket APIs that come from the browser are used.  There’s not much to it — it’s managed by a &lt;a href=&#34;https://stimulus.hotwired.dev/&#34;&gt;Stimulus&lt;/a&gt; controller which sets up the websocket and listen for updates.  The updates are then pushed as custom events to the main &lt;code&gt;window&lt;/code&gt;, which the Stimulus controllers used to update the progress bar are listening out for.  This allows for a single Stimulus controller to manage the websocket connection and make use of the &lt;code&gt;window&lt;/code&gt; as a message bus.&lt;/p&gt;
&lt;p&gt;Working out the layers of the progress bar took me a bit of time, as I wanted to make sure the text in the progress bar itself was readable as the progress bar filled.  I settled in a HTML tree that looked like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&amp;quot;progressbar&amp;quot;&amp;gt;
  &amp;lt;!-- The filled in layer, at z-index: 10 --&amp;gt;
  &amp;lt;div class=&amp;quot;complete&amp;quot;&amp;gt;
    &amp;lt;span class=&amp;quot;label&amp;quot;&amp;gt;45% complete&amp;lt;/span&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;!-- The unfilled layer --&amp;gt;
  &amp;lt;span class=&amp;quot;label&amp;quot;&amp;gt;45% complete&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, there’s a base layer and a filled in layer that overlaps it.  Both of these layers have a progress label that contain the same status message.  As the .complete layer fills in, it will hide the unfilled layer and label.  The various CSS properties used to get this effect can be found &lt;a href=&#34;https://github.com/lmika/broadtail/blob/main/assets/css/progress.css&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The backend was a little easier.  There is a &lt;a href=&#34;https://github.com/gorilla/websocket&#34;&gt;nice websocket library&lt;/a&gt; for Go which handles the connection upgrades and provides a nice API for posting JSON messages.  Once the upgrade is complete, a goroutine servicing the connection will just start listening to status updates from the jobs manager and forward these messages as JSON text messages.&lt;/p&gt;
&lt;p&gt;Although this works, it’s not perfect.  One small issue is that updates will not reconnect if there is an error.  I imagine that it’s just a matter of listening out for the relevant events and retrying, but I’ll need to learn more about how this actually works.  Another thing is that the styling of the progress bar relies of fixed widths.  If I get around to reskinning the style of the entire application, that might be the time to address this.&lt;/p&gt;
&lt;p&gt;The second thing this release has is a simple integration with Plex.  If this integration is configured, Broadtail will now send a request to Plex to rescan the library for new files, meaning that there’s no real need to wait for the schedule rescan to occur before the videos are available in the app.  This simply uses Plex’s API, but it needs the Plex token, which can be &lt;a href=&#34;https://www.plexopedia.com/plex-media-server/general/plex-token/&#34;&gt;found using this method&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Anyway, that’s it for this version.  I’m working on re-engineering how favourites work for the next release.  Since this is still in early development, I won’t be putting in any logic to migrate the existing favourites, so just be weary that you may loose that data.  If that’s going to be a problem, feel free to let me know.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Learning Through Video</title>
      <link>https://lmika.org/2022/03/15/learning-through-video.html</link>
      <pubDate>Tue, 15 Mar 2022 09:40:23 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/03/15/learning-through-video.html</guid>
      <description>&lt;p&gt;Mike Crittenden wrote a post this morning about how he hates &lt;a href=&#34;https://critter.blog/2022/03/14/i-hate-learning-with-videos/&#34;&gt;learning through videos&lt;/a&gt;.  I know for myself that I occasionally do prefer videos for learning new things, but not always.&lt;/p&gt;
&lt;p&gt;Usually if I need to learn something, it would be some new technology that I have to know for my job.  In those cases, I find that if I have absolutely no experience in the subject matter, a good video which provides a decent overview of the major concepts helps me a great deal.  Trying to learn the same thing from reading a lengthy blog post, especially when jargon is used, is less effective for me.  I find myself getting tired and loosing my place.  Now, this could just be because of the writing — dry blocks of text are the worst, but I tend to do better if the posts are shorter and formulated more like a tutorial.&lt;/p&gt;
&lt;p&gt;If there is a video, I generally prefer them to be delivered in the style of a lecture or presentation.  Slides that I can look at while the presenter is speaking are fine, but motion graphics or a live demo is better, especially if the subject is complex enough to warrant them.  But in either case, I need something visual that I can actually watch.  Having someone simply talk to the camera really doesn&amp;rsquo;t work for me, and makes watching the video more of a hassle (although it&amp;rsquo;s slightly better if I just listen to the audio).&lt;/p&gt;
&lt;p&gt;Once I&amp;rsquo;ve become proficient in the basics, learning through video become less useful to me and a decent blog post or documentation page works better.  By that time, my learning needs become less about the basics and more about something specific, like how to do a particular thing or details of a particular item.  At that point, speed is more important to me, and I prefer to have something that I can skim and search in my own time, rather than watch videos that tend to take much longer.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s how and when I prefer to learn something from video.  I&amp;rsquo;ll close by saying that this is my preferred approach when I need to learn something for work.  If it&amp;rsquo;s during my downtime, either a video or blog-post is fine, so long as my curiosity is satisfied.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some More Updates of Broadtail</title>
      <link>https://lmika.org/2022/03/05/some-more-updates.html</link>
      <pubDate>Sat, 05 Mar 2022 19:49:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/03/05/some-more-updates.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve made some more changes to Broadtail over the last couple of weeks.&lt;/p&gt;
&lt;p&gt;The home page now shows a list of recently published videos below the currently running jobs.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/a191cab735.jpg&#34; width=&#34;600&#34; height=&#34;460&#34; alt=&#34;&#34; /&gt;
&lt;p&gt;Clicking through to “Show All” displays all the published videos.  A simple filter can be applied to filter them down to videos with titles containing the keywords (note: nothing fancy with the filter, just tokenisation and an OR query).&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/21c680453b.jpg&#34; width=&#34;600&#34; height=&#34;460&#34; alt=&#34;&#34; /&gt;
&lt;p&gt;Finally, items can now be favourited.  This can be used to select videos that you may want to download in the future.  I personally use this to keep the list of “new videos” in the Plex server these videos go to to a minimum.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/6fa1a51a80.jpg&#34; width=&#34;600&#34; height=&#34;453&#34; alt=&#34;&#34; /&gt;
</description>
    </item>
    
    <item>
      <title>Time and Money</title>
      <link>https://lmika.org/2022/03/02/time-and-money.html</link>
      <pubDate>Wed, 02 Mar 2022 09:36:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/03/02/time-and-money.html</guid>
      <description>&lt;p&gt;Spending a lot of time in Stripe recently.  It&amp;rsquo;s a fantastic payment gateway and a pleasure to use, compared to something like PayPal which really does show its age.&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s so stressful and confusing dealing with money and subscriptions.  The biggest uncertainty is dealing with anything that takes time.  The problem I&amp;rsquo;m facing now is if the customer chooses to buy something like a database, which is billed a flat fee every month, and then they choose to buy another database during the billing period, can I track that with a single subscription and simply adjust the quantity amount?  My current research suggests that I can, and that Stripe will handle the prorating of partial payments and credits.  They even have a nice API to &lt;a href=&#34;https://stripe.com/docs/billing/subscriptions/prorations#preview-proration&#34;&gt;preview the next invoice&lt;/a&gt; which can be used to show the customer how much they will be paying for.&lt;/p&gt;
&lt;p&gt;But despite all the documentation, test environments, and simulations, I still can&amp;rsquo;t be sure that it will happen in real life, when real money is exchanged in real time.  I guess some real life testing would be required. 💸&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Cling Wrap</title>
      <link>https://lmika.org/2022/02/21/i-bought-this.html</link>
      <pubDate>Mon, 21 Feb 2022 11:58:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/02/21/i-bought-this.html</guid>
      <description>&lt;p&gt;I bought this roll of cling wrap when I moved into my current place.  Now, after 6.5 years and 150 metres, it’s finally all used up.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2022/0061f90163.jpg&#34; width=&#34;600&#34; height=&#34;600&#34; alt=&#34;Cling wrap, now empty&#34; /&gt;
&lt;p&gt;In the grand scheme of things, this is pretty unimportant.  It happens every day: people buy something, they use it, and eventually it&amp;rsquo;s all used up.  Why spend the time and energy writing and publishing this post to discuss it?  Don’t you have better things to do?&lt;/p&gt;
&lt;p&gt;And yet, there’s still a feeling of weight to this particular event that I felt was worth documenting.  Perhaps it’s because it was the first roll of cling wrap I bought after I moved out.  Or maybe it’s because it lasted for this long, so long in fact that the roll I bought to replace it was sitting in my cupboard for over a year.  Or maybe it’s the realisation that with my current age and consumption patterns, I probably wouldn’t use up more than 7 rolls like this in my lifetime.&lt;/p&gt;
&lt;p&gt;Who knows?  All I know is that despite the banality of the whole affair, I spent just spent the better part of 20 minutes trying to work out how best to talk about it here.&lt;/p&gt;
&lt;p&gt;I guess I&amp;rsquo;m in a bit of a reflective mood today.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Trip to Ballarat and the Beer Festival</title>
      <link>https://lmika.org/2022/02/20/scenes-of-ballarat.html</link>
      <pubDate>Sun, 20 Feb 2022 08:45:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/02/20/scenes-of-ballarat.html</guid>
      <description>&lt;p&gt;I had the opportunity to go to Ballarat yesterday to attend the beer festival with a couple of mates.  It&amp;rsquo;s been a while since I last travelled to Ballarat — I think the last time was when I was a kid.  It was also the first time I took the train up there.  I wanted to travel the Ballarat line for a while but I never had a real reason to do so.&lt;/p&gt;
&lt;p&gt;The festival started at noon but I thought I&amp;rsquo;d travel up there earlier to look around the city for a while.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/669a161e10.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Photo of Lyidard St., just outside the railway station.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/9fd86f670c.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Photo of Stirt St. and what I&amp;#39;m assuming was the town hall, now an information centre.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/61847a0e18.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Corner of Stirt St. and Lyidard St. looking at the hotel.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/4c182bb14f.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    The anglican church.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/146454e7e4.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Looking east down Stirt St. and the landscape just outside of town.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/00e744d228.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Another church.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;I didn&amp;rsquo;t stay long in the city centre as I needed to take the train to Wendouree, where the festival was located.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/be68f94fc2.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Ballarat railway station.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/a84b4219ae.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Under the canopy looking downline towards Melbourne.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/e9314b85eb.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Looking upline towards Wendouree and Ararat.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/0410519ae8.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Approaching train to Wendouree.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;The beer festival itself was at Wendouree park.  Layout of the place was good: vendors (breweries, food, etc.) was laid out along the perimeter, and general seating was available in the middle.  They did really well with the seating.  There were more than enough tables and chairs for everyone there.&lt;/p&gt;
&lt;p&gt;Day was spectacular, if a bit sunny: the tables and chairs in the shade were prime real-estate.  Whole atmosphere was pleasant: everyone was just out to have a nice time.  Got pretty crowded as the day wore on.  Lots of people with dogs, and a few families as well.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not a massive beer connoisseur so I won&amp;rsquo;t talk much about the beers.  Honestly, the trip for me was more of a chance to get out of the city and catch up with mates.  But I did tried a pear cider for the first time, which was a little on the sweet side, which I guess was to be expected.  I also had a Peach Melba inspired pale ale that was actually kind of nice.&lt;/p&gt;
&lt;div class=&#34;img-gallery&#34;&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/fd2bf9432d.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    The festival in Wendouree park, under the shade of the Giant Sequoias.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/051fe3f5f3.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Many local independent breweries from around the state, and also a couple from other states.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/4b7614bbd7.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Some reasonably good food, including the classic sausage sizzle c/o the Rotary Club.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/224500a61d.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Heaps of seating, although the tables and chairs under the shade went fast.
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
  &lt;figure&gt;
  &lt;img src=&#34;https://lmika.org/uploads/2022/d57698a508.jpg&#34;
       
        /&gt;
  
    &lt;figcaption&gt;
    
    Sure thing, coach (honestly, I don&amp;#39;t know why I was attracted to this sign).
    &lt;/figcaption&gt;
  
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Trip home was a bit of an adventure.  A train was waiting at Wendouree station when I got there.  There was nobody around and it was about 5 minutes until departure so I figured I’d board.  Turns out it was actually not taking passengers.  I was the only one that boarded, and when I actually realised that it was not in service, the doors closed and the train departed.  I had to make my presence known to the driver and one other V/Line worker.  They were really nice about it, and fortunately for me, they were on their way to Ballarat anyway, so it wasn’t a major issue.  Even so, it was quite embarrassing.  Fortunately the train home was easy enough.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>OS Vendors and Online Accounts</title>
      <link>https://lmika.org/2022/02/18/os-vendors-and.html</link>
      <pubDate>Fri, 18 Feb 2022 06:14:19 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/02/18/os-vendors-and.html</guid>
      <description>&lt;p&gt;Looks like the next version of Windows will &lt;a href=&#34;https://arstechnica.com/gadgets/2022/02/new-preview-build-adds-microsoft-account-requirement-to-windows-11-pro/&#34;&gt;require an online account&lt;/a&gt;, and while the reason for this could be something else, I’m guessing this would be used to enable file sync, mail account sync, calendar sync, etc.&lt;/p&gt;
&lt;p&gt;I think it’s a mistake for OS vendors to assume that people would want to share their sole online identity across different devices.  Say that I had a work computer and a home computer, and I’d use the same online account for both.  Do I really want my personal files and work files being synced across, or my scheduled meetings to start showing up in my personal calendar?&lt;/p&gt;
&lt;p&gt;I guess the response would be to create two online accounts: one for work and one for home.  This might be possible: I don’t know how difficult it would be to create multiple Microsoft accounts for the same person.  But if I do this&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, and there’s software that I’ve purchased with my home account that I’d like to use on my work device, I’d have to repurchase it.  I guess if I’m employed full time it should be work purchasing software, but come on, am I really going to go through the whole precurement buracracy to buy something like a $29 image editor?&lt;/p&gt;
&lt;p&gt;This could be all theoretical: might be that this wouldn’t be a problem for Windows users.  But I know from my limited experience with using MacOS that issues based on the assumption that &lt;em&gt;everything associated with an online account should be shared on every device&lt;/em&gt; can crop up.  That’s why I don’t open Mail.app on my home computer.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;This is all hypothetical.  I’m not a Windows user.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>My YouTube Watching Setup</title>
      <link>https://lmika.org/2022/02/15/my-youtube-watching.html</link>
      <pubDate>Tue, 15 Feb 2022 13:46:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/02/15/my-youtube-watching.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m not a sophisticated YouTube watcher but I do watch a lot of YouTube. For a while I was happy enough to simply use the YouTube app with a Chromecast.  Yes there were ads, but the experience was nice enough that I tolerated them.&lt;/p&gt;
&lt;p&gt;Recently, however, this became untenable.&lt;/p&gt;
&lt;p&gt;It started with Google deciding to replace their simple Chromecast target with a Google TV style app, complete with a list of video recommendations I had no interest in watching.  This redesign also came with more ads, which themselves would be annoying enough.  But with this year being an election year, I started seeing campaign ads from a political party I have absolutely zero interest in seeing ads from.  Naturally Google being Google, there was no way for me to block them&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.  I guess I could have just paid to remove the ads, but this wouldn&amp;rsquo;t solve the Chromecast problem.  Besides, the feeling of paying for something that is arguably not a great use of my time felt wrong.  I felt that a bit of friction in my YouTube watching habits wouldn&amp;rsquo;t be a bad thing to introduce.&lt;/p&gt;
&lt;p&gt;It was time to consider an alternative setup.&lt;/p&gt;
&lt;h2 id=&#34;plex&#34;&gt;Plex&lt;/h2&gt;
&lt;p&gt;Taking inspiration from those on &lt;a href=&#34;https://burk.io/2020/my-youtube-dl-setup&#34;&gt;Micro.blog&lt;/a&gt; and &lt;a href=&#34;https://www.caseyliss.com/2015/4/21/plex&#34;&gt;certain podcasters&lt;/a&gt;, I decided to give Plex a go.  I had an &lt;a href=&#34;https://www.intel.com.au/content/www/au/en/products/details/nuc/mini-pcs.html&#34;&gt;Intel Nuc&lt;/a&gt; that I purchased a few years ago that I wasn&amp;rsquo;t using and it seemed like a good enough machine for a Plex server.  The Nuc is decent enough, but it&amp;rsquo;s a little loud and I didn&amp;rsquo;t want it anywhere where I usually spend my time.  It&amp;rsquo;s currently in a wardrobe in my spare bedroom.&lt;/p&gt;
&lt;p&gt;After upgrading it to Ubuntu 20.04 LTS, I installed the &lt;a href=&#34;https://www.plex.tv/media-server-downloads/#plex-media-server&#34;&gt;Plex Media Server&lt;/a&gt;.  I had to create a Plex account, which was a little annoying, but after doing so, I was able to setup a new library for YouTube videos relatively easily.  I configured the library to poll every hour, which would come in handy for the next part of this setup.&lt;/p&gt;
&lt;p&gt;I also installed the Plex app on my Android phone to act as the media player.  The app has support for Chromecast, which is my preferred setup.  Getting the app to talk with the media server was a little fiddly.  I can&amp;rsquo;t remember all the details as it was a couple of months ago, but I do remember it taking several times before the app was listing videos in the library.  But once the link was established, it because quite easy to play downloaded videos on my TV.  I&amp;rsquo;ll have more to say about the app near the end of the post.&lt;/p&gt;
&lt;h2 id=&#34;youtube-dl-and-broadtail&#34;&gt;Youtube-dl And Broadtail&lt;/h2&gt;
&lt;p&gt;Once Plex was setup, I needed a way to download the YouTube videos.  I was hoping to use &lt;a href=&#34;https://github.com/ytdl-org/youtube-dl&#34;&gt;youtube-dl&lt;/a&gt;, but the idea of SSH&amp;rsquo;ing into the media server to do so was unappealing.  I was also aware that it was possible to subscribe to YouTube channels via RSS, which is my preferred way to be notified of new content.  I&amp;rsquo;m tend not to subscribe to channels within YouTube itself as I rather Google didn&amp;rsquo;t know too much about my viewing preferences (sorry YouTubers).&lt;/p&gt;
&lt;p&gt;I figured having a small web-app which will run alongside Plex that would allow me to subscribe to YouTube RSS feeds, and download the videos using youtube-dl to the Plex library, would be ideal.  I&amp;rsquo;m sure that such applications already exist, but I decided to build my own.&lt;/p&gt;
&lt;p&gt;So I built a small Go web-app to do this.  I called it Broadtail, mainly because I&amp;rsquo;m using &lt;a href=&#34;https://en.wikipedia.org/wiki/Broad-tailed_parrot&#34;&gt;bird-related terms&lt;/a&gt; for working project names and I couldn&amp;rsquo;t think of anything better.  It&amp;rsquo;s pretty basic, and it is ugly as sin, but it does the job.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2022/5dab2e5a48.png&#34; alt=&#34;List of videos from a YouTube RSS feed in Broadtail&#34; /&gt;
&lt;p&gt;I can setup an RSS subscription to YouTube channels and playlists, which it will periodically poll and store in a small embedded database.  I can get a list of videos for each feed I&amp;rsquo;ve subscribed to and if it looks interesting, I can start a download from the UI.  The app will run the appropriate youtube-dl incantation and provide a running status update, with some really basic job controls.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2022/651b72232b.png&#34; alt=&#34;Video details in Broadtail&#34; /&gt;
&lt;p&gt;The downloaded videos are saved as MP4s in a directory configured as a Plex library.  The one hour scan will pick them up, although I occasionally need to trigger a rescan manually if the video was downloaded relatively recently.  During the day, I look for any new videos which look interesting, and start downloads in Broadtail.  The videos would (usually) be ready and available in Plex by evening.  The only exception are videos which are 3 to 4 hours long, which usually take around a day to download thanks to YouTube&amp;rsquo;s throttling.&lt;/p&gt;
&lt;h2 id=&#34;how-its-working-out&#34;&gt;How It&amp;rsquo;s Working Out&lt;/h2&gt;
&lt;p&gt;Putting this together took roughly a month or so, and I&amp;rsquo;ve been using it for my YouTube viewing for a couple of months now.  In general, it&amp;rsquo;s working OK.  The Plex media server is working quite well, as is the Plex mobile app.  Broadtail is pretty bare bones but I&amp;rsquo;ve been slowly making changes to it over time as my needs evolve.&lt;/p&gt;
&lt;p&gt;There are a few annoyances though.  One large one is that the Plex app for Android is a little buggy.  It gets into a state in which it is unable to start playback of a video, and the only way I know of fixing this is by rebooting the Chromecast device.  This is really annoying and it&amp;rsquo;s gotten to the point when I&amp;rsquo;m doing this almost daily.  I contemplated actually setting the Chromecast up on a smart plug so that I can force a restart simply by killing power to it in the middle of the night.  It hasn&amp;rsquo;t quite gotten to the point where I&amp;rsquo;ve done this, but if Plex doesn&amp;rsquo;t fix their app soon, I think I may go ahead with this.&lt;/p&gt;
&lt;p&gt;Also annoying is that sometimes the Plex app will loose connection with the media server, and will not list the contents of my library.  Fortunately a restart of the mobile app is enough to resolve this.&lt;/p&gt;
&lt;p&gt;As for the Intel Nuc itself, there have been instances when it seems to lock up, and I had to hard power it down.  I don&amp;rsquo;t know what&amp;rsquo;s causing this.  It could be that either Plex or Broadtail is causing a kernel panic of sorts, or it could be something in the the Nuc itself: it&amp;rsquo;s reasonably low cost hardware that is tailored more for Windows.  I may eventually replace the Nuc with the Mac Mini I&amp;rsquo;m currently using as a desktop, once it&amp;rsquo;s time to upgrade.&lt;/p&gt;
&lt;p&gt;But all in all, I think this is working for me.  Not seeing any ads or crappy recommendations is a major win, and it&amp;rsquo;s also nice to actually run out of things to watch, forcing me to do something productive.  Sometimes question whether the time it took to set this all up was worth it.  Maybe, maybe not.  But it feels a little better having something a little more in my control, than simply paying YouTube to remove the ads.&lt;/p&gt;
&lt;p&gt;Finally, if Broadtail sounds interesting to you, it&amp;rsquo;s &lt;a href=&#34;https://github.com/lmika/broadtail&#34;&gt;available on GitHub&lt;/a&gt;.  I&amp;rsquo;ve only recently open-sourced it, so there&amp;rsquo;s a lot of missing things like decent documentation (it only got a README today).  So please consider it in a bit of a &amp;ldquo;here be dragons&amp;rdquo; state at the moment.  But if you have any questions, feel free to contact me.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Hey Google, having a way to indicate zero interest in seeing ads from someone is signal of intent.  Consider making this option available to us and you get more info for your user profiles.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Reminder That Your Content Isn&#39;t Really Yours on Medium #3</title>
      <link>https://lmika.org/2022/02/11/reminder-that-your.html</link>
      <pubDate>Fri, 11 Feb 2022 15:23:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/02/11/reminder-that-your.html</guid>
      <description>&lt;p&gt;Looks like Medium has had a redesign recently, with recommended posts now being featured more prominently.  Instead of appearing at the end of the post, they&amp;rsquo;re now in a right-hand sidebar, which doesn&amp;rsquo;t scroll, that is directly below the author of the post you&amp;rsquo;re reading.&lt;/p&gt;
&lt;p&gt;And let me be clear: as far as I can tell, these are not recommendations from the same author.  They can be from anyone, covering any topic that I can only assume Medium algorithmically thinks you&amp;rsquo;d be interested in.  It reminds me a lot of the anxiety supplier that is Twitter Trending Topics.&lt;/p&gt;
&lt;p&gt;Thank goodness.  Here I was, reading someone&amp;rsquo;s post on UI design, without being made aware of, or being constantly reminded of whenever I move my eyes slightly to the right, of another post by a different author informing me that NFTs have been superseded by &amp;ldquo;Super NFTs&amp;rdquo;.  Thank you for that, Medium.  My reading experience has been dramatically improved! (Sarcasm test complete)&lt;/p&gt;
&lt;p&gt;Honestly, I&amp;rsquo;m still wondering why people choose to use Medium for publishing long-form writing.  And yes, I acknowledge that it could be worse: their &amp;ldquo;post&amp;rdquo; could just as easily been a Twitter thread&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.  But from this latest redesign, it seems to me that Medium is doing it&amp;rsquo;s best to close the reading experience gap between the two services.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Please don&amp;rsquo;t publish your long form writing as a Twitter thread.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>The &#34;Too Much Data&#34; Error in Buffalo Projects</title>
      <link>https://lmika.org/2022/02/02/too-much-data.html</link>
      <pubDate>Wed, 02 Feb 2022 20:56:57 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/02/02/too-much-data.html</guid>
      <description>&lt;p&gt;If there&amp;rsquo;s anyone else out there using &lt;a href=&#34;https://gobuffalo.io/en/&#34;&gt;Buffalo&lt;/a&gt; to build web-apps, I just discovered that it doesn&amp;rsquo;t clean up old versions of bundled JavaScript files.  This means that the &lt;code&gt;public/asset&lt;/code&gt; directory can grow to gigabytes in size, eventually reaching the point where Go will simply refuse to embed that much data.&lt;/p&gt;
&lt;p&gt;The tell-tail sign is this error message when you try to run the application:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;too much data in section SDWARFSECT (over 2e+09 bytes)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you see that, deleting &lt;code&gt;public/assets&lt;/code&gt; should solve your problem.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Posting Daily</title>
      <link>https://lmika.org/2022/01/26/on-posting-daily.html</link>
      <pubDate>Wed, 26 Jan 2022 10:23:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/01/26/on-posting-daily.html</guid>
      <description>&lt;p&gt;I recently listen to an interview with &lt;a href=&#34;https://pca.st/l2TH#t=32m48s&#34;&gt;Seth Godin on the Tim Ferris podcast&lt;/a&gt;.  In that interview, Seth mentions that he writes up to five blog posts a day.  He just doesn&amp;rsquo;t publish them all.  I guess that means that he has at least one or two drafts that can be touched up and published when he needs them.&lt;/p&gt;
&lt;p&gt;Although I don&amp;rsquo;t think of this blog as being anywhere near the quality of Seths, I think I&amp;rsquo;d like to start trying to publish on this site at least once a day.  I don&amp;rsquo;t post to any specific schedule here, and there have been stretches of days in which this blog has not seen an update at all.  But over the last week, I&amp;rsquo;ve found myself falling into a streak, and I like to see how long I can maintain it.&lt;/p&gt;
&lt;p&gt;The thing that has thwarted me in the past (apart from not even thinking about it) was either not being in the right frame of mind or not being available that day to post something.  I&amp;rsquo;m not sure this blog warrants the discipline to set a specific time each day to sit down and write something.  I treat this blog more or less like a public journal; a place to document thoughts, opinions or events of the day.&lt;/p&gt;
&lt;p&gt;But I&amp;rsquo;m wondering if maintaining an inventory of unpublished drafts might help in maintaining this streak.  So even though the goal is to write and publish a post on the same day, having something to fall back on when I can&amp;rsquo;t might be worthwhile.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Future of Computing</title>
      <link>https://lmika.org/2022/01/21/the-future-of.html</link>
      <pubDate>Fri, 21 Jan 2022 10:22:59 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/01/21/the-future-of.html</guid>
      <description>&lt;p&gt;I got into computers when I was quite young, and to satisfy my interest, I read a lot of books about computing during my primary school years.  I remember one such book that included a discussion about how computing could evolve in the future.&lt;/p&gt;
&lt;p&gt;The book approached the topic using a narrative of a &amp;ldquo;future&amp;rdquo; scenario, that would probably correspond with today&amp;rsquo;s present.  In that story, the protagonist was late for school because of a fault with the  &amp;ldquo;home computer&amp;rdquo; regarding the setting of the thermostat or something similar.  Upon arriving home from school, he interacted with the computer by speaking to it as if he was talking to another person, expressing his anger of the events that morning by speaking in full, natural-language sentences.  The computer responded in kind.&lt;/p&gt;
&lt;p&gt;This book was published at a time when most personal computing involved typing in BASIC programs, so you could imagine that a bit of creative license was taken in the discussion.  But I remember reading this and being quite ambivalent about this prospective future.  I could not imagine the idea of central computers being installed in houses and controlling all aspects of their environment.  Furthermore, I balked at the idea of people choosing to interact with these computers using natural language.  I&amp;rsquo;m not much of a people person so the idea of speaking to computers as if it was another person, and having to deal with the computer speaking back, was not attractive to me.&lt;/p&gt;
&lt;p&gt;Such is the feeling I have now with the idea of anyone wanting to put on AR and VR headsets.  This seems to be the current focus of tech companies like Apple and Google, trying to find the successor to the smartphone.  And although nothing from these companies have been announced yet, and these technologies have yet to escape the niche of gaming, I still cannot see a future in which people walk around with these headsets outside in public.  Maybe with AR if they can do so in a device that looks like a pair of regular-looking glasses, but VR?  No way.&lt;/p&gt;
&lt;p&gt;But as soon as I reflected on those feelings, that book I read all those years ago came back to me.  As you can probably guess, the future predicted in that story has more-or-less become reality, with the rise of the cloud, home automation, and smart speakers like the Amazon Echo.  And more than that, people are using these systems and liking it, or at least putting up with it.&lt;/p&gt;
&lt;p&gt;So might the same thing happen with AR and VR headsets.  I should probably stay out of the future predicting business.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>PGBC Scoring Rules</title>
      <link>https://lmika.org/2022/01/13/pgbc-scoring-rules.html</link>
      <pubDate>Thu, 13 Jan 2022 22:55:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/01/13/pgbc-scoring-rules.html</guid>
      <description>&lt;p&gt;I get a bit of a thrill when there&amp;rsquo;s a need to design a mini-language.  I have one facing me now for a little project I&amp;rsquo;m responsible for, which is maintaining a scoring site for a bocce comp I&amp;rsquo;m involve in with friends.&lt;/p&gt;
&lt;p&gt;How scoring works now is that the winner of a particular bocce match gets one point for the season.  The winner for the season is the person with the most points.  However, we recently discuss the idea of adding &amp;ldquo;final matches,&amp;rdquo; which will give the match winner 7 points, the runner up 2 points, and the person who came in third 1 point.  At the same time I want to add the notion of &amp;ldquo;friendly matches&amp;rdquo; which won&amp;rsquo;t count to the season score.&lt;/p&gt;
&lt;p&gt;It might have been that a simple solution was to encode these rules directly in the app, and have a flag indicating whether a match was normal, final or friendly.  But this was suboptimal as there is another variant of the game we play which do not have the notion of finals, and if we did, we may eventually have different rule for it.  So I opted for a design in which a new &amp;ldquo;match type&amp;rdquo; is added as a new database entity, which will have the scoring rules encoded as a &lt;a href=&#34;https://www.postgresql.org/docs/9.3/datatype-json.html&#34;&gt;PostgreSQL JSON column type&lt;/a&gt;.  Using this as a mechanism of encoding free(ish) structured data when there&amp;rsquo;s no need to query it has worked for me in the past.  There was no need to add the notion of seasons points as it was already present as an easy way to keep track of wins for a season.&lt;/p&gt;
&lt;p&gt;For the scoring rules JSON structure, I&amp;rsquo;m considering the use of an array of conditions.  When a player meets the conditions of a particular array element, they will be awarded the points associated with that condition.  Each player will only be permitted to match one condition, and if they don&amp;rsquo;t match any, they won&amp;rsquo;t get any points.  The fields of the condition that a player can be matched to can be made up of the following attributes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;rank:&lt;/strong&gt; (int) the position the player has in the match just played in accordance with the scoring, with &lt;code&gt;1&lt;/code&gt; being the player with the highest score, &lt;code&gt;2&lt;/code&gt; being the player with the second highest score, and so on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;winner:&lt;/strong&gt; (bool) whether the player is considered the winner of the match.  The person with the highest score usually is, but this is treated as an independent field and so it should be possible to define rules accordingly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;draw:&lt;/strong&gt; (bool) whether the player shares their rank with another player.  When a draw occurs, both winning players will have a rank of &lt;code&gt;1&lt;/code&gt;, with the player of the second highest score having a rank of &lt;code&gt;2&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using this structure, a possible scoring rules definition for a normal match may look like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{ &amp;quot;season_score&amp;quot;: [
  { &amp;quot;condition&amp;quot;: { &amp;quot;winner&amp;quot;: true }, &amp;quot;points&amp;quot;: 1 }
]}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;whereas a rules definition for the final match may look like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{ &amp;quot;season_score&amp;quot;: [
  { &amp;quot;condition&amp;quot;: { &amp;quot;rank&amp;quot;: 1 }, &amp;quot;points&amp;quot;: 7 },
  { &amp;quot;condition&amp;quot;: { &amp;quot;rank&amp;quot;: 2 }, &amp;quot;points&amp;quot;: 2 },
  { &amp;quot;condition&amp;quot;: { &amp;quot;rank&amp;quot;: 3 }, &amp;quot;points&amp;quot;: 1 }
}]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, for friendlies, the rules can simply look like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{ &amp;quot;season_score&amp;quot;: [] }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I think this provides a great deal of flexibility and extensibility without making the rules definition too complicated.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On the Moxie Marlinspike Post About web3</title>
      <link>https://lmika.org/2022/01/12/on-the-moxie.html</link>
      <pubDate>Wed, 12 Jan 2022 14:05:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/01/12/on-the-moxie.html</guid>
      <description>&lt;p&gt;Today, I took a look at the &lt;a href=&#34;https://moxie.org/2022/01/07/web3-first-impressions.html&#34;&gt;Moxie Marlinspike post&lt;/a&gt; about web3&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.  I found this post interesting for a variety of reasons, not least because unlike many other posts on the subject, it was a post that was level-headed and was coming from a position of want to learn more rather than persuade (or hustle).  Well worth the read, especially for those that are turned off by the whole web3 crap like I am.&lt;/p&gt;
&lt;p&gt;Anyway, there were a few things from the post that I found amusing.  The first, and by far the most shocking, was that the &amp;ldquo;object&amp;rdquo; of a NFT is not derived from the actual item in question, like the artwork image, or the music audio, etc.  It&amp;rsquo;s essentially just a URL.  And not even a URL with an associated hash.  Just a plain old URL, as in “&lt;a href=&#34;http://example.com/&#34;&gt;example.com&lt;/a&gt;”, which points to a resources on the internet that can be change or removed at any time.  Not really conducive to the idea of digital ownership if the thing that you &amp;ldquo;own&amp;rdquo; is just something that points to something else that you don&amp;rsquo;t actually control.&lt;/p&gt;
&lt;p&gt;Also amusing was the revelation that for a majority of these so-called &amp;ldquo;distributed apps&amp;rdquo;, the &amp;ldquo;distribution&amp;rdquo; part is a bit of a misnomer.  They might be using a blockchain to handle state, but many of the apps themselves are doing so by calling regular API services.  They don&amp;rsquo;t build their own blockchain or even run a node on an existing blockchain, which is what I assumed they were doing.  I can achieve the same thing without a blockchain if I make the database I use for my apps public and publish the API keys (yes, I&amp;rsquo;m being facetious).&lt;/p&gt;
&lt;p&gt;The final thing I found amusing was that many of these platforms are actually building features into the platform that are not even using the blockchain at all.  Moxie made the excellent point that the speed to which a protocol evolves, especially ones that are distributed by design, is usually very slow.  Likely too slow if you&amp;rsquo;re trying to add features to a platform in an attempt to make it attractive to users.  So services like OpenSeas are sometimes bypassing the blockchain altogether, and just adding propriety features which are backed by regular data stores like Firebase.  Seems to me this is undermining the very idea of web3 itself.&lt;/p&gt;
&lt;p&gt;So given these three revelations, what can we conclude from all the rhetoric of web3 that&amp;rsquo;s currently out there?  That, I&amp;rsquo;ll leave up to you.  I have my own opinions which I hope comes through from the tone of this post.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll close by saying that I think the most insightful thing I learnt from the post had nothing to do with web3 at all.  It was the point that the reason why Web 2 came about was that people didn&amp;rsquo;t want to run their own servers, and never will.  This is actually quite obvious now when I think about it.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Ben Thompson &lt;a href=&#34;https://stratechery.com/2022/npm-sabotage-convenience-matters-moxie-marlinspike-on-web3&#34;&gt;wrote a teriffic post&lt;/a&gt; about it as well.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Burnt Out on Design</title>
      <link>https://lmika.org/2022/01/12/burned-out-on.html</link>
      <pubDate>Wed, 12 Jan 2022 08:46:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2022/01/12/burned-out-on.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been doing a heap of design work at my job at the moment; writing documents, drawing up architecture diagrams, etc.  I&amp;rsquo;d thought I would like this sort of work but I realise now that I can only tolerate it in small doses.  Doing it for as long as I have been is burning me out slightly.  I&amp;rsquo;d just like to go back to coding.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m wondering why this is.  I think the biggest feeling I have is that it feels like I&amp;rsquo;m not delivering value.  I understand the need to get some sort of design up so that tasks can be written up and allocated.  I think a big problem is the feeling that everything needs to be in the design upfront, waterfall style, whereas the method I&amp;rsquo;d prefer is to have a basic design upfront — something that we can start work on — which can be iterated and augment over time.&lt;/p&gt;
&lt;p&gt;I guess my preference with having something built vs. something perfect on paper differs from those that I work with.  Given my current employer, which specialise more in hardware design, I can understand that line of thinking.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also guessing that software architecture is not for me.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Still Off Twitter</title>
      <link>https://lmika.org/2021/12/21/still-off-twitter.html</link>
      <pubDate>Tue, 21 Dec 2021 07:20:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/12/21/still-off-twitter.html</guid>
      <description>&lt;p&gt;A little while ago, I stopped using Twitter on a daily basis as the continuous barrage of news was getting me down.  Six weeks after doing so, I &lt;a href=&#34;https://lmika.org/2021/08/02/six-weeks-off.html&#34;&gt;wrote a post about it&lt;/a&gt;.  Those six weeks have now become six months, and I can say I&amp;rsquo;m still off Twitter and have no immediate intention of going back.&lt;/p&gt;
&lt;p&gt;My anxiety levels dropped since getting off&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, and although they&amp;rsquo;ve not completely gone, the baseline has remained low with occasional spikes that soon subside.  But the best thing is that the time I would have spend reading Twitter I now spend reading stuff that would have taken longer than 30 seconds to write.  Things like books, blog posts and long-form articles (and Micro.blog posts, I always have time for those).  It feels like the balance of my information diet has centred somewhat.  I still occasionally read the news (although I stay away from the commercial news sources) but I try not to spend too much time on it.  Most things I don&amp;rsquo;t need to be informed about in real time: if I learn about it the follow day, it&amp;rsquo;s no big deal.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also seeing more and more people making the same choice I&amp;rsquo;ve made.  The continuous stream of news on Twitter is just becoming too much for them, and they want off.  I think &lt;a href=&#34;https://blog.timokoola.com/2021/12/20/i-wonder-how.html&#34;&gt;Timo Koola&amp;rsquo;s post&lt;/a&gt; sums it up pretty well:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;I wonder how much studies there are about harmfulness of following the news too closely? I don’t think our minds were made for constant bombardment of distressing things we can’t do anything about.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;It&amp;rsquo;s not healthy being constantly reminded of events going on, most of them undesirable, that you can&amp;rsquo;t change.  Better for myself that I spend my attention on things that interest me and help me grow.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;It&amp;rsquo;s amusing that the language I found myself using for this post sounds like I&amp;rsquo;m recovering from some form of substance abuse.  I&amp;rsquo;m guessing the addictive nature of Twitter and its ilk are not too different.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>100 Day Writing Streak</title>
      <link>https://lmika.org/2021/12/14/i-promise-i.html</link>
      <pubDate>Tue, 14 Dec 2021 08:03:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/12/14/i-promise-i.html</guid>
      <description>&lt;p&gt;I promise I won&amp;rsquo;t post about every single milestone that comes along, but I&amp;rsquo;m quite happy that I reached 100 consecutive days of at least one blog post or journal entry.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2021/a10e7eeffd.png&#34; width=&#34;600&#34; height=&#34;421&#34; alt=&#34;100 day Day One streak&#34; /&gt;
</description>
    </item>
    
    <item>
      <title>On Treating Users As If They&#39;re Just There To Buy Stuff</title>
      <link>https://lmika.org/2021/12/03/ars-technica-has.html</link>
      <pubDate>Fri, 03 Dec 2021 07:16:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/12/03/ars-technica-has.html</guid>
      <description>&lt;p&gt;Ars Technica has published a third post about the &lt;a href=&#34;https://arstechnica.com/gadgets/2021/12/microsoft-edge-will-now-warn-users-about-the-dangers-of-downloading-google-chrome/&#34;&gt;annoying user experience of Microsoft Edge&lt;/a&gt; in as many days.  Today’s was about a notice that appears when the user tries to use Edge to download Chrome.  These are notices that are displayed by the browser itself whenever the user opens up the Chrome download page.&lt;/p&gt;
&lt;p&gt;Now, setting aside the fact that these notices shouldn’t be shown to the user at all, what got up my goat was the copy that appears in one of them:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;&amp;lsquo;I hate saving money,&amp;rsquo; said no one ever. Microsoft Edge is the best browser for online shopping.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;What is with this copy?  Do they assume that all users do with their computers is buy stuff?  That their only motivation with using a browser at all is to participate in rampant consumerism?&lt;/p&gt;
&lt;p&gt;I’m not a Microsoft Edge user, so it’s probably not worth my time to comment on this.  But what bothers me is that I&amp;rsquo;m seeing a trend suggesting that large software companies only think their users are just using their devices to consume stuff.  This might be true in the majority — I really don&amp;rsquo;t know — but the problem is that this line of thinking starts to bleed into their product decisions, and reveals what lengths they will go to to extract more money from these users.  I&amp;rsquo;m going on about Edge here but Apple does the same thing in their OSes: showing notifications for TV+ or Apple Music or whatever service they&amp;rsquo;re trying to flog onto their customers this month.  At least with web companies like Google, Twitter and Meta (née Facebook 😒), we get to use the service for free.&lt;/p&gt;
&lt;p&gt;I know software is expensive to build and maintain, etc, etc.  But this mode of thinking is so sleazy it&amp;rsquo;s becoming insulting.  It just makes the experience of using the product worse all around, like going to a &amp;ldquo;free&amp;rdquo; event when you know you&amp;rsquo;ll be pushed to buy something.  This is how these software companies want their users to feel?&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Weekend In Mansfield</title>
      <link>https://lmika.org/2021/11/29/weekend-in-mansfield.html</link>
      <pubDate>Mon, 29 Nov 2021 12:18:35 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/11/29/weekend-in-mansfield.html</guid>
      <description>&lt;p&gt;Over the weekend, I had the opportunity to spend some time with my parents who were staying in Mansfield, in regional Victoria.  We were staying in a small cottage located on a hill, which meant some pretty stunning views, especially in the evening light.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2021/edeee60cf8.jpg&#34; width=&#34;600&#34; height=&#34;450&#34; alt=&#34;The cottage in the late evening light&#34; /&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2021/0c1ee49484.jpg&#34; width=&#34;600&#34; height=&#34;450&#34; alt=&#34;View from the balcony&#34; /&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2021/7cd3e05004.jpg&#34; width=&#34;600&#34; height=&#34;450&#34; alt=&#34;&#34; /&gt;
&lt;p&gt;We didn&amp;rsquo;t do a heap during our trip, although we did manage to do the The Paps trail on Saturday, which involved a 700 metre climb.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2021/f4c9dc7809.jpg&#34; width=&#34;600&#34; height=&#34;450&#34; alt=&#34;Annotated image of The Paps&#34; /&gt;
&lt;p&gt;(Apologies for the photo, I had another one that was zoomed in a bit more but the photo turned out quite muddy.  Might need to consider another phone or camera.)&lt;/p&gt;
&lt;p&gt;It was a bit of a challenge — the trail was quite steep at times — and there were a few instances when we considered turning back.  But we did eventually reached the summit, and got some spectacular views of Lake Eldon, which was quite full thanks to all the rainfall we got over the last few months.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2021/8c1091ac21.jpg&#34; width=&#34;600&#34; height=&#34;450&#34; alt=&#34;One the path&#34; /&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2021/3a9b3b43b6.jpg&#34; width=&#34;600&#34; height=&#34;450&#34; alt=&#34;Approaching the summit&#34; /&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2021/718d23f8fb.jpg&#34; width=&#34;600&#34; height=&#34;450&#34; alt=&#34;View of the lake&#34; /&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2021/2b61e83e92.jpg&#34; width=&#34;600&#34; height=&#34;450&#34; alt=&#34;Another view of the lake&#34; /&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2021/17dbd9e0ee.jpg&#34; width=&#34;600&#34; height=&#34;450&#34; alt=&#34;View up the highway towards Bonni Doon&#34; /&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2021/9ea6ed688d.jpg&#34; width=&#34;600&#34; height=&#34;450&#34; alt=&#34;The summit&#34; /&gt;
&lt;p&gt;This was followed by a pub lunch at the Bonni Doon Hotel.  The place was chokers, probably with people eager to get out of the city at the end of lockdown (likewise for the cottage we stayed at, which has been booked solid for the next couple of months).  But the food (and beer) was good and it was perfect weather to be dining outside, with the sun shining and temperature in the low 20&amp;rsquo;s Celsius.&lt;/p&gt;
&lt;p&gt;All in all it was good to get out of the city, and out of my weekend routine, for a spell.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Cookie Disclosure Popups Should be Handled by the Browser</title>
      <link>https://lmika.org/2021/11/17/cookie-disclosure-popups.html</link>
      <pubDate>Wed, 17 Nov 2021 10:45:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/11/17/cookie-disclosure-popups.html</guid>
      <description>&lt;p&gt;I really dislike the cookie disclosure popups that appear on websites.  Ideally I shouldn&amp;rsquo;t be seeing them at all — I know that the EU requires it, but I&amp;rsquo;m not a citizen of the EU so the regulation should not apply to me.  But I&amp;rsquo;m pragmatic enough to know that not every web developer can or will selectively show this disclosure popup based on the geographic region of the visitor.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s why I&amp;rsquo;m wondering if these disclosure popups would be better handled by the browser.&lt;/p&gt;
&lt;p&gt;The way I see this working is that when a website tries to set a cookie, either through a response header or within JavaScript, and the user is located in a jurisdiction that requires them to be aware of this, the browser would be responsible for telling them.  They could show it as a permission request popup, much like the ones you see already when the site wants to use your microphone or get your location.  The user can then choose to &amp;ldquo;accept&amp;rdquo;, in which case the cookie would be saved; or they can choose to &amp;ldquo;deny&amp;rdquo;, in which case the cookie would be silently dropped or an error will be returned.&lt;/p&gt;
&lt;p&gt;This has some major advantages over the system we have now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It would save the website dev from building the disclosure popup themselves.  I&amp;rsquo;ve seen some real creative ways in which websites show this disclosure, but honestly it would just be simpler not to do it.  It would also cover those web developers that forget (or &amp;ldquo;forget&amp;rdquo;) to disclose the presence of cookies when they need to.&lt;/li&gt;
&lt;li&gt;The website does not need to know where the user is browsing from.  Privacy issues aside, it&amp;rsquo;s just a hassle to lookup the jurisdiction of the originator based on their IP address.  Which is probably why no-one does it, and why even non-EU citizens see these disclosure popups.  This is not a problem for the browser, which I&amp;rsquo;d imagine would have the necessary OS privileges to get the users&amp;rsquo; current location.  This would be especially true for browsers bundled with the OS like Safari and Edge.&lt;/li&gt;
&lt;li&gt;When the user chooses an option, their choice can be remembered.  The irony of this whole thing is that I rarely see websites use cookies to save the my preferences for allowing cookies.  These sites seem to just show the popup again the next time I visit.  Of course for a user chooses to deny the use of cookies, it wouldn&amp;rsquo;t be possible for the site to use cookies to record this fact.  If the browser is managing this preference, it can be  saved alongside all the other site permissions like microphone access, thereby sitting outside what the site can make use of.&lt;/li&gt;
&lt;li&gt;Most important of all to me: those outside the jurisdiction don&amp;rsquo;t even need to see the disclosure popup.  Websites that I visit could simply save cookies as they have been for 25 years now.  This can be an option in the browser, so that users that prefer to see the disclosure prompt can do so.  This option could come in handy for those EU citizens that prefer to just allow (or deny) cookies across the board, so they don&amp;rsquo;t have to see the disclosure popup either (I don&amp;rsquo;t know if this is possible in the regulation).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course the actual details of this would need to be ironed out, like how a website would know whether the user has denied cookie storage.  That&amp;rsquo;s something for standards committee to work out.  But it seems to me that this feature is a no-brainer.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>My Impressions of GitHub Codespaces</title>
      <link>https://lmika.org/2021/11/14/github-universe-started.html</link>
      <pubDate>Sun, 14 Nov 2021 07:25:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/11/14/github-universe-started.html</guid>
      <description>&lt;p&gt;The &lt;a href=&#34;https://www.youtube.com/watch?v=etMvd9IKPH4&#34;&gt;GitHub Universe 2021&lt;/a&gt; conference started a few days ago and one of the features touted in the day one keynote was &lt;a href=&#34;https://github.com/features/codespaces&#34;&gt;GitHub Codespaces&lt;/a&gt;.  This is a development environment that is accessible from within your web browser.   It’s based on VSCode, which is a popular and well-designed IDE that is already written in JavaScript&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, and also provides access to a Linux shell running in the cloud, allowing you to do various things like build and test your code.&lt;/p&gt;
&lt;p&gt;After playing around with Codespaces during the beta, and seeing the feature develop into something that is available for everyone, I&amp;rsquo;d thought I&amp;rsquo;d say a few words on how I use it and what I think of it.&lt;/p&gt;
&lt;p&gt;First, I should probably say that I&amp;rsquo;m not a heavy Codespaces user.  The keynote seemed to be touting the features of Codespaces that makes it useful as a dev environment for collaborative coding.  This is something I have no personal use for since it&amp;rsquo;s mainly just me working on repos I use with Codespaces, so I haven&amp;rsquo;t really explored these features myself.&lt;/p&gt;
&lt;p&gt;The main use I have for Codespaces is making changes to repos on a machine that is not my own.  There are times when I need to add a new feature or fix a bug on a tool I use, and for various reasons I cannot (or choose not to) setup a dev environment on the machine I&amp;rsquo;m working on to make the change.  A case like this would have me look at one of the alternatives, or even make use of the GitHub web-editor.  The web-editor works but doesn&amp;rsquo;t really offer much when it comes to building and testing your changes (I&amp;rsquo;ll talk more about the alternatives later).&lt;/p&gt;
&lt;p&gt;I should say that this doesn&amp;rsquo;t happen very often.  Most of my time I&amp;rsquo;m on my own machine, and I have no need for a web-based dev environment as I can just use the environment I have.  But when that&amp;rsquo;s not possible, being able to spin up and  make the change in Codespaces, complete with a Linux environment which you have sudo access to, is quite useful.&lt;/p&gt;
&lt;p&gt;Codespaces is also pretty nice in terms of a coding environment.  This is no real surprise since it’s based on VSCode, but compared to the alternatives, the little things like keystroke performance and doing things quickly in the editor make a huge difference.  I’ve tried a bunch of alternatives in the past like Cloud9 and CodeAnywhere, and Codespaces is by far the most polished.&lt;/p&gt;
&lt;p&gt;Another advantage Codespaces have over the comparison is that it seems suited to throw-away development environments.  It might be possible to keep an environment around for an extended period of time, but I tend to spin up temporary workspace when I need them.  The alternatives tend to prefer a more long-lived environment, which involves a lot setup for something that is kept around.  This feels like splitting your dev environments in two, and I always feel the  need to select one as the &amp;ldquo;definitive workspace&amp;rdquo; for that particular project going forward.  I don&amp;rsquo;t feel that with Codespaces: I can quickly spin up a new environment, which has most of what I need already installed, and make the change I need with the full knowledge that once I no longer need it, it will be teared down (pro-tip: always push your changes to origin once you&amp;rsquo;re done making your changes in Codespaces).  It helps that spinning up a new environment is quite fast.&lt;/p&gt;
&lt;p&gt;So, that&amp;rsquo;s my impression of GitHub Codespaces.  I&amp;rsquo;m not sure who has access to it: you may need to be on a paid plan, for example.  But if it&amp;rsquo;s enable for your account, and you find yourself needing a temporary, cloud-based dev environment to do your work in, I&amp;rsquo;d suggest giving it a try.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;It’s actually TypeScript&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Alto Catalogue Update</title>
      <link>https://lmika.org/2021/11/13/alto-catalogue-update.html</link>
      <pubDate>Sat, 13 Nov 2021 21:55:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/11/13/alto-catalogue-update.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve really tied myself up in knots here.  I&amp;rsquo;m spending some time working on Alto Catalogue, trying to streamline the process of uploading individual tracks into a new album.  This is a workflow that is absolutely not user friendly at the moment, and the only way I&amp;rsquo;ve gotten tracks into the catalogue is to run a hacked-together tool to upload the tracks from the command line.  The reason why I&amp;rsquo;m addressing this now is that it&amp;rsquo;s slightly embarrassing to have this open-source project without having a nice way of doing something that, by all accounts, is quite fundamental (a good hint for when you&amp;rsquo;re facing this is when it comes time to write the end-user documentation: if you can&amp;rsquo;t explain how to do something in a way that doesn&amp;rsquo;t include the word &amp;ldquo;hack&amp;rdquo;, &amp;ldquo;complicated&amp;rdquo;, or &amp;ldquo;unsupported&amp;rdquo;, then something is missing).&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;m trying to close this feature gap, but it&amp;rsquo;s proving to be more complicated than I expected.  The main issue relates ID3 tags and how media is arrange in the repository.  Previous versions of the catalogue actually did have a way of uploading track media to the repository, which is essentially an S3 bucket.  The way this work is that the catalogue will issue the browser a pre-signed Put URL, and the browser could upload the track media directly to S3.  But in order to get a pre-signed URL, you need to know the object key, which is a bit like a file path.  The old upload flow had the user enter the object key manually in the upload form.&lt;/p&gt;
&lt;p&gt;This worked but I had some real issues with it.  The first is that I&amp;rsquo;d like the objects within the S3 bucket to be organised in a nice way, for example &amp;ldquo;artist/album/tracknum-title.mp3&amp;rdquo;.  I&amp;rsquo;m hoping that this S3 bucket will be my definitive music collection and I don&amp;rsquo;t want just some random IDs that are completely indecipherable when I browse the objects in the S3 bucket.  That way, if I were ever to shutdown the catalogue down or loose all the metadata, I&amp;rsquo;d still be able to navigate my collection via the object keys alone.&lt;/p&gt;
&lt;p&gt;The second was that this approach did not take into account the track metadata.  Track metadata is managed in a PostgreSQL database and had to be entered in manually; yes, this included the track duration.  The only reason I used the hacked together tool to upload tracks was that it was a tool I was already using to set ID3 tags on MP3 files, and it was trivial to add a HTTP client to do the upload from there.  Obviously, asking users to run a separate tool to do their track uploads is not going to fly.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;m hoping to improve this.  The ideal flow would be that the user will simply select an MP3 from their file system.  When they click upload, the following things will happen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The ID3 tags of the MP3 will be read.&lt;/li&gt;
&lt;li&gt;That metadata will be used to determine the location of the object in S3.&lt;/li&gt;
&lt;li&gt;A pre-signed URL will be generated and sent to the browser to upload the file.&lt;/li&gt;
&lt;li&gt;The file is uploaded to S3.&lt;/li&gt;
&lt;li&gt;A new track record is created with the same metadata.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The libraries I&amp;rsquo;m using to read the &lt;a href=&#34;https://github.com/bogem/id3v2&#34;&gt;ID3 tags&lt;/a&gt; and &lt;a href=&#34;https://github.com/kgiannakakis/mp3duration&#34;&gt;track duration&lt;/a&gt; requires the track media to be available as a file on the local file system (I assume this is for random access).  Simply uploading the track media to the local file system would be the easiest approach, since it would allow me to read the metadata, upload the media to the repository on the backend, and setup the track metadata all in a single transaction.  But I have some reservations about allowing large uploads to the server, and most of the existing infrastructure already makes use of pre-signed URLs.  So the first run at this feature involved uploading the file to S3 and then downloading it on the server backend to read the metadata.&lt;/p&gt;
&lt;p&gt;But you see the problem here: in order to generate a pre-signed URL to upload the object to S3, I need to know the location of the media, which I want to derive from the track metadata.  So if I don&amp;rsquo;t want uploads to go straight to the file system, I need the object to already be in S3 in order to work out the best location of where to put the object in S3.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;m wondering what the best ways to fix this would be.  My current thing is this series of events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a pre-signed URL to a temporary location in the S3 bucket.&lt;/li&gt;
&lt;li&gt;Allow the user to Upload the media directly to that location in the S3 bucket.&lt;/li&gt;
&lt;li&gt;On the server, download that media object to get the metadata and duration.&lt;/li&gt;
&lt;li&gt;From that, derive the objects location and move the object within S3, something I&amp;rsquo;m guessing should be relatively easy if the objects are in the same bucket.&lt;/li&gt;
&lt;li&gt;Create a new track record from the metadata.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The alternative is biting the bullet and allowing track uploads directly to the file system.  That will simplify the crazy workflow above but means that I&amp;rsquo;ll need to configure the server for large uploads.  This is not entirely without precedence though: there is a feature for uploading tracks in a zip file downloaded from a URL which uses the local file system.  So there&amp;rsquo;s not a whole lot stopping me from doing this altogether.&lt;/p&gt;
&lt;p&gt;The third approach might be looking for a JavaScript library to read the ID3 tags.  This is not great as I&amp;rsquo;d need to get the location from the server anyway, as the metadata-derive object location is configured on a per repository basis.  It also means I&amp;rsquo;ll be mixing up different ways to get metadata.&lt;/p&gt;
&lt;p&gt;In any case, not a great set of options here.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Feeds In Broadtail</title>
      <link>https://lmika.org/2021/11/06/feeds-in-broadtail.html</link>
      <pubDate>Sat, 06 Nov 2021 20:32:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/11/06/feeds-in-broadtail.html</guid>
      <description>&lt;p&gt;My quest to watch YouTube without using YouTube got a little closer recently with the addition of feeds in Broadtail.  This uses the &lt;a href=&#34;https://mjtsai.com/blog/2020/01/16/youtube-rss-feeds/&#34;&gt;YouTube RSS feed endpoint&lt;/a&gt; to list videos recently added to a channel or playlist.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/1e7e3beeb5.jpg&#34; width=&#34;600&#34; height=&#34;409&#34; alt=&#34;Feed listing, in all it&#39;s 90&#39;s web style glory.&#34; /&gt;
&lt;p&gt;There are a bunch of channels that I watch regularly but I&amp;rsquo;m very hesitant to subscribe to them within YouTube itself (sorry YouTubers, but I choose not to smash that bell icon).  I&amp;rsquo;m generally quite hesitant to give any signal to YouTube about my watching habits, feeding their machine learning models even more information about myself.  But I do want to know when new videos are available, so that I can get them into Plex once they&amp;rsquo;re released.  There is where feeds come in handy.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/1bfefa3950.jpg&#34; width=&#34;600&#34; height=&#34;496&#34; alt=&#34;Recent videos of a feed.&#34; /&gt;
&lt;p&gt;Also improved is the display of video metadata when selecting a feed item or entering a video ID in the quick look bar.  Previously this would immediately start a download of the video, but I prefer knowing more about the video first.  These downloads aren&amp;rsquo;t free, and they usually take many hours to get.  Better to know more about them before committing to it.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/40ca90a92c.jpg&#34; width=&#34;600&#34; height=&#34;569&#34; alt=&#34;Video details page.&#34; /&gt;
&lt;p&gt;Incidentally, I think this mode of watching has a slight benefit.  There are days when I spend the whole evening binging YouTube, not so much following the algorithm but looking at the various channels I&amp;rsquo;m interested in for videos that I haven&amp;rsquo;t seen yet.  Waiting several hours for a video download feels a little more measured, and less likely to send me down the YouTube rabbit hole.  I&amp;rsquo;m sure there will still be evenings when I do nothing else other than watch TV, but hopefully that&amp;rsquo;s more of a choice rather than an accident.&lt;/p&gt;
&lt;p&gt;I think this is enough on Broadtail for the time being.  It&amp;rsquo;s more or less functional for what I want to do with it.  Time to move onto something else.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some Screenshots Of Broadtail</title>
      <link>https://lmika.org/2021/10/31/some-screenshots-of.html</link>
      <pubDate>Sun, 31 Oct 2021 12:09:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/10/31/some-screenshots-of.html</guid>
      <description>&lt;p&gt;I spent some time this morning doing some styling work on Broadtail, my silly little YouTube video download manager I&amp;rsquo;m working on.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/1fd10c6799.jpg&#34; width=&#34;600&#34; height=&#34;455&#34; alt=&#34;&#34; /&gt;
&lt;p&gt;Now, I think it&amp;rsquo;s fair to say that I&amp;rsquo;m not a designer.  And these designs look a little dated, but, surprisingly, this is sort of the design I&amp;rsquo;m going for: centered pages, borders, etc.  A bit of a retro, tasteless style that may be ugly, but still usable(-ish).&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2023/e0eb3d3bd3.jpg&#34; width=&#34;600&#34; height=&#34;455&#34; alt=&#34;&#34; /&gt;
&lt;p&gt;It&amp;rsquo;s not quite finished — the colours need a bit of work — but it&amp;rsquo;s sort of the style I have in my head.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Start of Yet Another Project Because I Can&#39;t Help Myself</title>
      <link>https://lmika.org/2021/10/22/start-of-yet.html</link>
      <pubDate>Fri, 22 Oct 2021 19:16:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/10/22/start-of-yet.html</guid>
      <description>&lt;p&gt;One of the reasons why I stopped work on Lorikeet was that I was inspired by those on Micro.blog to setup a Plex server for my YouTube watching needs.  A few years ago, I actually bought an old Intel Nuc for that reason, but I never got around to setting it up.  I managed to do so last Wednesday and so far it&amp;rsquo;s working pretty well.&lt;/p&gt;
&lt;p&gt;The next thing I&amp;rsquo;d like to do is setup RSS subscriptions for certain YouTube channels and automatically download the videos when they are publish.  I plan to use &amp;ldquo;youtube-dl&amp;rdquo; for the actual video downloading part, but I&amp;rsquo;m hoping to build something that would poll the RSS feeds and trigger the download when new videos are published.  I&amp;rsquo;m hoping that this service would have a web-based frontend so I don&amp;rsquo;t have to login via SSH to monitor progress, etc.&lt;/p&gt;
&lt;p&gt;The download&amp;rsquo;s would need to be automatic as the requests made by &lt;code&gt;youtube-dl&lt;/code&gt; seem to be throttled by YouTube and a longish video may take several hours to download.  If this was a manual process, assuming that I would actually remember to start the download myself, the video won&amp;rsquo;t be ready for my evening viewing.  I&amp;rsquo;m hoping that my timezone would work to my advantage here.  The evenings on the US East Coast are my mornings, so if a video download starts at the beginning of the day, hopefully it would be finish when my evening rolls around.  I guess we&amp;rsquo;ll see.&lt;/p&gt;
&lt;p&gt;Anyway, that&amp;rsquo;s what my current coding project will be on: something that would setup RSS subscriptions for YouTube channels, and download new videos when they are published.&lt;/p&gt;
&lt;p&gt;This is probably one of those things that already exist out there.  That may be true, but there are certain things that I&amp;rsquo;m hoping to add down the line.  One such thing might be adding the notion of an &amp;ldquo;interest level&amp;rdquo; to channels which would govern how long a video would be kept around.  For example, a channel that is marked as very interested would have every video downloaded and stored into Plex straight away.  Mildly interested channels would have videos download but kept in a holding place until I choose to watch it, in which case it would be moved to Plex.  If that doesn&amp;rsquo;t happen in 7 days or so, the videos would be removed.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d like to also add some video lifecycle management into the mix as well, just to avoid the disk being completely used up.  I can see instances where I&amp;rsquo;d like to mark videos as &amp;ldquo;keep for ever&amp;rdquo; and all the others will churn away after 14 days or so.  It might be worth checking out what Plex offers for this, just to avoid doubling up on effort.&lt;/p&gt;
&lt;p&gt;But that&amp;rsquo;s all for the future.  For the moment, my immediate goal is to get the basics working.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Abandoning Project Lorikeet</title>
      <link>https://lmika.org/2021/10/18/abandoning-project-lorikeet.html</link>
      <pubDate>Mon, 18 Oct 2021 19:58:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/10/18/abandoning-project-lorikeet.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ll admit it: the mini-project that I have been working on may not have been a good idea.&lt;/p&gt;
&lt;p&gt;The project, which I gave the codename Lorikeet, was to provide a way to stream YouTube videos to a Chromecast without using the YouTube app.  Using the YouTube app is becoming a real pain.  Ads aside, they&amp;rsquo;ve completely replaced the Chromecast experience from a very basic viewing destination to something akin to a Google TV, complete with recommendations of &amp;ldquo;Breaking News&amp;rdquo; from news services that I have no interest in seeing.&lt;/p&gt;
&lt;p&gt;So I spent some time trying to build something to avoid the YouTube app completely, using a mixture of youtube-dl, a Buffalo web-app, and a Flutter mobile app.  I spent the last week on it (it&amp;rsquo;s not pretty so no screenshots), but at this stage I don&amp;rsquo;t see much point continuing to work on it.&lt;/p&gt;
&lt;p&gt;For one, the experience is far from perfect.  Video loading is slow and there are cases when the video pauses due to buffering. I&amp;rsquo;m sure there are ways around this, but I really don&amp;rsquo;t want to spend the time learning how to do this.&lt;/p&gt;
&lt;p&gt;It was also expensive. I have a Linode server running in Sydney which acts as a bit of a hobby server (it&amp;rsquo;s also running Pagepark to serve this site); but in order to be closer to the YouTube CDNs that are closer to me, I had to rent a server that would run in Melbourne.  And there are not many VPS hosting providers that offer hosting here.&lt;/p&gt;
&lt;p&gt;So I went with Google Cloud.&lt;/p&gt;
&lt;p&gt;Now, I&amp;rsquo;m sure there&amp;rsquo;s a lot to like about Google Cloud, but I found its VPS hosting to be quite sub-par.  For just over $10 USD a month, I had a Linux virtual server with 512 MB of RAM, 8 GB of storage, and a CPU which I&amp;rsquo;d imagine is throttled all the way back as trying to do anything of significants slowed it to a crawl.  I had immense issues installing OS updates, getting the Dokku based web-app deployed, and trying to avoid hitting the storage limit.&lt;/p&gt;
&lt;p&gt;For the same amount of money, Linode offers me a virtual server with 2 GB of RAM, 50 GB of storage, and a real virtual CPU.  This server is running 4 Dokku apps, 3 of them with dedicated PostgreSQL databases, and apart from occasionally needing to remove dangling Docker images, I&amp;rsquo;ve had zero issues with it.  None!  (The podcasters were right).&lt;/p&gt;
&lt;p&gt;Where was I?  Oh, yeah.  So, that&amp;rsquo;s the reason why I&amp;rsquo;m abandoning this project and will need to re-evaluate my online video watching experience.  I might give Plex a try, although before doing something like setting up a dedicated media server, I&amp;rsquo;ll probably just use the Mac Mini I&amp;rsquo;ve been using for a desktop in the short term.&lt;/p&gt;
&lt;p&gt;So, yeah, that&amp;rsquo;s it.  It&amp;rsquo;s hard to abandon a project you spent any amount of time on.  I suppose the good thing is that I got to play around with Flutter and learnt how to connect to a Chromecast using Dart, so it&amp;rsquo;s not a complete waste.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Two People</title>
      <link>https://lmika.org/2021/10/16/two-people.html</link>
      <pubDate>Sat, 16 Oct 2021 07:21:53 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/10/16/two-people.html</guid>
      <description>&lt;p&gt;There are two people, and each one has the same problem that they want to get solved.&lt;/p&gt;
&lt;p&gt;The first person chooses the option to pay $10 a month, and all they have to do is sign up to a service that will solve the problem for them.  The service they sign up for takes care of the rest.&lt;/p&gt;
&lt;p&gt;The second person chooses the option to pay $15 a month, 20 hours of work to get something built, and an ongoing commitment to keep it maintained.&lt;/p&gt;
&lt;p&gt;Guess which person I am today.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>(Hyper)critical Acclaim</title>
      <link>https://lmika.org/2021/10/06/hypercritical-acclaim.html</link>
      <pubDate>Wed, 06 Oct 2021 09:36:39 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/10/06/hypercritical-acclaim.html</guid>
      <description>&lt;p&gt;There were a couple of events that led me to writing this post.  I&amp;rsquo;m sure part of it was seeing the posts on the 10 year anniversary of Steve Jobs, although such an event would probably not have been sufficient in itself.  What tipped it over the edge was seeing the Ars Technica review of iOS showing up in my RSS feed on the same day.  Pretty soon I&amp;rsquo;m expecting the MacOS review to drop as well.&lt;/p&gt;
&lt;p&gt;The quality of the reviews are still quite good, and I try to read them when I have the time.  But sadly they do not grab me the way the Siracusa reviews did.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s usually around this time of year I would start expecting them, waiting for the featured article to show up on Ars&amp;rsquo; homepage.  Once they came out, I would read them from cover to cover.  I wouldn&amp;rsquo;t rush it either, taking my time with them over a period of about a week, reading them slowly and methodically as one would sip a nice glass of wine.&lt;/p&gt;
&lt;p&gt;Thinking back on them now, what grabbed me about these pieces was the level of detail.  It was clear from the writing that a lot of effort was put into them: every pixel of the new OS was looked at methodically with a fine eye to detail.  This level of study made to the design of the OS release, trying to find the underlying theme running through the decisions made, was something that was not found in any of the other OS reviews on Ars or any other tech site.  I&amp;rsquo;m sure it was in no small part the reason why I eventually move to Apple for my computing needs.&lt;/p&gt;
&lt;p&gt;Eventually, the Siracusa reviews stopped.  But by then I was well down the rabbit hole of Apple tech-nerd podcasts like the Talk Show and ATP.  Now a regular listener to these shows, I still enjoy getting my fix of software technologies critical review, albeit in audio form.  I eventually discovered the Hypercitical podcast, well after it finished, and I still occasionally listen to old episodes when there&amp;rsquo;s nothing new.&lt;/p&gt;
&lt;p&gt;Incidentally, there is one episode that I haven&amp;rsquo;t listen to yet.  Episode 37, recorded on 8th October 2021, just after the death of Steve Jobs.  Here, on the 10 year anniversary of his death, it might be a good time to have a listen.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Choosing the Hard Way Forward</title>
      <link>https://lmika.org/2021/10/01/on-choosing-the.html</link>
      <pubDate>Fri, 01 Oct 2021 13:53:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/10/01/on-choosing-the.html</guid>
      <description>&lt;p&gt;This is about work, so the usual disclaimers about opinions being my own, etc. apply here.&lt;/p&gt;
&lt;p&gt;I have an interesting problem in front of me at the moment: I&amp;rsquo;ve need to come up with a way to be notified when a user connects to, or disconnects from, a PostgreSQL database.  This is not something that&amp;rsquo;s supported by PostgreSQL out of the box&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, so my options are limited to building something that sits outside the database.  I can think of two ways that I can do this: have something that sits in front of the database which acts as a proxy, or have something that sits behind the database and generates the notifications by parsing the server log.&lt;/p&gt;
&lt;p&gt;A database proxy is probably the better option in the long run.  Not only will it allow us to know exactly when a user connects or disconnects — since they will be connecting to the proxy itself — it could potentially allow us to do a few other things that have been discussed, such as IP address whitelisting.  It might be a fair bit of work to do, and would require us to know the PostgreSQL wire protocol, but given how widespread PostgreSQL is, I&amp;rsquo;m suspecting that this could be done once and not need many changes going forward.&lt;/p&gt;
&lt;p&gt;Despite these advantages, I find myself considering the log parsing approach as the recommended solution.  It&amp;rsquo;s probably a more fragile solution — unlike the wire protocol, there&amp;rsquo;s nothing stopping the PostgresSQL devs from changing a log message whenever they like — and it would not allow us to do all the other stuff that we&amp;rsquo;d like it to do.  But it will be faster to build, and would involve less &amp;ldquo;hard programming&amp;rdquo; than the alternative.  It can be knocked out quite quickly with a couple of regular expressions.&lt;/p&gt;
&lt;p&gt;Weighing the two options, I find myself wondering why I&amp;rsquo;m preferring the latter.  Why go for the quick and easy solution when the alternative, despite requiring more work, would give us the greatest level of flexibility?  It&amp;rsquo;s not like we couldn&amp;rsquo;t do it: I&amp;rsquo;m pretty confident anyone on the team would be able to put this proxy service together.  It&amp;rsquo;s not even like my employer is requiring one particular solution over another (they haven&amp;rsquo;t yet received any of the suggestions I&amp;rsquo;m planning to propose, so they haven&amp;rsquo;t given a preference one way or the other).  So what&amp;rsquo;s giving me pause from recommending it?&lt;/p&gt;
&lt;p&gt;No decision is completely made in a vacuum, and this is especially true in the mind of the decider.  There are forces that sit outside the immediate problem that weigh on the decision itself: personal experience mixed with the prevailing zeitgeist of the industry expressed as opinions of &amp;ldquo;best practice&amp;rdquo;.
Getting something that works out quickly vs. taking the time to build something more correct; a sense that taking on something this large would also result in a fair amount of support and maintenance (at the very least, we would need to be aware of changes in the wire protocol); and just a sense that going for the proxy option would mean we&amp;rsquo;re building something this is &amp;ldquo;not part of our core business&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Ah, yes, the old &amp;ldquo;core business&amp;rdquo; argument.  I get the sense that a lot of people treat this one as a binary decision: either it&amp;rsquo;s something that we as a business does, or it&amp;rsquo;s not.  But I wonder if it&amp;rsquo;s more of a continuum.  After all, if we need to block users based on their IP address, is it not in our interest that we have something that does this?  At what point does the lost opportunity of not having this outweigh the cost of taking on the development work now to build it?  If we build the easy thing now, and later on we find ourselves needing the thing we don&amp;rsquo;t have, would we regret it?&lt;/p&gt;
&lt;p&gt;This is a bit of a rambling post, but I guess I&amp;rsquo;m a little conflicted about the prevailing approach within the tech industry of building less.  It&amp;rsquo;s even said that part of the job is not only know what to build, but when NOT to build.  I imagine no-one wants to go to the bad old days where everyone and their dog was building their own proprietary database from scratch.  I definitely don&amp;rsquo;t want that either, but I sometimes wonder whether we&amp;rsquo;ve overcorrected in the other direction somewhat.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I didn&amp;rsquo;t look at whether this is doable with hooks or extensions.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Some Notes About the Covid-19 Situation</title>
      <link>https://lmika.org/2021/09/19/some-notes-about.html</link>
      <pubDate>Sun, 19 Sep 2021 07:21:59 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/09/19/some-notes-about.html</guid>
      <description>&lt;p&gt;Now that the vaccines are here and are (slowly) being rolled out, and that Covid zero is no longer achievable in any realistic sense, the pandemic seems to be taking on a bit of a different vibe at the moment.  I am no longer religiously watching the daily press conferences as I did in the past.  They’re still occurring as far as I know, and I do appreciate that the authorities are showing up every day once again to brief the public.&lt;/p&gt;
&lt;p&gt;But I’m starting to get the sense that people are generally loosing interest in it now.  Well, maybe “loosing interest” is the wrong way to say it.  It’s not like it’s something that could be ignored: if you don’t get affected yourself, you’re still  bound by the current restrictions in some way.  Maybe it’s more like it’s starting to slip into the background somewhat.&lt;/p&gt;
&lt;p&gt;Slowly the we’re-all-in-this-together collectivism is morphing into one of personal responsibility.  Except for the need to keep the medical systems from being overwhelmed, it’s now up to the individual to take care of themselves, whether it’s by masking up and social distancing, or by getting vaccinated.  Everyone that I love has done just this: they’re got their shots when they could and are generally being very careful.  But there are people out there that are not.  Even some of my friends parents are hesitant to get the vaccine, either waiting for Pfizer (they’ll be waiting a while) or just being suspect of the vaccine at all.&lt;/p&gt;
&lt;p&gt;I also think that the success of the lockdowns before the delta variant has lulled people into a sense of security that is no longer present anymore.  The latest lockdown has largely failed, and now it’s immunity from the vaccines that will have to protect us.  I hope these people that are not taking this seriously realise that the protection that comes from collective actions will no longer be around when the virus comes for them.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Confluence</title>
      <link>https://lmika.org/2021/09/07/on-confluence.html</link>
      <pubDate>Tue, 07 Sep 2021 09:40:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/09/07/on-confluence.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m sorry.  I know the saying about someone complaining about their tools.  But this has been brewing for a little while and I need to get it off my chest.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s becoming a huge pain using Atlassian Confluence WISIWIG editor to create wiki pages.  Trying to use Confluence to write out something that is non-trivial, with tables and diagrams, so that it is clear to everyone in the team (including yourself) is so annoying to do now I find myself wishing for alternatives.  It seems like the editor is actively resisting you in your efforts to get something down on paper.&lt;/p&gt;
&lt;p&gt;For one thing, there&amp;rsquo;s no way to type something that begins with &lt;code&gt;[&lt;/code&gt; or &lt;code&gt;{&lt;/code&gt;.  Doing so will switch modes for adding links or macros.  This actively breaks my train of thought.  The rude surprise that comes from this shunts me out of my current thought to one that tries to remember the proper way to back out of this unwanted mode change, which is not easy to do.  There&amp;rsquo;s no easy way to get out of the new mode, and simply leave the brace as you typed it.  It seems to be that the only way to disable this is to turn off all auto-formatting.  I never need to create new macros by typing them out, but I do use &lt;code&gt;h3.&lt;/code&gt; to create a new headings and &lt;code&gt;*&lt;/code&gt; to bold text all the time.  In order to actually type out an opening brace, I have to turn these niceties off.&lt;/p&gt;
&lt;p&gt;The second issue is that it&amp;rsquo;s soooo sloooow.  For a large page, characters take around a second to appear on the screen after being typed.  This does not help when you&amp;rsquo;re trying to get your thoughts down on the page as quickly as they come to you.  You find yourself pausing and waiting for the words to catch up, which just slows your thinking down.  And I won&amp;rsquo;t mention the number of errors that show up because of this (to be fair, I&amp;rsquo;m not the best typer in the world, but I find myself typing out fewer errors in an editor with faster feedback than the one that Confluence uses).&lt;/p&gt;
&lt;p&gt;I appreciate the thinking behind moving from a plain text editor to a WISIWIG one: it does make it more approachable to users not comfortable working with a markup language (although I also believe this is something that could be learnt, and that these users will eventually get comfortable with it and appreciate the speed at which they could type things out).  It&amp;rsquo;s just a shame that there&amp;rsquo;s no alternative to those who need an interface that is fast and will just get out of the way.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Apple&#39;s Media Release Gymnastics</title>
      <link>https://lmika.org/2021/09/06/on-apples-media.html</link>
      <pubDate>Mon, 06 Sep 2021 08:02:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/09/06/on-apples-media.html</guid>
      <description>&lt;p&gt;I started listening to &lt;a href=&#34;https://daringfireball.net/thetalkshow/2021/09/01/ep-321&#34;&gt;the latest Talk Show&lt;/a&gt;, where John Gruber and MG Siegler discuss Apple&amp;rsquo;s media release of the class action settlement.  Releasing it to the major media outlets in such a way as to spin the guideline clarification as a concession to developers, even though nothing has actually change, is genius if true.  I imagine that&amp;rsquo;s why Apple&amp;rsquo;s PR department get the big bucks.&lt;/p&gt;
&lt;p&gt;But I wonder if Apple has considered the potential blowback of this approach.  I might be naive here, but I can&amp;rsquo;t help wonder whether these media outlets publishing that Apple hasn&amp;rsquo;t actually conceded  anything will eventually realised that they have been had.  Would that affect the relationship between the two in any way?  Say Apple wants to publish some good news and expect these outlets to maintain the favourable air of their release.  Would they do it?&lt;/p&gt;
&lt;p&gt;Then again, it&amp;rsquo;s most likely that nothing will really change.  There&amp;rsquo;s little trust lost between the two anyway, and if this gymnastics actually happened, Apple knows it.  Also, it sounds like Apple&amp;rsquo;s media release has had the desired effect of reaching those in the US government applying anti-trust pressure on the company.  They probably think it&amp;rsquo;s worth the credibility they have burned with these outlets, if any&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;One thing that seems clear though: this is doing no favours in addressing the trust lost between Apple and their developers, no matter how much clarifying this release does.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I realise that I&amp;rsquo;m probably so far removed from how much the general zeitgeist knows or cares about the relationship between Apple and their developers, so even expecting that these outlets know that they have been had is a huge assumption.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Post Of Little Consequence</title>
      <link>https://lmika.org/2021/08/24/post-of-little.html</link>
      <pubDate>Tue, 24 Aug 2021 09:40:15 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/08/24/post-of-little.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m wouldn&amp;rsquo;t call myself a regular poster on this blog.  I don&amp;rsquo;t have a goal of writing a post a day or something.  But I do want to keep it up with some frequency, and post at least one item a week.  I realised today that it&amp;rsquo;s been a week since my last post.&lt;/p&gt;
&lt;p&gt;However, due to the current lockdown, very little of note has happened over the last week.  Apart from work, TV, reading, and doing a few personal projects on the side, there really isn&amp;rsquo;t much going on.  So it was hard to come up with something that was interesting to write about.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;m playing my post-about-how-difficult-it-is-to-post post, which is what you are reading now.  In short: there&amp;rsquo;s nothing spectacular that&amp;rsquo;s going on at the moment.&lt;/p&gt;
&lt;p&gt;That said, I did start three drafts that I thought about publishing.  It felt a little strange posting them individually, so here they are as bullet points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I&amp;rsquo;m completely recovered from the side effects of the vaccine now.  I did have a sore arm for about a week, and actually saw to the doctor about it, but he said that it took him about a week to get over it as well, so nothing to worry about here.  Incidentally, this was a day after seeing the same doctor about renewing a prescription, making it the first time in my life I went to the doctors two days in a row, which I guess is something.  Now just need to wait 11 more weeks for my next dose.&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;ve got a research task at work today, meaning no podcast listening at work today.&lt;/li&gt;
&lt;li&gt;Related, I find it relatively easy to listen to music and podcasts at the same time I&amp;rsquo;m writing code, but as soon as I need to write prose (documentation, copy, or writing a blog post), I have to turn the music off.  I guess my mind needs complete focus when writing English sentences, probably because it&amp;rsquo;s an area that has been underdeveloped somewhat.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Anyway, that&amp;rsquo;s what&amp;rsquo;s happing at the moment.  Hope I didn&amp;rsquo;t waste too much of your time 🙂&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Six Weeks Off Twitter</title>
      <link>https://lmika.org/2021/08/02/six-weeks-off.html</link>
      <pubDate>Mon, 02 Aug 2021 07:41:48 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/08/02/six-weeks-off.html</guid>
      <description>&lt;p&gt;It’s been roughly six weeks since I’ve stopped using Twitter on a daily basis.  I initially took a break to stay away from some anxiety inducing news, and I was initially going to return to daily use once that passed.  But after hearing others on Micro.blog post about their experience closing their Twitter account, I decided to see how long I can go staying off Twitter myself.&lt;/p&gt;
&lt;p&gt;I wouldn’t say that I am a big Twitter user.  I don’t have a Twitter audience (I think my follower count is in the single digits), I hardly ever tweet or reply, and although I have a few friends and family on there, I have other means of communicating with them that I tend to use more often.  The only thing I would miss are the occasional interesting or amusing tweets from those that I follow, something that is not guaranteed in any particular reading session.&lt;/p&gt;
&lt;p&gt;In those six weeks, I notice my reading patterns have change.  I’m reading a lot more books and blog posts now.  I found that having something to read during the time you’re usually browsing Twitter helps a great deal, so there’s always some long form written piece that I can turn to when I’ve caught up with everything else.  And although I wouldn’t say my anxiety has gone, I do think that it’s lower than it was.  It’s calming to know that there are no shocking/depressing items that can jump out of me during a particular reading session.  I think that mechanic has a lot to do with the addictiveness of Twitter and it’s ilk.&lt;/p&gt;
&lt;p&gt;I’m not quite at the point where I will completely close my Twitter account, and there are some users that I may move over to Feedbin (I haven’t done that yet, so I’m not sure how interested in them I really am).  But, all in all, I think this break from daily use of Twitter has been good for me, and I found myself having no real urge to going back.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Seeking Out Bad News</title>
      <link>https://lmika.org/2021/07/22/seeking-out-bad.html</link>
      <pubDate>Thu, 22 Jul 2021 09:59:16 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/07/22/seeking-out-bad.html</guid>
      <description>&lt;p&gt;Sometimes I wonder if I’m just going out of the way to seek bad news.  Maybe it’s because I think that if I don’t, then a problem will go unaddressed as no-one else is aware of it.&lt;/p&gt;
&lt;p&gt;There’s probably some evolutionary trait to this.  Being the one that hears a predator, and reacts to it before anyone else, is an advantage.  But in this day and age, many of the problems that I have anxiety about is pretty much known by everyone, and addressing it in any meaningful way is beyond my direct control.&lt;/p&gt;
&lt;p&gt;So in the interest for my own mental health, I should cut down on seeking out these stories, do what I can to help with the problem, and just hope that someone who does have the ability to do something substantial knows about it, and can address it in some way.&lt;/p&gt;
&lt;p&gt;While total ignorance is probably not ideal, being up to speed with the woes of the world is probably not healthy either.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Year On Micro.blog</title>
      <link>https://lmika.org/2021/07/12/a-year-on.html</link>
      <pubDate>Mon, 12 Jul 2021 07:47:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/07/12/a-year-on.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s been a year since I&amp;rsquo;ve signed up to Micro.blog and &lt;a href=&#34;https://lmika.micro.blog/2020/07/12/ive-signed-up.html&#34;&gt;written my first post&lt;/a&gt;, and the only regret I have is that I didn&amp;rsquo;t do it sooner.&lt;/p&gt;
&lt;p&gt;The reason for joining was to write more; to focus less on the blogging engine and more on the blog itself.  At the time I have only posted seven times over a period of 9 months.  In the last year, that post count has risen to 222.  I guess I can call that objective achieved.&lt;/p&gt;
&lt;p&gt;But the biggest reason for staying, and one I wish I knew sooner, is the fantastic community here.  Having such a great bunch of people online is quite rare now, and this has quickly been my favourite place on the internet.  We truly have something special here.&lt;/p&gt;
&lt;p&gt;Thank you all for being such awesome people.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Working On The Weekend</title>
      <link>https://lmika.org/2021/06/14/working-on-the.html</link>
      <pubDate>Mon, 14 Jun 2021 07:58:47 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/06/14/working-on-the.html</guid>
      <description>&lt;p&gt;I’ve saw a Tweets last night saying that the best thing a young person can do to help their career is to work on the weekend.  The implication there is that being the one that “puts in the extra hours” can seem, in the eyes of your employer, like you’re the hardest worker there, that you’re committed to the project and the job.  This could lead to bonuses, promotions, perks, a reputation, you name it.&lt;/p&gt;
&lt;p&gt;I’m always sceptical when I see advice like that.  Coming in on the weekend on a voluntary bases might be good for your career, but is it actually good for you?  Are you doing yourself any favours spending two additional days a week creating value for someone else?&lt;/p&gt;
&lt;p&gt;What about the things that will create value for you? That can help you be a more rounded person?  Things like learning a new skill, starting a new side project, socialising, taking up a hobby.  When will you have time for that? Not to mention just fricken resting, which is really not as valued as it should be.&lt;/p&gt;
&lt;p&gt;My feeling is that you already work for someone else 5 out of 7 days a week.  By all means work on the weekend if you want to, but make sure you’re doing it for yourself.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Stumbling Into the Concept of Narrating Your Work</title>
      <link>https://lmika.org/2021/06/01/narrate-your-work.html</link>
      <pubDate>Tue, 01 Jun 2021 13:20:12 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/06/01/narrate-your-work.html</guid>
      <description>&lt;p&gt;About a week ago, we had a retro.  One of the things that was brought up was the sense that management felt that the team was not delivering as much as we could be.  There are a number of reasons for this.  For one, the higher ups work in Perth, a good 3,452 KM away from Melbourne.  Another was that a lot of the work the team deals with is experimental in nature: more R&amp;amp;D vs. product development (a large portion of it involves dealing with a lot of barely supported, and &lt;a href=&#34;https://www.caseyliss.com/2020/11/10/on-apples-pisspoor-documentation&#34;&gt;completely undocumented&lt;/a&gt; APIs for MacOS).&lt;/p&gt;
&lt;p&gt;Nevertheless, it was a bit of a downer that management felt this way.  A solution proposed by a team member was to maintain a work log.  Doing so would give the product owner, who works in Perth, the ammunition needed to push back on the notion that the team was just sitting on their hands.  Prior to the pandemic, I started keeping a bullet journal, which helped me keep on top of things that I needed to do.  But this would been different: it would essentially be keeping a log of what I did, and how I did it.&lt;/p&gt;
&lt;p&gt;Last week I started to do this.  I took a silly little web-app that I built for maintaining a &amp;ldquo;now&amp;rdquo; page (which I never used), and started to use it a bit more like a journal.  I added it as a web panel in Vivaldi so that I could open it whenever I was in the browser.  Every so often while I&amp;rsquo;m working on a task, I would quickly jot down a paragraph of what I just did.  If I got stuck, I would write down what the problem is and my thoughts on how I can get out of it.  I originally thought about setting up a reminder to do this like once every 30 minutes, but I found that simply journalling the thoughts as they come work quote well.  At the end of the day I usually had at-least 3 bullet points on what I was working on (the record was 12) and at the beginning of the next day, I cut-and-pasted these bullet points into the Jira work log.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s too early to say whether this would help dispel the notion that the team is not delivering; we may get an update from the product owner when the next retro comes around.  But I&amp;rsquo;ve found the process actually quite useful for myself.  I&amp;rsquo;ve forgotten how beneficial it is to write my thoughts down as they come, and I&amp;rsquo;ve never used a process like this before: everything up to date has been simply todo items or scrawls of the next task to look at.&lt;/p&gt;
&lt;p&gt;I learnt this morning that this is not a new concept.  Dave Winer wrote a blog post about this in 2009, called &lt;a href=&#34;http://scripting.com/stories/2009/08/09/narrateYourWork.html&#34;&gt;Narrate Your Work&lt;/a&gt; which talks about how he adopted this practice at UserLand.  I guess the same thing could be helpful here.  After all, you could probably say the place I work at is distributed in nature, given that there exist a whole continent between the two offices.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s only been a week so the habit has not fully settled in but I hope to continue to do this.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On the Souring Relationship Between Apple and its Developers</title>
      <link>https://lmika.org/2021/05/16/on-the-souring.html</link>
      <pubDate>Sun, 16 May 2021 08:29:24 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/05/16/on-the-souring.html</guid>
      <description>&lt;p&gt;Listening to &lt;a href=&#34;https://atp.fm/430&#34;&gt;episode #430 of ATP&lt;/a&gt; yesterday, it was kind of shocking to hear the loss of good will experienced by the hosts towards Apple and their developer relations.  I can’t say that I blame them though.  Although John’s point about lawyers making the case for Apple is a good one, I do get the same feeling that Marco does about Apples opinion about developers, which is not a positive one.&lt;/p&gt;
&lt;p&gt;It feels a lot like Apple believes that developers building on their platform owe them everything to them, and that without Apple none of these businesses would exist at all.  It does feel a lot like they are entitled to a cut of everything that is happening on their platform.  It does feel a lot like they think a developer releasing their app for free on the App Store is an ungrateful free-loader, that is taking advantage of all their hard work building the platforms and developer toolkits.  This is not just from what’s coming up during the Epic-Apple lawsuit discovery.  Remember what happened to Basecamp last year, when tried to release a new version of their Hey iOS app.&lt;/p&gt;
&lt;p&gt;None of these is accurate in the remotest sense.  Although it is true that some of these business may not be around if iOS was ever invented, it’s not to say that these developers wouldn’t be doing something else.  Also, these developers DO pay for the privilege to build on their platform.  Let’s not forget the $99.00 USD ($149.00 AUD) that these developers pay yearly, not to mention all the hardware they buy to run XCode and these other tools.  And it’s not like any of these tools wouldn’t exist at all if these devs were free to use another IAP provider.  Apple, I assume, would like something like XCode to exist so that they can build their own apps.&lt;/p&gt;
&lt;p&gt;I hope people in Apple are listening to this.  Anti-trust regulation aside, they are doing themselves a massive disservice by treating their developers like this.  These people are their biggest evangelists. I’m not sure it will come to the point where they will abandon the iOS platform, at least not at this stage.  But I could foresee these developers being hesitant to adopt any new app platforms that Apple release, say a future AR platform that will feature hardware devices.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Podcast Roll 2021</title>
      <link>https://lmika.org/2021/05/04/podcast-roll.html</link>
      <pubDate>Wed, 05 May 2021 08:54:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/05/04/podcast-roll.html</guid>
      <description>&lt;p&gt;Yesterday, @Munish had the courage to share his podcast subscriptions&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.  Sensing an opportunity to talk about with what I&amp;rsquo;m currently listening to, even though it may reveal more about myself that I&amp;rsquo;m usually comfortable with, I&amp;rsquo;m taking up his dare and sharing mine.&lt;/p&gt;
&lt;p&gt;So, here is my podcast roll as of early May 2021:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2021/e8ba129a43.png&#34; width=&#34;722&#34; height=&#34;362&#34; alt=&#34;Podcast subscriptions 2021&#34; /&gt;
&lt;p&gt;The shows above can roughly be divided up into the following categories:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Technology:&lt;/strong&gt; This is a topic that I&amp;rsquo;m very interested in so there are fair few of these.  A lot of them are Apple centric, but this is more of an accident than by design.  The second podcast that I started regularly listening to was &lt;a href=&#34;https://daringfireball.net/thetalkshow&#34;&gt;The Talk Show&lt;/a&gt; since I was a casual reader of Daring Fireball at the time (and I still am).  That opened me up to &lt;a href=&#34;https://atp.fm/&#34;&gt;ATP&lt;/a&gt;, which led to a bunch of Relay.fm shows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Business:&lt;/strong&gt; These could probably be lumped into technology, but are focused more on the business side of things rather than product development.  Ben Thompson shows up a lot here, with &lt;a href=&#34;http://exponent.fm/&#34;&gt;Exponent&lt;/a&gt;, &lt;a href=&#34;https://dithering.fm/&#34;&gt;Dithering&lt;/a&gt;, and the &lt;a href=&#34;https://stratechery.com/&#34;&gt;Stratechery&lt;/a&gt; daily update my regular gotos.  The release of that last one helped set some new routines while I was working from home last year.  There&amp;rsquo;s a decent collection of shows from indy developers here as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Science, History, and Philosophy:&lt;/strong&gt; These are where the real heavy podcast listening comes in, the shows that are 2 to 4 hours long and go deep into a particular topic or event.  I have to be in the right kind of mood for these one.  Key drivers here are &lt;a href=&#34;http://www.samharris.org/&#34;&gt;Making Sense&lt;/a&gt;, &lt;a href=&#34;https://wondery.com/shows/sean-carrolls-mindscape/&#34;&gt;Mindscape&lt;/a&gt; and &lt;a href=&#34;http://www.dancarlin.com/&#34;&gt;Hardcore History&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Politics and Society:&lt;/strong&gt; I am somewhat interested in US politics, which could explain the shows that appear in this category.  &lt;a href=&#34;http://thedsrnetwork.com/&#34;&gt;Deep State Radio&lt;/a&gt; is one that I still listen to occasionally.  Also of note is the &lt;a href=&#34;https://www.npr.org/planetmoney&#34;&gt;NPR Planet Money&lt;/a&gt; podcast, which was the first podcast that I&amp;rsquo;ve ever subscribed to.  A recent addition is the &lt;a href=&#34;https://www.abc.net.au/radio/programs/coronacast/&#34;&gt;ABC Coronacast&lt;/a&gt; which provides a decent briefing of the coronavirus pandemic in Australia.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Popular Culture:&lt;/strong&gt; This is probably where all the Incomparable shows come in, when I&amp;rsquo;m in the mood for something lighthearted and funny.  My usual goto&amp;rsquo;s there are the &lt;a href=&#34;https://www.theincomparable.com/gameshow/&#34;&gt;Incomparable Game Show&lt;/a&gt;, &lt;a href=&#34;https://www.theincomparable.com/robot/&#34;&gt;Robot or Not&lt;/a&gt; and &lt;a href=&#34;https://www.theincomparable.com/pants/&#34;&gt;Pants in the Boot&lt;/a&gt;.  One or two Relay.fm shows fall in here as well, including &lt;a href=&#34;https://www.relay.fm/rd&#34;&gt;Reconcilable Differences&lt;/a&gt;, which is a favourite of mine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Micro.blog:&lt;/strong&gt; The final category is more-or-less podcasts that I&amp;rsquo;ve subscribed to while spending time on Micro.blog.  This includes shows like the &lt;a href=&#34;https://monday.micro.blog/&#34;&gt;Micro Monday&lt;/a&gt; podcast, but also shows from those on Micro.blog like &lt;a href=&#34;http://www.coreint.org/&#34;&gt;Core Intuition&lt;/a&gt; and &lt;a href=&#34;https://hemisphericviews.com/&#34;&gt;Hemispheric Views&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s it.  There are a fair few subscriptions listed above, not all of them I regularly listen to.  I guess I should probably unsubscribe from those that I haven&amp;rsquo;t listen to for a while.  I probably keep them around for the same reason why I keep RSS feeds around: just in case something worth listening to pops up in there.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Or &amp;ldquo;follows&amp;rdquo;, which is I guess the new term for it.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>On Basecamp</title>
      <link>https://lmika.org/2021/05/01/on-basecamp.html</link>
      <pubDate>Sat, 01 May 2021 20:57:46 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/05/01/on-basecamp.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been thinking about the incident at Basecamp for most of the week.  I wanted to write something about it earlier, after hearing about the policy changes on Tuesday, but I&amp;rsquo;d figure that it would probably be best to wait a bit and learn more about the issue first.  The last thing I wanted to do is add one more knee-jerk reaction to the mix during the heat of the moment.&lt;/p&gt;
&lt;p&gt;But it is something that I want to comment on.  I&amp;rsquo;ve only recently started following the writing of DHH and Jason Fried but I am aware of their reputation in both their approach to business and their dealings in the open-source community.  To hear of a scandal form a business founded by these two took me aback.  That&amp;rsquo;s probably why I&amp;rsquo;m writing this post at all.  Hearing news like this from companies as large as Facebook or Google, with tens of thousands of employees, doesn&amp;rsquo;t surprise me as much as something arising from a company of about sixty people.&lt;/p&gt;
&lt;p&gt;The thing about being on the outside looking in is that you have an imperfect picture of the whole incident.  And even after reading the &lt;a href=&#34;https://janeyang.org/2021/04/27/an-open-letter-to-jason-and-david/&#34;&gt;open letter&lt;/a&gt;, the report from &lt;a href=&#34;https://www.platformer.news/p/-what-really-happened-at-basecamp?token=eyJ1c2VyX2lkIjo0MzA5MTk4LCJwb3N0X2lkIjozNTcwNDk4NCwiXyI6IlVrSTNSIiwiaWF0IjoxNjE5ODQxMTQyLCJleHAiOjE2MTk4NDQ3NDIsImlzcyI6InB1Yi03OTc2Iiwic3ViIjoicG9zdC1yZWFjdGlvbiJ9.1srC4crAZ5NQ6IRLNoq1QKo4bUhT65G0sfjrLeTZLic&#34;&gt;Casey Newton&lt;/a&gt;, and the response from &lt;a href=&#34;https://world.hey.com/dhh/let-it-all-out-78485e8e&#34;&gt;DHH&lt;/a&gt;, it&amp;rsquo;s still not the full picture, as you cannot be inside the head of those involved.  You can only work with what you read, and how it shapes your view of the principal actors that lives inside your head.  This, along with the current environment that this event occurred in, makes it difficult for me to comment on the matter.&lt;/p&gt;
&lt;p&gt;But I do see a few things that are regrettable.  It&amp;rsquo;s regrettable that such a thing like the Best Customer Names list exists.  Far be it from me to be above making light of those that I deal with on a day-to-day basis, that such a list exited seemed like a step too far, particularly when it deals with those that you are being paid to serve.  I can understand if this was a small startup with a handful of employees working hard to get it off the ground, and there was a need to vent.  But for a company of sixty, especially one that thinks highly about how they operate they write blog posts about, it strikes me as unprofessional, and it does them no credit.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also regrettable that this matter could not be have been dealt with internally.&lt;br&gt;
One thing that struck me was how long the build up to this policy change was.  It did not happen overnight; clearly something was brewing in Basecamp for a little while.  And although I recognise that there were attempts to settle the matter internally, with the list removed and an apology from the founders, I also recognise that we are dealing with people with very strong personalities that are not afraid to air their opinions.  So I wonder whether or not it could have remained an internal matter at all.  Nevertheless, it&amp;rsquo;s in the public now, with the associated backlash and loss of credibility.&lt;/p&gt;
&lt;p&gt;Which brings me to the third regrettable thing, which is the ban on political discussion.  In the abstract, this is not something that I personally support; a company does not operate in a vacuum after all.  Maybe for a company like Basecamp it could act as bit of a circuit breaker if deployed for a little while; I get the sense that the founders thought similar.  But it is a shame that it got to that level, and I do hope that they reverse it once things settle down.  I think it&amp;rsquo;s good of Basecamp to offer severance packages to those employees that disagree with this measure, and wish to find work elsewhere.  That indicates that they are willing to back their employees decision to leave, rather than leave their employees with a bit of a Sophies&amp;rsquo;s choice situation.&lt;/p&gt;
&lt;p&gt;So, what will I be doing going forward?  I&amp;rsquo;ll probably continue to read posts from Jayson and DHH, and continue to use their open-source frameworks.&lt;br&gt;
I&amp;rsquo;m not a paying customer of Basecamp, although I do use their free offering, and I&amp;rsquo;m planning to continue doing so.  Of course, I can understand if others choose a different course of action.  Although I&amp;rsquo;m not a fan of making decisions in the heat of the moment, I can understand and respect the decisions made now, given that more on the incident has surface.  I will be interested in seeing how Basecamp operates going forward.  An event like this could have a lasting impact on a company, it&amp;rsquo;s founders, and it&amp;rsquo;s employees, and it would be interesting in seeing how to move on from it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some thoughts on Apple Subscription Podcasts offering</title>
      <link>https://lmika.org/2021/04/22/some-thoughts-on.html</link>
      <pubDate>Thu, 22 Apr 2021 07:13:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/04/22/some-thoughts-on.html</guid>
      <description>&lt;p&gt;You’ve probably seen the &lt;a href=&#34;https://nathangathright.com/the-future-of-apple-podcasts/&#34;&gt;blog post&lt;/a&gt; outlining the details of Apple’s new Podcast Subscription offering.  The &lt;a href=&#34;https://daringfireball.net/linked/2021/04/21/gathright-apple-podcasts&#34;&gt;latest post from Daring Fireball&lt;/a&gt; has a link to it if you haven’t seen it on Twitter already.&lt;/p&gt;
&lt;p&gt;After reading this, I’m not sure who would choose to go with Apple’s Podcast hosting.  Money aside, it looks like another case of Apple mediating the relationship between host and listener, not to mention keeping subscriber content exclusively on Apple’s app.&lt;/p&gt;
&lt;p&gt;The easy money doesn’t sound like it’s worth the cost of independence that comes from publishing shows on the existing open podcast ecosystem.  Sure it would be harder — you’ll have to build your audience yourself, and it will likely take some time before you can get sponsors or a membership program — but the benefits that come from independence sound to me like it would be worth it.  And once you have the audience, the support will follow: just look at Dithering, ATP, and Relay.&lt;/p&gt;
&lt;p&gt;I hope podcaster’s realise this.  There’s a good thing here: podcasters connecting directly to listeners via the open web.  I don’t want one more large company coming in to wreck that.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Surmounting The Hill</title>
      <link>https://lmika.org/2021/04/08/surmounting-the-hill.html</link>
      <pubDate>Thu, 08 Apr 2021 09:20:18 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/04/08/surmounting-the-hill.html</guid>
      <description>&lt;p&gt;Sometimes adding features to software is like cycling on a hilly road.&lt;/p&gt;
&lt;p&gt;You start off at the bottom of the hill, a little unsure of the hight and gradient, and how well you&amp;rsquo;ll be able to tackle it.  You start the uphill climb, writing new code, adding tests, trying an approach that may not work, backtracking and starting again.   This uphill climb is starting to tire you out.  You&amp;rsquo;re making forward progress, even thought it may not feel like it, but it&amp;rsquo;s slow and you&amp;rsquo;re not sure how much longer you can keep cycling for.&lt;/p&gt;
&lt;p&gt;Eventually, you reach the top: you have a solution that does what it needs to do with  decent test coverage, but it&amp;rsquo;s ugly as sin.  There&amp;rsquo;s an approach there that works, but it&amp;rsquo;s hidden underneath all the attempts that didn&amp;rsquo;t.  You&amp;rsquo;re tired, but you&amp;rsquo;ve got a sense of accomplishment.&lt;/p&gt;
&lt;p&gt;Now the downhill coast begins.  You begin hacking and slashing, deleting code that you no longer need, and generally simplifying the solution, every time running tests to make sure you haven&amp;rsquo;t removed too much.  Travelling further along the road gets easier with each file deleted and each model refactored, until you have something that actually looks good.
Eventually you level out, and you&amp;rsquo;ll need to start peddling again, as you tidy up and add documentation in preparation for the pull request.&lt;/p&gt;
&lt;p&gt;The feature is built, the hill is behind you, and you are further along the road, ready to tackle the next hill.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Year Under The Pandemic</title>
      <link>https://lmika.org/2021/03/12/a-year-under.html</link>
      <pubDate>Fri, 12 Mar 2021 07:07:05 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/03/12/a-year-under.html</guid>
      <description>&lt;p&gt;This was originally a journal entry but I thought I’d share it here as well.  Today is the end of week 52, almost a year to the day that the pandemic became all to real for me.  I’ve taken today day off to spend some time in Warburton.  It was in Warburton last year, almost to the day (13th of March), that things began to get serious.  The news coming out of China and Italy was grave: hundreds of deaths, thousands of new cases, hospitals filling up, lack of ventilators and staff to operate them, PPE shortages, scenes of people locked down in their home.  The outbreak in New York was becoming serious as well, and the US government announced closure of their borders to Europe.&lt;/p&gt;
&lt;p&gt;There were also a number of new cases here as well, it may have been 100 or so around the country.  That Friday a number public events were cancelled, like the AFL and Grand Prix, and the borders were closed off to the rest of the world — nobody was allowed in or out.  There was a run on things at the shops as officials advised people to be stocked for two weeks should you need to isolate.  Toilet paper was in short supply, along with some other staples like pasta and tuna.  There was a general sense of unease around the place.&lt;/p&gt;
&lt;p&gt;It was also the time when I started working from home.  I returned one last time to the office of my old job on Tuesday the following week.  The city was quite quiet.  A lot more people were wearing marks and half the cafes were closed for the afternoon.  I haven’t been back to that office since.  I think the weekend following I stopped meeting my parents for dinner, and only went out for groceries.&lt;/p&gt;
&lt;p&gt;I guess it’s hard to describe how scary the situation was at the time.  The testing and tracing infrastructure was not yet setup, so nobody really knew where the virus was.  The government ensured us that there was no local transmission, but it was difficult to believe them, especially as case numbers were rising rapidly.  The reported death rate was also terrifying — up to 3% at the time but higher in certain places.  I was fearful of everyone I loved, as well as myself, catching the disease and ending up on a ventilator, or worse, dying.  Taiwan was the only country at the time to have curbed the virus: most Western countries were struggling with outbreaks, so at the time I had little faith that Australia would be able to manage the virus as well.&lt;/p&gt;
&lt;p&gt;I was also afraid that the lock downs would last until a vaccine is available.  At the time medical experts were tempering expectations of a speedy delivery of a vaccine.  Turnaround times were usually 1 to 1.5 years.  The fact that they were ready &lt;em&gt;the same year&lt;/em&gt; was considered a bit of a breakthrough (I guess these things really do happen).&lt;/p&gt;
&lt;p&gt;A lot has happened this past 52 weeks.  The nation has managed to keep the virus more or less under control.  There were setbacks though: the second Melbourne lock down was regrettable.  But we have managed to setup a somewhat decent testing and contact tracing regime, along with hotel quarantine, and new local cases have been at or close to zero for most of the past 5 months.  Vaccinations of the border workers, front line workers, and people at risk are currently in progress.&lt;/p&gt;
&lt;p&gt;A sense of normalcy has returned, in what is generally called “Covid normal”.  The borders are still closed to everyone except New Zealanders, and no one is generally permitted to leave the country.  Since November, things have pretty much remained more-or-less opened.  Events like the AFL are back on with small crowds that are socially distanced.&lt;/p&gt;
&lt;p&gt;But the threat remains.  Every day I’m looking on Twitter to see what the latest number of new cases is.  There’s a constant trickle of positive cases coming in from overseas, where the virus is still raging.  There have been new, more contagious and deadly, variants popping up, and it’s a constant struggle to keep them out: we have had to go through a 5 day snap lock-down to stop local transmission of one.&lt;/p&gt;
&lt;p&gt;So there’s little to do but wait.  I appreciate that we’ve managed to gain some semblance of normalcy back, something that I’m aware others around the world have been denied so far.  Eventually this will pass as well, but I’m hoping it doesn’t take another 52 weeks.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Australia’s ABC News shot to the top of the App Store charts following Facebook’s news ban</title>
      <link>https://lmika.org/2021/02/20/link-australias-abc.html</link>
      <pubDate>Sat, 20 Feb 2021 07:30:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/02/20/link-australias-abc.html</guid>
      <description>&lt;p&gt;From &lt;a href=&#34;https://www.theverge.com/2021/2/19/22291406/abc-news-app-top-charts-facebook-ban-australia&#34;&gt;the Verge&lt;/a&gt;:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;The Australian Broadcasting Corporation’s ABC News app shot to the top of Apple’s App Store charts in Australia over the course of the last few days, not long after Facebook banned Australian news sources on its platform.&lt;/p&gt;
&lt;p&gt;[&amp;hellip;]&lt;/p&gt;
&lt;p&gt;ABC News currently sits at No. 2 in the App Store’s overall app rankings in Australia, according to the analytics firm App Annie, and No. 1 in the news app charts. When Patel noticed the change, the app was also briefly No. 1 overall, ahead of Instagram, Facebook Messenger, and the Facebook app itself.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;ve been seeing these banners on ABC News site myself, and I was sceptical that anything would come of it.  Turn&amp;rsquo;s out I&amp;rsquo;m pleasantly mistaken.  It&amp;rsquo;s really good to see people choosing to go to a reputable news source directly.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some uninformed thoughts about the ACCC Media Bargaining Code</title>
      <link>https://lmika.org/2021/02/19/some-uninformed-thoughts.html</link>
      <pubDate>Fri, 19 Feb 2021 12:28:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/02/19/some-uninformed-thoughts.html</guid>
      <description>&lt;p&gt;Yesterday, when the news about the news and Facebook was making the rounds in Australia, I have been wondering about my position about the whole thing.  After listening to the &lt;a href=&#34;https://stratechery.com/2021/google-makes-deal-with-news-corp-facebook-blocks-news-in-australia-microsofts-shamelessness/&#34;&gt;Stratechery Daily Update&lt;/a&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; from Ben Thompson about it, I think my position on this has solidified.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m no fan of Facebook, but I can completely understand why they took the action they did, and I believe that it is in their right to do so.  It could be argued that banning links and pages from government and NPO organisations was wide-reaching, and for that, I think it&amp;rsquo;s important to consider the motivation here.  Was Facebook being cautious about their interpretation of the proposed law, which was written so poorly to suggest that anything related to the goings on in Australia fell under the code?  Where they just being sloppy about which organisations were banned?  Or were they going to such broad lengths to make a point and leverage their negotiation position?   I don&amp;rsquo;t know: all three scenarios seem plausible.&lt;/p&gt;
&lt;p&gt;But I think Ben Thompson is spot-on in making the point that there was a lack of political ground work prior to Facebook taking this action.  Contrast that with Google, which since last month was warning about maybe pulling out of the Australian market if there was a chance that they would violate the code.  I didn&amp;rsquo;t see anything like this from Facebook, and Ben Thompson made the point that this was a lot like the saga around WhatsApp and the privacy changes.  Who knows?  Maybe if they did this, they would have more sympathy in the eyes of the public.&lt;/p&gt;
&lt;p&gt;It may sound like I&amp;rsquo;m taking Facebook side here.  I won&amp;rsquo;t go so far as to say that, but living in a society with free enterprise I don&amp;rsquo;t see why they couldn&amp;rsquo;t do what they did.  No one is preordained with a right to post on Facebook, and expect that they can extract rent for doing so.  It was a mistake on part of the government and media organisations (hi, Mr. Murdoch) to think otherwise.&lt;/p&gt;
&lt;p&gt;The whole mess is regrettable but I hope everyone comes out of this a little wiser.  I think for me it just reinforces the importance of maintaining your own, independent position on the web.  There might be subsequent words on this thought down the line.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;This article is paywalled, but if you are in any way interested in technology, I highly recommend subscribing.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>A $2000.00 Smartphone with Ads</title>
      <link>https://lmika.org/2021/02/17/a-smartphone-with.html</link>
      <pubDate>Wed, 17 Feb 2021 09:46:55 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/02/17/a-smartphone-with.html</guid>
      <description>&lt;p&gt;I just learnt today that the &lt;a href=&#34;https://9to5google.com/2021/02/09/samsung-galaxy-ads-turn-off/&#34;&gt;Samsung Galaxy S21 Ultra has ads&lt;/a&gt;.
I&amp;rsquo;m generally not that interested in Samsung phones, but the idea of putting ads on a device that costs up to $US 2,000.00 offends me so much that I had to comment.&lt;/p&gt;
&lt;p&gt;If I shell out that amount of money for a device, I expect an experience that is worthy of that price.  Having that experienced degraded with crappy banner ads, and a built-in app&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; which hijacks the lock screen, really brings down the intrinsic worth of the whole device to a point that doesn&amp;rsquo;t justify the price they&amp;rsquo;re asking for.  It shows contempt for the customer — you know, the person that, by right, is the &lt;em&gt;owner&lt;/em&gt; of the phone they paid for — and it&amp;rsquo;s just overall &lt;a href=&#34;http://5by5.tv/hypercritical/83&#34;&gt;dishonorable&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I know how difficult it can be for Android OEMs to compete given the current race to the bottom.  But my understanding is that Samsung is actually second to Apple in terms of revenue per device, so I see absolutely no reason why they would consider a move like this.&lt;/p&gt;
&lt;p&gt;Yeah, I know it&amp;rsquo;s a first world problem, but I&amp;rsquo;m seeing more and more phone vendors thinking that the device that they sell, supposably at a profit, is also a vector for which they can push marketing messages through without regard, and  I really don&amp;rsquo;t want that to happen.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;The built in app, called &amp;ldquo;&lt;a href=&#34;https://play.google.com/store/apps/details?id=com.samsung.sree&amp;amp;hl=en_US&amp;amp;gl=US&#34;&gt;Samsung Global Goals&lt;/a&gt;&amp;rdquo;, is designed to promote charitable causes through the use of promotions.  I appreciate the motivation of funding these causes, but not the approach they use to do so.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Seth Godin on Rank Choice Voting</title>
      <link>https://lmika.org/2021/02/10/seth-godin-on.html</link>
      <pubDate>Wed, 10 Feb 2021 07:22:54 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/02/10/seth-godin-on.html</guid>
      <description>&lt;p&gt;Seth Godin on &lt;a href=&#34;https://seths.blog/2021/02/the-surprising-problem-with-ranked-choice-voting/&#34;&gt;Rank Choice Voting&lt;/a&gt;:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;The surprising thing? In a recent primary in New York, some people had trouble with the new method. It’s not that the method of voting is particularly difficult. The problem is that we’ve trained ourselves to be RIGHT. To have “our candidate” and not be open (or pushed) to even consider that there might be an alternative. And to feel stress when we need to do the hard work of ranking possible outcomes, because that involves, in advance, considering acceptable outcomes that while not our favorite, would be acceptable.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;Living in a country that has rank-choice voting across the board, I could be biased in this, but I think that&amp;rsquo;s one of the beautiful things about this voting system.  It changes the thinking of &amp;ldquo;will my candidate win&amp;rdquo; to &amp;ldquo;what candidate can I live with&amp;rdquo;.  A candidate representing several thousand people is not going to be everyones first preference, but they might be happy enough if they&amp;rsquo;re their second or third.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Feature Request for Twitter, Free of Charge</title>
      <link>https://lmika.org/2021/01/23/a-feature-request.html</link>
      <pubDate>Sat, 23 Jan 2021 10:41:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/01/23/a-feature-request.html</guid>
      <description>&lt;p&gt;It looks like Twitter&amp;rsquo;s product design team need some help.  Their recent ideas, &amp;ldquo;inspired&amp;rdquo; by the features of other companies like Snap (Stories) and Club House (Audio Clips), don&amp;rsquo;t seem to be setting the world on fire.  Well, here&amp;rsquo;s an idea for them to pursue, free of charge.&lt;/p&gt;
&lt;p&gt;A lot of people I follow seem to use Twitter threads for long-form writing.  This might be intentional, or it might be because they had a fleeting thought that they developed on the spot.  But the end result is a single piece of writing, quantised over a series of tweets, and assembled as a thread.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d argue that consuming long-form writing this way is suboptimal.  It&amp;rsquo;s doable: I can read the thread as they&amp;rsquo;re currently presented in the Twitter app and website.  But it would also be nice to read it as a single web page, complete with a link that can be used to reference the thread as a whole.&lt;/p&gt;
&lt;p&gt;Now, I don&amp;rsquo;t see these writers changing their behaviour any time soon.  It&amp;rsquo;s obvious that people see value in producing content this way, otherwise they would do something else.  But it&amp;rsquo;s because of this value that Twitter should lean in to this user behaviour, and make it easier to consume these threads as blog posts.  The way they should do this is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Offer the author the choice to publish the thread on a single web page, complete with a permalink URL that can be shared, once they have finished writing it.  This could be on demand as they&amp;rsquo;re composing the thread, or it can be done automatically once the thread reaches a certain size.&lt;/li&gt;
&lt;li&gt;Provide the link to this web page on the first tweet of the thread.  The reader can follow the link to consume the thread on a single page, or can use the link to reference the thread as a whole.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I know that this is possible now, with things like the Thread Reader app, but there are some benefits for Twitter adding first party support for this.  The first being that it keeps user on their &amp;ldquo;property&amp;rdquo;, especially if they add this feature to the app as well as the Twitter website.  This neutralises the concern of sending the author or reader to another site to consume or publish their content, thereby feeding into the second benefit, which is that it elevates Twitter as a platform for writing long-form writing, in addition to microblogging.  If their content can be enjoyed in a nicer reading experience, more writers would use Twitter to write this form of content, keeping users and writers on Twitter.  The user benefits, the publisher benefits, and Twitter benefits.  Win-win-win all round.&lt;/p&gt;
&lt;p&gt;So there you are, Twitter, the next feature for the product backlog.  It could be that I&amp;rsquo;m the only that that wants this, but I personally see more value in this than their other pie-in-the-sky endeavours that Twitter is perusing.&lt;/p&gt;
&lt;p&gt;One final thing: I&amp;rsquo;m a big proponent of the open web and owning your own content, so I don&amp;rsquo;t endorse this as a way to publish your work.  I&amp;rsquo;m coming at this as a reader of those that choose to use Twitter this way.  Just because they&amp;rsquo;re OK with Twitter owning their content this way doesn&amp;rsquo;t mean I should have a less-than-adequate reading experience.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Adding Blog Posts to Day One using RSS</title>
      <link>https://lmika.org/2021/01/22/adding-blog-posts.html</link>
      <pubDate>Fri, 22 Jan 2021 08:04:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/01/22/adding-blog-posts.html</guid>
      <description>&lt;p&gt;Prior to joining Micro.blog, I had a journal in Day One, which was the sole destination for all my personal writing.  I still have the journal, mainly for stuff that I keep to myself, but since starting the blog, I always wondered how I could get my posts in there as well.  It would be nice to collect everything I&amp;rsquo;ve written in a single place.  In fact, there was a time I was considering building something that used Day One&amp;rsquo;s &lt;a href=&#34;https://help.dayoneapp.com/en/articles/3116925-create-entries-with-email&#34;&gt;email to entry&lt;/a&gt; feature, just so I could achieve this.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve since discovered, after &lt;a href=&#34;https://www.patrickrhone.net/journal-without-journaling-using-day-one/&#34;&gt;reading this blog post&lt;/a&gt;, that Day One actually has an IFTTT integration.  This means that it&amp;rsquo;s possible to setup an applet that takes new entries from an RSS feed, and add them as entries into a Day One journal.&lt;/p&gt;
&lt;p&gt;I decided to give this a go, and it was quite simple to set up.  I&amp;rsquo;m using the blog&amp;rsquo;s RSS feed as the source, and a new &amp;ldquo;Blog Journal&amp;rdquo; as the destination new entries will be created in.  Setting up the integration with Day One was straight forward, however I had to make sure that the encryption mechanism of the new journal was &amp;ldquo;Standard&amp;rdquo; instead of &amp;ldquo;End-to-end&amp;rdquo;.  This is slightly less secure but everything in that journal is going to be public anyway, so I&amp;rsquo;m not too concerned about that.&lt;/p&gt;
&lt;p&gt;The Day One integration allows you to select the journal, any tags, and how the content is to look.  This follows something resembling a template and allows the use of placeholders to select elements of the post, like the title and the body.  The integration also allows you to specify the location and entry image, although I left that blank.  In fact, I ran into trouble trying to set the entry image when a post didn&amp;rsquo;t have one: journal entries were being created with a generic IFTTT 404 image instead.&lt;/p&gt;
&lt;p&gt;So far it&amp;rsquo;s been working well.  I&amp;rsquo;ve only being writing short posts without a title — this will be the first long post with a title — but they&amp;rsquo;ve been showing up in Day One without any issues.  There are still some unknowns about this integration.  For example, I don&amp;rsquo;t know how images will work.  I would hope that even though they&amp;rsquo;re links that Day One will handle them properly if you wanted to do something like make a physical book.  It&amp;rsquo;s likely I&amp;rsquo;ll need to make a few tweaks before this is perfect.&lt;/p&gt;
&lt;p&gt;But all-in-all, I&amp;rsquo;m pleased with this setup.  It&amp;rsquo;s nice seeing everything I write show up in a single place now.  In fact, I&amp;rsquo;m wondering if there are other things this integration could be useful for, now that I know that all that needs doing is setting up an RSS feed.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some thoughts on app permissions in macOS</title>
      <link>https://lmika.org/2021/01/12/some-thoughts-on.html</link>
      <pubDate>Tue, 12 Jan 2021 09:46:37 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/01/12/some-thoughts-on.html</guid>
      <description>&lt;p&gt;It’s funny how the casual meandering of your mind can be a source of inspiration.  This morning, my mind casually turned to thinking about all the work that Mac developers need to do to get access to privileged APIs — like location, contacts, or the accessibility APIs.  My experience of going through the motions to enable these permissions for the apps I use, along with hearing of the lengths developers go through to make this as seamless as they can, reveals to me the clunkiness that this entails.  I could imaging this being a huge source of frustration for these developers, not to mention a huge source of support requests.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s laudable of Apple to lock-down access to these APIs: the days of assumed access to everything is over, and I believe we are better for it.  But I believe it’s worth considering ways to streamline the process of granting these permissions, making it easier to work with them without any sacrifice to the user&amp;rsquo;s security or privacy.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m wondering if one such approach could be to do something similar to how permissions for web pages are managed.  In most browsers, clicking on the pad-lock icon just left of the URL brings up a popup listing all the capabilities the website has access to, such as whether they can use the microphone, whether they can show notifications, etc.  Within this popup, the user can grant or revoke these permissions, controlling which APIs the JavaScript on the website can use.  The web-site itself cannot do anything with this pane: all that can be done is to provide instructions on how enable these permissions, and progressively enabling or disabling features based on whether those permissions are granted.&lt;/p&gt;
&lt;p&gt;Maybe something similar would help for macOS apps.  What I had pictured in my head is something similar to the following (no mock-ups, sorry; you’ll need to use your imagination):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The app will disclosed in their Info.plist the privileged APIs that they need access to (they might do this already, I’m really not sure).&lt;/li&gt;
&lt;li&gt;A new “Permissions” menu item will be added by the OS to the application menu.  This menu item is beyond the control of the app: it could not be automatically triggered or otherwise controlled.&lt;/li&gt;
&lt;li&gt;Clicking this menu item will bring up a window listing all the permissions that the app has disclosed.  Beside each one is a toggle, allowing the user to turn them on and off at will.  Like the menu item, this window is managed by the OS itself, not the application, and could require users to enter their admin passwords before enabling the permissions if necessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I can see this approach having some benefits to both users and developers.  This will reduce the level of friction involved in dealing with permissions, making it easier for the user to enable, and most importantly &lt;em&gt;disable&lt;/em&gt;, these permissions when needed.  The app developer has an easier time asking the user to enable these permissions: no need to do things like open the Settings app and draw arrows on screen pointing to the accessibility pane.  Since this is all managed by the OS, the various setting panes can still exists, but they become secondary avenues to controlling these permissions.  Conceptually, the permissions belong to the app, which maintains the wholistic app paradigm that Apple is moving towards, not to mention eliminating the need for the user to context switch when they open the Settings app.&lt;/p&gt;
&lt;p&gt;I’m not a Mac developer so I’m not sure how possible this is.  I can’t imagine this approach would break any of the existing APIs of AppKit: this will all be stuff that Apple needs to do.  I’d be interested in hearing what others think of this approach, so let me know your thoughts on this.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>First encounters with GitHub (and Substack)</title>
      <link>https://lmika.org/2021/01/11/first-encounters-with.html</link>
      <pubDate>Mon, 11 Jan 2021 16:05:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/01/11/first-encounters-with.html</guid>
      <description>&lt;p&gt;All these new Substack newsletters that I&amp;rsquo;m seeing reminds me of my first encounter with GitHub.&lt;/p&gt;
&lt;p&gt;Back in 2009, I was checking out the source code of an open-source library we were using.  Clicking on the link to the  source bought me to this strange, new code-hosting service that I&amp;rsquo;ve never seen before.  As someone who was use to the heaviness that was SourceForge, or the boring uniformity that was Google Code, the experience felt very minimal and slightly unintuitive.  It took me a while, for instance, to realise that the version tags were selectable from a drop-down list.  I also thought that it was quite restrictive to only offer checking out the source code with this weird SCM client called &amp;ldquo;git&amp;rdquo;.  The whole experience left me thinking of this website as rather niche, and I never really expected to see it that often, given that Source Forge or Google Code reigned supreme at the time.&lt;/p&gt;
&lt;p&gt;I held this believe for a year or two.  Then, as I continued to deal with different open-source projects, I started noticing more and more of them showing up on this weird new service.  This was infrequent at first: maybe around one in ten projects or so.  But the ratio was starting to shift faster and faster, soon becoming one in eight projects, then one in five.  Around this time, GitHub was starting to gain momentum in the technological zeitgeist: projects announced that they were moving off SourceForge and migrating their codebase to GitHub.  Then, Google Code announce that they were shutting down, and that they were migrating projects over to GitHub.  Eventually, a tipping point was reached, and GitHub was the code hosting service for pretty much every project I encountered.&lt;/p&gt;
&lt;p&gt;My experience with Substack was similar, except on a much shorter timescale. I remember subscribing to my first Substack newsletter back in 2019.  I was already a Stratechy subscriber so the whole email-newsletter thing was familiar to me.  But it was another case of being a little unimpressed with the Substack experience — difficult to read on-line, impossible to get it as RSS, weird font choices, etc. — that I was expecting the platform to remain relatively niche.  Contrast that to today, where every forth or fifth link I see is to a Substack newsletter, and not a month goes by where a new Substack publication is announced.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s no real lesson to this (or if there is, I&amp;rsquo;m too dense to see it).  Just the amusing observation of first encountering something that, to you, seems rather niche and unusual until one day, faster than you can blink, it is the only thing anyone uses.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Quick Review of the Year</title>
      <link>https://lmika.org/2021/01/01/a-quick-review.html</link>
      <pubDate>Fri, 01 Jan 2021 09:38:12 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/01/01/a-quick-review.html</guid>
      <description>&lt;p&gt;Here are a few words about the year gone by, and what I&amp;rsquo;m hoping to focus on the year ahead.  It&amp;rsquo;s not a full &amp;ldquo;year in review&amp;rdquo; type post, although there&amp;rsquo;s a bit of that, and there&amp;rsquo;s no dramatic insight or anything of that nature.  It&amp;rsquo;s more of a chance for reflection, plus a bit of a document for future me on what the year was like.&lt;/p&gt;
&lt;p&gt;Personally, as difficult as this past year was, I wouldn&amp;rsquo;t necessarily say 2020 was a bad year.  I know I&amp;rsquo;m saying this from a position of good fortune: I didn&amp;rsquo;t loose my job, or my house, or my health.  So I know there are a lot of others that have experienced a much worst year than I have.  But for me, I&amp;rsquo;m coming out of this year feeling a little better than I have the previous couple of years gone by.&lt;/p&gt;
&lt;p&gt;I think a big reason for this was that I was forced to change my routine.  I&amp;rsquo;m someone that can operate on a routine that don&amp;rsquo;t change for a long period of time.  There are benefits to this but it does mean that every passing year feels more-or-less like the previous one, and I always find myself feeling a little bad on New Years Eve for not &amp;ldquo;doing something different&amp;rdquo;.  Being forced to change my routine by the pandemic and resulting lock-downs was a small positive that came out of an otherwise bad situation.  The did mean that I could no longer rely on the the various chronological anchors, like birthdays or even going to the office, that are useful for experiencing the passage of time, resulting in a year that felt to be passing too slowly and quickly simultaneously.  But it also added some variety to the activities that got me through the day, which resulted in a year that was slightly more novel to the previous few.&lt;/p&gt;
&lt;p&gt;Working from home also provided an opportunity to try something new.  The whole working from home experience was something that I was curious about, and I am glad that I had an opportunity to try it out.  It worked out better than I expected: although there were times when I really did miss working closely with other people, I found that I could work effectively at home as long as I had work to do.  The lack of a commute also meant that I had more time on my hands.  I&amp;rsquo;m glad that I didn&amp;rsquo;t just spend that time just coding on personal projects or vegging out on the couch (although there was a bit of that as well).  I joined Micro.blog, which was probably one the best decisions I&amp;rsquo;ve made this year.  I also learnt a lot about writing and creativity, and I got back into music composition, something that I have neglected for a while.&lt;/p&gt;
&lt;p&gt;Writing and publishing is something that I&amp;rsquo;m hoping to continue in 2021.  I hope to setup new routines and systems to write more often, both on this Micro.blog and another writing project that I&amp;rsquo;m in the process of starting.  I&amp;rsquo;m trying a few things to keep myself true to this, like keeping a daily log (which is private at the moment, but I&amp;rsquo;m hoping to make it public eventually).  Publishing things online is something that I need to work on a bit more but it is also an area that I&amp;rsquo;m hoping to work on. Although I not seriously following the &lt;a href=&#34;https://www.thethemesystem.com&#34;&gt;yearly theme system&lt;/a&gt;, if I had to choose, I plan to make this one a year of &amp;ldquo;sharing&amp;rdquo;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Hope you all have a Happy New Year and here&amp;rsquo;s to a great 2021.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Incidentally, the theme for 2020 was &amp;ldquo;chance&amp;rdquo;, which became a little morbid as the year went on.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Vivaldi - My Recommended Alternative to Chrome</title>
      <link>https://lmika.org/2020/12/16/vivaldi-my-recommended.html</link>
      <pubDate>Wed, 16 Dec 2020 07:32:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/12/16/vivaldi-my-recommended.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m seeing a few people on Micro.blog post about how &lt;a href=&#34;https://chromeisbad.com&#34;&gt;Chrome Is Bad&lt;/a&gt;.  Instead of replying to each one with my recommendations, I figured it would be better just to post my story here.&lt;/p&gt;
&lt;p&gt;I became unhappy with Chrome about two years ago.  I can&amp;rsquo;t remember exactly why, but I know it was because Google was doing something that I found distasteful.  I was also getting concerned about how much data I was making available to Google in general.  I can prove nothing, but something about using a browser offered for free by an advertising company made me feel uneasy.&lt;/p&gt;
&lt;p&gt;So I started looking for alternatives.  The type of browser I was looking for needed the following attributes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It had to use the Blink rendering engine: I liked how Chrome rendered web pages.&lt;/li&gt;
&lt;li&gt;It had to offer the same developer tools as Chrome: in my opinion, they are the best in the business.&lt;/li&gt;
&lt;li&gt;It had to run Chrome plugins.&lt;/li&gt;
&lt;li&gt;It had to be a bit more customisable than Chrome was: I was getting a little bored with the Chrome&amp;rsquo;s minimalist aesthetic, and there were a few niggling things that I wanted to change.&lt;/li&gt;
&lt;li&gt;It had to have my interests at hand: so no sneaky business like observing my web browsing habits.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I heard about the &lt;a href=&#34;https://vivaldi.com&#34;&gt;Vivialdi browser&lt;/a&gt; from an &lt;a href=&#34;https://arstechnica.com/information-technology/2015/03/hands-on-with-vivaldi-the-new-web-browser-for-power-users/&#34;&gt;Ars Technica article&lt;/a&gt; and I decided to give it a try.  The ambition of the project intrigued me: building a browser based on Chromium but with a level of customisation that can make the browser yours.  I use a browser &lt;em&gt;every day&lt;/em&gt; so the ability to tailor the experience to my needs was appealing.  I decided to download it and give it a try.  First impressions were mixed: the UI is not a polished as Chrome&amp;rsquo;s, and seeing various controls everywhere was quite a shock compared to the minimalism that Chrome offered.  But I eventually set it as my default browser, and over time I grew to really like it.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m now using Vivialdi on all my machines, and my Android phone as well.  It feels just like Chrome, but offers so much more.  I came to appreciate the nice little utilities that the developers added; like the note taking panel which comes in really handy for writing Jira comments that survive when Jira decides to crash.  It&amp;rsquo;s not as stable as Chrome; it feels a tad slower, and it does occasionally crash — this is one of those browsers that need to be closed at the end of the day.  But other than that, I&amp;rsquo;m a huge fan.&lt;/p&gt;
&lt;p&gt;So for those looking for an alternative to Chrome that feels a lot like Chrome, I recommend giving &lt;a href=&#34;https://vivaldi.com&#34;&gt;Vivialdi&lt;/a&gt; a try.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Revisiting the decision to build a CMS</title>
      <link>https://lmika.org/2020/12/13/revising-the-decision.html</link>
      <pubDate>Sun, 13 Dec 2020 09:49:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/12/13/revising-the-decision.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s been almost a month since I wrote about my decision to &lt;a href=&#34;https://lmika.org/2020/11/19/on-building-vs.html&#34;&gt;write a CMS&lt;/a&gt; for a blog that I was planning.  I figured it might be time for an update.&lt;/p&gt;
&lt;p&gt;In short, and for the &lt;em&gt;second&lt;/em&gt; time this year, I&amp;rsquo;ve come to the conclusion that maintaining a CMS is not a good use of my time.  The largest issue was the amount of effort that would have been needed in order to work on the things that don&amp;rsquo;t relate to content, such as styling.  I&amp;rsquo;m not a web designer, so building the style from scratch would have taken a fair amount of time, which would have eaten into the amount of time I would have spent actually writing content.  A close second was the need to add additional features to the CMS that were missing, like the ability to add extra pages, and a RSS feed.  If I were to do this properly without taking any shortcuts, this too would have resulted in less time spent on content.&lt;/p&gt;
&lt;p&gt;The final issue is that the solutions that I was trying to optimise for turned out not to be as big a deal as I first thought, such as the &amp;ldquo;theoretical ability to blog from anywhere&amp;rdquo;.  I&amp;rsquo;ve tried using the CMS on the iPad a few times, and although it worked, the writing experience was far from ideal, and making it so would have meant more effort put into the CMS.  In addition to this, I&amp;rsquo;ve discovered that I prefer working on the blog using my desktop, as I&amp;rsquo;ll more likely be in the state of mind to do so.  Since my desktop already has tools like Git, I already had what I needed to theoretically blog from anywhere, and it was no longer necessary to recreate this requirement within the CMS itself.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;ve switched to a statically generated Hugo site served using GitHub Pages.  I&amp;rsquo;m writing blog posts using &lt;a href=&#34;https://www.nova.app/&#34;&gt;Nova&lt;/a&gt;, which is actually a pretty decent tool for writing prose.  To deploy, I simply generate the site using Hugo&amp;rsquo;s command line tool, and commit it all to Git.  GitHub Pages does the rest.&lt;/p&gt;
&lt;p&gt;After working this way for a week and a half, it turns out that I actually prefer the simplicity of this approach.  At this stage both the CMS, and the blog it would have powered, has been in the works for about a month.  The result is zero published posts, and it will probably not be launched at all&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.  Using the new approach, the new blog is &lt;a href=&#34;https://www.refactoredtelegram.net&#34;&gt;public right now&lt;/a&gt; and I have already written 5 posts on it within the last 11 days, which I consider a good start.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll see how far I&amp;rsquo;ll go with this new approach before I consider a custom CMS again, but I think it&amp;rsquo;s one that I&amp;rsquo;ve managed to get some traction now, particularly since it actually resulted in something.  I think it&amp;rsquo;s also an approach I will adopt for new blogs going forward.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;This is not wholly because of the CMS: the focus of the blog would have been a bit narrower than I&amp;rsquo;d would have liked; but the custom CMS did has some bearing over this decision.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>A Brief Look at Stimulus</title>
      <link>https://lmika.org/2020/12/08/a-brief-look.html</link>
      <pubDate>Tue, 08 Dec 2020 10:00:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/12/08/a-brief-look.html</guid>
      <description>&lt;p&gt;Over the last several months, I&amp;rsquo;ve been doing a bit of development
using &lt;a href=&#34;https://gobuffalo.io/en&#34;&gt;Buffalo&lt;/a&gt;, which is a rapid web development framework in Go,
similar to &lt;a href=&#34;https://rubyonrails.org&#34;&gt;Ruby on Rails&lt;/a&gt;.  Like Ruby on Rails, the
front-end layer is very simple: server-side rendered HTML with a bit of jQuery augmenting
the otherwise static web-pages.&lt;/p&gt;
&lt;p&gt;After a bit of time, I wanted to add a bit of dynamic flare to the frontend, like automatically
fetch and update elements on the page.  These projects were more or less small
personal things that I didn&amp;rsquo;t want to spend a lot of time maintaining, so
doing something dramatic like rewriting the UI in React or Vue would have been overkill.
jQuery was available to me but using it always required a bit of boilerplate to setup the bindings
between the HTML and the JavaScript.  Also, since Buffalo uses Webpack to produce a single, minified
JavaScript file that is included on every page, it would also be nice to have a mechanism
to selectively apply the JavaScript logic based on the attributes on the HTML itself.&lt;/p&gt;
&lt;p&gt;I since came across &lt;a href=&#34;https://stimulusjs.org&#34;&gt;Stimulus&lt;/a&gt;, which looks to provide what I was looking for.&lt;/p&gt;
&lt;h2 id=&#34;a-whirlwind-tour-of-stimulus&#34;&gt;A Whirlwind Tour of Stimulus&lt;/h2&gt;
&lt;p&gt;The best places to look if you&amp;rsquo;re interest in learning about Stimulus is
the &lt;a href=&#34;https://stimulusjs.org/handbook/introduction&#34;&gt;Stimulus handbook&lt;/a&gt;, or for those that
prefer a video, there is one available at
&lt;a href=&#34;https://www.driftingruby.com/episodes/the-stimulus-2-0-tutorial&#34;&gt;Drifting Ruby&lt;/a&gt;.  But to provide
some context for the rest of this post, here&amp;rsquo;s an &lt;em&gt;extreamily&lt;/em&gt; brief introduction to Stimulus.&lt;/p&gt;
&lt;p&gt;The basic element of an application using Stimulus is the &lt;em&gt;controller&lt;/em&gt;, which is the JavaScript aspect of your
frontend.  A very simple controller might look something like the following (this example was taken from
the Stimulus home page):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// hello_controller.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;Controller&lt;/span&gt; } &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stimulus&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;extends&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Controller&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;targets&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;output&amp;#34;&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;greet&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;outputTarget&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;textContent&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;`Hello, &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;nameTarget&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;!`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A controller can have the following things (there are more than just these two, but these are the minimum to make the controller
useful):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Targets&lt;/em&gt;, which are declared using the static &lt;code&gt;target&lt;/code&gt; class-level attribute, and are used to reference
individual DOM elements within the controller.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Actions&lt;/em&gt;, which are methods that can be attached as handlers of DOM events.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The link between the HTML and the JavaScript controller is made by adding
a &lt;code&gt;data-controller&lt;/code&gt; attributes within the HTML source, and setting it to the name of the controller:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data-controller&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data-hello-target&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&amp;gt;  	
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data-action&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;click-&amp;gt;hello#greet&amp;#34;&lt;/span&gt;&amp;gt;Greet&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;span&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data-hello-target&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;output&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;span&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If everything is setup correctly, then the controllers should be automatically attached to the HTML elements with the
associated &lt;code&gt;data-controller&lt;/code&gt; annotation.
Elements with &lt;code&gt;data-*-target&lt;/code&gt; and &lt;code&gt;data-action&lt;/code&gt; attributes will also be attached as targets and actions respectively
when the controller is attached.&lt;/p&gt;
&lt;p&gt;There is some application setup that is not included in the example above.
Again, please see the &lt;a href=&#34;https://stimulusjs.org/handbook/introduction&#34;&gt;handbook&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;why-stimulus&#34;&gt;Why Stimulus?&lt;/h2&gt;
&lt;p&gt;Far be it for me to suggest yet another frontend framework in an otherwise large and churning
ecosystem of web frontend technologies.  However, there is something about Stimulus which seems appealing
for small projects that product server-side rendered HTML.  Here are the reasons that attract it to me:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;It was written by, and is used in, &lt;a href=&#34;https://basecamp.com/&#34;&gt;Basecamp&lt;/a&gt;, which gives it some industry
credibility and a high likelihood that it will be maintained (in fact, I believe version 2.0 was just release).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It doesn&amp;rsquo;t promise the moon: it provides a mechanism for binding to HTML elements, reacting to
events, and providing some mechanisms for maintaining state in the DOM,
and &lt;em&gt;that&amp;rsquo;s it.&lt;/em&gt;  No navigation, no pseudo-DOM with diffing logic, no requirement for maintaining a global
state with reducers, no templating: just a simple mechanism for binding to HTML elements.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It plays nicely with jQuery.  This is because the two seem to touch different aspects of web-development:
jQuery with providing a nicer interface with the DOM, and Stimulus with providing a way to easily
bind to DOM elements declared via HTML attributes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;That said, it doesn&amp;rsquo;t require jQuery.  You are free to use whatever JavaScript framework that you need, or no framework at all.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It maintains the relationship between HTML and JavaScript, even if the DOM is changed dynamically.  For example,
modifying the &lt;code&gt;innerHTML&lt;/code&gt; by including an element with an appropriate &lt;code&gt;data-controller&lt;/code&gt; attribute will automatically
setup a new controller and bind it to the new elements, all without having you to do anything yourself.&lt;/p&gt;
&lt;p&gt;It doesn&amp;rsquo;t matter how the HTML gets to the browser, whether it&amp;rsquo;s AJAX or front-end templates.
It will also work with manual DOM manipulation, like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;elem&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; document.&lt;span style=&#34;color:#a6e22e&#34;&gt;createElement&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;div&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;elem&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;setAttribute&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;data-controller&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;document.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;elem&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This allows a dynamic UI without having to worry about making sure each element added to the DOM is appropriately
decorated with the logic that you need, something that was difficult to do with jQuery.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Finally, and probably most importantly, it does not require the UI to be completely rewritten as JavaScript.
In fact, it seems to be built with
&lt;a href=&#34;https://stimulusjs.org/handbook/origin&#34;&gt;with this use case in mind&lt;/a&gt;.  The
tag-line on the site, &lt;em&gt;A modest JavaScript framework for the HTML you already have&lt;/em&gt;, is true to it&amp;rsquo;s word.&lt;/p&gt;
&lt;p&gt;So, if you have a web-app with server-side rendering, and you need something a bit more — but not too much more —
than what jQuery or native DOM provides, this JavaScript framework might be worth a look.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some uninformed thoughts about Salesforce acquiring Slack</title>
      <link>https://lmika.org/2020/12/02/some-uninformed-thoughts.html</link>
      <pubDate>Wed, 02 Dec 2020 12:51:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/12/02/some-uninformed-thoughts.html</guid>
      <description>&lt;p&gt;John Gruber &lt;a href=&#34;https://daringfireball.net/linked/2020/12/01/salesforce-slack&#34;&gt;raised an interesting point&lt;/a&gt; about the future of Slack after being purchased by Salesforce:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;First, my take presupposes that the point of Slack is to be a genuinely good service and experience. [&amp;hellip;] To succeed by appealing to people who care about quality. Slack, as a public company, has been under immense pressure to do whatever it takes to make its stock price go up in the face of competition from Microsoft’s Teams.&lt;/p&gt;
&lt;p&gt;[&amp;hellip;]&lt;/p&gt;
&lt;p&gt;Slack, it seems to me, has been pulled apart. What they ought to be entirely focused on is making Slack great in Slack-like ways. Perhaps Salesforce sees that Slack gives them an offering competitive to Teams, and if they just let Slack be Slack, their offering will be better — be designed for users, better integrated for developers.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;When I first heard the rumour of Salesforce was buying Slack, I really had no idea why they would.  The only similarity between the markets the two operate in is that they are things businesses buy, and I saw no points of &lt;em&gt;synergy&lt;/em&gt; between the two products that would make this acquisition worth it.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m starting to come round to the thinking that the acquisition is not to integrate the two products, at least not to the degree I was fearing.  I think Gruber&amp;rsquo;s line of thinking is the correct one: that Salesforce recognises that it&amp;rsquo;s in their interest to act as Slack&amp;rsquo;s benefactor to ensure that they can continue to build a good product.   Given that Salesforce has bought Tableau and Heroku and more-or-less left them alone, there&amp;rsquo;s evidence that the company can do this.&lt;/p&gt;
&lt;p&gt;As to what Salesforce gets out of it, Jason Calacanis raises a few good reasons in his &lt;a href=&#34;https://pca.st/otufjf4p#t=10m29s&#34;&gt;Emergency Pod on the topic&lt;/a&gt; around markets and competition.  Rather than attempt to explain them, I recommend that you take a listen and hear them from him.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Why I&#39;m Considering Building A Blogging CMS</title>
      <link>https://lmika.org/2020/11/19/on-building-vs.html</link>
      <pubDate>Thu, 19 Nov 2020 08:29:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/11/19/on-building-vs.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m planning to start a new blog about Go development and one of the things that I&amp;rsquo;m currently torn on is how to host it.  The choice look to be either using a service like &lt;a href=&#34;https://blot.im/&#34;&gt;blot.im&lt;/a&gt; or &lt;a href=&#34;https://micro.blog/&#34;&gt;micro.blog&lt;/a&gt; or some other hosting service, using a static site generation tool like &lt;a href=&#34;https://gohugo.io&#34;&gt;Hugo&lt;/a&gt;, or building my own CMS for it.  I know that one of the things people tell you about blogging is that building your CMS is not worth your time: I myself even described it as &amp;ldquo;second cardinal sin of programming&amp;rdquo; on my &lt;a href=&#34;https://lmika.micro.blog/2020/07/12/ive-signed-up.html&#34;&gt;first post to micro.blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Nevertheless, I think at this stage I will actually do just that.  Despite the effort that comes from building a CMS I see some advantages in doing so:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The (theoretical) ability to blog from anywhere:&lt;/strong&gt;  This is one of the weaknesses of static site hosting that I&amp;rsquo;ve run into when trying this approach before.  I tend to work on different machines throughout the week, which generally means that when I find inspiration, I don&amp;rsquo;t have the source files on hand to work on them.  The source files are kept in source control and are hosted on GitHub, but I&amp;rsquo;ve find that I tend to be quite lax with making sure I have the correct version checked out and that any committed changes are pushed.  This is one of the reasons why I like micro.blog: having a service with a web interface that I can just log into, and that will have all the posts there, means that I can work on them as long as I have an internet connection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Full control over the appearance and workflow:&lt;/strong&gt; Many of the other services provide the means for adjusting the appearance of the web-page, so this is only a minor reason for taking on this effort.  But one thing that I would find useful is to have some control over is the blogging workflow itself.  There are some ideas that I might like to include, like displaying summaries in the main page, or sharing review links for posts prior to publishing them.  Being able to easily do that in a codebase that I&amp;rsquo;m familiar with would help.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Good practice of my skills:&lt;/strong&gt; As someone who tends to work on backend systems for his day-to-day job, some of my development and operational experience are a little rusty.  Building, hosting and operating a site would provide an opportunity to exercise these muscles, and may also come in handy if I were to choose to build something for others to use (something that I&amp;rsquo;ve been contemplating for a while).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note that price is not one of these reasons.  In fact it might actually cost me a little more to put together a site like this.  But I think the experience and control that I hope to get out of this endeavour might be worth it.&lt;/p&gt;
&lt;p&gt;I am also aware of some of the risks of this approach.  Here is how I plan to mitigate them:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Security and stability:&lt;/strong&gt; This is something that comes for free from a blogging platform that I&amp;rsquo;ll need to take on myself.  There&amp;rsquo;s always a risk with putting a new system onto the internet, and having a web site with remote administration is an invitation for others to abuse it.  To me this is another area of development I believe I need to work on.  Although I don&amp;rsquo;t intend to store any personal information but my own, I do have to be mindful of the risks of putting anything online, and making sure that the appropriate mitigations are in place to prevent that.  I&amp;rsquo;ll also have to make sure that I&amp;rsquo;m maintaining proper backups of the content, and periodically exercising them to make sure they work.  The fact that my work is at stake is a good incentive to keep on top of this.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Distractions:&lt;/strong&gt; Yeah, this is a classic problem with me: I use something that I build, I find a small problem or something that can be improved, then instead of actually finishing the task, I actually work on the code for the service.  This may have to be something that only gets addressed with discipline.  It may help using the CMS on a machine that doesn&amp;rsquo;t have the source code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I will also have to be aware of the amount of time I put into this.  I actually started working on a CMS several months ago so I&amp;rsquo;m not starting completely from scratch, but I&amp;rsquo;ve learnt with too many other of my personal projects that maintaining something like this is for the long term.  It might be fine to occasionally tinker with it, but I cannot spend too much effort working on the system at the expense of actually writing content.&lt;/p&gt;
&lt;p&gt;So this is what I might do.  I might give myself the rest of the month to do what I need to do to get it up to scratch, then I will start measuring how much time I spend working on it, vs. the amount of time I actually use it to write content.  If the time I spend working on the code base is more than 50% of time I use it to write content, then it will indicate to me that it&amp;rsquo;s a distraction and I will abandon it for an alternative setup.  To keep myself honest, I&amp;rsquo;ll post the outcomes of this on my micro blog (if I remember).&lt;/p&gt;
&lt;p&gt;A few other minor points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Will this delay publishing of the blog?  No. The CMS is functionally complete but there are some rough edges that I&amp;rsquo;d like to smooth out.  I hope to actually start publishing this new blog very shortly.&lt;/li&gt;
&lt;li&gt;Will I be moving the hosting of this blog onto the new CMS?  No, far from it. The services here works great for how I want to maintain this blog, and the community aspects are fantastic.  The CMS also lacks the IndiWeb features that micro.blog offers and it may be some time before they get built.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;ll be interested if anyone has any thoughts on this so feel free to reply to this post on micro.blog.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I&amp;rsquo;ve posted a &lt;a href=&#34;https://lmika.org/2020/12/13/revising-the-decision.html&#34;&gt;follow-up&lt;/a&gt; on this decision about a month after writing this.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>An anecdote regarding the removal of iSH from the App Store</title>
      <link>https://lmika.org/2020/11/09/some-random-thoughts.html</link>
      <pubDate>Mon, 09 Nov 2020 08:36:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/11/09/some-random-thoughts.html</guid>
      <description>&lt;p&gt;Around April this year, my old Android Nexus 9 tablet was becoming unusable due to it&amp;rsquo;s age and I was considering which tablet to move to next.  I have been a user of Android tablets since the &lt;a href=&#34;https://en.wikipedia.org/wiki/Nexus_7_(2013)&#34;&gt;Nexus 7&lt;/a&gt; and I have been quite happy with them (yes, we do exist).  However, it was becoming clear that Google&amp;rsquo;s was no longer interested in maintaining first-party support for Android on a tablet, and none of the other brands that were available were very inspiring.&lt;/p&gt;
&lt;p&gt;I took this as a sign to move to a new platform.  It was a toss-up between a Chromebook or an iPad.  I understood that it was possible to get a Linux shell on a Chromebook by enabling development mode, so for a while I was looking a possible Chromebooks that would work as a tablet replacement.  But after hearing about &lt;a href=&#34;https://ish.app&#34;&gt;iSH&lt;/a&gt; from an ATP episode, I got the impression that it would be possible to recreate the same thing with nicer hardware and app ecosystem, despite how locked down it is.  So the iPad won out.&lt;/p&gt;
&lt;p&gt;If at the time I knew that &lt;a href=&#34;https://ish.app/app-store-removal&#34;&gt;Apple would be removing iSH from the App Store&lt;/a&gt; (via &lt;a href=&#34;https://twitter.com/marcoarment/status/1325556102214381569?s=20&#34;&gt;Twitter&lt;/a&gt;) I&amp;rsquo;m not sure I would have bought the iPad, and would have probably gone with the Chromebook.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Tracking Down a Lost Album</title>
      <link>https://lmika.org/2020/11/03/the-lost-album.html</link>
      <pubDate>Tue, 03 Nov 2020 10:05:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/11/03/the-lost-album.html</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s a short story about my endeavours to find an album that seems to have disappeared from the face of the internet.  I&amp;rsquo;m a bit of a sucker for original sound tracks, particularly instrumental ones.  One that I remember being very good is the music from &lt;a href=&#34;https://en.wikipedia.org/wiki/The_Private_Life_of_Plants&#34;&gt;The Private Life of Plants&lt;/a&gt;, a documentary series from David Attenborough made in the mid 1990s.  It was one of those sound tracks that occasionally popped into my mind, particularly when looking at lovely autumn leaves or other scenes from the show.  But it has been a while since I last watched it, and I never though to look at whether an album of the sound track actually existed.&lt;/p&gt;
&lt;p&gt;It was only earlier this year when I discovered that this was a possibility.  I was watching Curb Your Enthusiasm and &lt;a href=&#34;https://www.imdb.com/title/tt0551414/?ref_=ttep_ep10&#34;&gt;one episode&lt;/a&gt; featured a scene which had the music from the documentary series in the background.  I recognised it immediately and after some quick searches online, I discovered that there did exist at one point an album of the sound track.&lt;/p&gt;
&lt;p&gt;I started looking around to see if it was available to listen to.  I started with Spotify, the music service that I&amp;rsquo;m subscribe to, but searches there did not return any results.  I then went to the other streaming services that were available, like Apple Music and Amazon music, but there was no luck there either.  I then started looking to see if I could get the physical CD.  I looked on Amazon, eBay, JB Hi-Fi and even Sanity, a music shop that is still operating here in Australia.  None of these sites turned up anything indicating that this album was available.  I then tried my local library, the online ABC shop, and the BBC shops, but those turned up with no results as well.  It looked like this album was no longer available for sale anywhere.&lt;/p&gt;
&lt;p&gt;I then started making generic web searches on Google and DuckDuckGo.  There were very few hits, most of them referencing the documentary series itself.  It was here that I started venturing into the abandoned areas of the web, with old pages, riddled with ads, that are barely functioning at all.  I found a &lt;a href=&#34;https://www.last.fm/music/Richard+Grassby-Lewis&#34;&gt;last.fm&lt;/a&gt; page for the composer with looked to have the track list of the album, but attempting to play the tracks through the in-browser player only produced errors.  Going further through the abandoned web, I found an old download site which looked to have links to some of the tracks on the album.  After following the links however, it looked like the site has since stopped operating: the links only produced 404 Not Founds, and attempts to go to the main site only produced a page indicating that the domain was for sale.&lt;/p&gt;
&lt;p&gt;It was then that I remembered Wayback Machine, and I went there to see if it was possible to get to the archived version of the site.  Sure enough, there existed a snapshot of the site from 2006.  The site itself looked to be an &lt;a href=&#34;https://web.archive.org/web/20060104190436/http://www.2ndsight.co.uk/records/2NDCD003/2NDCD003.html&#34;&gt;old online music store&lt;/a&gt; that at one time offered the album for sale.  The album page was there and was indexed by Wayback Machine.  Better still, the site posted 5 of the tracks online, I&amp;rsquo;m guessing as samples, which were also indexed by Wayback Machine and were available for download.  Success!  I was able to download the 5 sample tracks from Wayback Machine and play them on my computer.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t know if there&amp;rsquo;s a moral to this story.  I guess if it&amp;rsquo;s anything, it&amp;rsquo;s that preserving these archives is important, especially for media under the control of gatekeepers that can pull it from distribution at any time.  It&amp;rsquo;s certainly made me appreciate the important work that the Internet Archive does, and I have since made a small donation to them to allow that work to continue.&lt;/p&gt;
&lt;p&gt;I think it&amp;rsquo;s also fair to say that this story is not yet over.  I don&amp;rsquo;t care how long it will take me, I&amp;rsquo;ll continue to track this album down until I&amp;rsquo;ve found it and be able to play it in it&amp;rsquo;s entirety.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Offical support for file embedding coming to Go</title>
      <link>https://lmika.org/2020/10/31/offical-support-for.html</link>
      <pubDate>Sat, 31 Oct 2020 07:50:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/10/31/offical-support-for.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m excited to see, via &lt;a href=&#34;https://golangweekly.com/issues/336&#34;&gt;Golang Weekly&lt;/a&gt;, that the &lt;a href=&#34;https://go.googlesource.com/proposal/+/master/design/draft-embed.md&#34;&gt;official support for embedding static files in Go&lt;/a&gt; is being realised, with the &lt;a href=&#34;https://github.com/golang/go/commit/25d28ec55aded46e0be9c2298f24287d296a9e47&#34;&gt;final commit merged to the core a couple of days ago&lt;/a&gt;.  This, along with the &lt;a href=&#34;https://go.googlesource.com/proposal/+/master/design/draft-iofs.md&#34;&gt;new file-system abstraction&lt;/a&gt;, will mean that it will be much easier to embed files in Go applications, and make use of them within the application itself.&lt;/p&gt;
&lt;p&gt;One of the features I like about coding is Go is that the build artefacts are statically linked executables that can be distributed without any additional dependencies.  This means that if I wanted to share something, all I need to do is to give them a single file that they can just run, without needing to worry about whether particular dependencies or runtimes are installed prior to doing so.&lt;/p&gt;
&lt;p&gt;However there are times when the application requires static files, and to maintain this simple form of distribution, I generally want to embed these files within the application itself.  This comes up surprisingly often and is not something that was officially supported within the core language tools, meaning that this gap had to be filled by 3rd parties.  Given the &lt;a href=&#34;https://github.com/golang/go/wiki/Projects#resource-embedding&#34;&gt;number of tools available to do this&lt;/a&gt;, I can see that I&amp;rsquo;m not alone in needing this.  And as great as it is to see the community step in to fill this gap, relying on an external tool complicates the build process a bit&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;: making sure the tool is installed, making sure that it is executed when the build run, making sure that the tool is actively being maintained so that changes to the language will be supported going forward, etc.&lt;/p&gt;
&lt;p&gt;One other thing about these tools is that the API used to access the file is always slightly different as well, meaning that if there&amp;rsquo;s a need to change tools, you end up needing to change your code to actually access the statically embedded file.&lt;/p&gt;
&lt;p&gt;Now that embedding files is officially coming to the language itself, there is less need to rely on all of this.  There&amp;rsquo;s no need to worry about various tools being installed on the machines that are building the application.  And the fact that this feature will work hand in hand with the new file system abstraction means that embedded files would be easier to work with within the code base itself.&lt;/p&gt;
&lt;p&gt;So kudos to the core Go development team.  I&amp;rsquo;m really looking forward to using this.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Although not as much as many other languages.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Advice to those working with annotations in Preview</title>
      <link>https://lmika.org/2020/10/29/warning-to-those.html</link>
      <pubDate>Thu, 29 Oct 2020 06:20:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/10/29/warning-to-those.html</guid>
      <description>&lt;p&gt;For those of you using Preview in macOS for viewing an annotated PDF, if you need to move or delete the annotations in order to select the text, be sure to undo your changes prior to closing Preview. Otherwise your changes will be saved without asking you first.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;This just happened to me. I have a PDF annotated with edits made with the iPad pencil and I wanted to copy the actual text. The annotations seemed to sit on top of the text in an image layer, which means that in order to select the text, I have to move or delete this layer first. I didn&amp;rsquo;t want the annotations mixed up with the ones on the other page, so I decided to delete this layer instead of moving it. This was a mistake.&lt;/p&gt;
&lt;p&gt;I copied the text and wanted to get the annotations back. I probably should have just pressed ⌘Z to undo my changes, but I saw &amp;ldquo;– Edited&amp;rdquo; in the title bar so I assumed that if I just closed Preview, it would discard my changes and I would be able to get my annotations back just by reopening the PDF. But it turns out, after closing it and opening it again, the changes were saved without asking me first, resulting in my annotations being lost.&lt;/p&gt;
&lt;p&gt;Developers of macOS: this is a terrible user experience that needs to be fixed. Preview saving my changes from under me has now resulted in data loss, the cardinal sin of any software. Either ask me before saving changes when the application is closed, support a notion of versions, or do something else. But do not just save my changes without asking me, and do not imply that Preview is aware of pending changes by having &amp;ldquo;– Edited&amp;rdquo; in the title bar if it isn&amp;rsquo;t going to discard the changes, or confirm that they should be saved when I close the app.&lt;/p&gt;
&lt;p&gt;Ugh, I need another coffee now.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;This is macOS Mojave.  I hope this has been fixed some way in later versions.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Doughnut Day 2020</title>
      <link>https://lmika.org/2020/10/26/good-day-today.html</link>
      <pubDate>Mon, 26 Oct 2020 08:07:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/10/26/good-day-today.html</guid>
      <description>&lt;p&gt;Good day today.  From a high of 725 Covid-19 cases in August 25, Victoria has just had 24 hours of zero new cases and zero deaths.  This is during a period of extensive testing in the north of the metropolitan, during a testing blitz in an attempt to contain an outbreak.  Labs have been processing tests late into the night, with not a single one so far coming back positive.&lt;/p&gt;
&lt;p&gt;As good as this news is, I&amp;rsquo;d imagine the government wants to remain cautious here.  The easing of restrictions that were scheduled for yesterday have been delayed, I guess to make sure that contact tracers are on top of things in the north.  As disappointing as this is, I can see why they did this.  It makes sense that they take advantage of the current situation to get as much information about where the virus is as they can.  I don&amp;rsquo;t believe anyone wants to go back into lock-down a third time so they really have one shot of this. The government is still confident that we are on track for lifting restrictions before November 1st.  I guess we&amp;rsquo;ll see what happens when they give their briefing this morning, but after going through this for 4 months, I can wait a few more days.&lt;/p&gt;
&lt;p&gt;For the moment, it&amp;rsquo;s good seeing this result.  Truth is, things were always touch and go in Victoria even during the relative period of free movement that we experienced in June, when we last had a day of zero new cases.  Seeing this now, with the restricted movement and testing blitz, gives me hope that we can keep this virus suppressed until a vaccine comes in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update at 15:45:&lt;/strong&gt; Results from the testing blitz from the northern suburbs have been trickling all day, and so far still no positive cases.  It looks like the Victorian government is happy with this result because they have announced that the state will be moving to the 3rd step of re-opening on Wednesday.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Reflections On Writing On The Web</title>
      <link>https://lmika.org/2020/10/22/i-fell-into.html</link>
      <pubDate>Thu, 22 Oct 2020 08:15:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/10/22/i-fell-into.html</guid>
      <description>&lt;p&gt;I fell into a bit of a rabbit hole about writing and publishing online yesterday after reading &lt;a href=&#34;https://www.preetamnath.com/blog/why-you-should-write&#34;&gt;this article from Preetam Nath&lt;/a&gt; and &lt;a href=&#34;https://jamesclear.com/scale&#34;&gt;this article from James Clear&lt;/a&gt;.  I&amp;rsquo;ve been thinking about creating and publishing on the web for a little while now, which is probably why these two articles resonated with me.&lt;/p&gt;
&lt;p&gt;These articles highlight the importance of creating and publishing regardless of what the topic is.  There have been a few things that I&amp;rsquo;ve been wanting to share but I haven&amp;rsquo;t done so, probably because I worry about what other people think.  The interesting thing about that line of thinking is that I tend to enjoy reading posts from other people as they go about their lives.  I guess that&amp;rsquo;s what the original &lt;a href=&#34;http://scripting.com/2020/06/12.html#a201817&#34;&gt;intention of blogging&lt;/a&gt; actually is.&lt;/p&gt;
&lt;p&gt;There have also been times during the past week that I&amp;rsquo;ve been craving content, either from Twitter, Micro.blog or the various RSS feeds that I read.  And there have been times when I&amp;rsquo;ve caught up with everything that I follow, and nothing happens for a while.  I think to myself, &amp;ldquo;when will someone post something?  I need to be distracted for a while.&amp;rdquo;  I think I need to remember that someone needs to create that content in order for it to be consumed, and although it&amp;rsquo;s much easier consuming content than it is to produce it, I should not feel entitled to it and expect others to amuse me.&lt;/p&gt;
&lt;p&gt;The interesting thing about these thoughts is that it is joining a confluence of other changes to my daily work setup that has happened recently.  I use to write in my Day One journal almost every day, but since moving to Linux for work  that has prooved a little difficult to maintain.  It might be that more of my journalling will go here instead, given that micro.blog provides a nice, cross-plaform interface for writing entries of any size.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Unit Tests and Verifying Mocks</title>
      <link>https://lmika.org/2020/10/14/unit-tests-and.html</link>
      <pubDate>Wed, 14 Oct 2020 07:46:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/10/14/unit-tests-and.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m working with a unit test that uses mocks in which every method in the mock is verified after the method under test is called, even if it is not relevant to the test.  Furthermore, the tear down method verifies that every dependent services has no more interactions, which means that removing a verification that is not relevant to the specific test case will cause the test to fail.&lt;/p&gt;
&lt;p&gt;Please do not do this.  It makes modifying the tests really difficult and results in really long unit tests that hides what the test is trying to assert.  It also makes it harder to create new tests to verify a particular behaviour, as you find yourself copying all the verification code that is not relevant to the case that you&amp;rsquo;re trying to test for.&lt;/p&gt;
&lt;p&gt;In my opinion, tests should clearly demonstrate the specific behaviour that you&amp;rsquo;re trying to verify, and should only include verification of mocks that are directly related to that case.  Writing tests that are effectively photo-negatives of the method being tested, one in which the dependent services are verified instead of called, is not a good practice for unit testing.&lt;/p&gt;
&lt;p&gt;Instead, have multiple, smaller unit-tests that asserts a particular behaviour, and only verify the mocks that are explicitly required.  You gain the coverage by having all these unit tests effectively overlap the various paths a particular method will take.  But the important benefit is that it results in more maintainable tests that are easier to work with.  That makes it easier to write tests, which means you find yourself doing so more often.  Path of least resistance and all that.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Database Client Wishlist</title>
      <link>https://lmika.org/2020/10/09/what-i-want.html</link>
      <pubDate>Sat, 10 Oct 2020 10:49:01 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/10/09/what-i-want.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve recently started a new job so I&amp;rsquo;ve been spending a bit of time trying to become familiar with how the relational databases are structured.  Usually when I&amp;rsquo;m doing any database work, I tend to use the CLI clients like &lt;code&gt;mysql&lt;/code&gt; or &lt;code&gt;pg_sql&lt;/code&gt;.  I tend to prefer them, not only as they&amp;rsquo;re usually easy to use via SSH, but the &lt;a href=&#34;https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop&#34;&gt;REPL&lt;/a&gt; is a nice interaction model when querying data: you type a query, and the results appear directly below it.  The CLI tools do have a few drawbacks though.  Dealing with large result sets or browsing the schema tend to be harder, which makes it difficult when dealing with an unfamiliar database.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;ve been finding myself using the GUI database browsers more, like DataGrip or MySQL Workbench.  It is much easier and nicer to navigate the schema using these, along with deal with large result sets, but they do remove the connection between a query and the associated results.  The queries are usually entered in an editor-like console, like those used to enter code, and the results are usually in another window panel, or in a separate tab.  This mode of interaction has nothing like the recency or locality between the query and the results that you get from a CLI.&lt;/p&gt;
&lt;p&gt;While working with both of these tools and seeing their shortcomings, I&amp;rsquo;ve been casually wondering what a &lt;span style=&#34;text-decoration: line-through&#34;&gt;perfect&lt;/span&gt; decent database client would have.  I think it will need these attributes in some prominent way (this covers the complaints listed above but also addresses some others I think would also help):&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Results appearing below queries:&lt;/strong&gt;  I think this tool will need an interaction model similar to the CLI tools.  There is so much benefit in seeing the results directly below the query that produce it.  Anything other than this is likely to result in a situation where I&amp;rsquo;ll be looking at seven different queries and wondering which of them produced the single result set that I see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;An easy way to view, filter and export large result sets:&lt;/strong&gt;  Although the interaction should be is closer to the CLI, there must be a way to deal with large queries and result sets, something that the GUI tools do really well.  There should also be a way to export them to CSV files or something similar without having to remember the appropriate copy command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Some sort of snippet support and persistent scroll-back:&lt;/strong&gt;  This one can be best summarised as &amp;ldquo;whatever you find yourself copy and pasting into notepad&amp;rdquo;.  The ability to store snippets and saved queries will save time trying to find or rewrite the big complex queries.  And the persistent scroll-back of previously executed queries, &lt;em&gt;with their results&lt;/em&gt;, will help with maintaining my train of thought while investigating something.  This can come in handy especially when the investigation spans multiple days.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A quick way to annotate queries or results:&lt;/strong&gt; Big long SQL queries eventually look the same to me after a while, so it would also be nice to add inline comments or notes to remind myself what the results actually represent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;An easy way to browse the schema:&lt;/strong&gt; This could be a tree-like structure similar to all the GUI tools, which will make browsing the schema really easy.  I think at a minimum, it should be a &lt;em&gt;consistent&lt;/em&gt; set of meta-commands such as listing tables in a database or describing a tables columns, etc.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;An easy way to run automation tasks:&lt;/strong&gt; Finally, some form of scripting language to be able to &amp;ldquo;orchestrate&amp;rdquo; multiple queries without having to formulate one large SQL query, or copy and paste result sets around.  It&amp;rsquo;s true that writing an external tool to do this is also possible, but avoiding the context switch would be a huge benefit if this was available from within the app.  Doesn&amp;rsquo;t have to be full featured either, in fact it&amp;rsquo;s probably better if it isn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;It would be interesting exploring this further.  I think the last thing I need now is another project to work on, but maybe over the weekend I might start prototyping this to see if the workflow makes sense.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Sharing links to private podcast episodes</title>
      <link>https://lmika.org/2020/10/06/sharing-links-to.html</link>
      <pubDate>Tue, 06 Oct 2020 10:35:59 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/10/06/sharing-links-to.html</guid>
      <description>&lt;p&gt;There have been times when I&amp;rsquo;ve wanted to share a link to an episode of a podcast that I pay for, but I&amp;rsquo;m hesitant to do so as the feed is private and unique to my account.  The episode is also available in the public feed, but has been trimmed as an incentive for listeners to pay for the show.  I can always find the episode in the public feed and share that, but I&amp;rsquo;m wondering if there&amp;rsquo;s a better way to handle this.&lt;/p&gt;
&lt;p&gt;How do other podcast listeners share links to episodes from private feeds that also have a public version? Is there something in the RSS standard&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; that allows for podcast producers to link a private episode to the same one in the public feed?  If so, do the major podcast players, specifically Pocketcasts, honour this link when sharing an episode?&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m asking as a podcast listener: I don&amp;rsquo;t have a podcast myself (yet).&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;&amp;ldquo;Standard&amp;rdquo; is probably not the right word here but let&amp;rsquo;s go with it for the moment.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Let&#39;s hold the line, Melbourne.  We&#39;ve got this.</title>
      <link>https://lmika.org/2020/09/23/melbournes-day-daily.html</link>
      <pubDate>Wed, 23 Sep 2020 09:17:56 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/09/23/melbournes-day-daily.html</guid>
      <description>&lt;p&gt;Today is a good day.  &lt;a href=&#34;https://www.abc.net.au/news/2020-09-23/victoria-coronavirus-cases-to-15-melbourne-average-falls-again/12688292&#34;&gt;Melbourne&amp;rsquo;s 14 day daily Covid-19 case average is now 29.4&lt;/a&gt;, which is beyond the 30 to 50 band required to move to the next stage of reopening.  Seeing the fruits of our collective sacrifice, bringing the daily case numbers from a peak of around 740 in August down to the 11 we saw on Monday, makes me proud to be a Melburnian.&lt;/p&gt;
&lt;p&gt;As much as I like for things to reopen sooner than planned, I think we should hold the line for as long as we possibly can.  The potential prizes for doing so &amp;ndash; the crushing of the virus, the ability to travel interstate again, the chance to eat at restaurants without fear of infection, the chance for a normalish Christmas and summer &amp;ndash; are within reach.  I know that&amp;rsquo;s easy for me to say as someone who has the ability to work from home, and I completely recognise those of us suffering right now being unable to work at all.  But just like the darkest hour is before the dawn, so too will the sweet taste of victory and accomplishment be when we finally crush this virus and meet the rest of the country where they are.  To rush this, to reopen too early, and see our effort thrown away would be upsetting.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s hold out that little bit longer, Melbourne.  We&amp;rsquo;ve got this.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Getting screen capture working in Vivaldi on Fedora 32</title>
      <link>https://lmika.org/2020/09/17/getting-screen-capture.html</link>
      <pubDate>Thu, 17 Sep 2020 10:05:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/09/17/getting-screen-capture.html</guid>
      <description>&lt;p&gt;Moving from a Mac Pro back to Linux for work, I&amp;rsquo;ve come to appreciate how well things just work out of the box in macOS.  Things like Web RTC display capture, which is used for sharing the screen in browser-based video conferencing sites (and I think also in Slack, since it&amp;rsquo;s using Electron and, thus, the Blink rendering engine), work flawlessly in macOS, but proved to be a bit of trouble within Linux.&lt;/p&gt;
&lt;p&gt;From my &lt;a href=&#34;https://news.ycombinator.com/item?id=22178011&#34;&gt;limited reading&lt;/a&gt;, it looks like this might be related to the use of Wayland, the new user-space video stack that is currently being built, and the corresponding security model.  This exists alongside a new mechanism for acquiring audio and video feeds called &lt;a href=&#34;https://pipewire.org&#34;&gt;PipeWire&lt;/a&gt;, but this is not enabled by default in &lt;a href=&#34;https://vivaldi.com&#34;&gt;Vivaldi&lt;/a&gt;, the browser I&amp;rsquo;m using.&lt;/p&gt;
&lt;p&gt;Using the instructions &lt;a href=&#34;https://screen.canny.io/bugs/p/linux-no-screen-capture-when-running-under-wayland&#34;&gt;found here&lt;/a&gt;, I think I&amp;rsquo;ve managed to fix this by:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Going to &lt;a href=&#34;chrome://flags&#34;&gt;chrome://flags&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Enabling &amp;ldquo;WebRTC PipeWire support&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Restarting Vivaldi&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I then went to a &lt;a href=&#34;https://webrtc.github.io/samples/src/content/getusermedia/getdisplaymedia/&#34;&gt;test WebRTC site&lt;/a&gt; to verify that it works (there is &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/Screen_Capture_API/Using_Screen_Capture&#34;&gt;also one on MDC&lt;/a&gt;).  After going through some security prompts to allow sharing of the screen, I was now able to see my desktop being displayed back to me.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not sure how I can fix this in Electron apps like Slack.  Prior to this fix, Vivaldi did allow sharing of individual windows, but this doesn&amp;rsquo;t seem possible in Slack at the moment.  If I find a fix for this, I might update this post.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>First Foray Into Home Automation</title>
      <link>https://lmika.org/2020/09/12/first-foray-into.html</link>
      <pubDate>Tue, 15 Sep 2020 09:04:36 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/09/12/first-foray-into.html</guid>
      <description>&lt;p&gt;After recently changing jobs, I&amp;rsquo;ve received a brand new Lenovo work laptop.  As good as the laptop is, and it&amp;rsquo;s OK for a work laptop, it has one annoying feature.  Whenever the laptop is plugged in and powered, there is a bright white LED that is always illuminated.  Because I&amp;rsquo;m still working from home — and it is likely that after the pandemic I will be working from home at least a few days a week — and my desk is in my bedroom, having this white LED is no good for my sleep.&lt;/p&gt;
&lt;p&gt;For the first few evenings, I&amp;rsquo;ve been unplugging the laptop prior to going to bed.  I rather not use electrical tape to block out the LED: the is not my laptop and such tape would be ugly, and the LED itself is close to other ports which would make tape placement a bit awkward.  Plus the LED does serves the useful purpose of indicating that the laptop is powered.  It&amp;rsquo;s just not useful indicating this fact at night.  Unplugging the laptop works but I&amp;rsquo;m not too keen with this solution long term: it&amp;rsquo;s only going to be a matter of time when I unplug it one day, forget to plug it in the next day and I eventually run out of juice when I need it the most.&lt;/p&gt;
&lt;p&gt;Another solution for this problem is a dumb timer.  I do own a timer — one that has a circular clock that is configured by pressing in a black nub for each 15 minutes that you want the plug to be energised — and it could work in this scenario, but it does has some awkward properties.  The principal one being that I&amp;rsquo;d like to ensure that the laptop is powered when I&amp;rsquo;m using it, and there could be times when I&amp;rsquo;m using it during the hours that I&amp;rsquo;m usually asleep, like when I&amp;rsquo;m to responding to incidents or working late.  The timer does have an override, but it&amp;rsquo;s along the side of the plug itself, so in these cases I&amp;rsquo;d have to get under my desk to turn it on.&lt;/p&gt;
&lt;p&gt;So I decided to take this opportunity to try out some home automation.&lt;/p&gt;
&lt;h2 id=&#34;the-smart-plug&#34;&gt;The Smart Plug&lt;/h2&gt;
&lt;p&gt;The way I plan to tackle this problem is by doing the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Connecting the laptop to a smart plug&lt;/li&gt;
&lt;li&gt;Setting up a schedule so that the smart plug will automatically turn off at 10:00 in the evening, and on at 6:30 in the morning&lt;/li&gt;
&lt;li&gt;Having a way to override the schedule if I need to turn on the plug if I need to&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The smart plug chosen for this is the &lt;a href=&#34;https://www.jbhifi.com.au/products/tp-link-hs100-smart-wi-fi-plug-kit&#34;&gt;TP-Link HS-100 Smart Wi-Fi Plug&lt;/a&gt;.  It&amp;rsquo;s was not my first choice but it was in stock and was delivered within a few days, way before the expected delivery date (good job Australia Post).&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2020/7b46a4df3c.jpg&#34; width=&#34;403&#34; height=&#34;537&#34; alt=&#34;The TP-Link HS 100 Smart&#34; /&gt;
&lt;p&gt;The plugs themselves are nothing remarkable.  It&amp;rsquo;s just a standard plug, with a LED indicating the current state of the plug and Wi-Fi connectivity.  They&amp;rsquo;re a little bulky, and the do encroach a bit on some of the adjacent plugs: I needed to move a few plugs around in the power board that I&amp;rsquo;m using.  Fortunately there is some clearance between the prongs and the actual body of the device, which made it possible to position it so that it overlaps some of the other plugs with slimmer profiles.  The relay within the plug is much quieter than I expected, which was a nice surprise.&lt;/p&gt;
&lt;p&gt;Linking the smart plug up to the Wi-Fi was relatively painless, however I did need to download an app and create a new TP-Link Kasa Smart account.  During the actual on-boarding, the app also asked for my location for some reason.  It could have been to configure time-zones?  I don&amp;rsquo;t know, but it would have been nice for the app to disclose why it needed my location.  But after that, it was more or less what you&amp;rsquo;d expect: after and following the instructions within the app to plug the device in and turn it on, the smart plug started a Wi-Fi hotspot that the phone connected to.  One the pairing was complete, it was possible to turn the device on and off within the app.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2020/720df49937.jpg&#34; width=&#34;360&#34; height=&#34;640&#34; alt=&#34;Google Home with the smart plugs registered&#34; /&gt;
&lt;h2 id=&#34;setting-up-the-schedule&#34;&gt;Setting Up The Schedule&lt;/h2&gt;
&lt;p&gt;I first tried setting up the schedule for the smart plug in Google Home.  First, I&amp;rsquo;ve got to say that doing something mildly complicated like this in a mobile app was annoying, and I wish Google published a web or desktop version of their Home management app so this could use a mouse and keyboard.  But I had no trouble registering the smart plug in Google Home.  It basically involved linking the Kasa Smart account with my Google account and once that was done, the smart plug could be added to a room and was ready to go.&lt;/p&gt;
&lt;p&gt;Setting up a schedule within Google Home involved creating a new  &amp;ldquo;Scene&amp;rdquo;, and expected information like trigger words and a spoken response when the scene ran.  There were also some built in scenes but they didn&amp;rsquo;t seem suitable for my use case.  The whole thing seems geared towards triggering the scene with a Google Home smart speakers (I just realised the the app and the smart speakers had the same name), and seems to assume that one is available.  I don&amp;rsquo;t have a smart speaker and the prospect of the Google Assistant speaking when the scene is triggered did not appeal to me.  It might have been possible to set it up the way I desired, but it felt like my use case was not exactly what this automation system is geared towards, so I abandoned pursuing this approach any further.&lt;/p&gt;
&lt;p&gt;Fortunately the smart plugs integrate with IFTTT, so I turned to that next.  After recovering my old account, I set out to configure the schedule.&lt;/p&gt;
&lt;p&gt;Firstly, I have to say that the UX of IFTTT&amp;rsquo;s site is dramatically different than what I remember, and not in a good way.  It seemed like they noticed that most of their users were accessing their site from their mobiles, and they redesigned the UI to work for them at the expense of desktop users.  They reduce the amount of information density of each page so that it took three clicks to do anything, and cranked up the font size so much that every label or line of copy was larger than a header.  This, mixed with a garish new colour scheme, made the page physically hard to look at.  I&amp;rsquo;d recommend that IFTTT&amp;rsquo;s UX designers reconsider their design decisions.&lt;/p&gt;
&lt;p&gt;Usability aside, setting up the schedule was reasonably straightforward here as well.  I first had to link the IFTTT and Kasa Smart accounts, which made the devices selectable within IFTTT.  I then went about setting up an applet to turn off the plug at the scheduled time.  Initially I set it up to turn it off 15 minutes from the current time, just so that I could test it.  It was not successful on the first go and I had to ensure that the plug was selected within the applet correctly; but on the second go, it worked without any problem: at the scheduled time, the plug turned itself off.  Most importantly of all, the state of the plug was properly reflected within the Google Home app and I was able to turn it back on from there.&lt;/p&gt;
&lt;p&gt;One last thing about the schedules, IFTTT does not make this clear when you&amp;rsquo;re setting up the applet, but dates and times that are used by an applet are in the time-zone of your account.  To check or change it, go to your account profile settings and it should be listed there.&lt;/p&gt;
&lt;p&gt;I then had to create a second applet to turn the plug on at a scheduled time, which was just as easy to do.  The entire schedule was set up in a few minutes, minus the test time, with two IFTTT applets.  This leaves me with one remaining applet on the free IFTTT plan, which means that I&amp;rsquo;ll need to consider something else when I set up the other plug.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2020/dacd40fd19.jpg&#34; width=&#34;487&#34; height=&#34;266&#34; alt=&#34;IFTTT with the two applets setup&#34; /&gt;
&lt;p&gt;After testing the entire set up end to end, and confirming that the override works, I reconfigured the schedule for the evening times and it was good to go.&lt;/p&gt;
&lt;p&gt;That evening, the scheduled ran without a hitch.  The smart plug cut power to the laptop at 10:00 and the LED was extinguished, giving me the much needed darkness for a good night sleep.  The next morning at 6:30, the smart plug was turned on again and power was restored to the laptop.  The only downside is that the smart plug itself has a green LED which, although not as distracting as the one on the laptop, is still visible during the night.  Fortunately this is something I could easily fix with electrical tape.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;So far, I&amp;rsquo;d say this set up has been successful.  It&amp;rsquo;s been two nights now, and in both cases power to the laptop was turned off on schedule, and restored the next morning.  The LED from the laptop no longer distracts me and I don&amp;rsquo;t have to manually unplug the laptop every evening.  This is now something that I can forget, which is the ultimate indication of success.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Ordered Lists in Markdown</title>
      <link>https://lmika.org/2020/09/01/on-ordered-lists.html</link>
      <pubDate>Tue, 01 Sep 2020 15:51:29 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/09/01/on-ordered-lists.html</guid>
      <description>&lt;p&gt;One of the things I like about Markdown as a form of writing online, is that ordered lists can simply begin with the prefix &lt;code&gt;1.&lt;/code&gt;, and there is no need to update the leading number in the subsequent items.  To produce the following list:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First&lt;/li&gt;
&lt;li&gt;Second&lt;/li&gt;
&lt;li&gt;Third&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One only needs to write:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1. First
1. Second
1. Third
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1. First
2. Second
3. Third
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or even:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1. First
3. Second
2. Third
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The one downside to this approach, unfortunately, is that there is no nice way to specify what the first ordinal should be.  If I were to use &lt;code&gt;3.&lt;/code&gt; as the prefix of the first item, the generated ordered list will always begin at 1.&lt;/p&gt;
&lt;p&gt;This means that there&amp;rsquo;s no nice way to continue lists that are separated by block elements.  For example, let&amp;rsquo;s say I want to have a list of 4 items, then a paragraph of text or some other block element, then continue the list from 5.  The only way to do so in &amp;ldquo;common-style&amp;rdquo; Markdown is to write the second list in HTML with an &lt;code&gt;&amp;lt;od start=5&amp;gt;&lt;/code&gt; tag:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ol start=5&amp;gt;
  &amp;lt;li&amp;gt;Fifth&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;Sixth&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;Seventh&amp;lt;/li&amp;gt;  
&amp;lt;/ol&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It would be nice if this was representable within Markdown itself.  Maybe by taking into account the first ordinal and just incrementing by 1 from there.  For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;5. Fifth
5. Sixth
5. Seventh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;becomes&lt;/p&gt;
&lt;ol start=5&gt;
  &lt;li&gt;Fifth&lt;/li&gt;
  &lt;li&gt;Sixth&lt;/li&gt;
  &lt;li&gt;Seventh&lt;/li&gt;  
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;So what?&lt;/em&gt;, you might say.  &lt;em&gt;You just demonstrated that this could be done in HTML.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s true, however I use wiki software with rich-text editors that don&amp;rsquo;t allow modifying the underlying HTML (they may have the ability to specify a &amp;ldquo;region&amp;rdquo; of HTML, but not a way to modify the underlying body text itself) and they use Markdown as a way of triggering formatting changes.  For example, typing &lt;code&gt;*&lt;/code&gt; twice will enable bold face, typing three ` will start a code block&amp;hellip; and typing &lt;code&gt;1.&lt;/code&gt; will start an ordered list.&lt;/p&gt;
&lt;p&gt;Changing the first ordinal or continuing the previous list might be considered an advanced operation that the developers of these wikis may not have considered.  But I can&amp;rsquo;t help wonder if Markdown had this feature from the start, all these editors would have supported it in one form or another.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>If Google does this to the Pixel 4, just what do they expect for the Pixel 5?</title>
      <link>https://lmika.org/2020/08/07/if-google-does.html</link>
      <pubDate>Wed, 26 Aug 2020 08:49:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/08/07/if-google-does.html</guid>
      <description>&lt;p&gt;What is Google doing &lt;a href=&#34;https://daringfireball.net/linked/2020/08/06/google-pixel-4&#34;&gt;cancelling the Pixel 4 after 6 months&lt;/a&gt;?  They spend &lt;a href=&#34;https://www.theverge.com/2017/9/20/16340108/google-htc-smartphone-team-acquisition-announced&#34;&gt;$1.1 billion buying the HTC mobile division&lt;/a&gt; and state that they &lt;a href=&#34;https://www.androidauthority.com/samsung-producing-google-chips-1144585/&#34;&gt;plan to start making their own mobile chips&lt;/a&gt;, giving the impression that they are serious about producing decent, flagship hardware for Android.  And then go ahead with discontiuning their current flagship phone after 6 months?&lt;/p&gt;
&lt;p&gt;Look, I know from a purely economical perspective, the Pixel line makes little sense.  Android is not iOS. They don&amp;rsquo;t hold the prestigious high-end of the market, with the margins that come from it.  But that&amp;rsquo;s not Google&amp;rsquo;s business.  They&amp;rsquo;re an advertising company first, and a search company second.  So I can understanding that Android to them is more of a cost centre; the price of keeping access to their services open to mobile users.&lt;/p&gt;
&lt;p&gt;But I had the impression that they also recognised that there exists a market of Android users that appreciate good quality hardware and decent, stock-standard software stack with no shovelware, and are willing to pay a premium for it.  It might not be a big market, that&amp;rsquo;s true.  But if they&amp;rsquo;re serious about keeping Android around and want to keep these customers (you know, the one&amp;rsquo;s with disposable income that advertisers love), they should continue to be a player in it.  I guess it&amp;rsquo;s possible that they simply offload this to another device manufacturer like Nokia, but then they&amp;rsquo;re giving up any leverage of ensuring good quality hardware which will attract these buyers.&lt;/p&gt;
&lt;p&gt;As a Pixel owner myself, this move really concerns me.  It&amp;rsquo;s getting &lt;a href=&#34;https://daringfireball.net/linked/2020/08/25/pixel-5-rumors&#34;&gt;increasingly harder&lt;/a&gt; to recommend Pixel phones to anyone, and I&amp;rsquo;m starting to wonder whether it&amp;rsquo;s time to consider something else.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Suppression vs. Elimination</title>
      <link>https://lmika.org/2020/07/21/on-suppression-vs.html</link>
      <pubDate>Tue, 21 Jul 2020 10:04:48 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/07/21/on-suppression-vs.html</guid>
      <description>&lt;p&gt;It was around the beginning of June, when the number of new Covid-19 cases for Victoria were around 10-20 a day, that there was a general feeling that suppression was working and that it was time to begin opening up.   I will admit I took advantage of the looser restrictions, but I always wondered whether it would be better to remain closed for a little while longer and go for elimination.  This was not the official strategy though: we have testing and tracing up and running and as long as we know where the virus is, we can continue to roll-back restrictions and achieve some semblance of normalcy.&lt;/p&gt;
&lt;p&gt;Fast-forward to today and the daily number of cases is higher than what it was back in March, Melbourne is back under Stage 3 restrictions and I&amp;rsquo;m shopping on-line for masks.&lt;/p&gt;
&lt;p&gt;It seems obvious to me that suppression as a strategy may not be enough.  We may eventually (hopefully) get the virus tamped down once more, but it&amp;rsquo;s still out there and our efforts to keep it at bay are only as &lt;a href=&#34;https://www.abc.net.au/news/2020-07-20/victoria-coronavirus-hotel-quarantine-inquiry-day-1-in-melbourne/12471916?WT.ac=localnews_melbourne&#34;&gt;strong as our weakest link&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I think it&amp;rsquo;s time we go for elimination.  It won&amp;rsquo;t be easy, but there are three reasons why I reckon it&amp;rsquo;s worth a shot:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Most of the other states in the country have effectively achieved eliminated.  Some of them have gone weeks without any new cases, and are cautiously in the process of opening up once again.  However, this can only hold as long as the state borders remain close to Victorians (and possibly soon to the New South Welsh) and I don&amp;rsquo;t see these states willing throwing away their hard won achievement just because the official strategy is suppression.  If Victoria (and NSW) go for elimination, we can meet the other states where they are, making it a no-brainer to open up interstate travel once again, not to mention the trans-Tasman bubble with New Zealand.&lt;/li&gt;
&lt;li&gt;It seems more economically stable over the long term.  &lt;a href=&#34;https://www.abc.net.au/news/2020-07-21/coronavirus-elimination-or-suppression-economy/12472888&#34;&gt;Economic activity is tied to confidence&lt;/a&gt;: people will only go out and spend money if they believe it&amp;rsquo;s safe to do so.  Even when restrictions are rolled-back, I&amp;rsquo;m doubtful people will be quick to flock to cafes and gyms if there&amp;rsquo;s a risk of another wave.  Compare this with elimination: evidence from New Zealand shows that consumer spending is pretty much back to pre-pandemic levels, despite going harder during the initial lock-down.&lt;/li&gt;
&lt;li&gt;It may be a way to win back the public&amp;rsquo;s confidence in the government.  The &lt;a href=&#34;https://www.abc.net.au/news/2020-07-20/daniel-andrews-brand-damaged-coronavirus-fight-victoria/12470896&#34;&gt;Victorian government&lt;/a&gt; has taken a hit in the polls due to the mistakes that caused the current round of lock-downs.  I can see rallying the public around the goal of elimination a way to win them back.  You can even use the current situation as a unique opportunity to achieve this, maybe by saying, &amp;ldquo;given that we&amp;rsquo;re already going through another round of lock-downs, let&amp;rsquo;s go for broke and remain locked down until we&amp;rsquo;ve eliminated this virus once and for all.&amp;rdquo;  Now you have a something that people can work towards, and the feeling that their current sacrifice is not for nothing if (when?) another wave comes through.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;m aware that this a post written by someone who is in a position of relative privilege.  I haven&amp;rsquo;t lost my job, and I remain relatively healthy and financially secure.  I also know that it will be expensive and will cause a fair bit more suffering for those with small businesses that will need to shut their doors.  So I recognised that I don&amp;rsquo;t have all the facts, and this may not be feasible at all.  But I also question the feasibility of maintaining a long-term suppression strategy until treatments or a vaccine become available: this is a tricky virus to handle.&lt;/p&gt;
&lt;p&gt;In the end, I guess I&amp;rsquo;m just a bit disappointed by the lack of abition in attempting this as a goal.  It seems advantageous, especially now, to seize the moment and go for making our second round of lock-downs our last.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Remarks on Go&#39;s Error Handling using Facebook&#39;s SDK Crashes As a Framing Device</title>
      <link>https://lmika.org/2020/07/14/there-are-new.html</link>
      <pubDate>Tue, 14 Jul 2020 08:43:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/07/14/there-are-new.html</guid>
      <description>&lt;p&gt;There are new &lt;a href=&#34;https://mjtsai.com/blog/2020/07/13/another-facebook-sdk-crash/&#34;&gt;reports of Facebook&amp;rsquo;s SDK crashing apps again&lt;/a&gt; due to server changes.  The post above links to Bugsnag article which explores the underlying cause: that&amp;rsquo;s worth a read.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m going to throw a shout-out to Go&amp;rsquo;s approach to error handling here.  I&amp;rsquo;m not saying that this shows the superiority of Go over Objective C: these sorts of things can happen in any language.  The difference I want to highlight is that Go treats error handling as part of the standard flow of the language, rather than the exceptional flow.  This forces you to think about error conditions when you&amp;rsquo;re making calls to code that can fail.&lt;/p&gt;
&lt;p&gt;This does result in some annoying code of the form:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;result, err := doOperation()
if err != nil {
    return nil, err
}

result2, err := doSecondOperation(result)
if err != nil {
    return nil, err
}

// and so on
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and there&amp;rsquo;s nothing stopping your from completely ignoring the error.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s no way to call these two functions without dealing with the error in some way.  That is, there&amp;rsquo;s no way to simply write &lt;code&gt;doSecondOperation(doOperation())&lt;/code&gt;: you&amp;rsquo;re given an error and you have to do something with it.  So you might as well handle it gracefully.&lt;/p&gt;
&lt;p&gt;P.S. I should probably state that I know very little about Objective C.  I do know that a fair number of APIs in AppKit and UIKit make use of completion handlers which can provide an error value, although to me it seems a littler easier to ignore it vs. deailing with the error values in Go.  I also know that Swift makes improvements here, forcing you to prefix calls that can fail with the &lt;code&gt;try&lt;/code&gt; keyword.  Again, this is not to rag on Objective C; rather it&amp;rsquo;s a comment on the idioms of error handling in Go and how these sort of events could prevent the app from crashing.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Signed Up To micro.blog</title>
      <link>https://lmika.org/2020/07/12/ive-signed-up.html</link>
      <pubDate>Sun, 12 Jul 2020 08:57:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/07/12/ive-signed-up.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve signed up with micro.blog in an attempt to post to the blog more frequently than I have been.  The last post I had on my existing blog was in March, and it felt to me like it was starting to become a bit negelected.  I think the main reason for the delay is that I feel the need to publish long form articles, which involves a lot of work to write, review, etc.  I will try to continue to do that, but I also want to start posting shorter articles more often.&lt;/p&gt;
&lt;p&gt;Interesting story: I had this idea for a while, since the start of June.  Back then my blog was a simple Hugo site managed in Git, and hosted within Google Cloud&amp;rsquo;s object store.  I had a few posts there — these have been migrated to this site — and I also had a few ideas for posts in the pipeline.  I knew I wanted to write more often, but I was starting to get the sense of &amp;ldquo;overhead&amp;rdquo; involved in creating new posts.  Writing doesn&amp;rsquo;t come naturally to me, and I think one of the barriers of posting was the amount of non-writing involved in doing so, things like checking out the latest copy, writing it, pushing the branch holding the draft, reviewing the PR (not that there was much to review), merging it, checking out master and runing &amp;ldquo;make&amp;rdquo; to generate and deploy it.  Each step is not hard in itself, I do it many times a day at work.  But it&amp;rsquo;s just more overhead making the actual act of posting just a little bit harder, and I was begining to realise that if I wanted to write more often, I needed a way to do so effortlessly.&lt;/p&gt;
&lt;p&gt;So I committed the second cardinal sin of programming and spent a few weeks making my own CMS (I was also close to committing the first cardinal sin of programming — making my own text editer — much earlier in my programming life, but luckly lost intrested after starting).  The aim was to setup a service and workflow that would make it easier to post smaller articles, more often, and from any machine that I was currently on.  I also got swepted away with hearing others discuss the techonologies of their own blogging engines, plus their approach to &amp;ldquo;owning the entire stack&amp;rdquo; as it was.  Plus, I cannot resist starting a new project, epecially now when it&amp;rsquo;s difficult doing things outside or with other people around.&lt;/p&gt;
&lt;p&gt;However, as I got closer to &amp;ldquo;launch&amp;rdquo;, I was beginning to consider the amount of work involved in maintaining it and extending it to suppot things I want further down the line, things like extra pages, etc.  This is a classic problem of mine.  I get a sense of enthusiasm as I see the core features come togeather&amp;hellip; and then I think about what work I need to do to support afterwards, and I completely loose interest.  The project then begins to deterorate as additional hacks are added to support these things, and it just becomes less maintainable and fun to work on over time.&lt;/p&gt;
&lt;p&gt;It also serves as a great distraction: what better way to avoid writing, than to work on an application that would reduce the barriers that inhibit me to write.&lt;/p&gt;
&lt;p&gt;So, I&amp;rsquo;m doing the smart thing: I&amp;rsquo;ve stopped working on it and have moved to micro.blog.  Being a subscriber to Martin Reece&amp;rsquo;s feed, I see the amount of effort and care he puts into this platform, something that I don&amp;rsquo;t see myself doing for my own CMS.  I can only hope this would result in me publishing posts more frequently, we&amp;rsquo;ll see.  But now I have no more excuses to actually write.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Features From Android In iOS 14, and The Enthusiasm Gap</title>
      <link>https://lmika.org/2020/07/12/features-from-android.html</link>
      <pubDate>Fri, 26 Jun 2020 09:56:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/07/12/features-from-android.html</guid>
      <description>&lt;p&gt;John Gruber on &lt;a href=&#34;https://daringfireball.net/linked/2020/06/25/wong-ios-14-android&#34;&gt;Daring Fireball&lt;/a&gt;, commenting on an &lt;a href=&#34;https://www.inputmag.com/tech/after-ios-14-theres-almost-no-reason-to-buy-an-android-phone-anymore&#34;&gt;article about features in iOS 14 that Android had first&lt;/a&gt;:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;Do you get the sense that Google, company-wide, is all that interested in Android? I don’t. Both as the steward of the software platform and as the maker of Pixel hardware, it seems like Google is losing interest in Android. Flagship Android hardware makers sure are interested in Android, but they can’t move the Android developer ecosystem — only Google can.&lt;/p&gt;
&lt;p&gt;Apple, institutionally, is as attentive to the iPhone and iOS as it has ever been. I think Google, institutionally, is bored with Android.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;As an Android user, and occasional dabbler in Android app development, this concerns me if it is true.  I doubt Google will completely give up on Android, but given the recent shutdowns of Googles services over the years, it&amp;rsquo;s clear that there are very few things Google is &amp;ldquo;married&amp;rdquo; to in the long term.&lt;/p&gt;
&lt;p&gt;With Android&amp;rsquo;s success and it&amp;rsquo;s &lt;a href=&#34;https://arstechnica.com/gadgets/2016/10/building-android-a-40000-word-history-of-googles-mobile-os/&#34;&gt;raison d&amp;rsquo;être&lt;/a&gt;, one could argue that Google has room to take a more relaxed attitude towards advancing Android as a platform, so long as cheap phones are still being bought and people are still using them.  But I certantly hope that they do not completely abandon it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Go’s Type Parameters Proposal</title>
      <link>https://lmika.org/2020/07/12/on-gos-type.html</link>
      <pubDate>Thu, 25 Jun 2020 11:30:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/07/12/on-gos-type.html</guid>
      <description>&lt;p&gt;The developers of Go have release a new draft proposal for type parameters.  The biggest change is the replacing the concept of constraints, which complicated the proposal somewhat, and replaced it with interfaces to express the same thing.  You can read the proposal here &lt;a href=&#34;https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md&#34;&gt;latest proposal here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I think they’re starting to reach a great balance between what currently exists in the language, and the features required to make a useful type parameter system.  The use of interfaces to constrain the type, that is declare the operators that a type must implement in order to be used as a type parameter for a function or struct, makes total sense.  It also makes moving to type parameters in some areas of the standard library trivial.  For example, the &lt;a href=&#34;https://golang.org/pkg/sort/#Sort&#34;&gt;sort.Sort&lt;/a&gt; function prototype:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Sort(data Interface)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;can simply be written as:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Sort(type T Interface)(data T)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I do have some minor concerns though.  The biggest one is the use of interfaces to express constraints related to operators, which are expressed as &lt;a href=&#34;https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#type-lists-in-constraints&#34;&gt;type lists&lt;/a&gt;.  I think listing the types that a particular type parameter can instantiate makes sense.  It dramatically simplifies the process of expressing a constraint based on the operators a particular type supports.  However, using the concept of interfaces for this purpose seems a little strange, especially so when these interfaces cannot be used in areas &lt;a href=&#34;https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#type-lists-in-interface-types&#34;&gt;other than type constraints&lt;/a&gt;.  To be fair, it seems like they recognise this, and I&amp;rsquo;m suspecting that in practice these interfaces will be defined in a package that can simply be used, thereby not requiring us to deal with them directly unless we need to.&lt;/p&gt;
&lt;p&gt;But all in all, this looks promising.  It is starting to feel like the design is coming together, with the rough edges starting to be smoothed out.  I appreciate the level of careful consideration the core Go developers are exhibiting in this process.  This is after all a large change to the language, and they only have one real chance at this.  Sure we have to wait for it, but especially in language design, mistakes are forever.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>YouTube Music and Uploaded Music Libraries</title>
      <link>https://lmika.org/2020/07/12/youtube-music-and.html</link>
      <pubDate>Thu, 25 Jun 2020 11:30:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/07/12/youtube-music-and.html</guid>
      <description>&lt;p&gt;Ron Amado, from &lt;a href=&#34;https://arstechnica.com/gadgets/2020/06/youtube-music-library-transfers-your-purchased-music-is-not-welcome-here/&#34;&gt;Ars Technica&lt;/a&gt;:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;YouTube Music is really only for The Music Renter—someone who wants to pay $10 per month, every month, forever, for &amp;ldquo;Music Premium.&amp;rdquo; This fee is to buy a monthly streaming license for music you do not own, and I&amp;rsquo;d imagine a good portion of it goes to music companies. When you don&amp;rsquo;t pay this rental fee, YouTube Music feels like a demo app.&lt;/p&gt;
&lt;p&gt;I prefer to own my music, and I own a lot of independent music that wouldn&amp;rsquo;t be covered under this major-record-label-streaming-license anyway, so I have no interest in this service. The problem is YouTube Music also locks regular music-playback features behind this monthly rental fee, even for music you&amp;rsquo;ve uploaded to the service. The biggest offense is that you can&amp;rsquo;t use Google Cast without paying the rental fee, but when it&amp;rsquo;s music that I own and a speaker that I own, that&amp;rsquo;s really not OK. Google Music did not do this.&lt;/p&gt;

  &lt;/blockquote&gt;
&lt;p&gt;These last couple of weeks I&amp;rsquo;ve actually been working on a personal music app that will playback music uploaded to S3.  It was mainly for listening to music that I composed myself, although being able to listen to music that I&amp;rsquo;ve purchased and ripped to MP3 was a key motivating factor here as well.  I was aware that such services existed so I occasionally wondered if my time could be better spent doing something else.  Now, I feel like I&amp;rsquo;ve made the right choice here.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Don&#39;t Get it Now</title>
      <link>https://lmika.org/2020/03/22/dont-get-it.html</link>
      <pubDate>Sun, 22 Mar 2020 11:00:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/03/22/dont-get-it.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s scary times at the moment.  The Corona Virus (SARS-CoV-2 and Covid-19) is raging through Europe at this moment,
with hundreds of people dying in Italy, Spain and France and most of the those countries, along with the US, in
lock-down.  The hospital system is currently not equipped to be able to handle the peak
number of patients that will require intensive care: doctors from Italy, France and New York are telling stories
about how they have to choose who lives and dies, and I&amp;rsquo;m fearful that we may start hearing stories like that here.
There is currently no cure, nor no treatment.  There&amp;rsquo;s been &lt;a href=&#34;https://www.imperial.ac.uk/media/imperial-college/medicine/sph/ide/gida-fellowships/Imperial-College-COVID19-NPI-modelling-16-03-2020.pdf&#34;&gt;models&lt;/a&gt; indicating that even if we take steps to suppress the virus
now, there will be continuous surges in outbreaks until a vaccine is ready in 12 to 18 months, suggesting that
we may need to be in a state of lock-down or at the very least, rigid social distancing until August 2021 at the
latest.  The WHO reckons that a majority of the worlds population will get infected over the next year.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not an doctor, nor an etymologist.  I cannot begin to suggest what we should do as a society.  But I&amp;rsquo;m going
to give a few thoughts as to how I plan to weather this storm.&lt;/p&gt;
&lt;p&gt;I think at this current stage, our enemy, along with the virus, is time.  I hope I don&amp;rsquo;t have to tell you that the
virus is moving through the worlds population now, even as we speak.  But humanity is not standing still either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We are researching the hell out of this thing.  One such example is on Tuesday we
&lt;a href=&#34;https://www.abc.net.au/news/2020-03-17/research-how-bodys-immune-system-fights-coronavirus-covid-19/12059266&#34;&gt;learnt how the body reacts to the virus&lt;/a&gt;, which could help with understanding how best to treat it.  Along with this, there are still some very important
unanswered questions about the [actual death rate and transmission rate](&lt;a href=&#34;https://www.abc.net.au/news/2020-03-22/covid-19-how-deadly-and-contagious-is&#34;&gt;www.abc.net.au/news/2020&amp;hellip;&lt;/a&gt; coronavirus/12068106), as well as [whether heard immunity will work](&lt;a href=&#34;https://www.abc.net.au/news/2020-03-22/doubt-over-contracting-coronavirus-covid&#34;&gt;www.abc.net.au/news/2020&amp;hellip;&lt;/a&gt; 19-twice/12075878), that we&amp;rsquo;ll hopefully get the answer to soon.&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;re &lt;a href=&#34;https://www.abc.net.au/news/health/2020-03-19/coronavirus--drugs-and-vaccines-to-treat-and-prevent-covid-19/12067496&#34;&gt;started clinical trials&lt;/a&gt; of potential treatments, and a vaccine.  It&amp;rsquo;s still early days at the moment, and we probably won&amp;rsquo;t have anything ready soon, but the early indications of this sounds promising.&lt;/li&gt;
&lt;li&gt;And, if the above should fail, we are (should?) be ramping up our hospital capacity to handle the influx of patients,
meaning that if someone should unfortunately die from this, it won&amp;rsquo;t be because they didn&amp;rsquo;t have a bed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So my mantra for the next few months is &lt;em&gt;&amp;ldquo;don&amp;rsquo;t get it now.&amp;rdquo;&lt;/em&gt;  Wait to get infected for as long as you can.  The ideal case
is not to catch it at all, but if we&amp;rsquo;re destine to get infected, best to get in infected later, when some of the
points above have been addressed, instead of sooner when they have not.  This will obviously mean sacrificing things
like going to the gym, going out for coffee, or seeing friends and family.  But I
believe that this is a price worth paying, especially if the alternative is loosing someone you love, or potentially your
own life.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s my current strategy at this time.  I don&amp;rsquo;t know if it will work, and as things develop it may need refining.
But after thinking about this for the previous few weeks, it&amp;rsquo;s
the best strategy I can think of.  And I think it will help me get through this.&lt;/p&gt;
&lt;p&gt;P.S.  A lot of my thoughts on this came from reading &lt;a href=&#34;https://medium.com/@tomaspueyo/coronavirus-the-hammer-and-the-dance-be9337092b56&#34;&gt;this article by Tomas Pueyo&lt;/a&gt;.  He&amp;rsquo;s obviously more knowledgeable about how we should act on this as a whole.  It is worth your time reading this.&lt;/p&gt;
&lt;p&gt;P.P.S. I spoke quite abstractly about the health system, but it&amp;rsquo;s important to remember that these systems are made
up of people: doctors, nurses and paramedics on the front line, along with the researchers, manufacturers and logistics
who support them.  At this time, they are giving their &lt;em&gt;all&lt;/em&gt;, and then some, to help us through this crisis.
Once this is over, I think we owe every single one of these individuals a beer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update On 4th Dec 2022:&lt;/strong&gt; Almost three years since writing this post, I tested positive for Covid-19 for the first time. My symptoms were that of a pretty rough cold which, given what the possibilities could have been when I wrote this post, meant that I weathered the disease pretty well. I finally caught it at a time when vaccines and treatments were wildly available and I was up to date with my inoculations. So all in all, I&amp;rsquo;m glad the whole &amp;ldquo;don&amp;rsquo;t get it now&amp;rdquo; worked in my favour.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Reflections On Virus Scanners on Windows</title>
      <link>https://lmika.org/2020/02/29/reflections-on-virus.html</link>
      <pubDate>Sat, 29 Feb 2020 09:00:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/02/28/reflections-on-virus.html</guid>
      <description>&lt;p&gt;I was listening to &lt;a href=&#34;https://daringfireball.net/thetalkshow/2020/02/24/ep-277&#34;&gt;Episode 277 of The Talk Show&lt;/a&gt; in which
John Gruber was discussing virus scanners on Apple Macs with John Moltz.  The discussion turned briefly to
the state of virus scanners on Windows, and how invasive these commercial scanners were compared to Windows Defender
provided by Microsoft.&lt;/p&gt;
&lt;p&gt;Hearing this discussion brought memories of my experience with virus scanners back in the days of
Windows XP and earlier.  There was no Microsoft Defender back then so we had to have a license for one
of the commercial scanners that were sold to home users at the time, such as Norton AntiVirus.  Given how insecure Windows
was back then, it was one of the first things we had to put on a fresh install of Windows.  And these things certainly
slowed Windows down.  But we recognised that it was necessary and after a couple of weeks, we eventually got use to it.&lt;/p&gt;
&lt;p&gt;However, after setting up a new install, there was this brief period of time when we got to experience Windows without a virus scanner.
And the difference in the user experience was significant.  The boot processed was fast, the UI snappy,
and the applications quick to launch.
In fact it was so good, it felt strange and slightly uneasy, as the knowledge that there was no virus scanner protecting the
system was evident.  Only after the virus scanner was installed, with the resulting
hit in performance, did it fell safe to use Windows again.  It was not until I listened to this episode that I
realised &lt;a href=&#34;https://craigmod.com/essays/fast_software/&#34;&gt;how perverted this feeling is&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I cannot imagine how it must feel for those Microsoft developers who worked hard on providing a user experience that
was responsive only to see it slowed down on almost every machine by a virus scanner.  I&amp;rsquo;m sure they knew that,
due to the prevalence of malware for Windows back then, it was necessary.  Still, I could not imagine that they would
have been thrilled about it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>New Home of Steve Yegge&#39;s Rant About Google Services</title>
      <link>https://lmika.org/2020/07/12/new-home-of.html</link>
      <pubDate>Sat, 07 Dec 2019 21:50:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/07/12/new-home-of.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve always enjoyed &lt;a href=&#34;https://gist.github.com/chitchcock/1281611&#34;&gt;this rant&lt;/a&gt; from &lt;a href=&#34;http://steve-yegge.blogspot.com/&#34;&gt;Steve Yegge&lt;/a&gt; about how Google differed from Amazon in how they develop their services.  Not sure if it&amp;rsquo;s applicable now but it was quite interesting to hear how the two companies differed in their approach in building and releasing products.  After hearing that Google+ was being shutdown, I wondered what would happen with the rant, and whether it would be lost to time.  It was fortunate that someone saved it.&lt;/p&gt;
&lt;p&gt;For those of you who haven&amp;rsquo;t read any of Steve&amp;rsquo;s other blogposts, please checkout his current &lt;a href=&#34;http://steve-yegge.blogspot.com/&#34;&gt;blog&lt;/a&gt;, plus several of his other &lt;a href=&#34;https://sites.google.com/site/steveyegge2/blog-rants&#34;&gt;Drunken Blog Rants&lt;/a&gt;.  They are well worth your time.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Five Common Data Stores and When to Use Them</title>
      <link>https://lmika.org/2020/07/12/five-common-data.html</link>
      <pubDate>Mon, 28 Oct 2019 20:48:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/07/12/five-common-data.html</guid>
      <description>&lt;p&gt;Very interesting post on the &lt;a href=&#34;https://engineering.shopify.com/blogs/engineering/five-common-data-stores-usage&#34;&gt;Shopify Engineering Blog&lt;/a&gt;
on the difference between 5 types of data-stores available to developers,
and under what circumstances they should be used.&lt;/p&gt;
&lt;p&gt;I find it tricky to decide
on the best technology for storing data for a particular project.  I guess
the important thing to keep in mind is to try and figure out as best you can
how the data is going to be used (i.e. queried).  If you know that, the decision
should be easy once you know what&amp;rsquo;s out there, and this blog post certainly
helps in this regard.  If you don&amp;rsquo;t, I guess the next best thing is to try
to find the option that will give you the most flexibility with hopefully
not too much loss in performance.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>