<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Colas.Nahaboo.net - code</title>
    <subtitle>Colas Nahaboo personal site, with discussions about programming code, web and computing topics, surfing and SUPing, and various musings.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://colas.nahaboo.net/tags/code/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://colas.nahaboo.net"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-08-30T00:00:00+00:00</updated>
    <id>https://colas.nahaboo.net/tags/code/atom.xml</id>
    <entry xml:lang="en">
        <title>KXF: The Koala XML Form</title>
        <published>2026-08-30T00:00:00+00:00</published>
        <updated>2026-08-30T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/kxf-the-koala-xml-form/"/>
        <id>https://colas.nahaboo.net/blog/kxf-the-koala-xml-form/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/kxf-the-koala-xml-form/">&lt;div class=&quot;obsolete-overlay&quot;&gt;
    Legacy
&lt;/div&gt;
&lt;style&gt;
.obsolete-overlay {
    position: fixed;
    top: 40px;
    left: 60%;
    transform: translate(-50%, -50%) rotate(-5deg);
    font-size: 2vw;
    font-weight: 800;
    color: rgba(255, 0, 0, 0.4);
    background: rgba(255, 255, 0, 0.2);
    border: 15px solid rgba(255, 0, 0, 0.3);
    padding: 10px 20px;
    z-index: 9999;
    pointer-events: none;
    text-transform: uppercase;
    white-space: nowrap;
    user-select: none;
}
&lt;/style&gt;
&lt;p&gt;A blast from the past... In the last century, when we wrote XML by hand, I designed a format to get rid of one of XML multiple annoyances: as it used ordinary characters as deleimters, such as &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, it means often quoting hell.&lt;/p&gt;
&lt;p&gt;The idea: Use non-ascii-7bits characters as markup!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;«foo»&lt;/code&gt; to delimit tags, instead of &lt;code&gt;&amp;lt;foo&amp;gt;&amp;lt;/foo&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Use these regular elements as escape sequences instead of entities. Escape &lt;code&gt;«&lt;/code&gt; by &lt;code&gt;«-»&lt;/code&gt;. Not really readable — but more than entities — but natural and consistent to parse.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wrote the KXF &amp;lt;-&amp;gt; XML converter in Klone, my lisp scripting language, that I definitely need to publish here as part of these legacy series.&lt;/p&gt;
&lt;p&gt;Here is the doc of the format:&lt;/p&gt;
&lt;div style=&quot;background-color: #eeffee;&quot;&gt;
&lt;h1&gt; KXF: The Koala XML Form&lt;/h1&gt;&lt;h2&gt;Purpose&lt;/h2&gt;
    The &lt;b&gt;Koala XML Form&lt;/b&gt; XML avatar has been defined with the following design 
    goals: 
    &lt;ul&gt;
      &lt;li&gt;Ease writing by hand&lt;/li&gt;
      &lt;li&gt;Ease human readability&lt;/li&gt;
      &lt;li&gt;Solve only syntaxic problems
	&lt;ul&gt;
          &lt;li&gt;no entities&lt;/li&gt;
          &lt;li&gt;no DTD&lt;/li&gt;
          &lt;li&gt;no CDATA&lt;/li&gt;
          &lt;li&gt;straightforward whitespace handling&lt;/li&gt;
          &lt;li&gt;get rid of most quoting hell: use less special chars, and use non ascii ones&lt;/li&gt;
        &lt;/ul&gt;&lt;/li&gt;
    &lt;/ul&gt;&lt;h2&gt;Design&lt;/h2&gt;&lt;p&gt;   Here are some examples:
    &lt;table&gt;
        &lt;tr&gt;
          &lt;th&gt;KXF&lt;/th&gt;
          &lt;th&gt;XML&lt;/th&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&amp;laquo;tag&amp;raquo;&lt;/td&gt;
          &lt;td&gt;&amp;lt;tag/&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&amp;laquo;tag foo &amp;lt;&gt; bar&amp;raquo;&lt;/td&gt;
          &lt;td&gt;&amp;lt;tag&gt;foo &amp;amp;lt; &amp;amp;gt; bar&amp;lt;/tag&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&amp;laquo;tag(x=y z=&quot;1 2&quot; t=&#39;gp&#39;) gee&amp;raquo;&lt;/td&gt;
          &lt;td&gt;&amp;lt;tag x=&#39;y&#39; y=&#39;1 2&#39; t=&#39;gp&#39;&gt;gee&amp;lt;/tag&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&amp;laquo;! comments&amp;raquo;&lt;/td&gt;
          &lt;td&gt;&amp;lt;!-- comments --&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&amp;laquo;? PIs&amp;raquo;&lt;/td&gt;
          &lt;td&gt;&amp;lt;?PIs?&gt;&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/table&gt;&lt;p&gt;Quoting special chars:
	&lt;table&gt;
        &lt;tr&gt;
          &lt;th&gt;Char&lt;/th&gt;
          &lt;th&gt;Quoted form&lt;/th&gt;
          &lt;th&gt;Where&lt;/th&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&amp;laquo;&lt;/td&gt;
          &lt;td&gt;&amp;laquo;-&amp;raquo;&lt;/td&gt;
          &lt;td&gt;character data contents&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&amp;raquo;&lt;/td&gt;
          &lt;td&gt;&amp;laquo;+&amp;raquo;&lt;/td&gt;
          &lt;td&gt;character data contents&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&#39;&lt;/td&gt;
          &lt;td&gt;&amp;laquo;!&amp;raquo;&lt;/td&gt;
          &lt;td&gt;attribute values&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&#39;&lt;/td&gt;
          &lt;td&gt;&amp;laquo;/&amp;raquo;&lt;/td&gt;
          &lt;td&gt;attribute values&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&quot;&lt;/td&gt;
          &lt;td&gt;&amp;laquo;#&amp;raquo;&lt;/td&gt;
          &lt;td&gt;attribute values&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&quot;&lt;/td&gt;
          &lt;td&gt;&amp;laquo;*&amp;raquo;&lt;/td&gt;
          &lt;td&gt;attribute values&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/table&gt;&lt;ul&gt;
      &lt;li&gt;&amp;raquo;&amp;laquo;/tag&amp;raquo; optional ending forms can be used to check nesting as in
     &lt;br&gt;&lt;tt&gt;&amp;laquo;foo a very big contents... &amp;raquo;&amp;laquo;/foo&amp;raquo;&lt;/tt&gt;&lt;ul&gt;
          &lt;li&gt;ending contents is ignored and can be used for comments, as in:
    &lt;br&gt;&lt;tt&gt;&amp;laquo;foo a very big contents... &amp;raquo;&amp;laquo;/foo the main loop&amp;raquo;&lt;/tt&gt;&lt;/li&gt;
          &lt;li&gt;attributes if present must match the starting tag attributes:
    &lt;br&gt;
              &lt;tt&gt;&amp;laquo;foo(x=1) a very big contents... &amp;raquo;&amp;laquo;/foo(x=1)&amp;raquo;&lt;/tt&gt;
             matches, but
    &lt;br&gt;
              &lt;tt&gt;&amp;laquo;foo a very big contents... &amp;raquo;&amp;laquo;/foo(x=2)&amp;raquo;&lt;/tt&gt;
             raises an error
    &lt;/li&gt;
        &lt;/ul&gt;&lt;/li&gt;
      &lt;li&gt; Whitespace around sub-elements are discarded, but not around character data.
 &lt;br&gt;You can keep them 
 by postfixing element name with = (it sets the xml:space attribute to
 &quot;preserve&quot;), such as 
     &lt;br&gt;&lt;tt&gt;&amp;laquo;pre=(a=b) &amp;laquo;b x&amp;raquo; &amp;laquo;i y&amp;raquo;&amp;raquo;&lt;/tt&gt;
 or by enclosing them in the pseudo node &amp;laquo;= &amp;raquo;
&lt;br&gt;&lt;tt&gt;&amp;laquo;x &amp;laquo;=   &amp;raquo; &amp;laquo;y&amp;raquo; &amp;laquo;z&amp;raquo;&amp;raquo;&lt;/tt&gt;
 keeps space before y, but not between y and z
&lt;/li&gt;
      &lt;li&gt;For comfortable use, it is recommended to assign &amp;laquo;&amp;raquo; to 
shift-keypad-minus  and shift-keypad-plus respectively&lt;/li&gt;
      &lt;li&gt;Encodings are raw: either ISO-Latin-1, or UCS2, which can be autodetected 
 as fields must begin with &amp;laquo;&amp;lt;letter&gt; (ISO-Latin-1), \0&amp;laquo; (UCS2), &amp;laquo;\0 (MS UCS2)
&lt;/li&gt;
    &lt;/ul&gt;&lt;h2&gt;Benefits&lt;/h2&gt;&lt;ul&gt;
      &lt;li&gt;More readable&lt;/li&gt;
      &lt;li&gt;Most text editors know how to balance &amp;laquo; and &amp;raquo; better than 
    &amp;lt;foo&gt; and &amp;lt;/foo&gt;&lt;/li&gt;
      &lt;li&gt;It is self sufficient: whitespace handling is explicit&lt;/li&gt;
      &lt;li&gt;No need to quote the common chars &amp;lt;, &gt;, &amp;amp;&lt;/li&gt;
    &lt;/ul&gt;&lt;h2&gt;Drawbacks&lt;/h2&gt;&lt;ul&gt;
      &lt;li&gt;You cannot use XML Tools on this format. Not really a problem, as 
    you can convert to and from XML&lt;/li&gt;
    &lt;/ul&gt;&lt;h2&gt;Tools&lt;/h2&gt;
    For now, there exists only a parser/writer in 
    &lt;a href=&quot;ftp://koala.ilog.fr/klone&quot;&gt;Klone&lt;/a&gt; (&lt;tt&gt;xml-ksf.kl&lt;/tt&gt;, used to implement Klone
    scripts (&lt;tt&gt;xml2kxf&lt;/tt&gt;, &lt;tt&gt;kxf2xml&lt;/tt&gt;) that converts back and forth to XML.
&lt;p&gt;A java SAX parser is planned
&lt;h2&gt;Author&lt;/h2&gt;&lt;a href=&quot;http://colas,nahaboo.net&quot;&gt;Colas Nahaboo&lt;/a&gt;&lt;hr&gt;&lt;a href=&quot;index.html&quot;&gt;Back to XML Avatars&lt;/a&gt;
&lt;/div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>My first AI-assisted pull request accepted</title>
        <published>2026-08-25T00:00:00+00:00</published>
        <updated>2026-08-25T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/first-ai-assisted-accepted-pr/"/>
        <id>https://colas.nahaboo.net/code/first-ai-assisted-accepted-pr/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/first-ai-assisted-accepted-pr/">&lt;p&gt;&lt;img src=&#39;https://nicefox.net/logo/highres%20fox.png&#39; width=120px align=right&gt;I just got my first AI-assisted pull request accepted and merged. I must say, I am quite pleased — and a bit humbled: the AI wrote the code, I did the dull part: project manager.&lt;/p&gt;
&lt;p&gt;The project is &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/co-l/openfox&quot;&gt;Openfox&lt;/a&gt;&lt;/strong&gt;, a smart local-LLM-first agentic coding assistant. I found a bug: it did not see projects that are symlinks to directories. I keep my git repos in various places, and symlink them into the folder Openfox scans — but &lt;code&gt;Dirent.isDirectory()&lt;/code&gt; just tested if a file was a directory, so it failed to detect that a symlink could be a link to an actual directory. Thus symlinked project folders were invisible in the directory browser, the workspace listing, the orphaned-workspace scan, and the plugin listing.&lt;/p&gt;
&lt;p&gt;Following my own &lt;a href=&quot;/blog/dont-do-pull-request-do-prompt-suggest/&quot;&gt;prompt request idea&lt;/a&gt;, I asked Openfox itself — running DeepSeek V4 Flash — to analyze and fix the bug on a local copy. It found the root cause, proposed a shared &lt;code&gt;isDirectoryEntry&lt;/code&gt; helper that follows symlinks via &lt;code&gt;stat()&lt;/code&gt; (excluding broken and cyclic links), and even produced a careful analysis of Windows junction points. I opened the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/co-l/openfox/issues/243&quot;&gt;issue&lt;/a&gt; and offered the prompt to the maintainer.&lt;/p&gt;
&lt;p&gt;Then I decided on my own to submit a real pull request anyway. My prompt-request post concedes there are cases where a PR is still better — this was one: the fix was small and well-contained, tests existed, and I had a full suite to prove it. Leaving all that on the maintainer would have been rude.&lt;/p&gt;
&lt;p&gt;So &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/co-l/openfox/pull/245&quot;&gt;PR #245&lt;/a&gt; it was: the &lt;code&gt;isDirectoryEntry&lt;/code&gt; helper used at all four call sites, with 43 new tests covering symlinked workspaces and the directories API — broken symlinks excluded, and a Windows skip where needed. The full unit suite passed (over 4000 tests), static checks were clean, and I rebased onto &lt;code&gt;develop&lt;/code&gt; three times as the upstream moved. Merged as &lt;code&gt;37e78208&lt;/code&gt;, ships in v2.0.125.&lt;/p&gt;
&lt;p&gt;Which prompted me to think: the AI used an AI tool - openfox — co-made by AI to generate the code, I did the specs, the validation of the diagnostics, the tests, the rebases, the cross-platform care — and the judgment call to ship a proper PR with full coverage instead of prompt slop. I acted as an auditor, the role that &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.cringely.com/2026/07/16/the-auditors-opinion/&quot;&gt;Cringely is saying is our added value for us humans&lt;/a&gt;.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>walkpadspeed v1: the walkpad app for geeks</title>
        <published>2026-08-17T00:00:00+00:00</published>
        <updated>2026-08-17T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/walkpadspeed-v1/"/>
        <id>https://colas.nahaboo.net/code/walkpadspeed-v1/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/walkpadspeed-v1/">&lt;p&gt;&lt;img src=&#39;walkpadspeed.svg&#39; align=right width=300&gt;At last, I decided to declare version 1 of &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/walkpadspeed&quot;&gt;walkpadspeed&lt;/a&gt;&lt;/strong&gt;, my app to drive my Bluetooth walking pad. Two months after the first working version, it is now mature and feature-complete enough to be released as &quot;version one&quot;.&lt;/p&gt;
&lt;p&gt;The pitch: a single web page that connects to a &quot;smart&quot; walking pad or treadmill over Bluetooth and drives it through a workout you design yourself. No app store, no install, no account, no ads. Open it in Google Chrome on your phone, connect, walk. &lt;strong&gt;The walkpad app for geeks!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://colas.nahaboo.net/code/walkpadspeed-v1/legends-v1.png&quot; alt=&quot;legend&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;what&quot;&gt;What?&lt;a class=&quot;zola-anchor&quot; href=&quot;#what&quot; aria-label=&quot;Anchor link for: what&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A walking pad is a bare treadmill: a belt, a motor, and a speed. The &quot;smart&quot; ones (the kind sold with a companion phone app, without a console full of buttons) speak the standard &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.bluetooth.com/specifications/specs/fitness-machine-service-1-0/&quot;&gt;FTMS Bluetooth protocol&lt;/a&gt;, which is all you need to control them. So walkpadspeed lives entirely in your browser, uses the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/WebBluetoothCG/web-bluetooth&quot;&gt;Web Bluetooth API&lt;/a&gt;, and needs nothing else.&lt;/p&gt;
&lt;p&gt;You design your routines as simple text files — each line a step of speed, optional incline, and duration:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;HIIT Sprints&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3 60 Warmup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;6 30 sprint #1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3 30&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;6 30 sprint #2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3 2m Cooldown&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Load the file, pick a routine, start walking. The app handles the timer and the speed changes for you, beeping one second before every change so you are never caught off guard.&lt;/p&gt;
&lt;h2 id=&quot;why&quot;&gt;Why?&lt;a class=&quot;zola-anchor&quot; href=&quot;#why&quot; aria-label=&quot;Anchor link for: why&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I bought a simple, entry level walking pad (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.youtube.com/watch?v=PrnlVPZXXoY&quot;&gt;a Fousae ZX-390&lt;/a&gt;, many brands sell similar ones: Urevo, Sperax, DeerRun, Costway...), favoring mechanical qualities over sophisticated features. I wanted an app where it was easy to program various routines, as I wanted to experiment a lot.&lt;/p&gt;
&lt;p&gt;The existing apps either required expensive subscriptions, or were super complex to program, or had bugs because they tried to cater to very complex treadmills or full health tracking plans. &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://myhomefit.de/&quot;&gt;MyHomeFit&lt;/a&gt; was the closest to satisfying my needs, but writing programs in their XML format or built-in editor was horrible — you cannot even duplicate a routine or a step — and speeds drifted because it relied on device data and accumulated rounding errors.&lt;/p&gt;
&lt;p&gt;So I designed walkpadspeed to &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://dev.to/lirena00/scratch-your-own-itch-how-to-build-and-ship-50a9&quot;&gt;&quot;scratch my own itch&quot;&lt;/a&gt;. And I think all the people like me, wanting freedom to control simply their simple walking pads, will like it too.&lt;/p&gt;
&lt;h2 id=&quot;what-is-distinctive&quot;&gt;What is distinctive&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-distinctive&quot; aria-label=&quot;Anchor link for: what-is-distinctive&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Routines as text files.&lt;/strong&gt; An open format, terse and editable in any editor — not an XML abomination. Comments, blanks between routines, speeds in km/h or mph, durations in seconds or minutes, optional incline percentages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Driven by your heart rate.&lt;/strong&gt; Wear any device using the standard BLE Heart Rate Service (Coros, Coospo, Garmin, Polar, Wahoo...) and the pad speed is auto-adjusted to keep your heart in a chosen zone. There are the usual Z1–Z5 zones, plus a &quot;digestive&quot; Z0 for the optimal effort to reduce glycemic peaks after meals.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Speed that adapts to the day.&lt;/strong&gt; Nudge the whole routine faster or slower on the fly (&lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;+++&lt;/code&gt;...) without losing your place. Feeling great? Tap up. Tired? Tap down.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modular routines&lt;/strong&gt; Each routine is composed of steps (a line in the file), and during your workout you can go back and forth steps, and even start at any step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test mode.&lt;/strong&gt; No walkpad on hand? The whole app runs simulated, so you can design and rehearse routines anywhere.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logs of your sessions.&lt;/strong&gt; Kept as simple text files, in the same format as the routines, tracking speed, zone, incline and heart rate step by step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Routines that are shareable.&lt;/strong&gt; Store your routine anywhere on the web and import it by URL.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;why-a-single-html-file&quot;&gt;Why a single HTML file?&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-a-single-html-file&quot; aria-label=&quot;Anchor link for: why-a-single-html-file&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Because I am a fan of the single self-contained artifact — I &lt;a href=&quot;/blog/go-single-binary-fanatic/&quot;&gt;ranted about it for Go binaries&lt;/a&gt;, and this is the same idea for the web. The whole app is one &lt;code&gt;walkpadspeed.html&lt;/code&gt; file embedding modern vanilla JavaScript: no build step, no dependencies, no server. Host it anywhere, open it in a browser on a Bluetooth-enabled device, done. I even host a copy at &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://walkpad.fr&quot;&gt;walkpad.fr&lt;/a&gt; so you do not have to type a long URL on your phone.&lt;/p&gt;
&lt;p&gt;Everything happens inside your browser. Your routines, your Bluetooth connection, your history never leave your device — no server, no account, no tracking.&lt;/p&gt;
&lt;h2 id=&quot;installation-usage-and-more-info&quot;&gt;Installation, Usage, and more info&lt;a class=&quot;zola-anchor&quot; href=&quot;#installation-usage-and-more-info&quot; aria-label=&quot;Anchor link for: installation-usage-and-more-info&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/walkpadspeed&quot;&gt;github.com/ColasNahaboo/walkpadspeed&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;AI co-written: don&#39;t like, don&#39;t use.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Bug reports: Favoring Prompt Requests over Pull Requests</title>
        <published>2026-08-15T00:00:00+00:00</published>
        <updated>2026-08-15T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/dont-do-pull-request-do-prompt-suggest/"/>
        <id>https://colas.nahaboo.net/blog/dont-do-pull-request-do-prompt-suggest/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/dont-do-pull-request-do-prompt-suggest/">&lt;p&gt;More and more often, I find a bug in some code that is not mine — often in a language or codebase I barely master. My reflex used to be to hack a fix anyway and send a pull request. But recently I spontaneously began to do the opposite: I let an AI agent fix the bug on a local copy, and instead of a pull request, I submitted the prompt I used — the one that seemed to fix the issue for me, passing the tests. An evolution of my &lt;a href=&quot;/blog/ai-without-ai&quot;&gt;AI without AI&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;I did this twice, recently: for the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/not-matthias/apollo/issues/183&quot;&gt;Apollo Zola theme&lt;/a&gt; and the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/co-l/openfox/issues/243&quot;&gt;Openfox AI harness&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Which prompted me to wonder whether this practice already had a name. I found the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.coderabbit.ai/blog/show-me-the-prompt-what-to-know-about-prompt-requests&quot;&gt;CodeRabbit post on prompt requests&lt;/a&gt;, which was right on the spot, but nothing else. Hence this post, to draw attention to the matter.&lt;/p&gt;
&lt;h2 id=&quot;what&quot;&gt;What?&lt;a class=&quot;zola-anchor&quot; href=&quot;#what&quot; aria-label=&quot;Anchor link for: what&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The idea, in short: when you find a bug in a project, and you use an AI coding agent to fix it, &lt;strong&gt;do not submit the AI-generated pull request&lt;/strong&gt;. Submit the &lt;strong&gt;prompt&lt;/strong&gt; you used to fix the bug, with a comment that it seems to fix the bug on your setup, and passes the tests. Plus an overview of the useful info your AI investigation produced: the root cause analysis, the edge cases, the alternative paths tried.&lt;/p&gt;
&lt;p&gt;The maintainers can then run the prompt themselves, in the harness and the LLM they are comfortable with — not the one you happened to have at hand — refine it, grill the AI with a &lt;code&gt;grill-me&lt;/code&gt; style interrogation, and adjust it to their codebase. Instead of having the pain to review an unfamiliar PR.&lt;/p&gt;
&lt;p&gt;This is not my invention, the name already exists: &lt;strong&gt;prompt requests&lt;/strong&gt;. The &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.coderabbit.ai/blog/show-me-the-prompt-what-to-know-about-prompt-requests&quot;&gt;CodeRabbit article&lt;/a&gt; traces the idea to a debate between &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://steipete.me/&quot;&gt;Peter Steinberger&lt;/a&gt;, creator of the self-hosted AI agent &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/clawdbot/clawdbot&quot;&gt;Clawdbot&lt;/a&gt;, and Gergely Orosz of &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.pragmaticengineer.com/&quot;&gt;Pragmatic Engineer&lt;/a&gt;.
At first I was going to call it &quot;PS&quot; for &lt;strong&gt;Prompt Suggest&lt;/strong&gt;, but let&#39;s not reinvent the wheel.&lt;/p&gt;
&lt;h2 id=&quot;why&quot;&gt;Why?&lt;a class=&quot;zola-anchor&quot; href=&quot;#why&quot; aria-label=&quot;Anchor link for: why&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Maintainers are drowning in &lt;strong&gt;PR slop&lt;/strong&gt;: AI-generated code that compiles, passes CI, and is utterly wrong for the codebase — style drift, redundant helpers, subtle architectural shortcuts. The &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://asdlc.io/concepts/pr-slop/&quot;&gt;asymmetric velocity problem&lt;/a&gt;: an agent generates code in seconds that takes a human hours to review.&lt;/p&gt;
&lt;p&gt;The numbers are staggering: GitHub merged pull requests went from &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.coderabbit.ai/blog/github-gives-maintainers-a-throttle-for-the-ai-pull-request&quot;&gt;25 million a month in January 2023 to 90 million a month in March 2026&lt;/a&gt;. A 3.6x increase, in an era where the number of human developers did not triple.&lt;/p&gt;
&lt;p&gt;The most spectacular example was the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.devclass.com/ai-ml/2025/11/27/ocaml-maintainers-reject-massive-ai-generated-pull-request/1728083&quot;&gt;13,000-line AI-generated pull request on the OCaml compiler&lt;/a&gt;, rejected by the maintainers, who cited the lack of review resources. The author, Joel Reymont, had &quot;carefully shepherded AI over the course of several days&quot;, and, asked why some files credited a maintainer as author, answered &quot;Beats me. AI decided to do so and I didn&#39;t question it.&quot;&lt;/p&gt;
&lt;p&gt;This is so bad that &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.blog/changelog/2026-02-13-new-repository-settings-for-configuring-pull-request-access&quot;&gt;GitHub now lets maintainers disable pull requests entirely&lt;/a&gt;, or restrict them to collaborators. Open source projects are starting to close their front door rather than drown.&lt;/p&gt;
&lt;p&gt;The root problem: &lt;strong&gt;code is now cheap&lt;/strong&gt;. As Jeremiah Lowin writes in &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://jlowin.dev/blog/oss-maintainers-guide-to-saying-no&quot;&gt;An Open-Source Maintainer&#39;s Guide to Saying No&lt;/a&gt;: &quot;Historically, we could assume that since writing code is an expensive, high-effort activity, contributors would engage in discussion before doing the work. Today, LLMs have inverted this. Code is now cheap, and we see it offered in lieu of discourse.&quot;&lt;/p&gt;
&lt;p&gt;Which prompted me to think: if the code is the cheap part, then the value of my contribution is not the diff — it is the investigation. The bug I found, the root cause my AI uncovered, the proof that a fix exists and passes the tests. And the prompt, which is the recipe to reproduce the investigation.&lt;/p&gt;
&lt;h2 id=&quot;how&quot;&gt;How?&lt;a class=&quot;zola-anchor&quot; href=&quot;#how&quot; aria-label=&quot;Anchor link for: how&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So here is what I think I will put from now on in a bug report instead of a pull request:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;markdown&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;**&lt;/span&gt;&lt;span style=&quot;color: #D20F39;font-weight: bold;&quot;&gt;Bug&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;**&lt;/span&gt;&lt;span&gt;: [short description, with a reproduction]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;**&lt;/span&gt;&lt;span style=&quot;color: #D20F39;font-weight: bold;&quot;&gt;Prompt&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;**&lt;/span&gt;&lt;span&gt; (tested, fixes the bug on my setup, tests pass):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #EA76CB;&quot;&gt;  &amp;gt; [the exact prompt(s) I gave to the agent]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;**&lt;/span&gt;&lt;span style=&quot;color: #D20F39;font-weight: bold;&quot;&gt;Harness / model&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;**&lt;/span&gt;&lt;span&gt;: opencode / DeepSeek v4 flash (or Claude Code / ...)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;**&lt;/span&gt;&lt;span style=&quot;color: #D20F39;font-weight: bold;&quot;&gt;Root cause&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;**&lt;/span&gt;&lt;span&gt;: [what the AI found]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;**&lt;/span&gt;&lt;span style=&quot;color: #D20F39;font-weight: bold;&quot;&gt;Edge cases&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;**&lt;/span&gt;&lt;span&gt;: [what the AI&amp;#39;s analysis surfaced]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The exact prompt is the key: like a minimal reproduction, it lets the maintainer run the fix in &lt;em&gt;his&lt;/em&gt; environment — the harness and model he trusts, in the context of his codebase. He can refine it, ask the AI questions, interrogate it about edge cases. I have a &lt;code&gt;grill-me&lt;/code&gt; skill in my own setup, I know it works :-)&lt;/p&gt;
&lt;h2 id=&quot;why-do-maintainers-should-like-it&quot;&gt;Why do maintainers should like it?&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-do-maintainers-should-like-it&quot; aria-label=&quot;Anchor link for: why-do-maintainers-should-like-it&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No trust question&lt;/strong&gt;: the code is generated in the maintainer&#39;s own sandbox, by his own model, with his own system prompts. The question is no longer &quot;do I trust this stranger&#39;s code?&quot; but &quot;does my setup produce a good fix for this prompt?&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Architecture control&lt;/strong&gt;: the generated code follows the codebase conventions, because it is generated &lt;em&gt;in&lt;/em&gt; the codebase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The maintainer stays the author&lt;/strong&gt;: he is not inheriting 800 lines of alien code, he is applying an insight to his own project.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the contributor still gets the satisfaction of having found and fixed the bug — which is the valuable part anyway.&lt;/p&gt;
&lt;h2 id=&quot;am-i-reinventing-the-wheel&quot;&gt;Am I reinventing the wheel?&lt;a class=&quot;zola-anchor&quot; href=&quot;#am-i-reinventing-the-wheel&quot; aria-label=&quot;Anchor link for: am-i-reinventing-the-wheel&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Partly. The CodeRabbit article is more cautious than me: it argues prompt requests and pull requests are &lt;strong&gt;not in conflict&lt;/strong&gt;, and prompt requests work best &lt;em&gt;before&lt;/em&gt; pull requests — review the intent before generating, then still review the code that ships. That makes sense &lt;em&gt;within a team&lt;/em&gt;, where you will have to maintain the result, and where determinism, git blame and accountability matter.&lt;/p&gt;
&lt;p&gt;My point is narrower, and for a different case: the &lt;strong&gt;drive-by contribution&lt;/strong&gt;. When you fix a bug in a project that is not yours, and you have no intention to maintain the result — which is most bug reports — the prompt is a better unit of exchange than the diff. It is the &lt;a href=&quot;/blog/ai-without-ai&quot;&gt;AI without AI&lt;/a&gt; of bug fixing: I provide the prompt, you run it in your own wetware (or your own GPU).&lt;/p&gt;
&lt;p&gt;There are cases where a PR is still better: the trivial one-line fix, the case where you &lt;em&gt;will&lt;/em&gt; maintain the fix, or the project that explicitly asks for PRs. Use your judgment.&lt;/p&gt;
&lt;p&gt;So, next time your AI agent fixes a bug in somebody else&#39;s project: resist the urge to &lt;code&gt;git push&lt;/code&gt;. Open an issue, and give the maintainers the prompt. They will be grateful, and the code will be better for it.&lt;/p&gt;
&lt;p&gt;Maybe a  modern twist on the old saying &quot;dont give fishes, show how to fish&quot;.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>gr: emacs-friendly grep of whole projects from anywhere</title>
        <published>2026-08-13T00:00:00+00:00</published>
        <updated>2026-08-13T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/gr-grep-the-whole-project-from-anywhere/"/>
        <id>https://colas.nahaboo.net/code/gr-grep-the-whole-project-from-anywhere/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/gr-grep-the-whole-project-from-anywhere/">&lt;p&gt;I just added &lt;strong&gt;gr&lt;/strong&gt; to my &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib&quot;&gt;colas-bash-lib&lt;/a&gt; collection. It is one of the essential tools I use under emacs.&lt;/p&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-problem&quot; aria-label=&quot;Anchor link for: the-problem&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I am always somewhere deep in a project, and I want to search the whole project, not just the current directory, and quickly go to found hits inside emacs, ignoring ancillary data inside &lt;code&gt;.git&lt;/code&gt; dirs and similar.&lt;/p&gt;
&lt;h2 id=&quot;the-solution&quot;&gt;The solution&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-solution&quot; aria-label=&quot;Anchor link for: the-solution&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;gr&lt;/code&gt; walks up from the current directory until it finds a &lt;strong&gt;mark&lt;/strong&gt; — a file or directory named &lt;code&gt;.git&lt;/code&gt;, &lt;code&gt;.hg&lt;/code&gt;, &lt;code&gt;.jj&lt;/code&gt;, &lt;code&gt;.ugrep&lt;/code&gt; or &lt;code&gt;.gr&lt;/code&gt;, indicating the root of the current project — and runs &lt;code&gt;ug&lt;/code&gt; (ugrep, a better grep) recursively from there. So from anywhere in the tree:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;~/src/foo/sub/sub$ gr max_connections&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;config/app.conf:12:4:MaxConnections = 100&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;src/server.go:45:1:maxConnections := ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output is the classic &lt;code&gt;file:line:column:line&lt;/code&gt; format of compiler errors understood by emacs, with &lt;code&gt;-I&lt;/code&gt; skipping binaries and &lt;code&gt;--color=never&lt;/code&gt; keeping it clean for piping.&lt;/p&gt;
&lt;h2 id=&quot;emacs&quot;&gt;Emacs&lt;a class=&quot;zola-anchor&quot; href=&quot;#emacs&quot; aria-label=&quot;Anchor link for: emacs&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The whole thing is designed to play with emacs compile mode. &lt;code&gt;gr&lt;/code&gt; prints the header &lt;code&gt;gr: Entering directory \&lt;/code&gt;...&#39;&lt;code&gt;— in the exact format emacs expects, and only when it actually changes directory — so the relative paths in the output resolve correctly. Then it is just&lt;/code&gt;M-x compile&lt;code&gt;followed by&lt;/code&gt;next-error&lt;code&gt;(Ctrl-x&lt;/code&gt;) to jump from match to match through the whole project. The precise header matters: emacs is confused by anything that does not follow its convention.&lt;/p&gt;
&lt;p&gt;A tip: I mapped F1/F2/F3 for easy use of emacs compile mode:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;;; compile: f1 search / f2 next hit / Shift-f2 previous hit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(global-set-key [f1] &amp;#39;compile)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(global-set-key [f2] &amp;#39;next-error)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(global-set-key (kbd &amp;quot;&amp;lt;S-f2&amp;gt;&amp;quot;) &amp;#39;previous-error)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;custom-marks&quot;&gt;Custom marks&lt;a class=&quot;zola-anchor&quot; href=&quot;#custom-marks&quot; aria-label=&quot;Anchor link for: custom-marks&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a non-git project — or to define a sub-project root inside a monorepo — just place a &lt;code&gt;.gr&lt;/code&gt; (or &lt;code&gt;.ugrep&lt;/code&gt;) marker there and &lt;code&gt;gr&lt;/code&gt; stops at it. Extra mark names are added with &lt;code&gt;GR_MARKS&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export GR_MARKS=&amp;#39;svn cvs bzr fossil vscode env&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;why-i-like-it&quot;&gt;Why I like it&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-i-like-it&quot; aria-label=&quot;Anchor link for: why-i-like-it&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One command, from anywhere&lt;/strong&gt;: no need to know where the repo root is.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Searches everything&lt;/strong&gt;, tracked or not — which &lt;code&gt;git grep&lt;/code&gt; can&#39;t do.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Emacs-ready output&lt;/strong&gt;: browse and jump to the matches instead of eyeballing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fast&lt;/strong&gt;: it is &lt;code&gt;ug&lt;/code&gt; under the hood, so no &lt;code&gt;grep -r&lt;/code&gt; slowness.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;installation-usage-and-more-info&quot;&gt;Installation, Usage, and more info&lt;a class=&quot;zola-anchor&quot; href=&quot;#installation-usage-and-more-info&quot; aria-label=&quot;Anchor link for: installation-usage-and-more-info&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib&quot;&gt;github.com/ColasNahaboo/colas-bash-lib&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;AI co-written: don&#39;t like, don&#39;t use.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>ups-monitor.sh: staged shutdowns when the power goes out</title>
        <published>2026-08-12T00:00:00+00:00</published>
        <updated>2026-08-12T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/ups-monitor-staged-shutdowns-when-the-power-goes-out/"/>
        <id>https://colas.nahaboo.net/code/ups-monitor-staged-shutdowns-when-the-power-goes-out/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/ups-monitor-staged-shutdowns-when-the-power-goes-out/">&lt;img src=&quot;ups-monitor-overview.png&quot;&gt;
&lt;p&gt;I released &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/ups-monitor.sh&quot;&gt;ups-monitor.sh&lt;/a&gt;&lt;/strong&gt;, a bash script that decides what happens when the power goes out.&lt;/p&gt;
&lt;p&gt;My UPS powers my servers, NAS, main switch and wifi access point. I want to cut off first my workstations in a clean way, to keep as much battery power to provide my main file server access via wifi for my phones and tablets.&lt;/p&gt;
&lt;p&gt;Configuring &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://networkupstools.org/&quot;&gt;NUT&lt;/a&gt; scripts for this seemed verbose and complex for my small setup, so I basically coded a simple but flexible bash control layer over the NUT infrastructure&lt;/p&gt;
&lt;h2 id=&quot;the-staged-shutdown&quot;&gt;The staged shutdown&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-staged-shutdown&quot; aria-label=&quot;Anchor link for: the-staged-shutdown&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So the script shuts things down in stages, as the battery drains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;90% battery&lt;/strong&gt;: shut down the non-essential &quot;first&quot; servers — storage, backups, the toy boxes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;80% battery&lt;/strong&gt;: shut down the &quot;extra&quot; servers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;20% battery&lt;/strong&gt;: shut down the monitoring host itself, and put the UPS in standby.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The UPS on the final step powers everything off and goes to sleep; when the mains come back, it wakes up, powers the equipment, and the machines can boot again. Recovery is automatic.&lt;/p&gt;
&lt;p&gt;The staging is fully configurable: the thresholds and the server lists live in &lt;code&gt;/etc/ups-monitor.conf&lt;/code&gt;, and servers can be prefixed &lt;code&gt;h:&lt;/code&gt; to hibernate instead of shutdown, or &lt;code&gt;wh:&lt;/code&gt;/&lt;code&gt;ws:&lt;/code&gt; for Windows hosts.&lt;/p&gt;
&lt;h2 id=&quot;the-config-is-bash&quot;&gt;The config is bash&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-config-is-bash&quot; aria-label=&quot;Anchor link for: the-config-is-bash&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I often set up my scripts, the config file is &lt;em&gt;sourced&lt;/em&gt; by the script, so it is not a list of options in some syntax, it is pure bash. You can override any variable — and any &lt;strong&gt;function&lt;/strong&gt;. Redefine &lt;code&gt;info&lt;/code&gt; to text you instead of mailing you. Redefine &lt;code&gt;remoteshut&lt;/code&gt; to do whatever exotic thing your network needs. You can even write &quot;pseudo servers&quot; as &lt;code&gt;funcname:params&lt;/code&gt; in the server lists, and the script will run that function before shutting down — say, dump the databases before the machine dies. The anti-framework approach, same as my &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib&quot;&gt;colas-bash-lib&lt;/a&gt; collection.&lt;/p&gt;
&lt;p&gt;It just polls the UPS every five seconds, so bash provides simplicity, stability and flexibility with a negligible overhead.&lt;/p&gt;
&lt;h2 id=&quot;goodies&quot;&gt;Goodies&lt;a class=&quot;zola-anchor&quot; href=&quot;#goodies&quot; aria-label=&quot;Anchor link for: goodies&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A dry-run mode toggled by &lt;code&gt;touch /tmp/NOUPSMON&lt;/code&gt; — no restart needed — so you can unplug the UPS and watch the logs before trusting it for real.&lt;/li&gt;
&lt;li&gt;Email alerts for power lost and restored, overload, and battery-to-replace. AVR events (TRIM/BOOST) logged with the exact input voltage, for info. Logs rotated monthly, and a &lt;code&gt;clean&lt;/code&gt; command for the cron.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;compatible-ups&quot;&gt;Compatible UPS&lt;a class=&quot;zola-anchor&quot; href=&quot;#compatible-ups&quot; aria-label=&quot;Anchor link for: compatible-ups&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One advice: choose a &lt;strong&gt;pure sine wave&lt;/strong&gt; UPS, otherwise some active-PFC power supplies may panic and shut down abruptly when the UPS switches to batteries. Save yourself the uncertainty.&lt;/p&gt;
&lt;h2 id=&quot;installation-usage-and-more-info&quot;&gt;Installation, Usage, and more info&lt;a class=&quot;zola-anchor&quot; href=&quot;#installation-usage-and-more-info&quot; aria-label=&quot;Anchor link for: installation-usage-and-more-info&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/ups-monitor.sh&quot;&gt;github.com/ColasNahaboo/ups-monitor.sh&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Note: This is AI-co-advised: chatting with Gemini helped me as a UPS novice, but it did not write the code or the docs.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>follow-symlinks: tracing the whole link chain</title>
        <published>2026-08-10T00:00:00+00:00</published>
        <updated>2026-08-10T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/follow-symlinks-tracing-the-link-chain/"/>
        <id>https://colas.nahaboo.net/code/follow-symlinks-tracing-the-link-chain/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/follow-symlinks-tracing-the-link-chain/">&lt;p&gt;I just added &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib/blob/master/bin/follow-symlinks&quot;&gt;follow-symlinks&lt;/a&gt;&lt;/strong&gt; to my &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib&quot;&gt;colas-bash-lib&lt;/a&gt; collection. It is a small bash script that traces and expands all the symlinks of a file, recursively, and shows you the mount points and the dangling links found along the way.&lt;/p&gt;
&lt;p&gt;I made it because there are no existing linux utilities that show both the mount points and the symbolic link traversed to get to the file.&lt;/p&gt;
&lt;h2 id=&quot;what-it-shows&quot;&gt;What it shows&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-it-shows&quot; aria-label=&quot;Anchor link for: what-it-shows&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Give it the path of a file or directory and it walks the chain, printing each hop:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;## /tmp/symtest/chain&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Mount: /tmp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;#39;/tmp/symtest/chain&amp;#39; -&amp;gt; &amp;#39;link-to-real&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  =&amp;gt; /tmp/symtest/link-to-real&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output is made for human consumption:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-&amp;gt;&lt;/code&gt; the file is a symbolic link to&lt;/li&gt;
&lt;li&gt;&lt;code&gt;=&amp;gt;&lt;/code&gt; the final target&lt;/li&gt;
&lt;li&gt;&lt;code&gt;=&lt;/code&gt; the path after normalization (resolving &lt;code&gt;/..&lt;/code&gt;, &lt;code&gt;/./&lt;/code&gt;, &lt;code&gt;//&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Mount:&lt;/code&gt; a filesystem boundary — printed when the mount point changes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*** File not found:&lt;/code&gt; a dangling link&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It also resolves the relative links and the &lt;code&gt;..&lt;/code&gt; segments for you, so what you read is what the kernel will actually open, not the spelling on disk.&lt;/p&gt;
&lt;h2 id=&quot;the-dangling-links&quot;&gt;The dangling links&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-dangling-links&quot; aria-label=&quot;Anchor link for: the-dangling-links&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Dangling (or broken) links get a useful diagnostic to know the missing link.&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;## /tmp/symtest/dangling&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Mount: /tmp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;#39;/tmp/symtest/dangling&amp;#39; -&amp;gt; &amp;#39;missing-file&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  *** File not found: /tmp/symtest/missing-file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Because in missing directory: /tmp/symtest/missing-file&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;the-mount-points&quot;&gt;The mount points&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-mount-points&quot; aria-label=&quot;Anchor link for: the-mount-points&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Links that cross a mount point are easy to miss — the file is the same name, but it lives on another filesystem, with its own space and speed characteristics:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;## /tmp/symtest/link-to-etc&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Mount: /tmp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;#39;/tmp/symtest/link-to-etc&amp;#39; -&amp;gt; &amp;#39;/etc&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Mount: /&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  =&amp;gt; /etc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;why-i-like-it&quot;&gt;Why I like it&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-i-like-it&quot; aria-label=&quot;Anchor link for: why-i-like-it&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One shot, whole chain&lt;/strong&gt;: it prints everything in one pass instead of me chaining &lt;code&gt;ls -l&lt;/code&gt; and &lt;code&gt;readlink&lt;/code&gt; by hand.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A reason, not a symptom&lt;/strong&gt;: for broken links it tells me the missing directory, not just &quot;file not found&quot;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pure bash, no dependencies&lt;/strong&gt; — it only uses &lt;code&gt;readlink&lt;/code&gt;, &lt;code&gt;stat&lt;/code&gt; and &lt;code&gt;realpath&lt;/code&gt;, all of which are everywhere.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See it and the rest of my bash scripts at &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib&quot;&gt;colas-bash-lib&lt;/a&gt;.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>browser-freezer: freeze the browser when the screen blanks</title>
        <published>2026-08-09T00:00:00+00:00</published>
        <updated>2026-08-09T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/browser-freezer/"/>
        <id>https://colas.nahaboo.net/code/browser-freezer/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/browser-freezer/">&lt;img src=&#39;browser-freezer.jpg&#39;&gt;
&lt;p&gt;I have a small NUC-style mini PC that runs linux with X11, and I cannot suspend it — probably some hardware incompatibility or BIOS bug, as is often the case on these mini-PCs. So, to minimize power consumption and heat generation, I made &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/browser-freezer&quot;&gt;browser-freezer&lt;/a&gt;&lt;/strong&gt;: a small and light utility to freeze the browsers when the screen blanks, and un-freeze them on wakeup. On such a machine, the browser is the only thing consuming power when not being used.&lt;/p&gt;
&lt;h2 id=&quot;why-two-parts-c-bash&quot;&gt;Why two parts? C + bash&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-two-parts-c-bash&quot; aria-label=&quot;Anchor link for: why-two-parts-c-bash&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It is composed of two parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a tiny C daemon &lt;strong&gt;browser-freezer&lt;/strong&gt; that polls the state of the monitor (On / Off) with as less CPU usage as possible (no forks under X11)&lt;/li&gt;
&lt;li&gt;on monitor status change detection, it runs the bash script &lt;strong&gt;browser-freezer-signal&lt;/strong&gt; that sends the SIGSTOP or SIGCONT signal to all browser processes (firefox by default). It uses a bash script for the most possible flexibility and customization, as performance is not an issue there.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;wayland&quot;&gt;Wayland&lt;a class=&quot;zola-anchor&quot; href=&quot;#wayland&quot; aria-label=&quot;Anchor link for: wayland&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The initial release was X11 only, but two weeks later I added a Wayland variant: a second binary &lt;code&gt;browser-freezer-wayland&lt;/code&gt; and a wrapper that picks the right one at login depending on &lt;code&gt;$WAYLAND_DISPLAY&lt;/code&gt;. As always with Wayland, it is a second incompatible API to implement, but it works.&lt;/p&gt;
&lt;p&gt;My browsers are the only things consuming power on that machine when idle, so freezing them is the next best thing to suspending the whole PC. Indeed :-)&lt;/p&gt;
&lt;h2 id=&quot;installation-usage-and-more-info&quot;&gt;Installation, Usage, and more info&lt;a class=&quot;zola-anchor&quot; href=&quot;#installation-usage-and-more-info&quot; aria-label=&quot;Anchor link for: installation-usage-and-more-info&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;See the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/browser-freezer&quot;&gt;browser-freezer repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some code and images generated by AI (Gemini), as my C was a bit rusty.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>passgen: a password generator with a memory</title>
        <published>2026-08-05T00:00:00+00:00</published>
        <updated>2026-08-05T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/passgen-a-password-generator-with-a-memory/"/>
        <id>https://colas.nahaboo.net/code/passgen-a-password-generator-with-a-memory/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/passgen-a-password-generator-with-a-memory/">&lt;p&gt;I just added &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib/blob/master/bin/passgen&quot;&gt;passgen&lt;/a&gt;&lt;/strong&gt;, a password generator, to my &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib&quot;&gt;colas-bash-lib&lt;/a&gt; collection. It is a tool that lives in your terminal, generates a password, copies it to your clipboard, and keeps a private log of what it generated and for what.&lt;/p&gt;
&lt;p&gt;Why a log, you may ask. Because the biggest password problem is not generating strong ones, it is remembering which password goes where. My log solves it in the laziest possible way: I search it when I need to know which password I used for a given site.&lt;/p&gt;
&lt;h2 id=&quot;what-it-does&quot;&gt;What it does&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-it-does&quot; aria-label=&quot;Anchor link for: what-it-does&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;passgen&lt;/code&gt; generates a password of length 24 by default, made of lowercase letters, uppercase, digits, and a few special characters. It is careful about the details:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It starts with a lowercase letter, so the password is a valid identifier anywhere.&lt;/li&gt;
&lt;li&gt;It deliberately avoids the zero character, to prevent confusion with the letter O.&lt;/li&gt;
&lt;li&gt;The special characters are limited to &lt;code&gt;-%=+_^/~?.&lt;/code&gt; — the ones that survive a &lt;strong&gt;double-click&lt;/strong&gt; to select the whole password, which is the way I copy things from a terminal.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then it copies the password to your clipboard, on Wayland or X11 alike. No need to select it yourself.&lt;/p&gt;
&lt;h2 id=&quot;the-log&quot;&gt;The log&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-log&quot; aria-label=&quot;Anchor link for: the-log&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every generated password is appended to &lt;code&gt;~/.local/state/passgen/log&lt;/code&gt;, with a date and an optional note. I use it this way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;passgen -l&lt;/code&gt; lists the last passwords generated, so I can find the one for a given site.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;passgen 32 facebook&lt;/code&gt; generates a 32-character password and logs it as the &quot;facebook&quot; one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The log is chmod&#39;ed to be readable only by me, and I keep it in sync and backed up like the rest of my home directory. It replaced my password manager for everything but shared secrets.&lt;/p&gt;
&lt;h2 id=&quot;checking-the-strength&quot;&gt;Checking the strength&lt;a class=&quot;zola-anchor&quot; href=&quot;#checking-the-strength&quot; aria-label=&quot;Anchor link for: checking-the-strength&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;passgen -s 24&lt;/code&gt; prints the strength of a password of that length in number of combinations, which for 24 characters is about 10^42 — comfortably future-proof even against quantum computers. The default length of 24 is chosen for exactly that reason.&lt;/p&gt;
&lt;h2 id=&quot;why-i-like-it&quot;&gt;Why I like it&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-i-like-it&quot; aria-label=&quot;Anchor link for: why-i-like-it&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One tool, one habit&lt;/strong&gt;: no account, no sync, no database, nothing to learn. Generate, paste, done.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The password is in my control&lt;/strong&gt;, stored in a plain file I can grep, rsync, and back up with the rest of my life.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Useful in a script&lt;/strong&gt;: pipe it, adapt the length, add a note. It composes like any other command-line tool.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See it and the rest of my bash functions at &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib&quot;&gt;colas-bash-lib&lt;/a&gt;.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>x-workspace-switcher: a menu to switch and rename X11 workspaces</title>
        <published>2026-08-04T00:00:00+00:00</published>
        <updated>2026-08-04T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/x-workspace-switcher/"/>
        <id>https://colas.nahaboo.net/code/x-workspace-switcher/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/x-workspace-switcher/">&lt;img src=&#39;x-workspace-switcher.png&#39; align=right width=300&gt;
&lt;p&gt;I switch workspaces dozens of times a day — browser here, terminal there, editor somewhere else — and I could not find a workspace switcher that was convenient. Either they were tiny unreadable icons in a panel, or huge fancy views. I just wanted a readable list of names, plus an easy way to rename the workspace via the programs that run in them.&lt;/p&gt;
&lt;p&gt;So I added &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib&quot;&gt;x-workspace-switcher&lt;/a&gt;&lt;/strong&gt; to my &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib&quot;&gt;colas-bash-lib&lt;/a&gt;, a single bash script in &lt;code&gt;bin/&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;switching&quot;&gt;Switching&lt;a class=&quot;zola-anchor&quot; href=&quot;#switching&quot; aria-label=&quot;Anchor link for: switching&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run it with no argument and a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/davatorium/rofi&quot;&gt;rofi&lt;/a&gt; menu pops up, listing each workspace with the classes of the windows currently on it:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;1  ws_1 | firefox, discord&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2  ws_2 | kitty, vim&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So I can see at a glance where everything is, instead of remembering. The current workspace is pre-selected. Move with the arrows or incremental search, hit Enter — or just hit the function key of the workspace you want: F6 goes straight to workspace 6.&lt;/p&gt;
&lt;p&gt;Note: I use &lt;code&gt;F6&lt;/code&gt; to go to workspace 6 instead of just &lt;code&gt;6&lt;/code&gt; to not mess with typing 6 during an incremental search. Also I use &lt;code&gt;Super-F6&lt;/code&gt; as a keyboard shortcut to go to Workspace 6, and using functions keys allow for 12 workspaces (or 14 on my &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://nzxt.com/en-intl/collections/gaming-keyboards&quot;&gt;NZXT keyboard&lt;/a&gt; :-)&lt;/p&gt;
&lt;h2 id=&quot;renaming&quot;&gt;Renaming&lt;a class=&quot;zola-anchor&quot; href=&quot;#renaming&quot; aria-label=&quot;Anchor link for: renaming&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;x-workspace-switcher new-name&lt;/code&gt; renames the current workspace.&lt;/p&gt;
&lt;p&gt;That turned out to be the interesting part: there is no standard way to rename a workspace on X11. For instance, I have a script that I run in a git repository that sets up my development environment  for it (emacs, kittyies, opencode, ...) and now also name the workspace with the repository name.&lt;/p&gt;
&lt;h2 id=&quot;why-renaming-needs-per-wm-code&quot;&gt;Why renaming needs per-WM code&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-renaming-needs-per-wm-code&quot; aria-label=&quot;Anchor link for: why-renaming-needs-per-wm-code&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Each window manager does it its own way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Xfce: &lt;code&gt;xfconf-query&lt;/code&gt; on &lt;code&gt;/general/workspace_names&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;i3: &lt;code&gt;i3-msg &quot;rename workspace to ...&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;bspwm: &lt;code&gt;bspc desktop -n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;GNOME: &lt;code&gt;gsettings&lt;/code&gt; on &lt;code&gt;workspace-names&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;KDE: &lt;code&gt;kwriteconfig&lt;/code&gt; plus a &lt;code&gt;qdbus&lt;/code&gt; reconfigure&lt;/li&gt;
&lt;li&gt;anything else: a python-xlib fallback that rewrites the &lt;code&gt;_NET_DESKTOP_NAMES&lt;/code&gt; property directly&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Detection is via &lt;code&gt;wmctrl -m&lt;/code&gt;, helped by &lt;code&gt;XDG_CURRENT_DESKTOP&lt;/code&gt; when the WM name is ambiguous.&lt;/p&gt;
&lt;h2 id=&quot;the-wayland-question&quot;&gt;The Wayland question&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-wayland-question&quot; aria-label=&quot;Anchor link for: the-wayland-question&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As always with Wayland: alas, no. The core protocol has no workspace support, and each compositor invents its own incompatible API. My switcher is X11 only — save your sanity, use X11. :-)&lt;/p&gt;
&lt;h2 id=&quot;installation-usage-and-more-info&quot;&gt;Installation, Usage, and more info&lt;a class=&quot;zola-anchor&quot; href=&quot;#installation-usage-and-more-info&quot; aria-label=&quot;Anchor link for: installation-usage-and-more-info&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;See the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib&quot;&gt;colas-bash-lib repository&lt;/a&gt;, the script is &lt;code&gt;bin/x-workspace-switcher&lt;/code&gt;. It needs &lt;code&gt;rofi&lt;/code&gt;; renaming on some WMs needs extra packages (&lt;code&gt;i3-wm bspwm plasma-workspace python3-xlib&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Just copy the script in your PATH (&lt;code&gt;/usr/local/bin&lt;/code&gt;, &lt;code&gt;~/.local/bin/&lt;/code&gt;, ...), and bind it to a keyboard shortcut for convenience. For instance I bound it to Super-W (aka Windows-W) since I bound all my window and workspace management function to Super shortcuts.&lt;/p&gt;
&lt;h2 id=&quot;license&quot;&gt;License&lt;a class=&quot;zola-anchor&quot; href=&quot;#license&quot; aria-label=&quot;Anchor link for: license&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;© Colas Nahaboo, 2026. MIT License. Do what you want with it.
&lt;img src=&quot;https://img.shields.io/badge/Co--Designed%20with-AI-blueviolet?style=flat-square&amp;amp;logo=openai&quot; alt=&quot;Co-Designed with AI&quot; /&gt;.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>gmail-to-fastmail: keep your Gmail labels in sync on Fastmail</title>
        <published>2026-08-03T00:00:00+00:00</published>
        <updated>2026-08-03T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/gmail-to-fastmail-sync-yourlabels/"/>
        <id>https://colas.nahaboo.net/code/gmail-to-fastmail-sync-yourlabels/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/gmail-to-fastmail-sync-yourlabels/">&lt;p&gt;&lt;img src=&#39;gmail-to-fastmail.svg&#39; align=right width=300&gt;I moved to Fastmail some time ago, but like many of us I kept one foot in Gmail: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.fastmail.help/hc/en-us/articles/1500000278022-Using-other-email-addresses-with-fetch&quot;&gt;Fastmail&#39;s &quot;fetch&quot;&lt;/a&gt; tool keeps duplicating my incoming Gmail into my Fastmail account — a backup, an escape plan, and a bridge for family members who are not quite ready to leave Gmail yet.&lt;/p&gt;
&lt;p&gt;But there was a hole: the &lt;strong&gt;labels&lt;/strong&gt; I add on Gmail after the initial migration (manually, or with my rules) were never reproduced on Fastmail. So I wrote &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/gmail-to-fastmail&quot;&gt;gmail-to-fastmail&lt;/a&gt;&lt;/strong&gt;, a single Go binary that incrementally syncs your Gmail labels to Fastmail.&lt;/p&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-it-works&quot; aria-label=&quot;Anchor link for: how-it-works&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It reads your Gmail via IMAP (read-only, &lt;code&gt;X-GM-LABELS&lt;/code&gt;), and applies the labels on Fastmail via JMAP (a more efficient variant of IMAP), in batches of 10 000. Progress is kept in a local SQLite database, so each run resumes where the last one stopped — once caught up, a run takes about 15 seconds. Perfect for cron.&lt;/p&gt;
&lt;h2 id=&quot;what-it-does&quot;&gt;What it does&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-it-does&quot; aria-label=&quot;Anchor link for: what-it-does&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Labels mapped&lt;/strong&gt;: your Gmail labels become Fastmail labels, created on demand, never deleted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Categories&lt;/strong&gt;: Gmail&#39;s system categories (Inbox, Updates, Social...) become underscore-prefixed labels &lt;code&gt;_Inbox&lt;/code&gt;, &lt;code&gt;_Updates&lt;/code&gt;, &lt;code&gt;_Social&lt;/code&gt;...&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trash&lt;/strong&gt;: emails deleted on Gmail are not deleted on Fastmail, just labelled &lt;code&gt;_Trash&lt;/code&gt; — a backup of your deleted mail, with an easy way to &quot;empty the bin&quot;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Local cache&lt;/strong&gt;: unchanged emails are skipped, so Fastmail&#39;s API is left in peace.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mirror mode&lt;/strong&gt;: &lt;code&gt;-m&lt;/code&gt;/&lt;code&gt;-M&lt;/code&gt; optionally imports emails missing on Fastmail, not just labels. There are more options, detailed on the GitGub repository.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;installation-usage-and-more-info&quot;&gt;Installation, Usage, and more info&lt;a class=&quot;zola-anchor&quot; href=&quot;#installation-usage-and-more-info&quot; aria-label=&quot;Anchor link for: installation-usage-and-more-info&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;See the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/gmail-to-fastmail&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;why-go&quot;&gt;Why Go?&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-go&quot; aria-label=&quot;Anchor link for: why-go&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Because it is a single self-contained binary with no runtime dependencies — you know my &lt;a href=&quot;/blog/go-single-binary-fanatic&quot;&gt;thing about that&lt;/a&gt;. No Python env, no node_modules, no nothing. Free (MIT).&lt;/p&gt;
&lt;p&gt;Also, AI co-written: don&#39;t like, don&#39;t use.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>mailpixtracker: a lightweight bash CGI to track emails</title>
        <published>2026-08-01T00:00:00+00:00</published>
        <updated>2026-08-01T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/mailpixtracker-a-bash-cgi-to-track-emails/"/>
        <id>https://colas.nahaboo.net/code/mailpixtracker-a-bash-cgi-to-track-emails/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/mailpixtracker-a-bash-cgi-to-track-emails/">&lt;p&gt;&lt;img src=&quot;mpt3-576.png&quot; width=300 align=right&gt;When I send an email, did the recipient read it? So I wrote &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/mailpixtracker&quot;&gt;mailpixtracker&lt;/a&gt;&lt;/strong&gt;, a small self-hosted CGI script that tracks when your emails are opened, via the classic invisible 1x1 pixel trick.&lt;/p&gt;
&lt;p&gt;Think of &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.getmailtracker.com/&quot;&gt;MailTracker&lt;/a&gt; or &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://mailtrack.email/&quot;&gt;MailTrack&lt;/a&gt;, but without all the marketing machinery: no account on a third-party server, no spyware concerns, no &quot;business intelligence&quot; dashboards. Just three bash scripts on your own web server.&lt;/p&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-it-works&quot; aria-label=&quot;Anchor link for: how-it-works&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You create a new tracker with an optional name on your mailpixtracker page. It gives you a tiny HTML snippet to paste in your email such as:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;img&lt;/span&gt;&lt;span style=&quot;color: #DF8E1D;&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;#39;https://my.mailpixtracker.org/_/12-I5rHJCFap&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: #DF8E1D;&quot;&gt; width&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #DF8E1D;&quot;&gt; height&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When someone opens your email and their mailer loads images, your server logs the request: date, IP, hostname, user-agent. No data ever leaves your server.&lt;/p&gt;
&lt;h2 id=&quot;features&quot;&gt;Features&lt;a class=&quot;zola-anchor&quot; href=&quot;#features&quot; aria-label=&quot;Anchor link for: features&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No accounts&lt;/strong&gt;: The URL &lt;em&gt;is&lt;/em&gt; the authentication, via &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.w3.org/TR/capability-urls/&quot;&gt;Capability URLs&lt;/a&gt;. The admin URL is your unguessable &quot;password&quot; — share it with nobody and you are safe.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plain files&lt;/strong&gt;: Logs are tab-separated text, one line per open. No database.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Colored logs&lt;/strong&gt;: Each distinct IP gets its own &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://programmingdesignsystems.com/color/perceptually-uniform-color-spaces/&quot;&gt;perceptually-spaced color&lt;/a&gt;, so you can spot repeat readers at a glance. Your own opens (while composing the email) show in dim grey, so they do not pollute the picture.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New entries highlighted&lt;/strong&gt;: Entries added since your last visit get a yellow marker.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notes field&lt;/strong&gt;: Each tracker has an editable name and notes — handy to remember what you sent, to whom, and when.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Guest accounts&lt;/strong&gt;: Create isolated spaces for different purposes (work, family, surfing...) with admin (you) accounts, or guest accounts that you can just give to others with a link. Non-admin guests only see their own trackers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Geolocalization&lt;/strong&gt; (optional): With the MaxMind databases installed, logs show the organization and country of each reader.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;caveats&quot;&gt;Caveats&lt;a class=&quot;zola-anchor&quot; href=&quot;#caveats&quot; aria-label=&quot;Anchor link for: caveats&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Email tracking is not foolproof. Most mailers now prompt before loading images, so people can read your email without ever triggering the tracker. Mailpixtracker will not tell you everything — but when it does fire, it is accurate, and the logs are all yours.&lt;/p&gt;
&lt;p&gt;Also note that it is a tool for personal curiosity, not for marketing analytics: it has no click tracking, no A/B testing, no &quot;campaigns&quot;. If you want that, there are plenty of SaaS products; this one is the lazy, self-hosted, private alternative.&lt;/p&gt;
&lt;h2 id=&quot;install&quot;&gt;Install&lt;a class=&quot;zola-anchor&quot; href=&quot;#install&quot; aria-label=&quot;Anchor link for: install&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For instructions, see the code is at &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/mailpixtracker&quot;&gt;github.com/ColasNahaboo/mailpixtracker&lt;/a&gt;, it is multi-licensed (MIT, Apache 2.0, MPL 2.0, LGPL v3, GPL v3) — pick whichever suits your project.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Fanatic of the single self-contained executable</title>
        <published>2026-07-31T00:00:00+00:00</published>
        <updated>2026-07-31T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/go-single-binary-fanatic/"/>
        <id>https://colas.nahaboo.net/blog/go-single-binary-fanatic/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/go-single-binary-fanatic/">&lt;p&gt;I am becoming more and more a fanatic of the single self-contained executable, with no runtime dependencies, that Go provides: one binary, forever. No npm, virtualenv, docker, flatpak, snap... no version hell.&lt;/p&gt;
&lt;p&gt;The qualities I lived with and took for granted during my years of coding in C and shell.&lt;/p&gt;
&lt;p&gt;To the point that when I find now a python/node/java/ruby/php piece of software I would like to install, my first reflex is to just recode the darn thing in Go. I know, I know, rewriting a perfectly working piece of software just to avoid a dependency is bordering on insanity, but the result is so satisfying... A single file that will still run in 20 years, with no upgrades, no breakage, no maintenance.&lt;/p&gt;
&lt;p&gt;And with AI, now, it is becoming less and less insane!&lt;/p&gt;
&lt;p&gt;(&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://bsky.app/profile/colas.nahaboo.net/post/3mrxclxl64s2e&quot;&gt;Bluepost &lt;img src=&#39;/icon/social/bluesky-blue.svg&#39;&gt;&lt;/a&gt;)&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>filewebx: a simple, private, web file exchange</title>
        <published>2026-07-30T00:00:00+00:00</published>
        <updated>2026-07-30T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/filewebx-simple-private-web-file-exchange/"/>
        <id>https://colas.nahaboo.net/code/filewebx-simple-private-web-file-exchange/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/filewebx-simple-private-web-file-exchange/">&lt;p&gt;&lt;img src=&quot;filewebx-logo-240x200.png&quot; align=right&gt;I often need to send files to people — photos, documents, archives — and I got tired of the usual suspects: WeTransfer limits you, SwissTransfer is great but you depend on a third party, and all of them impose size caps, retention limits, and account management overhead.&lt;/p&gt;
&lt;p&gt;So I wrote &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/filewebx&quot;&gt;filewebx&lt;/a&gt;&lt;/strong&gt;. It is a self-hosted CGI Bash system for private file exchange, using &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.w3.org/TR/capability-urls/&quot;&gt;Capability URLs&lt;/a&gt;: you upload a file, get back a non-guessable link, share it. No logins, no passwords, no accounts.&lt;/p&gt;
&lt;p&gt;It is your own private WeTransfer / MASV / Smash / SwissTransfer, on your own server.&lt;/p&gt;
&lt;h2 id=&quot;what-it-does&quot;&gt;What it does&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-it-does&quot; aria-label=&quot;Anchor link for: what-it-does&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No accounts needed&lt;/strong&gt;: The URL &lt;em&gt;is&lt;/em&gt; the authentication. Your secret admin URL (e.g: &lt;code&gt;https://your.site/yJDdYNEXmB&lt;/code&gt;) is your dashboard. Share it with nobody, and it is secure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Guest accounts&lt;/strong&gt;: Create isolated spaces for other people by just giving them a link. They can upload and get their own shareable links without seeing yours.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auto-expiry&lt;/strong&gt;: Files expire after a configurable delay (100 days by default). A daily cron job cleans them up.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nice interface&lt;/strong&gt; with for instance an upload progress bar, easy copying of the url, nice colored logs that work without JavaScript frameworks; Just plain HTML + CSS + JS served by a Bash CGI.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Slug URLs&lt;/strong&gt;: Download links are readable: &lt;code&gt;https://your.site/_/OLw6bZrh65lj/my-photo.jpg&lt;/code&gt; instead of raw url-encoded gibberish.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Geolocalized logs&lt;/strong&gt;: See where downloads come from, with IP colors assigned via &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://medium.com/@winwardo/simple-non-repeating-colour-generation-6efc995832b8&quot;&gt;golden-angle hue sampling&lt;/a&gt; to spot patterns at a glance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quotas&lt;/strong&gt;: Reserve free disk space. Per-guest quotas available.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-it-works&quot; aria-label=&quot;Anchor link for: how-it-works&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Three bash scripts, one web server, a single directory for data. Uploads go to &lt;code&gt;data/&lt;/code&gt;, metadata is stored as bash associative arrays via &lt;code&gt;declare -p&lt;/code&gt; (from my &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib&quot;&gt;colas-bash-lib&lt;/a&gt;). The download script (&lt;code&gt;_&lt;/code&gt;) logs access and serves the file. The admin script handles the UI, guest management, and cleaning.&lt;/p&gt;
&lt;p&gt;The whole thing is plain files — no database, no Docker, no snap, flatpak, no dependencies beyond &lt;code&gt;iconv&lt;/code&gt; and a CGI-capable web server (tested with Apache).&lt;/p&gt;
&lt;h2 id=&quot;why-bash&quot;&gt;Why bash?&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-bash&quot; aria-label=&quot;Anchor link for: why-bash&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Because for a CGI system that runs once per request, startup time does not matter, and bash makes deployment trivial: copy three files, rename one to your admin password, done. No compilation, no npm install, no virtualenv. No dependencies on library that can break at any time in the future. It works now, it will still work in twenty years and more. Also, the bash associative array metadata format (&lt;code&gt;declare -p&lt;/code&gt;) is surprisingly practical as simple database — it is both human-readable and machine-parseable without a custom serializer.&lt;/p&gt;
&lt;h2 id=&quot;guest-accounts&quot;&gt;Guest accounts&lt;a class=&quot;zola-anchor&quot; href=&quot;#guest-accounts&quot; aria-label=&quot;Anchor link for: guest-accounts&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can create guest accounts from the admin tab. A guest only sees its own files. Mark a guest as &quot;admin&quot; to get a link back to the main admin account — useful for creating separate namespaces for different audiences (work, family, surfing buddies).&lt;/p&gt;
&lt;p&gt;Non-admin guests cannot access the main account, see other guests, or create new guests. Perfect for giving to people who just need to send you a file.&lt;/p&gt;
&lt;h2 id=&quot;deploy&quot;&gt;Deploy&lt;a class=&quot;zola-anchor&quot; href=&quot;#deploy&quot; aria-label=&quot;Anchor link for: deploy&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Create a directory on your server with &lt;code&gt;data/&lt;/code&gt; and &lt;code&gt;cgi/&lt;/code&gt; subdirectories.&lt;/li&gt;
&lt;li&gt;Copy &lt;code&gt;filewebx&lt;/code&gt; as your admin password name into &lt;code&gt;cgi/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Copy &lt;code&gt;filewebdl&lt;/code&gt; as &lt;code&gt;_&lt;/code&gt; into &lt;code&gt;cgi/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Copy &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/cgibashopts&quot;&gt;cgibashopts&lt;/a&gt; into &lt;code&gt;cgi/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set up a virtual host, add a cron job for daily cleanup.&lt;/li&gt;
&lt;li&gt;Done.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;See the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/filewebx&quot;&gt;README&lt;/a&gt; for full install instructions and Apache config samples. Configuration is done via a single bash-syntax file: set &lt;code&gt;validity&lt;/code&gt;, &lt;code&gt;passlen&lt;/code&gt;, &lt;code&gt;freequota&lt;/code&gt;, &lt;code&gt;guestquotas&lt;/code&gt;, and a few more.&lt;/p&gt;
&lt;h2 id=&quot;what-i-like-about-it&quot;&gt;What I like about it&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-i-like-about-it&quot; aria-label=&quot;Anchor link for: what-i-like-about-it&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;It is the simplest thing that works: data is plain files, so I can &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;rsync&lt;/code&gt; everything without special tools.&lt;/li&gt;
&lt;li&gt;Capability URLs mean I never have to manage a user database. The random tokens are generated from &lt;code&gt;/dev/urandom&lt;/code&gt; — 12 characters give about 10^14 combinations.&lt;/li&gt;
&lt;li&gt;The log coloring with perceptually-spaced HSL hues makes browsing download history genuinely pleasant.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The code is at &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/filewebx&quot;&gt;github.com/ColasNahaboo/filewebx&lt;/a&gt;, MIT license. Contributions, issues, and ideas welcome.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Save these Firefox tabs for later</title>
        <published>2026-04-13T00:00:00+00:00</published>
        <updated>2026-04-13T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/save-these-firefox-tabs-for-later/"/>
        <id>https://colas.nahaboo.net/code/save-these-firefox-tabs-for-later/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/save-these-firefox-tabs-for-later/">&lt;p&gt;I often look for some concept out of curiority, and end up with tens of browser tabs opened on the subject, and realize that I have no time now to pursue this rabbit into his hole, end up with tons of Firefox windows with tens of tabs gathering dust (and eating RAM) on my desktop. Up to now I just created Foswiki pages or Trello tasks as reminders to tackle these projects later on, but it required enough manual work to often postpone doing it and either losing the info or be swamped in thousands of tabs. And saving them as Firefox bookmarks was cumbersome and required lots of manual management.&lt;/p&gt;
&lt;p&gt;What I decided to do is a simple way to save all these tabs offline, and be able to re-open the window simply. I bundle them into what I call &lt;strong&gt;.ffst files&lt;/strong&gt; (for FireFox Saved Tabs), simple text files of one url per line, into a &lt;code&gt;~/firefox-savedtabs/&lt;/code&gt; directory.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Saving a set of tabs&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Select the tabs (click on the first one, shift-click on the last one)&lt;/li&gt;
&lt;li&gt;Open the right-mouse-button menu on a tab, and select &lt;code&gt;Copy URLs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create a new file (e.g: &lt;code&gt;some-name.ffst&lt;/code&gt;) in the &lt;code&gt;~/firefox-savedtabs/&lt;/code&gt; directory with your favorite editor,&lt;/li&gt;
&lt;li&gt;Paste the tabs into it,&lt;/li&gt;
&lt;li&gt;Save.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Restoring the tabs into a new window:&lt;/strong&gt; either by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;running the script below &lt;code&gt;firefox-savedtabs-restore&lt;/code&gt; without arguments, and choosing which file to restore.&lt;/li&gt;
&lt;li&gt;running the script with the ffst file as argument:&lt;br /&gt;
&lt;code&gt;firefox-savedtabs-restore some-name.ffst&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Browsing the directory  &lt;code&gt;~/firefox-savedtabs/&lt;/code&gt; in a file explorer window, and choosing to open the .ffst file with &lt;code&gt;Firefox SavedTabs restore&lt;/code&gt; once you have created the &lt;code&gt;~/.local/share/applications/firefox-savedtabs.desktop&lt;/code&gt; file below&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I can thus just mention the .ffstr file in my various &quot;todo&quot; places (TODO lists, TODO files, pinboard, wikis, trello, ...)&lt;/p&gt;
&lt;p&gt;Note that you can adapt this to any browser, and choose whatever directory name and file extension you want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Updating&lt;/strong&gt; the tabs is just re-copying the urls and re-pasting in the file.&lt;/p&gt;
&lt;h2 id=&quot;the-two-scripts-are&quot;&gt;The two scripts are:&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-two-scripts-are&quot; aria-label=&quot;Anchor link for: the-two-scripts-are&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;usr-local-bin-firefox-savedtabs-restore&quot;&gt;/usr/local/bin/firefox-savedtabs-restore&lt;a class=&quot;zola-anchor&quot; href=&quot;#usr-local-bin-firefox-savedtabs-restore&quot; aria-label=&quot;Anchor link for: usr-local-bin-firefox-savedtabs-restore&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Note that you can modify in it the &lt;code&gt;restore&lt;/code&gt; function to use with another browser.&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #EA76CB;font-style: italic;&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;# shellcheck disable=SC1090,SC2155,SC2046 # source files, declare&amp;amp;assign&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;# restore saved tabs bundles (text files of urls, one per line)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dir&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;$HOME&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;/firefox-savedtabs/&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;restore&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(){&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;    mapfile&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; -t urls&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #E64553;font-style: italic;&quot;&gt;$1&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;    firefox&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; --new-window &amp;quot;&lt;/span&gt;&lt;span&gt;${urls&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;font-style: italic;&quot;&gt;    exit&lt;/span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; [[&lt;/span&gt;&lt;span&gt; $#&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; ==&lt;/span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; ]];&lt;/span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;    # Without args, list the saves, most recent first, and prompt to restore&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;font-style: italic;&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;In ~/.firefox-savedtabs/&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;font-style: italic;&quot;&gt;    cd&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$dir&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color: #D20F39;font-style: italic;&quot;&gt; exit&lt;/span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;    select&lt;/span&gt;&lt;span&gt; i in &lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; . -type f -name&lt;/span&gt;&lt;span style=&quot;color: #EA76CB;&quot;&gt; \*&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;.ffst&lt;/span&gt;&lt;span style=&quot;color: #EA76CB;&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;                    -printf &amp;#39;%TY-%Tm-%Td.%THh%TM:%TS %h/%f\n&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color: #EA76CB;&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;                   sed&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; -e &amp;#39;s| [.]/|_|&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt; sort&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color: #EA76CB;&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;                   sed&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; -re &amp;#39;s/:[0-9]{2}[.][0-9]*//&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;);&lt;/span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;        restore&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;${i&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;#*&lt;/span&gt;&lt;span&gt;_}&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;    done&lt;/span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;    # with a file name or path, restore the window&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    file&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #E64553;font-style: italic;&quot;&gt;$1&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; [[&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; -e&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$file&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; ]];&lt;/span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;        restore&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$file&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;    elif&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; [[&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; -e&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$file&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; ]] &amp;amp;&amp;amp; [[&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; -e&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$dir&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;/&lt;/span&gt;&lt;span&gt;$file&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; ]];&lt;/span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        file&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;$dir&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;/&lt;/span&gt;&lt;span&gt;$file&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;        restore&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$file&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;    else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;font-style: italic;&quot;&gt;        echo&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;***Error: file not found: &lt;/span&gt;&lt;span style=&quot;color: #EA76CB;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span&gt;$file&lt;/span&gt;&lt;span style=&quot;color: #EA76CB;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; in &lt;/span&gt;&lt;span&gt;$dir&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;font-style: italic;&quot;&gt;        exit&lt;/span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;local-share-applications-firefox-savedtabs-desktop&quot;&gt;~/.local/share/applications/firefox-savedtabs.desktop&lt;a class=&quot;zola-anchor&quot; href=&quot;#local-share-applications-firefox-savedtabs-desktop&quot; aria-label=&quot;Anchor link for: local-share-applications-firefox-savedtabs-desktop&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DF8E1D;&quot;&gt;Desktop Entry&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;Name&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;/span&gt;&lt;span&gt;Firefox SavedTabs restore&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;# update-desktop-database ~/.local/share/applications/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;Exec&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;/span&gt;&lt;span&gt;/usr/local/bin/firefox-savedtabs-restore %u&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;Type&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;/span&gt;&lt;span&gt;Application&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;Terminal&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;MimeType&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;/span&gt;&lt;span&gt;x-scheme-handler/x-firefox-savedtabs&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Converting Zola pages from TOML to YAML</title>
        <published>2026-04-07T00:00:00+00:00</published>
        <updated>2026-04-07T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/converting-zola-pages-from-toml-to-yaml/"/>
        <id>https://colas.nahaboo.net/code/converting-zola-pages-from-toml-to-yaml/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/converting-zola-pages-from-toml-to-yaml/">&lt;p&gt;Since &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.getzola.org/&quot;&gt;Zola&lt;/a&gt; now supports frontmatter in its markdown pages in the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://yaml.org/&quot;&gt;YAML&lt;/a&gt; format in addition to the original &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://toml.io/&quot;&gt;TOML&lt;/a&gt;, I decided to migrate all my pages to YAML, as it is both easier to read and write for humans, and natively understood by a majority of tools (Emacs, marktext, obsidian, vs-code, typos, zettlt...)&lt;/p&gt;
&lt;p&gt;So I wrote a small bash script for the conversion, &lt;code&gt;site-toml2yaml&lt;/code&gt; (all my personal site management tools start with the prefix &lt;code&gt;site-&lt;/code&gt; for taking advantage of the tab-completion).
It should run on any Linux or Unix system, and it requires &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/TomWright/dasel&quot;&gt;dasel&lt;/a&gt; and the usual gang: grep, sed, head...&lt;/p&gt;
&lt;p&gt;Note that you could also use &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://gohugo.io/commands/hugo_convert/&quot;&gt;hugo convert&lt;/a&gt;&lt;/strong&gt;, but I haven&#39;t tested it.&lt;/p&gt;
&lt;p&gt;You can grab it at my &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-zola-tools&quot;&gt;zola-tools repository&lt;/a&gt;. This repository is quite empty for now, but I will use it to publish the various small tools I use to maintain this public site.&lt;/p&gt;
&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;a class=&quot;zola-anchor&quot; href=&quot;#usage&quot; aria-label=&quot;Anchor link for: usage&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;site-toml2yaml&lt;/strong&gt; &lt;em&gt;md-files or directories...&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Converts in place the frontmatter of Markdown files from TOML to YAML.&lt;br /&gt;
It recurses in directories, but looking only for &lt;code&gt;*.md&lt;/code&gt; files and sub-dirs.&lt;br /&gt;
Without argument, run on &lt;code&gt;${ZOLA_ROOT}/content/&lt;/code&gt; &lt;br /&gt;
Should work on any Markdown files with TOML frontmatter.&lt;/p&gt;
&lt;p&gt;You should only run it on your content/ dir normally. For instance running it
on your themes/ dir will risk messing your imported themes&lt;/p&gt;
&lt;p&gt;WARNING: it modifies the files in place, so MAKE A BACKUP before running it!&lt;/p&gt;
&lt;h2 id=&quot;why-converting-to-yaml&quot;&gt;Why converting to YAML?&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-converting-to-yaml&quot; aria-label=&quot;Anchor link for: why-converting-to-yaml&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;TOML is arguably a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://npf.io/2014/08/intro-to-toml/&quot;&gt;better configuration file format&lt;/a&gt; than YAML, but only if you want to use it as a simpler JSON, i.e. a well-specified, unambiguous way to represent any data.&lt;/p&gt;
&lt;p&gt;However, I feel that for the simple and limited use case of Zola pages frontmatter, it is overkill and thus a real pain to read and edit by hand. Let&#39;s take an example:&lt;/p&gt;
&lt;div class=&#39;tables-horiz meta&#39;&gt;
&lt;div&gt;
&lt;h3&gt;TOML&lt;/h3&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;/span&gt;&lt;span&gt;table1&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    foo&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;bar&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;    [&lt;/span&gt;&lt;span&gt;table1.nested_table&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        baz&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;bat&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[[&lt;/span&gt;&lt;span&gt;comments&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;author&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;Nate&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;text&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;Great Article!&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[[&lt;/span&gt;&lt;span&gt;comments&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;author&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;Anonymous&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;text&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;Love it!&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;h3&gt;YAML&lt;/h3&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;comments&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt; author&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; Nate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;    text&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; Great Article!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt; author&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; Anonymous&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;    text&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; Love it!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;table1&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;  foo&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; bar&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;  nested_table&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;    baz&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; bat&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;See? Especially for people like me with a Unix culture, the YAML format seems both more natural (resembling the format of email or HTTP headers, the use of indentation à la Python), and TOML has a repulsive Windows flavor. Add to this that most of the tools I use understand natively YAML frontmatter but not TOML, and the decision is easy.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Explicit Action Pattern keeps you in the flow</title>
        <published>2026-04-05T00:00:00+00:00</published>
        <updated>2026-04-05T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/explicit-action-pattern-keeps-you-in-the-flow/"/>
        <id>https://colas.nahaboo.net/code/explicit-action-pattern-keeps-you-in-the-flow/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/explicit-action-pattern-keeps-you-in-the-flow/">&lt;p&gt;When designing interfaces for tools for my personal use, I have been using what I call an &lt;strong&gt;Explicit Action Pattern&lt;/strong&gt;, the opposite of modal design. Due to the general trend towards minimalism of modern UI practice, it is not used a lot, but I think it should, and here is why.&lt;/p&gt;
&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;a class=&quot;zola-anchor&quot; href=&quot;#examples&quot; aria-label=&quot;Anchor link for: examples&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;table style=&#39;background-color: #f0f7ff;&#39;&gt;
  &lt;tr&gt;
    &lt;th&gt;Explicit Action Pattern&lt;/th&gt;&lt;th&gt;Modal Design&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td style=&#39;padding-right:2em;&#39;&gt;
      &lt;div&gt;&lt;b&gt;Initial Search&lt;/b&gt;&lt;/div&gt;
      &lt;form style=&#39;display:inline&#39;&gt;
        &lt;input name=search value=&quot;&quot; size=24&gt;
          &lt;input type=submit value=&#39;SEARCH ALL&#39; style=&#39;font-weight:bold&#39;&gt;
      &lt;/form&gt;
      &lt;br&gt;
      &lt;form style=&#39;display:inline&#39;&gt;
        &lt;input name=search value=&quot;&quot; size=24&gt;
        &lt;input type=submit value=&#39;Search Movie&#39;&gt;
      &lt;/form&gt;
      &lt;br&gt;
      &lt;form style=&#39;display:inline&#39;&gt;
        &lt;input name=search value=&quot;&quot; size=24&gt;
        &lt;input type=submit value=&#39;Search Music&#39;&gt;
      &lt;/form&gt;
      &lt;br&gt;
      &lt;form style=&#39;display:inline&#39;&gt;
        &lt;input name=search value=&quot;Alan Cooper&quot; size=24&gt;
        &lt;input type=submit value=&#39;Search EBook&#39;&gt;
      &lt;/form&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;form style=&#39;display:inline&#39;&gt;
        &lt;input name=search value=&quot;Alan Cooper&quot; size=24&gt;
        &lt;input type=submit value=&#39;Search&#39;&gt;
        &lt;br&gt;
        &lt;input type=&quot;radio&quot; id=&quot;0&quot; name=&quot;movie&quot; value=&quot;all&quot; /&gt;
        &lt;label for=&quot;0&quot;&gt;&lt;b&gt;All&lt;/b&gt;&lt;/label&gt;
        &lt;input type=&quot;radio&quot; id=&quot;1&quot; name=&quot;movie&quot; value=&quot;movie&quot; /&gt;
        &lt;label for=&quot;1&quot;&gt;movie&lt;/label&gt;
        &lt;input type=&quot;radio&quot; id=&quot;2&quot; name=&quot;movie&quot; value=&quot;music&quot; /&gt;
        &lt;label for=&quot;2&quot;&gt;music&lt;/label&gt;
        &lt;input type=&quot;radio&quot; id=&quot;3&quot; name=&quot;movie&quot; value=&quot;ebook&quot; checked /&gt;
        &lt;label for=&quot;3&quot;&gt;ebook&lt;/label&gt;
      &lt;/form&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;
      &lt;div&gt;&lt;b&gt;Search results...&lt;/b&gt;&lt;/div&gt;
      &lt;form style=&#39;display:inline&#39;&gt;
        &lt;input name=search size=24 value=&quot;&quot;&gt;
        &lt;input type=submit value=Search&gt;
      &lt;/form&gt;
      &lt;br&gt;
      &lt;form style=&#39;display:inline&#39;&gt;
        &lt;input name=search value=&quot;Alan Cooper&quot; size=24&gt;
        &lt;input type=submit value=&quot;Re Search&quot;&gt;
      &lt;/form&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;form style=&#39;display:inline&#39;&gt;
        &lt;input name=search value=&quot;Alan Cooper&quot; size=24&gt;
        &lt;input type=submit value=&#39;Search&#39;&gt;
        &lt;input type=submit value=&#39;Clear&#39;&gt;
        &lt;br&gt;
        &lt;input type=&quot;radio&quot; id=&quot;0&quot; name=&quot;movie&quot; value=&quot;all&quot; /&gt;
        &lt;label for=&quot;0&quot;&gt;&lt;b&gt;All&lt;/b&gt;&lt;/label&gt;
        &lt;input type=&quot;radio&quot; id=&quot;1&quot; name=&quot;movie&quot; value=&quot;movie&quot; /&gt;
        &lt;label for=&quot;1&quot;&gt;movie&lt;/label&gt;
        &lt;input type=&quot;radio&quot; id=&quot;2&quot; name=&quot;movie&quot; value=&quot;music&quot; /&gt;
        &lt;label for=&quot;2&quot;&gt;music&lt;/label&gt;
        &lt;input type=&quot;radio&quot; id=&quot;3&quot; name=&quot;movie&quot; value=&quot;ebook&quot; checked /&gt;
        &lt;label for=&quot;3&quot;&gt;ebook&lt;/label&gt;
      &lt;/form&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;As you can see in the top row, for searching an ebook, I will &quot;flow&quot; through the form from left to right, filling the relevant field and hitting the return key, or pressing the &quot;Search EBook&quot; button. But with the traditional modal design, choosing the radio button &quot;ebook&quot; breaks my flow of entry.&lt;/p&gt;
&lt;p&gt;On the second row, you can see how I use this design for providing a &quot;search again&quot; form, where I can choose to enter a new search, or edit the previous one by selecting the relevant search box. It treats the search interface like a REPL (Read-Eval-Print Loop) rather than a static webpage. Whereas in the traditional way, I would have to break my flow to go hit the &quot;Clear&quot; button to empty the field.&lt;/p&gt;
&lt;h2 id=&quot;interaction-flow&quot;&gt;Interaction flow&lt;a class=&quot;zola-anchor&quot; href=&quot;#interaction-flow&quot; aria-label=&quot;Anchor link for: interaction-flow&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So, rather than breaking the flow of the interactions to set modes, you just flow seamlessly into branches of the decision tree. If we take a car driving analogy, you just choose a direction when encountering a fork in the road, rather than having to get out of the car to move a barrier before resuming your ride.&lt;/p&gt;
&lt;script src=https://colas.nahaboo.net/js/mermaid.js&gt;&lt;/script&gt;
&lt;pre class=&quot;mermaid&quot;&gt;

graph TD;
    User((User Interaction));

    %% LEFT SIDE: Explicit Pattern (Parallel)
    subgraph Explicit [&quot;Explicit Action Pattern (Parallel)&quot;];
        FormB[Dedicated Form: Search Books];
        InputB[/Input Field: Books/];
        FormM[Dedicated Form: Search Movies];
        InputM[/Input Field: Movies/];
    end;

    %% RIGHT SIDE: Modal Pattern (Sequential)
    subgraph Modal [&quot;Modal     Design (Sequential)&quot;];
        FormS[Single Search Form];
        %% The Vertical Spine (invisible link to keep things straight)
        FormS ~~~ InputS[/Generic Input Field/];
        %% The &quot;Hook&quot; - Positioned to the side
        Mode{{Mode Selection: Books vs Movies}};
    end;

    %% Explicit Connections (Direct Flow)
    User --&gt; FormB;
    User --&gt; FormM;
    FormB --&gt; InputB;
    FormM --&gt; InputM;
    InputB --&gt; Submit((Submit));
    InputM --&gt; Submit;

    %% Modal Connections (The Hook Flow)
    User --&gt; FormS;
    FormS --&gt; Mode;
    Mode --&gt; InputS;
    InputS --&gt; Submit;

    %% Styling from original source
    style FormB fill:#f9f,stroke:#333;
    style FormM fill:#f9f,stroke:#333;
    style InputB fill:#ccf,stroke:#333;
    style InputM fill:#ccf,stroke:#333;
    style Modal fill:#f5f5f5,stroke:#999,stroke-dasharray: 5 5;
    style FormS fill:#eee,stroke:#999;
    style Mode fill:#fff,stroke:#333,stroke-width:2px;

&lt;/pre&gt;
&lt;h2 id=&quot;benefits&quot;&gt;Benefits&lt;a class=&quot;zola-anchor&quot; href=&quot;#benefits&quot; aria-label=&quot;Anchor link for: benefits&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This Explicit Action Pattern has many benefits for power users:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Distributed Entry:&lt;/strong&gt; Breaking one complex task into multiple simple, dedicated entry points.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parallel UI:&lt;/strong&gt; Presenting all options simultaneously rather than hiding them behind a selection step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Non-Modal Interaction:&lt;/strong&gt; Avoiding the &quot;mode&quot; where you have to set a state (e.g., clicking a radio button) before you can perform the actual action.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the examples above:&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Feature&lt;/th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Separate Dedicated Fields&lt;/th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Single Field + mode switches&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Cognitive Load&lt;/strong&gt;&lt;/td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Higher initial load&lt;/strong&gt; (more to look at), unless you are already used to this UI.&lt;/td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Lower initial load&lt;/strong&gt; (cleaner look), especially for novices.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Physical Effort&lt;/strong&gt;&lt;/td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Lower&lt;/strong&gt; (Click bar → Type → Enter).&lt;/td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Higher&lt;/strong&gt; (Click radio → Click bar → Type → Enter).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Intent Clarity&lt;/strong&gt;&lt;/td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Absolute from the moment you click.&lt;/td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Ambiguous until the modifier is set.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Screen Space&lt;/strong&gt;&lt;/td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&quot;Expensive&quot;; requires more vertical/grid space.&lt;/td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Efficient; fits in a header.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;This is essentially applying &lt;strong&gt;Functional Programming&lt;/strong&gt; and &lt;strong&gt;CLI philosophy&lt;/strong&gt; to a GUI.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Immutability:&lt;/strong&gt; The pre-filled field is the &quot;original state.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pure Functions:&lt;/strong&gt; Each input field has a single, predictable output.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No Side Effects:&lt;/strong&gt; Clearing one field doesn&#39;t break your ability to see the other.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In a nutshell, &lt;strong&gt;Directness over Modality&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;for-power-users&quot;&gt;For power users&lt;a class=&quot;zola-anchor&quot; href=&quot;#for-power-users&quot; aria-label=&quot;Anchor link for: for-power-users&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you are used to CLI environments, or using mostly keyboards shortcuts in interfaces, or editors like Emacs or vi, your brain likely functions on &quot;Command-Action&quot; pairs. And Explicit Action Patterns offers you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Muscle Memory:&lt;/strong&gt; You can bind a specific field to a specific mental &quot;slot.&quot; You don&#39;t have to check the state of a radio button before hitting enter; you &lt;em&gt;know&lt;/em&gt; which field you are in. It transforms a two-click &quot;hunt and peck&quot; task into a single-click muscle memory task. You don&#39;t have to wait for an animation or look for a label; you just hit the coordinate on the screen you already know.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No &quot;Context Switch&quot; Tax:&lt;/strong&gt; Selecting a radio button is a &quot;mode switch.&quot; It requires you to stop thinking about your search query for a second to handle the UI mechanics. Separate fields eliminate that tax.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visual Affordance:&lt;/strong&gt; The label &quot;Search Movies&quot; is context-free, an immediate call to action. A generic &quot;Search&quot; box is a mystery until you look at the surrounding buttons.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;in-the-real-world&quot;&gt;In the real world&lt;a class=&quot;zola-anchor&quot; href=&quot;#in-the-real-world&quot; aria-label=&quot;Anchor link for: in-the-real-world&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While interfaces have moved toward the &quot;Google-style&quot; of Modal Design to flatten the learning curve for the general public, many expert-level interfaces still rely on the Explicit Action Pattern. You can find it in library catalogs, Bloomberg or Reuters terminals, and high-end enterprise software.&lt;/p&gt;
&lt;p&gt;You can also see this approach in media editing software. Instead of a single &quot;Save...&quot; dialog that asks for a format later, you often have explicit menu entries: &quot;Save,&quot; &quot;Export as PNG,&quot; &quot;Export as JPG,&quot; or &quot;Export as MP3.&quot;&lt;/p&gt;
&lt;p&gt;And one can consider that the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/Unix_philosophy&quot;&gt;Unix philosophy&lt;/a&gt; of having one tool per function, instead of mega-commands with tons of options is also an embodiment of the  Explicit Action Pattern.&lt;/p&gt;
&lt;h2 id=&quot;dive-deeper&quot;&gt;Dive deeper&lt;a class=&quot;zola-anchor&quot; href=&quot;#dive-deeper&quot; aria-label=&quot;Anchor link for: dive-deeper&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This approach is highly &quot;discoverable.&quot; You don&#39;t need to learn &quot;how the search bar works&quot;; the interface tells you exactly what it can do by simply existing. This is what ihas been advocated by many Usability experts, that I consider mandatory reads:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The boss: Alan Cooper books, especially &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://thedesignbooks.com/books/about-face-the-essentials-of-interaction-design&quot;&gt;About Face: The Essentials of Interaction Design&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The enjoyable &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://sensible.com/dont-make-me-think/&quot;&gt;Don&#39;t make me think&lt;/a&gt; by Steve Krug.&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.cs.umd.edu/users/ben/papers/Shneiderman1983Direct.pdf&quot;&gt;Direct Manipulation&lt;/a&gt; by Ben Shneiderman&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.edwardtufte.com/notes-sketches/?msg_id=0000Jr&quot;&gt;Data Density&lt;/a&gt; the examples above being described by him as &quot;Parallel Input&quot;.&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/The_Humane_Interface&quot;&gt;The human Interface&lt;/a&gt; by Jef Raskin, especially his &quot;Monotonicity&quot;: the idea that a specific goal should always be achieved by a specific, unchanging set of actions.&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Foswiki update script</title>
        <published>2026-03-26T00:00:00+00:00</published>
        <updated>2026-03-26T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/foswiki-update-script/"/>
        <id>https://colas.nahaboo.net/code/foswiki-update-script/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/foswiki-update-script/">&lt;p&gt;Although I have &lt;a href=&quot;/blog/a-new-version-of-the-site-for-2026-in-zola&quot;&gt;migrated my public site to zola&lt;/a&gt; which was on &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://foswiki.org&quot;&gt;Foswiki&lt;/a&gt;, I have decided for now to keep my private family wiki on Foswiki for now.&lt;/p&gt;
&lt;p&gt;So I needed to upgrade it to 2.1.11, it was still in 2.1.6, and since the upgrade is not exactly intuitive, I made a bash script to automatise the upgrade.&lt;/p&gt;
&lt;p&gt;You can find it &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/foswiki-upgrade&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;See also its publication on the Foswiki site, with a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://foswiki.org/Support/BestPracticeTip35#comment1.1774606339&quot;&gt;reminder of the standard upgrade method&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-the-script-works&quot;&gt;How the Script Works&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-the-script-works&quot; aria-label=&quot;Anchor link for: how-the-script-works&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This script supposes a modern version of Foswiki (v2+): it blindly overwrites the files that should not have been modified, so when customizing your wiki be sure to never directly modify distributed files.&lt;/p&gt;
&lt;p&gt;The script however will not overwrite blindly any topic file that has been edited via Foswiki itself, by checking the author metadata. It will perform a 3-way merge, as automated as possible.&lt;/p&gt;
&lt;p&gt;It also works only on a local copy of the site. You will have to download yourself a copy of your site, upgrade, and then re-upload it in place. This keeps the script simpler and safer.&lt;/p&gt;
&lt;p&gt;It works on linux, but should work in linux-like layers on top of other OSes, such as cygwin, WSL, Homebrew, ...&lt;/p&gt;
&lt;h2 id=&quot;why-staying-on-foswiki&quot;&gt;Why staying on Foswiki?&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-staying-on-foswiki&quot; aria-label=&quot;Anchor link for: why-staying-on-foswiki&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My private wiki is used by me, but also my wife, who is not really fluent in wiki or markdown  editing, so an excellent WYSIWYG is a must. And personnaly, I hate databases and strive for stability and simple architecture. Foswiki has all these qualities... and I would have a lot of content to convert.&lt;/p&gt;
&lt;p&gt;But I will probably switch to an open source markdown-based wiki with a very good WYSIWYG editor and flat file storage, and easy to maintain. For now I see 3 potential candidates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://silverbullet.md/&quot;&gt;SilverBullet&lt;/a&gt;, still evolving but very promising&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://otterwiki.com/&quot;&gt;OtterWiki&lt;/a&gt;, minimal but seems powerful enough&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://js.wiki/&quot;&gt;Wiki.js&lt;/a&gt; the current leader, but I will wait for the v3 as the v2 to v3 migration do not seem automatic, and the development &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.reddit.com/r/selfhosted/comments/1mebp67/is_wikijs_3_dead/&quot;&gt;seems to have slowed greatly&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Suntimes, a small shell utility</title>
        <published>2022-03-03T00:00:00+00:00</published>
        <updated>2022-03-03T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/suntimes-a-small-shell-utility/"/>
        <id>https://colas.nahaboo.net/code/suntimes-a-small-shell-utility/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/suntimes-a-small-shell-utility/">&lt;p&gt;I just made a small command-line utility to display the sunrise and sunset times at a location. It can be quite useful in shell scripts.&lt;/p&gt;
&lt;p&gt;It is a minimal Go wrapper around the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/nathan-osman/go-sunrise&quot;&gt;nathan-osman/go-sunrise library&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can find it at my repository: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/suntimes&quot;&gt;GitHub - ColasNahaboo/suntimes&lt;/a&gt;&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>One year retired</title>
        <published>2022-02-24T00:00:00+00:00</published>
        <updated>2022-02-24T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/one-year-retired/"/>
        <id>https://colas.nahaboo.net/blog/one-year-retired/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/one-year-retired/">&lt;p&gt;I have been retired for one year now, and what happened? Well...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I basically stopped coding and reading about anything computer-related for eight months. I didn&#39;t expect it, I was thinking I would start coding at once, going through my huge todo list of various projects.&lt;/li&gt;
&lt;li&gt;I enjoyed being able to go SUP surfing at will, but I ended up actually less on the water than before, simply because i didn&#39;t feel pressured into going on the water if the conditions were not enjoyable: I have now all the time in the world, I can wait for better conditions.&lt;/li&gt;
&lt;li&gt;I started what I should have done years ago: Stretching at least 30 minutes daily, and warming up a full 10 minutes before going surfing. Work and play for me were mainly sitting in front of a computer, and I had become awfully stiff. I will detail my routine in later posts, it is worth it.&lt;/li&gt;
&lt;li&gt;I dived deep in modern physics: astrophysics, cosmology and quantum mechanics. This also will warrant a full separate post.&lt;/li&gt;
&lt;li&gt;I started re-coding in the last four months:
&lt;ul&gt;
&lt;li&gt;The first month I began working on various bits of code (shell scripts), progressively getting back to speed, and also deciding to learn the language Go. I was looking for a very longtime for an efficient language to replace lisp or C as compliment to scripting (I never liked Java), and was hesitating between Rust and Go. A discussion with &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.arsac.org/olivier/&quot;&gt;Olivier Arsac&lt;/a&gt; convinced me to try Go.&lt;/li&gt;
&lt;li&gt;I switched to git (I was using mercurial) for version control, and &lt;a href=&quot;/code/moving-to-github/&quot;&gt;moved&lt;/a&gt; my publicly available source codes from my personal web site to my &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo&quot;&gt;GitHub&lt;/a&gt;. It may sound ridiculous, but at now 61, setting things up so they do not disappear when I do is becoming an important consideration. I will thus progressively stop using self-hosted sites to publish on places that will survive me, as nobody in my family is tech-savvy enough to maintain a web site.&lt;/li&gt;
&lt;li&gt;Then in December I stumbled upon the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://adventofcode.com/&quot;&gt;Advent of Code&lt;/a&gt; challenge that I decided to do in bash for the challenge, and it was unexpectedly productive? I learned more in these 25 puzzles than in my last 10 years of professional bash scripting. You can see my solutions in my post &lt;a href=&quot;/code/bash-lessons-learned-with-aoc-2021&quot;&gt;Bash lessons learned with AoC 2021&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Thus, after finishing the Go tutorial, I decided to do the previous AoC years in Go. And I just &lt;a href=&quot;/code/completed-the-2015-advent-of-code-challenge-in-go&quot;&gt;finished the AoC 2015 in Go&lt;/a&gt;. This definitively made me a Go enthusiast, I realized that the designers of Go had exactly the same opinion of what should be a general programming language: They worshiped readability, simplicity, performance and maintainability while hating inheritance and the feature creep of most modern languages.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That was my first year. And now, embarking for the next one!&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Completed the 2015 Advent of Code challenge in GO</title>
        <published>2022-02-24T00:00:00+00:00</published>
        <updated>2022-02-24T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/completed-the-2015-advent-of-code-challenge-in-go/"/>
        <id>https://colas.nahaboo.net/code/completed-the-2015-advent-of-code-challenge-in-go/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/completed-the-2015-advent-of-code-challenge-in-go/">&lt;p&gt;And I completed the 2015 challenge! (see my &lt;a href=&quot;/code/starting-the-2015-advent-of-code-challenge-in-go&quot;&gt;previous post&lt;/a&gt;). You can read my notes on it on my  &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/advent-of-code-my-solutions/tree/main/go/2015&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This was quite interesting, and I thing a great way to learn a language just after completing its tutorial, as this challenge as a feel of &quot;real life&quot; situations, and allow for a variety of solutions, so that you can put different approaches to the test.&lt;/p&gt;
&lt;p&gt;I forced myself to not look at the solutions available on the net, except for two cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using working code to generate more data to test against (random inputs + expected result number). I just ran the code of other solutions, without reading their source. I often used  solutions by &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/devjobe/advent-of-code-2015-golang&quot;&gt;devjobe&lt;/a&gt; and &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/schwern/adventofcode.go&quot;&gt;schwern&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Once I had a working solution, browsing the source code of other solutions to compare with mine and see where I could improve, and discover cool tricks. I mostly used the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.reddit.com/r/adventofcode/wiki/solution_megathreads/#wiki_december_2015&quot;&gt;reddit megathread&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also tried to use only the standard Go packages, and not the ones available on the web. I am sure I will end up using 3rd party packages to replace the standard ones (the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://pkg.go.dev/flag&quot;&gt;flag package &lt;/a&gt;  to parse options for instance is really too primitive). And I also didn&#39;t create a separate module or package for my library of common useful functions. I will surely do it in the future, but I feel I should have more experience first.&lt;/p&gt;
&lt;p&gt;Anyways, it has confirmed that Go was the language I have been waiting for all these years. On the one hand, I have a bit of regret not having jumped on it sooner, but on the other hand it was easier to just wait for my retirement (one year ago) to free myself from any work politics and constraints to be able to fully explore the programming language landscape with no strings attached.&lt;/p&gt;
&lt;p&gt;I can see myself using 4 languages in the future:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;bash&lt;/strong&gt; for any &quot;glue&quot; tooling and scripts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;go&lt;/strong&gt; for my main do-it-all language, and web backend.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;javascript&lt;/strong&gt; (and/or WebAssembly) for my web frontend, with pure modern HTML and CSS, but no framework, à la  &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;http://vanilla-js.com/&quot;&gt;Vanilla JS&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;and maybe a higher performance, specialized language, if I encounter such needs: E.g: &lt;strong&gt;zig&lt;/strong&gt; as a replacement of C for tasks where Go could be unsuited (if I encounter any), or &lt;strong&gt;julia&lt;/strong&gt; for math-specific things, etc...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Edit: I see that Neil Henning also enjoyed discovering a new language via AoC: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.duskborn.com/posts/2021-aoc-zig/&quot;&gt;A Review of the Zig Programming Language (using Advent of Code 2021)&lt;/a&gt;&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Starting the 2015 Advent of Code challenge in GO</title>
        <published>2022-02-09T00:00:00+00:00</published>
        <updated>2022-02-09T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/starting-the-2015-advent-of-code-challenge-in-go/"/>
        <id>https://colas.nahaboo.net/code/starting-the-2015-advent-of-code-challenge-in-go/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/starting-the-2015-advent-of-code-challenge-in-go/">&lt;p&gt;I discovered the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://adventofcode.com/&quot;&gt;advent of code&lt;/a&gt; in 2021, and did it in &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/advent-of-code-my-solutions/tree/main/bash&quot;&gt;bash&lt;/a&gt; for the challenge. But since I started learning GO, I decided to code the previous AoC years in GO as a mean to practice it. So, although my bash code can be useful to see some tricks of a seasoned bash programmer, these GO solutions must be considered as &quot;student code&quot;.&lt;/p&gt;
&lt;p&gt;So I started coding the 2015 year of AoC in GO, available on my GitHub: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/advent-of-code-my-solutions/blob/main/go/2015&quot;&gt;advent-of-code-my-solutions/go/2015&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The code is in GO, with some housekeeping scripts in bash.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>My bash library on Github</title>
        <published>2022-02-05T00:00:00+00:00</published>
        <updated>2022-02-05T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/my-bash-library-on-github/"/>
        <id>https://colas.nahaboo.net/code/my-bash-library-on-github/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/my-bash-library-on-github/">&lt;p&gt;I have started to publish on Github, at &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-bash-lib&quot;&gt;colas-bash-lib&lt;/a&gt;, my collection of various &lt;strong&gt;bash functions&lt;/strong&gt; I reuse often in my bash scripts.&lt;/p&gt;
&lt;p&gt;Fell free to copy and use in any of your projects or compilation of bash tools.&lt;/p&gt;
&lt;p&gt;I have tried to make them the &lt;strong&gt;fastest&lt;/strong&gt; possible, by avoiding forking sub-shells or external commands, and
benchmarking extensively to compare the possible way of coding them. Of course, I will gladly accept suggestions or code to make them faster.
But it means that error checking is often terse and minimal, and readability of the code was not a priority.&lt;/p&gt;
&lt;p&gt;I have a kind of «anti-npm» approach, in that I &lt;strong&gt;copy&lt;/strong&gt; these functions into my scripts rather that using hem as a true external library that I would load at runtime. It thus avoid installation issues, and the ependency problems that may arise from automated upgrades.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Bash lessons learned with AoC 2021</title>
        <published>2022-01-11T00:00:00+00:00</published>
        <updated>2022-01-11T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/bash-lessons-learned-with-aoc-2021/"/>
        <id>https://colas.nahaboo.net/code/bash-lessons-learned-with-aoc-2021/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/bash-lessons-learned-with-aoc-2021/">&lt;p&gt;I completed all the exercises of the Advent of Code 2021 in bash! (see &lt;a href=&quot;/code/advent-of-code-2021&quot;&gt;my previous post&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;You can find my solutions with comments in my GitHub repository at https://github.com/ColasNahaboo/advent-of-code-my-solutions/tree/main/bash/2021&lt;/p&gt;
&lt;p&gt;I must say I &quot;cheated&quot; a bit. My  solution for the Day 24 was too slow in bash, and as I was a bit out of  steam, I did not try to find a smart algorithm. I just noticed that I  solved it by building a bash arithmetic expression (of 1 million  characters...) that I then evaluated in bash, and since its syntax was  exactly the one for C... I just made the bash script compile the bash  expression in C and execute it, using the C compiler as a bash  arithmetic just-in-time compiler :-)&lt;/p&gt;
&lt;p&gt;I just discovered AoC this year, and I am impressed. Challenging, fun,  and a great way to progress. I am going to do the previous years, too,  but in &quot;real&quot; languages this time. At least ones with data structures... I will start with Go.&lt;/p&gt;
&lt;h2 id=&quot;what-i-learned&quot;&gt;What I learned:&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-i-learned&quot; aria-label=&quot;Anchor link for: what-i-learned&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Coding in bash is not so bad, even if it can border on insanity at times :-).&lt;/li&gt;
&lt;li&gt;Modern bash features are often overlooked but very useful.&lt;/li&gt;
&lt;li&gt;Passing shellcheck should be a mandatory goal for each bash programmer. I resented it at first since I thought it was adding unecessary syntaxic sugar to my code until I realized that it was a symptom that my coding style was the problem.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;[[...]]&lt;/code&gt; for strings and &lt;code&gt;((...))&lt;/code&gt; for integers other any of the legacy constructs like &lt;code&gt;[...]&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, etc... The code is then much cleaner and safer (and a tad faster), as you do not have to quote as much. E.g: &lt;code&gt;[[ -z foo ]]&lt;/code&gt; instead of &lt;code&gt;[ -z &quot;$foo&quot; ]&lt;/code&gt;, or even &lt;code&gt;((i=j))&lt;/code&gt; instead of &lt;code&gt;i=&quot;$j&quot;&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;but&lt;/strong&gt;, this makes traditional debugging with &lt;code&gt;set -x&lt;/code&gt; less useful as the values of variables are not displayed anymore. E.g if j is 2, the tracing of &lt;code&gt;i=&quot;$j&quot;&lt;/code&gt; shows &lt;code&gt;i=2&lt;/code&gt; whereas the tracing of &lt;code&gt;((i=j))&lt;/code&gt; only shows &lt;code&gt;((i=j))&lt;/code&gt;. This could be where a bash debugger would be useful, but I know only one, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://bashdb.sourceforge.net/bashdb.html&quot;&gt;bashdb&lt;/a&gt;, and it does not seem updated anymore, and I could not find a version working with bash 5.1. The &lt;code&gt;trap DEBUG&lt;/code&gt; trick can be useful, though.&lt;/li&gt;
&lt;li&gt;So, I tend to write now &lt;code&gt;i=$((j+k))&lt;/code&gt; while developing code, and maybe later for production switch to the a bit more efficient &lt;code&gt;((i = j+k))&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I avoided arrays in bash because I found out they were abysmally slow when first introduced, to the point that managing data in files with grep, sed, ... was actually faster than using arrays. But not anymore! Bash arrays should now be used as much as possible.&lt;/li&gt;
&lt;li&gt;A lot of bash functions can &quot;map&quot; on arrays. For instance &lt;code&gt;${tab[@]//x/y}&lt;/code&gt; will string-replace x by y in all the elements of the array tab, and is super fast.&lt;/li&gt;
&lt;li&gt;Use arrays rather than the classic way to represent lists in bash by space-separated (or tab-separated) substrings in a  string.&lt;/li&gt;
&lt;li&gt;Bash can have typed variables: integer ones via &lt;code&gt;declare -i&lt;/code&gt; or &lt;code&gt;local -i&lt;/code&gt;, and using them makes your code safer.&lt;/li&gt;
&lt;li&gt;Bash functions can be passed variables by name, useful for efficiency to avoid copying big arrays or strings, and to provide multiple return values by modifying passed variables. But it cannot recurse as it is not a passing by reference, but by name.&lt;/li&gt;
&lt;li&gt;Working with arrays makes using &lt;code&gt;$(...)&lt;/code&gt; impractical, as commands are executed in a subshell and cannot access arrays anymore to update them in the parent shell. So I tend to pass the return value(s) into global variables of the same name of a function. E.g. instead of &lt;code&gt;x=$(foo)&lt;/code&gt;, I write &lt;code&gt;foo; x=&quot;$foo&quot;&lt;/code&gt;. Or pass variables by name to set them if I want to return multiple results.&lt;/li&gt;
&lt;li&gt;To parse a space-separated string, the fastest is using &lt;code&gt;set&lt;/code&gt; to map the elements in the positional parameters &lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$2&lt;/code&gt;, ... then the &lt;code&gt;${string#* }&lt;/code&gt; and &lt;code&gt;${string% *}&lt;/code&gt; operators are the fastest, closely followed by a read, the full &lt;code&gt;[[ $string =~ ([-[:digit:]]+)[[:space:]]... ]]&lt;/code&gt; being 3 times slower. And if possible, using indexes is even faster: &lt;code&gt;${string:i:j}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use the faster &lt;code&gt;$(&amp;lt; filename)&lt;/code&gt; instead of &lt;code&gt;$(cat filename)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;To copy an associative array A1 to A2 in bash 4.4+, do:
&lt;code&gt;A1_def=$(declare -p A1) &amp;amp;&amp;amp; declare -A A2=&quot;${A1_def#*=}&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;To access more than 9 parameters in a function: use braces: &lt;code&gt;$10&lt;/code&gt; wont work, but &lt;code&gt;${10}&lt;/code&gt; does.&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Moving to Github</title>
        <published>2021-12-10T00:00:00+00:00</published>
        <updated>2021-12-10T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/moving-to-github/"/>
        <id>https://colas.nahaboo.net/code/moving-to-github/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/moving-to-github/">&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; I will move all the sources I maintained on my own &lt;strong&gt;mercurial&lt;/strong&gt; web instance in &lt;code&gt;hg.colas.nahaboo.net&lt;/code&gt;  to my GitHub repositories: https://github.com/ColasNahaboo/&lt;/p&gt;
&lt;p&gt;I have thus redirected the non-functional &lt;code&gt;hg.colas.nahaboo.net&lt;/code&gt; to this page.&lt;/p&gt;
&lt;p&gt;The move will be gradual, I started with &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/rsync-incr&quot;&gt;rsync-incr&lt;/a&gt;, and I hope to have finished before the end of 2021. Please ask me is you do not see yet your favorite source there.&lt;/p&gt;
&lt;p&gt;The reason is that Debian has phased out most of what the web tools for mercurial rely on (python2, wsgi, ...), and thus my web hg repository was broken. And since I wanted anyways to move it to GitHub, it is not worth repairing it.&lt;/p&gt;
&lt;h2 id=&quot;how-to-move-mercurial-hg-directories-to-github&quot;&gt;How to move mercurial (hg) directories to GitHub&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-move-mercurial-hg-directories-to-github&quot; aria-label=&quot;Anchor link for: how-to-move-mercurial-hg-directories-to-github&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As my hg repositories were very simple (no branches, I tend to use cloned repos than branches), what I do to migrate a repository &lt;code&gt;foo&lt;/code&gt; to github:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I used &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://repo.or.cz/fast-export.git&quot;&gt;hg-fast-export&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;I converted the hg repo of &lt;code&gt;foo&lt;/code&gt; to a local git repository of the same name, with the main hg branch mapped to github default &lt;code&gt;main&lt;/code&gt; instead of the script default of &lt;code&gt;master&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;I created an &lt;strong&gt;empty&lt;/strong&gt; repository &lt;code&gt;foo&lt;/code&gt; on github (no files such as README.md, LICENSE, ...)&lt;/li&gt;
&lt;li&gt;I added it as origin to my local git repo&lt;/li&gt;
&lt;li&gt;I deleted my hg repo, and git-pushed my local git repo (which became my working repo) to github. Plus I redirected the old hgweb repo urls to the new github repo adress.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I.e. as actual bash commands:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;repo=foo                        # the name of the repo to convert&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;hg=~/hg                         # where are my local hg repos&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git=~/git                       # where are my local git repos&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;hfe=/opt/fast-export            # where is my copy of hg-fast-export.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ghn=ColasNahaboo                # my name on github&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;mkdir $git/$repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cd $git/$repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git init&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$hfe/hg-fast-export.sh -r &amp;quot;$hg/$repo&amp;quot; -M main -n &amp;quot;$git/$repo&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git branch -M main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git checkout main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git remote add origin git@github.com:$ghn/$repo.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# Warning: for this last step, the github repo must exist and be empty&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git push -u origin main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Advent of Code 2021</title>
        <published>2021-12-07T00:00:00+00:00</published>
        <updated>2021-12-07T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/advent-of-code-2021/"/>
        <id>https://colas.nahaboo.net/code/advent-of-code-2021/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/advent-of-code-2021/">&lt;p&gt;I just discovered the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://adventofcode.com/&quot;&gt;Advent of code&lt;/a&gt; coding challenge. It exists since 2015, and seems really interesting in that it is language-agnostic.&lt;/p&gt;
&lt;p&gt;Each day a problem is described, with input files consisting of numeric values for the problem. You must submit the answer which is a number by whatever means you want.&lt;/p&gt;
&lt;p&gt;I started to do the current 2021 challenge, in &lt;strong&gt;bash&lt;/strong&gt; to force myself to code in a more modern style that pass &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.shellcheck.net/&quot;&gt;shellcheck&lt;/a&gt;. And later, i will probably do the other years in &lt;strong&gt;Go&lt;/strong&gt; to learn it.&lt;/p&gt;
&lt;p&gt;I publish my solutions as I code them on &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/advent-of-code-my-solutions/tree/main/bash/2021&quot;&gt;my github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;More info:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://adventofcode.com/&quot;&gt;advent of code&lt;/a&gt; (aka AOC) site&lt;/li&gt;
&lt;li&gt;The sub-reddit  &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.reddit.com/r/adventofcode/&quot;&gt;/r/adventofcode&lt;/a&gt; to discuss AOC&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Pinboard pricing model</title>
        <published>2021-03-01T00:00:00+00:00</published>
        <updated>2021-03-01T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/pinboard-pricing-model/"/>
        <id>https://colas.nahaboo.net/blog/pinboard-pricing-model/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/pinboard-pricing-model/">&lt;p&gt;I am a knowledge addict, and always has been. I was reading as many books as possible, and the advent of the web has been a marvelous gift for me. But I soon realized that I needed some tools to manage this knowledge and supplement my memory. It started with pen and papers, then I used a lot the PDAs (Personal Digital Assistants) such as the fabulous Psion &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/Psion_Series_3&quot;&gt;Series 3&lt;/a&gt; and &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/Psion_Series_5&quot;&gt;Series 5&lt;/a&gt;, the various smartphones, and finally the bookmarks-in-the-cloud service &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/Delicious_(website)&quot;&gt;Delicious&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But as Delicious closed, I searched a replacement, and found &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://pinboard.in/tour/&quot;&gt;Pinboard&lt;/a&gt;, and was hooked. Not only it offered the same functionalities as Delicious (and more), but if was fast and sleek, with a minimal no-nonsense design that I favor.&lt;/p&gt;
&lt;p&gt;It also had an original pricing system: you paid a one-time fee of $0.001 per the number of current users to get access for life. E.g $9.26 if there was already 9263 paying users and you wanted to become the 9264th. The goal was to entice people to register now rather than waiting, even though it may not have scaled. (see &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://leftovertakeout.com/post/3783798631/pinboardin-pricing-model-fixing-their-math&quot;&gt;Pinboard.in Pricing Model: Fixing Their Math&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Alas, it could not be sustained, so in 2015 it adopted a more mainstream yearly fee scheme: $11 / year. &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://blog.pinboard.in/2014/12/new_pricing_policy/&quot;&gt;New Pricing Policy (Pinboard Blog)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But we, old timers, kept our lifetime access we bought before the change. We were basically free-riding the new wave of customers...&lt;/p&gt;
&lt;p&gt;Of course it could not go on, so last week, Maciej, the Pinboard author, suggested we voluntarily switch to the yearly fee scheme. And I did it gladly, Pinboard is for me a model of what web sites should be: sleek, fast, and efficient (and not re-selling your private data). At the opposite of the bloated monstrosities that a lot of sites tend to be nowadays.&lt;/p&gt;
&lt;p&gt;Long live Pinboard!&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A New version of the site for 2021</title>
        <published>2020-12-27T00:00:00+00:00</published>
        <updated>2020-12-27T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/a-new-version-of-the-site-for-2021/"/>
        <id>https://colas.nahaboo.net/blog/a-new-version-of-the-site-for-2021/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/a-new-version-of-the-site-for-2021/">&lt;p&gt;A big change is planned for this site in 2021: I am going to abandon the Foswiki engine for it, to pursue my recent goal of going back to the web roots. Basically, it a quest that relies more on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fast rendering&lt;/strong&gt; by no rendering at all: pre-compiling the site pages into static HTML+CSS+JS pages: the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://jamstack.org/&quot;&gt;JAMstack&lt;/a&gt; approach.&lt;/li&gt;
&lt;li&gt;What I call a &lt;strong&gt;LAUW&lt;/strong&gt; approach (&lt;strong&gt;L&lt;/strong&gt;inux &lt;strong&gt;A&lt;/strong&gt;pache &lt;strong&gt;U&lt;/strong&gt;nix-utilities &lt;strong&gt;W&lt;/strong&gt;eb Standards) instead of the traditional &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/LAMP_(software_bundle)&quot;&gt;LAMP&lt;/a&gt;. Using the full strengths of Linux, Apache, the GNU Unix utilities, and the modern web standards (HTML5, CSS4, JS ES6) now that Internet Explorer is at last dead. This means not using software libraries (e.g: JQuery) and processors (e.g: Saas) that were designed to remedy to the poor state of some browsers.&lt;/li&gt;
&lt;li&gt;Using &lt;strong&gt;Markdown&lt;/strong&gt; for text editing&lt;/li&gt;
&lt;li&gt;Using ultra &lt;strong&gt;stable and reliable&lt;/strong&gt; technologies. I want to use things that will still work in 10 years, not the fad-of-the-day javascript framework that will be obsolete in 6 months, or that will break compatibility on each update.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technical&lt;/strong&gt;: And that do not try to be friendly to non-technical authors and admins, as  I will be the sole admin and editor.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;now&quot;&gt;Now&lt;a class=&quot;zola-anchor&quot; href=&quot;#now&quot; aria-label=&quot;Anchor link for: now&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So now (December 2020), I am still using &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://foswiki.org/Home/WebHome&quot;&gt;Foswiki&lt;/a&gt;, but progressively prototyping on it the features I will try to implement later:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A simplified skin, with all the &quot;wiki-ism&quot; (everything editable) removed. As you can see I got rid of the left toolbar, to keep only the top banner with its sections (Code/Surf/Blog) menu on the logo and minimal tools on the right&lt;/li&gt;
&lt;li&gt;Editing contents in Markdown instead of the Foswiki syntax: I now do not use the Foswiki system to edit topics, but desktop-edit via &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://marktext.app/&quot;&gt;marktext&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Having all the post sharing a common Form, so that the fields of this form can be easily converted to YAML as used as frontmatter (metadata) in modern Markdown-based systems.&lt;/li&gt;
&lt;li&gt;But still using all the Foswiki power for the site admin and non-content topics (indexes,...)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The idea is to migrate progressively the contents into a form (Markdown + Frontmatter) usable in a modern static web generator like Hugo, while adapting Foswiki to use this content form, preparing a future migration.&lt;/p&gt;
&lt;p&gt;I have developed the details of what this means in my post on the Foswiki site: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://foswiki.org/Development/FoswikiNonWikiWebSites&quot;&gt;Foswiki for non-wiki web sites&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;spring-2021&quot;&gt;Spring 2021&lt;a class=&quot;zola-anchor&quot; href=&quot;#spring-2021&quot; aria-label=&quot;Anchor link for: spring-2021&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After I retire, at the end of February 2021, I will have more time to ditch Foswiki and implement the site on a new engine. It will probably be a static website generator, most probably &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://gohugo.io/&quot;&gt;Hugo&lt;/a&gt;, but maybe also &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.getzola.org/&quot;&gt;Zola&lt;/a&gt;  &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt;, or &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.gatsbyjs.com/&quot;&gt;Gasby&lt;/a&gt;, and incorporating ideas of the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://jamstack.org/generators/&quot;&gt;myriad of other ones&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;summer-2021&quot;&gt;Summer 2021&lt;a class=&quot;zola-anchor&quot; href=&quot;#summer-2021&quot; aria-label=&quot;Anchor link for: summer-2021&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I will then probably replace progressively various components of this system by ones that I will handcraft optimally to incorporate all the ideas I have been toying with during all these years: the HTML+CSS+JS layout, the tag/category system, the search engine, the comment engine, the editing process...&lt;/p&gt;
&lt;h2 id=&quot;later&quot;&gt;Later...&lt;a class=&quot;zola-anchor&quot; href=&quot;#later&quot; aria-label=&quot;Anchor link for: later&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;And I may even at some time create a totally new web engine system, who knows?&lt;/p&gt;
&lt;p&gt;Or code a wiki replacement: a collaborative editing site based on the concepts learned via this experiment.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A New Version of the site for 2020</title>
        <published>2020-07-26T00:00:00+00:00</published>
        <updated>2020-07-26T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/a-new-version-of-the-site-for-2020/"/>
        <id>https://colas.nahaboo.net/blog/a-new-version-of-the-site-for-2020/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/a-new-version-of-the-site-for-2020/">&lt;p&gt;After 10 years of
neglect, it is time for a new start of this site, because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I am going to retire in early 2021 (in 6 months), and I will have now time to properly maintain a personal site&lt;/li&gt;
&lt;li&gt;I am feeling the need to archive in a central place all the contents I scatter in various different places, if only for the selfish reason to be able to retreive them more easily&lt;/li&gt;
&lt;li&gt;I was toying with the idea of going to a static web site, as it is now in vogue, and it has a kind of nostalgia for me as I started my web sites as static   sites anyways. But by looking at the existing solutions, I was not convinced by them, as a lot seemed to naively solve the 80% easy part of the problem, and them struggle without any clear vision on how to solve the 20% hard part... a classic situation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I thus decided to keep on using &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://foswiki.org&quot;&gt;Foswiki&lt;/a&gt;
(v2.1.6), as it is now quite mature, and thus stable: I like building on
solid, time-tested fundations that will not change every month. And
let&#39;s be honest, it is both extremely powerful and incredibly reliable.
I am just tweaking it for using it as a public site (one author with all
admin rights, and read-only viewers), rather than the standard wiki
setup of many authors, all able to edit contents, with some able to also
admin rights. My postulate is that a dynamic web site like a wiki, with
proper caching, has all the advantages of a static web site, but is more
practical to maintain (although more complex to implement).&lt;/p&gt;
&lt;p&gt;I will detail my views on how to website-ize a wiki in future posts, but
for now, on 2020-07-26 at noon French time, let go live!&lt;/p&gt;
&lt;p&gt;Welcome to my (new) world!&lt;/p&gt;
&lt;p&gt;Colas.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Usability issues on a payment form</title>
        <published>2009-03-03T00:00:00+00:00</published>
        <updated>2009-03-03T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/usability-issues-on-a-payment-form/"/>
        <id>https://colas.nahaboo.net/blog/usability-issues-on-a-payment-form/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/usability-issues-on-a-payment-form/">&lt;p&gt;I know, I know, it is easy to nitpick on other people design, but this
morning I tried to pay my phone bill online, and... failed. I had to
retry many times, because I kept making mistakes on a simple,
run-of-the-mill form to enter my credit card data. I was dumbfounded.
How can, in 2009, people manage to make such a trivial and essential
part of online business go so wrong? As I guess you are as curious as
me, here is what I found out: (See the picture for the 3 main pain
points)&lt;/p&gt;
&lt;img src=&quot;telefact-bloopers.png&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1&lt;/strong&gt; the expiration date example is ambiguous: does &lt;code&gt;0903&lt;/code&gt; here means
2009-03 or 2003-09 ? Why didnt they disambiguate it by also adding a
specification (like MMYY, or, as it is a french dialog, MMAA ?)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2&lt;/strong&gt; but today, it was not only ambiguous but plainly misleading! As
we were in March 2009, I assumed that the example was built from the
current month and year. I said cool, so it is YYMM. Wrong! it was
actually the reverse, MMYY, resulting in one failed attempt&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3&lt;/strong&gt; ok, I filled the fields, and hit the OK button... and it just
reset the page to empty fields! Grumbling, I re-typed the fields,
re-clic, and... another blank page! After these two more failures, I
tried to think (which is what you &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/Don&amp;#x27;t_Make_Me_Think&quot;&gt;do not want your users to
do&lt;/a&gt;, trust me). I
then read the button label and discovered that this big, prominent
button alone at the bottom of the page was &lt;strong&gt;not the Submit button&lt;/strong&gt;
but the Cancel one. I should have hit the middle one &quot;Valider&quot;, but
this button was not in the correct place on the flow of the form.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So,after 3 failed attempts, I managed to pay my bill. All this on a
simple form with no fancy verification code or Captcha. Well done
Telefact, I wonder if you can find a worse example still in use today.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>My work on TWiki</title>
        <published>2008-12-20T00:00:00+00:00</published>
        <updated>2008-12-20T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/my-work-on-twiki/"/>
        <id>https://colas.nahaboo.net/code/my-work-on-twiki/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/my-work-on-twiki/">&lt;p&gt;I discovered the wiki concept in 2000, and quickly decided that this was
the future of the web site building tools, after many attempts at trying
various methods of Web development. Doing some research made me choose
the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://twiki.org&quot;&gt;TWiki engine&lt;/a&gt;, and since then I have been part of
the TWiki community, providing some work and enhancements on it. For
more detailed info, the best place is to go look at &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://twiki.org/cgi-bin/view/Main/ColasNahaboo&quot;&gt;my page on the
TWiki site&lt;/a&gt;, and my
&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://twiki.org/cgi-bin/view/Codev/PersonalRoadmapForColasNahaboo&quot;&gt;personal goals&lt;/a&gt;
for it.&lt;/p&gt;
&lt;p&gt;Of course, since the brutal TWiki fork, I have
folowed the whole TWiki community into the new great
&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://foswiki.org&quot;&gt;Foswiki&lt;/a&gt; wiki project.&lt;/p&gt;
&lt;p&gt;I was working on: (latest first)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TWiki &quot;koala&quot; pattern skin A system to
properly customize the pattern skin. This is the one I use on this
site&lt;/li&gt;
&lt;li&gt;Enhancements to the Vote Plugin&lt;/li&gt;
&lt;li&gt;TWiki misc enhancements for freetown&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>What is good code?</title>
        <published>2008-10-22T00:00:00+00:00</published>
        <updated>2008-10-22T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/what-is-good-code/"/>
        <id>https://colas.nahaboo.net/blog/what-is-good-code/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/what-is-good-code/">&lt;p&gt;I stumbled upon the blog post &quot;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://web.archive.org/web/20190630095624/https://weblogs.asp.net/fredriknormen/is-it-important-to-write-good-code&quot;&gt;Is it important to write good
code?&lt;/a&gt;&quot;
the other day, and became more and more ill at ease as I realized that I
thought that I preferred the original code, that the author was trying
to ridicule, over his new &quot;improved&quot; object-oriented version. At first I
guessed this was another manifestation of the the &quot;Worst is best&quot;
scenario - enhancements are often not worth the added complexity - but I
realized that it was perhaps a more profound factor:&lt;/p&gt;
&lt;p&gt;The original code is very good because it ... is &lt;strong&gt;small&lt;/strong&gt;. It fits on a
teminal screen, so an human being can &lt;strong&gt;read&lt;/strong&gt; it at once and have less
items to maintain in his &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_or_Minus_Two&quot;&gt;short term
memory&lt;/a&gt;
and also &lt;strong&gt;understand&lt;/strong&gt; it easily because it follows a natural way of
thinking with &lt;strong&gt;sentences using IF&lt;/strong&gt;. This becomes obvious by reading
the body of the blog post surrounding the code samples, where you can
see that the author is using phrases such as &quot;if I need this I do that&quot;,
showing that in plain english, the if statement is the best way to make
people understand what you mean. And making code that &lt;strong&gt;people&lt;/strong&gt;
understand is the best way to make debuggable and maintainable code.&lt;/p&gt;
&lt;p&gt;At this moment I noticed the citation in the blog header: &lt;em&gt;&quot;Good
programmers write code that humans can understand&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Indeed :-)&lt;/p&gt;
&lt;p&gt;PS: I know I am a bit exaggerating the issues there, and that I unfairly
nitpick on Fredrik Normé, but it is that it seems to me from my personal
experience that the two changes I see most in my coding efficiency as I
grow older is a decrease of my short term memory capacity, and that I
make more and more typos where I realize I mix up totally words with
totally different meanings but that sound the same, for instance writing
&quot;never&quot; instead of &quot;nether&quot;, making me suspect that our natural way of
thinking may be much more language-based that I imagined...&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Mercurial web templating</title>
        <published>2008-10-09T00:00:00+00:00</published>
        <updated>2008-10-09T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/mercurial-web-templating/"/>
        <id>https://colas.nahaboo.net/blog/mercurial-web-templating/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/mercurial-web-templating/">&lt;p&gt;For some time now, I have seen the light and I switched to the new wave
of the Distributed Source Control management systems. &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.youtube.com/watch?v=4XpnKHJAok8&quot;&gt;Linus famous
video&lt;/a&gt; of his Google talk
decided me to try. I was a bit apprehensive at first, wary of engaging
myself on a technology that would bring more problems than solutions,
but after some days of use, the realisation dawned over me: Distributed
Source Control may be one little step for a programmer, but it is a
giant step for programming. Why? because, its &lt;strong&gt;mental model actually
follows your first intuitions&lt;/strong&gt; that most of us developed as young
programmers before using any source control system. Suppose you want to
try a feature? instead of just copying the directory, you clone it. You
mess an operation? you just remove the directory, no embarrassing traces
left to keep the burning scar of shame on you for the following decades
of a central repository. You are used to think of your enhancements as
patches, you can work with patches. I could go on and on, but many
people have done it much better than me so I&#39;ll just say that you should
definitely try it.&lt;/p&gt;
&lt;p&gt;By the way, between the 3 main contenders, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;,
&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.mercurial-scm.org/&quot;&gt;mercurial&lt;/a&gt; (aka HG), and
&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/GNU_Bazaar&quot;&gt;bazaar&lt;/a&gt;, I chose mercurial because of its
simplicity, its better support of windows (I work in Linux since 1995
but my coworkers currently use Windows), and because some people at ILOG
started using git so I wanted to be able to try something else. I did
not choose bazaar as I wanted to stay close enough of git, to be able to
switch to it if ever my dream to work in a windows-free world
materializes one day...&lt;/p&gt;
&lt;p&gt;So I started to set up a public web repository of my Open Source personal
work with mercurial, which gave me a simple way to publish my work in
full detail, but, although mercurial is quite easy to use, but I had a
bit of trouble figuring out how to customize its look &amp;amp; feel.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hgweb Mercurial template customization</title>
        <published>2008-04-06T00:00:00+00:00</published>
        <updated>2008-04-06T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/hgweb-mercurial-template-customization/"/>
        <id>https://colas.nahaboo.net/code/hgweb-mercurial-template-customization/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/hgweb-mercurial-template-customization/">&lt;p&gt;If you use the &lt;code&gt;hgwebdir.cgi&lt;/code&gt; system to provide a web view of your mercurial repository, to customize the style like I did for (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://web.archive.org/web/20101113204327/https://hg.colas.nahaboo.net/&quot;&gt;archived view&lt;/a&gt;), a way to do it is (in debian, paths to adapt to your linux distribution):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;copy &lt;code&gt;/usr/share/mercurial/templates/gitweb&lt;/code&gt; somewhere among your hg repositories. make a hg repository out of it&lt;/li&gt;
&lt;li&gt;clone it to another one, e.g. &lt;code&gt;myhg&lt;/code&gt;. This way, when upgrades will change the distributed templates, you will just have to copy the new ones in your &lt;code&gt;gitweb&lt;/code&gt; repo, commit, and pull+merge the changes into your &lt;code&gt;myhg&lt;/code&gt; repo.&lt;/li&gt;
&lt;li&gt;edit the files in it: header.tmpl to add css code, etc. Adding links in the page headers will make you edit most of the files&lt;/li&gt;
&lt;li&gt;install by copying it to &lt;code&gt;/usr/share/mercurial/templates/myhg&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;edit your /etc/mercurial/hgrc to tell hgwebdir.cgi to use your new templates by a line &lt;code&gt;style=myhg&lt;/code&gt; . in your &lt;code&gt;[web]&lt;/code&gt; section. Here is my file as an exemple:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# system-wide mercurial configuration file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# See hgrc(5) for more information&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[trusted]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;users=colas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[web]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;baseurl=https&amp;amp;#58;//hg.colas.nahaboo.net/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;contact=colas@nahaboo.net&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;description=Open source software made by Colas Nahaboo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;maxchanges=16&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;staticurl=https&amp;amp;#58;//hg.colas.nahaboo.net/static/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;style=colas&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;The above will make these settings global to the site. but you can still override them on a per-repository basis by adding these lines into the &lt;code&gt;hgweb.config&lt;/code&gt; of the repositories.&lt;/li&gt;
&lt;li&gt;Note that you can use HTML code in the description field&lt;/li&gt;
&lt;li&gt;This &lt;code&gt;hgrc&lt;/code&gt; is not cloned by &lt;code&gt;hg&lt;/code&gt;. So you must add/edit it directly on the web repository, you cannot prepare it in advance in your work dir and expect it to be pushed on the web site on first creation&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The rebirth of this site</title>
        <published>2008-04-04T00:00:00+00:00</published>
        <updated>2008-04-04T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/the-rebirth-of-this-site/"/>
        <id>https://colas.nahaboo.net/blog/the-rebirth-of-this-site/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/the-rebirth-of-this-site/">&lt;p&gt;At last, I decided myself to redo my old website, which was a collection
of static html and was based on many of the various tools I made in
these past years. As I grew in love with wikis over these years, the
natural tought was just to do it in a Wiki, and naturally in the Wiki I
like the most, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://twiki.org&quot;&gt;TWiki&lt;/a&gt;. So bear with me and keep
posted, as this site will slowly emerge in this first half of 2008.&lt;/p&gt;
</content>
        
    </entry>
</feed>
