This feed does not validate.
... icon-32x32.png</icon><updated>2025-11-02</updated><generator uri="https: ...
^
<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom"><id>https://burgeonlab.com/</id><title>Burgeon Lab</title><subtitle>Full-text Atom feed of Burgeon Lab: A Tech Enthusiast’s Logbook</subtitle><icon>https://burgeonlab.com/favicon-32x32.png</icon><updated>2025-11-02</updated><generator uri="https://gohugo.io/" version="0.147.2">Hugo</generator><link rel="self" type="application/atom+xml" href="https://burgeonlab.com/feed.xml"/><link rel="related" href="https://burgeonlab.com/subscribe" title="Subscribe to BurgeonLab's Atom or RSS feeds"/><author><name>Naty S</name><email>naty@eclecticpassions.net</email></author><rights>Copyright © 2023-2025 Naty S.</rights><entry><id>https://burgeonlab.com/weeknotes/2025/w44/</id><link rel="alternate" href="https://burgeonlab.com/weeknotes/2025/w44/"/><title type="html">Weeknote 2025:W44 — Firsts, Fate &amp; Fresh Ideas</title><published>2025-11-02T23:36:28Z</published><updated>2025-11-02T23:36:28Z</updated><author><name>Naty S</name></author><summary type="html">First week note: optimized Hugo page speed, got flu shot, posted about coffee, explored Gopher. Links on Spotify, agentic AI, Google&amp;rsquo;s sideloading saga.</summary><content type="html"><![CDATA[<h2 id="hello-weeknotes">Hello, Weeknotes<a href="#hello-weeknotes" class="h-anchor" title="Permalink to #Hello, Weeknotes"></a></h2>   <p>After much delay and deliberation (with an extra dose of encouragement from <a href="https://joelchrono.xyz/" target="_blank" class="ext-link" rel="noopener noreferrer">@joel</a>), I’ve finally decided to put writing week notes to the top of my to-do list. And being a perfectionist, I “had to” get everything, bar the actual writing part, sorted before I could focus on writing (or should I say typing). So off I went and happily created a new Hugo <a href="https://gohugo.io/quick-reference/glossary/#section" target="_blank" class="ext-link" rel="noopener noreferrer">section</a> (page kind) called <code>weeknotes</code>, made a new dedicated <a href="/weeknotes/feed.xml">Atom feed</a>, archetype template, and list view template. I felt fulfilled by the new additions because this time, while working in Hugo, I actually understood why and how to do most of the things without relying <em>fully</em> on guides or forum posts.</p><p>In case you don’t know what <a href="https://indieweb.org/week_note" target="_blank" class="ext-link" rel="noopener noreferrer">week notes</a> are—they’re like a weekly, public dairy entry where one can share their ups and downs that week, what they perhaps learnt, completed, or anything really! From the examples in the IndieWeb community, weeknotes seems to be a fairly flexible, personal, and is a sort of rough-around-the-edges type of format. It is uncharted territory for me, and a bit uncomfortable I have to admit, as I don’t like the thought of “light-hearted” or “unserious” work. But this has been a conundrum for me since secondary school and into my working years—I’ve been told by many that I’m always <em>too serious</em>. Will writing slightly less-polished, more carefree posts help? I have no clue. We will see.</p><p>I have not decided on any framework or format yet (because that’s just another excuse to not write), so I’ll just try something generic for my first week and see how I get on. If you write weeknotes too, send them my way! Or try <a href="https://telegraph.p3k.io/send-a-webmention" target="_blank" class="ext-link" rel="noopener noreferrer">sending me a Webmention</a>.</p><hr><h2 id="completed">Completed<a href="#completed" class="h-anchor" title="Permalink to #Completed"></a></h2>   <ul><li>Got my flu shot (a recombinant trivalent one) and by some wicked fate, when I booked into a random clinic to get the shot, I was assigned to a GP that is the younger sister of my childhood doctor! I am not that good at maths, but the odds of that must be….! Just dumbfounded.<ul><li>Anyway, if you haven’t had your influenza jab yet this year, consider it (especially if you’re in the higher risk groups) because flu tends to peak around winter and your body needs at least two weeks to build up the protection after the jab. In HK, cases are still rising from the summer flu season and current activity levels suggest it may even overlap with the winter flu season causing more cases of infection.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup> Take care!</li></ul></li><li>Ticked off “start weeknotes” that’s been on my blogging to-do list for <strong>a long time</strong>.</li><li>Changed the canonical URL from www to non-www. I think it looks neater!</li><li>Optimized this site to get near full marks in <a href="https://pagespeed.web.dev" target="_blank" class="ext-link" rel="noopener noreferrer">PageSpeed</a>. <a href="https://www.debugbear.com/test/website-speed" target="_blank" class="ext-link" rel="noopener noreferrer">Debug Bear’s free website speed test</a> was indispensable.</li></ul><h2 id="upcoming">Upcoming<a href="#upcoming" class="h-anchor" title="Permalink to #Upcoming"></a></h2>   <ul><li>Lots of draft posts created this past week, hopefully I can get at least one out.</li><li>Will be publishing my first Git repo project and docs site soon, all firsts for me!</li><li>Currently trying out some <a href="https://3dkeycap.com/" target="_blank" class="ext-link" rel="noopener noreferrer">3D-printed keyboard stems/tilters</a> mimicking a keywell arc shape. Want to test it out in a budget friendly way before looking more into boards like the <a href="https://www.moergo.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Glove80</a> or the <a href="https://www.zsa.io/voyager" target="_blank" class="ext-link" rel="noopener noreferrer">ZSA Voyager</a>.</li></ul><h2 id="life">Life<a href="#life" class="h-anchor" title="Permalink to #Life"></a></h2>   <ul><li>I recognized a famous Hong Kong barrister while I was waiting for the underground (MTR). <a href="https://www.lokchambers.com/members/franco-kuan/" target="_blank" class="ext-link" rel="noopener noreferrer">Mr. Franco Kuan</a> was one of three barrister mentors in a <a href="https://zh.wikipedia.org/zh-hk/%e4%bb%a4%e4%ba%ba%e5%bf%83%e5%8a%a8%e7%9a%84offer_%28%e7%ac%ac%e5%85%ad%e5%ad%a3%29" target="_blank" class="ext-link" rel="noopener noreferrer">reality TV show (article only in Chinese)</a> recently. He is a crime specialist, ex-senior public prosecutor, and deputy magistrate. I asked for a photo and chatted with him briefly before the train came. He was awfully nice about it and was heading to a nearby university for his class. I enjoyed the show despite not knowing much about legal issues in general. There were some medical cases in there so that was nice.</li><li>Went to a local annual coffee festival event on Friday. It seems to get worse every year—the crowds were a deterrent (I hate crowds).</li><li>Posted <a href="https://www.instagram.com/eclecticpassions/p/DQeYbNUiRUe/" target="_blank" class="ext-link" rel="noopener noreferrer">two coffee-related posts</a> to Instagram this week. I’ve been very much absent from the platform for maybe 3–4 months, which has been great honestly. I only open the app once or twice a week for under 30 min (got the timer setting turned on, and it’s off in the work profile the rest of the time); but I miss messages with friends who are not available on other platforms, unfortunately.</li></ul><h2 id="links">Links<a href="#links" class="h-anchor" title="Permalink to #Links"></a></h2>   <p>Here’s some content I came across this week that were interesting:</p><ul><li>I’m not a fan of music streaming platforms, but now seems to be a good time and reason to leave Spotify for good.<ul><li><a href="https://indivisible.org/statements/indivisible-announces-campaign-cancel-spotify-streaming-services-over-ice-recruitment" target="_blank" class="ext-link" rel="noopener noreferrer">https://indivisible.org/statements/indivisible-announces-campaign-cancel-spotify-streaming-services-over-ice-recruitment</a></li></ul></li><li>It’s from March 2025, but privacy issues are only getting more pertinent and important.<ul><li><a href="https://techcrunch.com/2025/03/07/signal-president-meredith-whittaker-calls-out-agentic-ai-as-having-profound-security-and-privacy-issues/" target="_blank" class="ext-link" rel="noopener noreferrer">https://techcrunch.com/2025/03/07/signal-president-meredith-whittaker-calls-out-agentic-ai-as-having-profound-security-and-privacy-issues/</a></li></ul></li><li>Follow up post from F-Droid about Google’s Developer Program. I’m <a href="https://fosstodon.org/@eclecticpassions/115399007699977939" target="_blank" class="ext-link" rel="noopener noreferrer">really upset</a> about this issue.<ul><li><a href="https://f-droid.org/2025/10/28/sideloading.html" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/2025/10/28/sideloading.html</a></li></ul></li><li>I just discovered this alternative “Internet protocol” called <a href="https://en.wikipedia.org/wiki/Gopher_%28protocol%29" target="_blank" class="ext-link" rel="noopener noreferrer">Gopher</a>. It’s a text-based World Wide Web alternative, or should I say precursor. I checked it out with <a href="https://formulae.brew.sh/formula/lynx" target="_blank" class="ext-link" rel="noopener noreferrer">Lynx</a>. Looks super cool and still very active. It’s on my list of things to explore one day!<ul><li><a href="https://gopher.floodgap.com/gopher/gw.lite" target="_blank" class="ext-link" rel="noopener noreferrer">https://gopher.floodgap.com/gopher/gw.lite</a></li></ul></li></ul><p>Until next week!</p><div class="footnotes"><hr><ol><li id="fn:1"><p><a href="https://www.info.gov.hk/gia/general/202510/28/P2025102800744.htm" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.info.gov.hk/gia/general/202510/28/P2025102800744.htm</a> <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/github-style-heatmap-calendar-widget-visualizing-hugo/</id><link rel="alternate" href="https://burgeonlab.com/blog/github-style-heatmap-calendar-widget-visualizing-hugo/"/><title type="html">Add a GitHub Style Hugo Calendar Heatmap Widget</title><published>2025-10-12T10:20:00Z</published><updated>2025-10-12T10:20:00Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/github-style-heatmap-calendar-widget-visualizing-hugo/og_img_025.webp"/><summary type="html">Turn your Hugo blog posts into a GitHub-like contribution calendar with Apache ECharts. I&amp;rsquo;ll show you how to extract blog post metadata to build a heatmap calendar using the Open Source JS Library. Including how to integrate it with Hugo partials and CSS, full code snippets, and tips on how to customize styling, scaling, and tooltip behavior.</summary><content type="html"><![CDATA[            <p><i>[Note: This post contains Mermaid diagram(s) which do not work in RSS feed readers. Please <a href='https://burgeonlab.com/blog/github-style-heatmap-calendar-widget-visualizing-hugo/'>load this page</a> in a web browser to render content correctly, thank you!]</i></p><hr>            <h2 id="update">Update<a href="#update" class="h-anchor" title="Permalink to #Update"></a></h2>   <p>I realise this Hugo calendar heatmap could be a good opportunity for me to create my first public ‘project’ repository, making it easier for anyone to get the code or variations of it, especially if I make changes to the current version. I will update this paragraph when I have the Git repo up and running!</p><h2 id="introduction">Introduction<a href="#introduction" class="h-anchor" title="Permalink to #Introduction"></a></h2>   <p>In this post, I will go through how I added a <a href="https://burgeonlab.com/blog/#blog-calendar-heatmap">GitHub like contribution calendar</a> to my Hugo blog (it should work on other CMS).</p>                             <a href="/blog/github-style-heatmap-calendar-widget-visualizing-hugo/blog_calendar_heatmap.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/github-style-heatmap-calendar-widget-visualizing-hugo/blog_calendar_heatmap.webp" alt="Screenshot of the contribution github calendar heatmap for visualizing blogs posts over a year.">    </a>                                              <a href="/blog/github-style-heatmap-calendar-widget-visualizing-hugo/github_contribution.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/github-style-heatmap-calendar-widget-visualizing-hugo/github_contribution.webp" alt="Screenshot of the GitHub contribution calendar">    </a>                 <p>The JavaScript-based calendar displays the days a post is published and uses a heatmap (colour variance) to indicate the word count. It’s a nice visualization of the post frequency over a year. I originally wanted to use a lightweight JS library called <a href="https://github.com/williamtroup/Heat.js" target="_blank" class="ext-link" rel="noopener noreferrer">Heat.js</a> by <a href="https://www.william-troup.com/" target="_blank" class="ext-link" rel="noopener noreferrer">William Troup</a>, but I couldn’t get that working. After some researching, I found a Chinese blogger, <a href="https://douchi.space/@mtfront" target="_blank" class="ext-link" rel="noopener noreferrer">@mtfront@douchi.space</a>, creating something similar with EChart.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup> Inspired by them, I have decided to also use the open source <a href="https://echarts.apache.org/en/index.html" target="_blank" class="ext-link" rel="noopener noreferrer">Apache EChart</a> JS library to make a calendar heatmap visualization widget.</p> <blockquote>  <p><strong>DISCLAIMER</strong>: I’m a self-taught, hobbyist Hugo user! I might be using the wrong terminology or doing things inefficiently (hopefully not erroneous or unsafe). Please double-check before using my code.</p></blockquote> <h2 id="process-overview">Process Overview<a href="#process-overview" class="h-anchor" title="Permalink to #Process Overview"></a></h2>   <p>EChart supports many types of cool <a href="https://echarts.apache.org/examples/en/index.html" target="_blank" class="ext-link" rel="noopener noreferrer">charts</a> and a very thorough <a href="https://echarts.apache.org/handbook/en/get-started/" target="_blank" class="ext-link" rel="noopener noreferrer">Getting Started Guide</a> which I highly recommend reading. To replicate the look of the GitHub contribution calendar, I have chosen the EChart <a href="https://echarts.apache.org/examples/en/index.html#chart-type-calendar" target="_blank" class="ext-link" rel="noopener noreferrer">horizontal calendar</a> with heatmap functionality. Everything is customizable to your liking, so feel free to adjust my code to fit your design preferences!</p><p>Here is a Mermaid diagram to show the main rundown:</p><pre class="mermaid">%%{init: { 'themeCSS': '.node rect, .node circle, .node ellipse, .node polygon { stroke: #FFB800; stroke-width: 1px; rx: 5px; ry: 5px;}' }}%%flowchart TD    A["Load EChart JS library (deferred) on pages with Mermaid diagrams"] -->|Generate a <a href='https://echarts.apache.org/en/builder.html'>selective min.js</a> & serve locally| B[Generate client-side dataMap with Hugo post data]    B --> |Embedded as a JavaScript Map object named dataMap| C["Filtering the map by year and counting posts/yr"]    C --> |Reads config, builds the chart dynamically, showing current year as default| D["Adds interactive tooltips and clickable links to posts 🗓️"]</pre> <p>A <code>.JSON</code> with the necessary data is generated on Hugo build, filtering for <code>post type = post</code> (i.e. not pages):</p><ul><li>Published date</li><li>Word count</li><li>Permalink</li><li>Title</li></ul>                             <a href="/blog/github-style-heatmap-calendar-widget-visualizing-hugo/datamap_json.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/github-style-heatmap-calendar-widget-visualizing-hugo/datamap_json.webp" alt="Screenshot of the dataMap that is generated from eChart">    </a>                 <h2 id="step-1-create-hugo-partials">Step 1: Create Hugo Partials<a href="#step-1-create-hugo-partials" class="h-anchor" title="Permalink to #Step 1: Create Hugo Partials"></a></h2>   <h3 id="heatmaphtml">heatmap.html<a href="#heatmaphtml" class="h-anchor" title="Permalink to #heatmap.html"></a></h3>          <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html"><span class="line"><span class="ln">1</span><span class="cl"><span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"heatmap-wrapper"</span><span class="p">></span></span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"heatmap-year-buttons"</span><span class="p">></span> <span class="c"><!-- The year-buttons can be removed if you only wish to show a single year --></span></span></span><span class="line"><span class="ln">3</span><span class="cl">        <span class="p"><</span><span class="nt">button</span> <span class="na">onclick</span><span class="o">=</span><span class="s">"changeYear('2023')"</span><span class="p">></span>2023<span class="p"></</span><span class="nt">button</span><span class="p">></span></span></span><span class="line"><span class="ln">4</span><span class="cl">        <span class="p"><</span><span class="nt">button</span> <span class="na">onclick</span><span class="o">=</span><span class="s">"changeYear('2024')"</span><span class="p">></span>2024<span class="p"></</span><span class="nt">button</span><span class="p">></span></span></span><span class="line"><span class="ln">5</span><span class="cl">        <span class="p"><</span><span class="nt">button</span> <span class="na">onclick</span><span class="o">=</span><span class="s">"changeYear('2025')"</span><span class="p">></span>2025<span class="p"></</span><span class="nt">button</span><span class="p">></span></span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="p"></</span><span class="nt">div</span><span class="p">></span></span></span><span class="line"><span class="ln">7</span><span class="cl">    <span class="p"><</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">"heatmap"</span> <span class="na">class</span><span class="o">=</span><span class="s">"heatmap"</span><span class="p">></</span><span class="nt">div</span><span class="p">></span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p"></</span><span class="nt">div</span><span class="p">></span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h4 id="remarks">Remarks<a href="#remarks" class="h-anchor" title="Permalink to #Remarks"></a></h4>   <p><code>heatmap.html</code> is to create a container (<code>id="heatmap" class="heatmap"</code>) for the chart to be rendered into. I have set it to have the year buttons above the chart, but you can choose another way to display the years.</p><p>Use this partial on HTML pages where you want the chart to appear by using Hugo’s partial call:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">{{</span> <span class="nx">partial</span> <span class="s">"heatmap.html"</span> <span class="p">.</span> <span class="p">}}</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>For example, I used it on my <a href="https://burgeonlab.com/blog/">Post Archive</a> page template, <code>layouts/_default/list.html</code>, like this:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html"><span class="line"><span class="ln">1</span><span class="cl"><span class="p"><</span><span class="nt">h2</span><span class="p">></span>Blog Calendar Heatmap<span class="p"></</span><span class="nt">h2</span><span class="p">></span></span></span><span class="line"><span class="ln">2</span><span class="cl"> {{ partial "heatmap.html" . }}</span></span><span class="line"><span class="ln">3</span><span class="cl"> {{ partial "heatmap-data.html" . }}</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h3 id="heatmap-datahtml">heatmap-data.html<a href="#heatmap-datahtml" class="h-anchor" title="Permalink to #heatmap-data.html"></a></h3>   <p>The second partial is a bit more complex, and is where most of the customization resides (other than the CSS which I will go over later). I’ll try my best to explain what it does, after the code block:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html"><span class="line"><span class="ln">  1</span><span class="cl">{{ $pages := sort .Site.RegularPages "Date" }}</span></span><span class="line"><span class="ln">  2</span><span class="cl">{{ $latestDate := (index $pages (sub (len $pages) 1)).Date.Format "2006-01-02" }}</span></span><span class="line"><span class="ln">  3</span><span class="cl">{{ $earliestDate := "2023-09-01" }}</span></span><span class="line"><span class="ln">  4</span><span class="cl">{{ if and (eq .Kind "section") (eq .Section "posts") }} </span></span><span class="line"><span class="ln">  5</span><span class="cl"></span></span><span class="line"><span class="ln">  6</span><span class="cl"><span class="p"><</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">"{{ "</span><span class="err">/</span><span class="na">js</span><span class="err">/</span><span class="na">echarts</span><span class="err">.</span><span class="na">min</span><span class="err">.</span><span class="na">js</span><span class="err">"</span> <span class="err">|</span> <span class="na">relURL</span> <span class="err">}}"</span> <span class="na">defer</span><span class="p">></</span><span class="nt">script</span><span class="p">></span></span></span><span class="line"><span class="ln">  7</span><span class="cl"></span></span><span class="line"><span class="ln">  8</span><span class="cl">{{ $heatmapCSS := resources.Get "css/heatmap.css" | minify | fingerprint }}</span></span><span class="line"><span class="ln">  9</span><span class="cl"><span class="p"><</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span> <span class="na">href</span><span class="o">=</span><span class="s">"{{ $heatmapCSS.Permalink }}"</span> <span class="na">integrity</span><span class="o">=</span><span class="s">"{{ $heatmapCSS.Data.Integrity }}"</span><span class="p">></span></span></span><span class="line"><span class="ln"> 10</span><span class="cl"></span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="p"><</span><span class="nt">script</span><span class="p">></span></span></span><span class="line"><span class="ln"> 12</span><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">"DOMContentLoaded"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 13</span><span class="cl">  <span class="kd">var</span> <span class="nx">dataMap</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Map</span><span class="p">();</span></span></span><span class="line"><span class="ln"> 14</span><span class="cl">  <span class="p">{{</span> <span class="nx">range</span> <span class="p">.</span><span class="nx">Site</span><span class="p">.</span><span class="nx">RegularPages</span> <span class="p">}}</span> <span class="c1">// Works on regular content pages, i.e. not list pages, taxonomy, terms.</span></span></span><span class="line"><span class="ln"> 15</span><span class="cl"><span class="c1"></span>    <span class="p">{{</span> <span class="k">if</span> <span class="nx">eq</span> <span class="p">.</span><span class="nx">Type</span> <span class="s2">"post"</span> <span class="p">}}</span>  <span class="c1">// IMPORTANT: Only includes pages where Type equals "post". Your Type might be "posts" (pleural) or another term like "blog" or "blogs". If not set explicitly in the front matter of content, it will be the content's directory (folder) name.</span></span></span><span class="line"><span class="ln"> 16</span><span class="cl"><span class="c1"></span>      <span class="nx">dataMap</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="s2">"{{ .Date.Format "</span><span class="mi">2006</span><span class="o">-</span><span class="mi">01</span><span class="o">-</span><span class="mi">02</span><span class="s2">" }}"</span><span class="p">,</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 17</span><span class="cl">        <span class="nx">wordCount</span><span class="o">:</span> <span class="p">{{</span> <span class="p">.</span><span class="nx">WordCount</span> <span class="p">}},</span></span></span><span class="line"><span class="ln"> 18</span><span class="cl">        <span class="nx">link</span><span class="o">:</span> <span class="s2">"{{ .RelPermalink }}"</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 19</span><span class="cl">        <span class="nx">title</span><span class="o">:</span> <span class="s2">"{{ .Title | htmlEscape }}"</span></span></span><span class="line"><span class="ln"> 20</span><span class="cl">      <span class="p">});</span></span></span><span class="line"><span class="ln"> 21</span><span class="cl">    <span class="p">{{</span> <span class="nx">end</span> <span class="p">}}</span></span></span><span class="line"><span class="ln"> 22</span><span class="cl">  <span class="p">{{</span> <span class="nx">end</span> <span class="p">}}</span></span></span><span class="line"><span class="ln"> 23</span><span class="cl"></span></span><span class="line"><span class="ln"> 24</span><span class="cl">  <span class="kd">function</span> <span class="nx">filterDataByYear</span><span class="p">(</span><span class="nx">year</span><span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 25</span><span class="cl">    <span class="kd">var</span> <span class="nx">filtered</span> <span class="o">=</span> <span class="p">[];</span></span></span><span class="line"><span class="ln"> 26</span><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="p">[</span><span class="nx">date</span><span class="p">,</span> <span class="nx">value</span><span class="p">]</span> <span class="k">of</span> <span class="nx">dataMap</span><span class="p">.</span><span class="nx">entries</span><span class="p">())</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 27</span><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">date</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="nx">year</span><span class="p">))</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 28</span><span class="cl">        <span class="nx">filtered</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="nx">date</span><span class="p">,</span> <span class="nx">value</span><span class="p">.</span><span class="nx">wordCount</span><span class="p">]);</span></span></span><span class="line"><span class="ln"> 29</span><span class="cl">      <span class="p">}</span></span></span><span class="line"><span class="ln"> 30</span><span class="cl">    <span class="p">}</span></span></span><span class="line"><span class="ln"> 31</span><span class="cl">    <span class="k">return</span> <span class="nx">filtered</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 32</span><span class="cl">  <span class="p">}</span></span></span><span class="line"><span class="ln"> 33</span><span class="cl"></span></span><span class="line"><span class="ln"> 34</span><span class="cl">  <span class="c1">// Count the total number of posts per year</span></span></span><span class="line"><span class="ln"> 35</span><span class="cl"><span class="c1"></span>  <span class="kd">function</span> <span class="nx">getDotCount</span><span class="p">(</span><span class="nx">year</span><span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 36</span><span class="cl">    <span class="kd">let</span> <span class="nx">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 37</span><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="p">[</span><span class="nx">date</span><span class="p">]</span> <span class="k">of</span> <span class="nx">dataMap</span><span class="p">.</span><span class="nx">entries</span><span class="p">())</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 38</span><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">date</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="nx">year</span><span class="p">))</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 39</span><span class="cl">            <span class="nx">count</span><span class="o">++</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 40</span><span class="cl">        <span class="p">}</span></span></span><span class="line"><span class="ln"> 41</span><span class="cl">    <span class="p">}</span></span></span><span class="line"><span class="ln"> 42</span><span class="cl">    <span class="k">return</span> <span class="nx">count</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 43</span><span class="cl">    <span class="p">}</span></span></span><span class="line"><span class="ln"> 44</span><span class="cl"></span></span><span class="line"><span class="ln"> 45</span><span class="cl">  <span class="kd">var</span> <span class="nx">chartDom</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'heatmap'</span><span class="p">);</span></span></span><span class="line"><span class="ln"> 46</span><span class="cl"> <span class="kd">var</span> <span class="nx">myChart</span> <span class="o">=</span> <span class="nx">echarts</span><span class="p">.</span><span class="nx">init</span><span class="p">(</span><span class="nx">chartDom</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="p">{</span> <span class="nx">renderer</span><span class="o">:</span> <span class="s1">'canvas'</span> <span class="p">});</span> <span class="c1">// Renderer can = canvas or svg. Try both to see which works better for you</span></span></span><span class="line"><span class="ln"> 47</span><span class="cl"><span class="c1"></span></span></span><span class="line"><span class="ln"> 48</span><span class="cl">  <span class="c1">// Resize event listener</span></span></span><span class="line"><span class="ln"> 49</span><span class="cl"><span class="c1"></span>    <span class="kd">let</span> <span class="nx">resizeTimeout</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 50</span><span class="cl">    <span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'resize'</span><span class="p">,</span> <span class="p">()</span> <span class="p">=></span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 51</span><span class="cl">        <span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">resizeTimeout</span><span class="p">);</span></span></span><span class="line"><span class="ln"> 52</span><span class="cl">        <span class="nx">resizeTimeout</span> <span class="o">=</span> <span class="nx">setTimeout</span><span class="p">(()</span> <span class="p">=></span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 53</span><span class="cl">            <span class="nx">myChart</span><span class="p">.</span><span class="nx">resize</span><span class="p">();</span></span></span><span class="line"><span class="ln"> 54</span><span class="cl">        <span class="p">},</span> <span class="mi">100</span><span class="p">);</span> <span class="c1">// Wait 100ms after last resize event</span></span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="c1"></span>    <span class="p">});</span></span></span><span class="line"><span class="ln"> 56</span><span class="cl"></span></span><span class="line"><span class="ln"> 57</span><span class="cl">  <span class="kd">function</span> <span class="nx">createOption</span><span class="p">(</span><span class="nx">year</span><span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 58</span><span class="cl">    <span class="kr">const</span> <span class="nx">dotCount</span> <span class="o">=</span> <span class="nx">getDotCount</span><span class="p">(</span><span class="nx">year</span><span class="p">);</span></span></span><span class="line"><span class="ln"> 59</span><span class="cl">    <span class="k">return</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 60</span><span class="cl">    <span class="nx">textStyle</span><span class="o">:</span> <span class="p">{</span> <span class="c1">// Global text styles</span></span></span><span class="line"><span class="ln"> 61</span><span class="cl"><span class="c1"></span>       <span class="nx">fontFamily</span><span class="o">:</span> <span class="s1">'monospace'</span></span></span><span class="line"><span class="ln"> 62</span><span class="cl">    <span class="p">},</span></span></span><span class="line"><span class="ln"> 63</span><span class="cl">    <span class="nx">title</span><span class="o">:</span> <span class="p">[</span> </span></span><span class="line"><span class="ln"> 64</span><span class="cl">        <span class="p">{</span></span></span><span class="line"><span class="ln"> 65</span><span class="cl">        <span class="nx">top</span><span class="o">:</span> <span class="mi">8</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 66</span><span class="cl">        <span class="nx">show</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 67</span><span class="cl">        <span class="nx">text</span><span class="o">:</span> <span class="nx">dotCount</span> <span class="o">+</span> <span class="s1">' posts published in '</span> <span class="o">+</span> <span class="nx">year</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 68</span><span class="cl">        <span class="nx">left</span><span class="o">:</span> <span class="s1">'left'</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 69</span><span class="cl">        <span class="nx">padding</span><span class="o">:</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">20</span><span class="p">],</span></span></span><span class="line"><span class="ln"> 70</span><span class="cl">        <span class="nx">textStyle</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 71</span><span class="cl">            <span class="nx">color</span><span class="o">:</span> <span class="s1">'#DBDBDB'</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 72</span><span class="cl">            <span class="nx">fontSize</span><span class="o">:</span> <span class="mi">16</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 73</span><span class="cl">            <span class="nx">fontWeight</span><span class="o">:</span> <span class="mi">600</span></span></span><span class="line"><span class="ln"> 74</span><span class="cl">        <span class="p">}</span></span></span><span class="line"><span class="ln"> 75</span><span class="cl">    <span class="p">},</span></span></span><span class="line"><span class="ln"> 76</span><span class="cl">    <span class="p">{</span></span></span><span class="line"><span class="ln"> 77</span><span class="cl">        <span class="nx">text</span><span class="o">:</span> <span class="s1">'ⓘ Shows days with posts, color intensity\ncorrelates to word count. Scroll → on mobile.'</span><span class="p">,</span> <span class="c1">// Enter your own message or remove this extra description section</span></span></span><span class="line"><span class="ln"> 78</span><span class="cl"><span class="c1"></span>        <span class="nx">left</span><span class="o">:</span> <span class="s1">'left'</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 79</span><span class="cl">        <span class="nx">bottom</span><span class="o">:</span> <span class="s1">'bottom'</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 80</span><span class="cl">        <span class="nx">padding</span><span class="o">:</span> <span class="p">[</span><span class="mi">13</span><span class="p">,</span> <span class="mi">22</span><span class="p">],</span></span></span><span class="line"><span class="ln"> 81</span><span class="cl">        <span class="nx">textStyle</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 82</span><span class="cl">            <span class="nx">color</span><span class="o">:</span> <span class="s1">'#A1A1A1'</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 83</span><span class="cl">            <span class="nx">fontStyle</span><span class="o">:</span> <span class="s1">'italic'</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 84</span><span class="cl">            <span class="nx">fontSize</span><span class="o">:</span> <span class="mi">10</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 85</span><span class="cl">            <span class="nx">fontWeight</span><span class="o">:</span> <span class="mi">200</span></span></span><span class="line"><span class="ln"> 86</span><span class="cl">        <span class="p">}</span></span></span><span class="line"><span class="ln"> 87</span><span class="cl">    <span class="p">}</span></span></span><span class="line"><span class="ln"> 88</span><span class="cl"><span class="p">],</span></span></span><span class="line"><span class="ln"> 89</span><span class="cl">    <span class="nx">tooltip</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 90</span><span class="cl">      <span class="nx">formatter</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">p</span><span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 91</span><span class="cl">        <span class="kr">const</span> <span class="nx">post</span> <span class="o">=</span> <span class="nx">dataMap</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span></span></span><span class="line"><span class="ln"> 92</span><span class="cl">        <span class="kr">const</span> <span class="nx">formattedDate</span> <span class="o">=</span> <span class="nx">echarts</span><span class="p">.</span><span class="nx">format</span><span class="p">.</span><span class="nx">formatTime</span><span class="p">(</span><span class="s1">'MM/dd'</span><span class="p">,</span> <span class="nx">p</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span></span></span><span class="line"><span class="ln"> 93</span><span class="cl">        <span class="k">return</span> <span class="nx">formattedDate</span> <span class="o">+</span> <span class="s1">' | '</span> <span class="o">+</span> <span class="nx">post</span><span class="p">.</span><span class="nx">wordCount</span> <span class="o">+</span> <span class="s1">' words'</span> <span class="o">+</span> <span class="s1">'<br/> '</span> <span class="o">+</span> <span class="nx">post</span><span class="p">.</span><span class="nx">title</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 94</span><span class="cl">      <span class="p">},</span></span></span><span class="line"><span class="ln"> 95</span><span class="cl">        <span class="nx">show</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 96</span><span class="cl">        <span class="nx">confine</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 97</span><span class="cl">        <span class="nx">appendToBody</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 98</span><span class="cl">        <span class="nx">trigger</span><span class="o">:</span> <span class="s1">'item'</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 99</span><span class="cl">        <span class="nx">triggerOn</span><span class="o">:</span> <span class="s1">'mousemove|click'</span><span class="p">,</span></span></span><span class="line"><span class="ln">100</span><span class="cl">        <span class="nx">textStyle</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln">101</span><span class="cl">            <span class="nx">color</span><span class="o">:</span> <span class="s2">"#DBDBDB"</span><span class="p">,</span></span></span><span class="line"><span class="ln">102</span><span class="cl">            <span class="nx">fontWeight</span><span class="o">:</span> <span class="mi">400</span><span class="p">,</span></span></span><span class="line"><span class="ln">103</span><span class="cl">            <span class="nx">fontSize</span><span class="o">:</span> <span class="mi">13</span></span></span><span class="line"><span class="ln">104</span><span class="cl">        <span class="p">},</span></span></span><span class="line"><span class="ln">105</span><span class="cl">        <span class="nx">backgroundColor</span><span class="o">:</span> <span class="s2">"#303030"</span><span class="p">,</span></span></span><span class="line"><span class="ln">106</span><span class="cl">        <span class="nx">borderWidth</span><span class="o">:</span> <span class="s1">'1'</span><span class="p">,</span></span></span><span class="line"><span class="ln">107</span><span class="cl">        <span class="nx">padding</span><span class="o">:</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">6</span><span class="p">]</span></span></span><span class="line"><span class="ln">108</span><span class="cl">    <span class="p">},</span></span></span><span class="line"><span class="ln">109</span><span class="cl">    <span class="nx">visualMap</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln">110</span><span class="cl">        <span class="nx">min</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span></span></span><span class="line"><span class="ln">111</span><span class="cl">        <span class="nx">max</span><span class="o">:</span> <span class="mi">4000</span><span class="p">,</span> <span class="c1">// Adjust max word count to get the maximum gradient color variation, and according to your usual post length</span></span></span><span class="line"><span class="ln">112</span><span class="cl"><span class="c1"></span>        <span class="nx">splitNumber</span><span class="o">:</span> <span class="mi">4</span><span class="p">,</span> <span class="c1">// Each colour group is 1000 words apart (4000/4)</span></span></span><span class="line"><span class="ln">113</span><span class="cl"><span class="c1"></span>        <span class="nx">type</span><span class="o">:</span> <span class="s1">'piecewise'</span><span class="p">,</span></span></span><span class="line"><span class="ln">114</span><span class="cl">        <span class="nx">orient</span><span class="o">:</span> <span class="s1">'horizontal'</span><span class="p">,</span></span></span><span class="line"><span class="ln">115</span><span class="cl">        <span class="nx">left</span><span class="o">:</span> <span class="s1">'right'</span><span class="p">,</span></span></span><span class="line"><span class="ln">116</span><span class="cl">        <span class="nx">padding</span><span class="o">:</span> <span class="p">[</span><span class="mi">14</span><span class="p">,</span> <span class="mi">20</span><span class="p">],</span> </span></span><span class="line"><span class="ln">117</span><span class="cl">        <span class="nx">top</span><span class="o">:</span> <span class="s1">'bottom'</span><span class="p">,</span></span></span><span class="line"><span class="ln">118</span><span class="cl">        <span class="nx">inRange</span><span class="o">:</span> <span class="p">{</span>   </span></span><span class="line"><span class="ln">119</span><span class="cl">          <span class="nx">color</span><span class="o">:</span> <span class="p">[</span><span class="s1">'#FFB800'</span><span class="p">,</span> <span class="s1">'#E64814'</span><span class="p">]</span> <span class="c1">// Light to dark gradient</span></span></span><span class="line"><span class="ln">120</span><span class="cl"><span class="c1"></span>        <span class="p">},</span></span></span><span class="line"><span class="ln">121</span><span class="cl">        <span class="nx">text</span><span class="o">:</span> <span class="p">[</span><span class="s1">'Long'</span><span class="p">,</span> <span class="s1">'Short'</span><span class="p">],</span></span></span><span class="line"><span class="ln">122</span><span class="cl">        <span class="nx">textStyle</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln">123</span><span class="cl">            <span class="nx">color</span><span class="o">:</span> <span class="s1">'#A1A1A1'</span><span class="p">,</span></span></span><span class="line"><span class="ln">124</span><span class="cl">            <span class="nx">fontStyle</span><span class="o">:</span> <span class="s1">'italic'</span><span class="p">,</span></span></span><span class="line"><span class="ln">125</span><span class="cl">        <span class="p">},</span></span></span><span class="line"><span class="ln">126</span><span class="cl">        <span class="nx">fontWeight</span><span class="o">:</span> <span class="mi">400</span><span class="p">,</span></span></span><span class="line"><span class="ln">127</span><span class="cl">        <span class="nx">showLabel</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span></span></span><span class="line"><span class="ln">128</span><span class="cl">         <span class="nx">pieces</span><span class="o">:</span> <span class="p">[</span></span></span><span class="line"><span class="ln">129</span><span class="cl">            <span class="p">{</span> <span class="nx">min</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">max</span><span class="o">:</span> <span class="mi">999</span><span class="p">,</span> <span class="nx">label</span><span class="o">:</span> <span class="s1">'0-1k'</span> <span class="p">},</span></span></span><span class="line"><span class="ln">130</span><span class="cl">            <span class="p">{</span> <span class="nx">min</span><span class="o">:</span> <span class="mi">1000</span><span class="p">,</span> <span class="nx">max</span><span class="o">:</span> <span class="mi">1999</span><span class="p">,</span> <span class="nx">label</span><span class="o">:</span> <span class="s1">'1k-2k'</span> <span class="p">},</span></span></span><span class="line"><span class="ln">131</span><span class="cl">            <span class="p">{</span> <span class="nx">min</span><span class="o">:</span> <span class="mi">2000</span><span class="p">,</span> <span class="nx">max</span><span class="o">:</span> <span class="mi">2999</span><span class="p">,</span> <span class="nx">label</span><span class="o">:</span> <span class="s1">'2k-3k'</span> <span class="p">},</span></span></span><span class="line"><span class="ln">132</span><span class="cl">            <span class="p">{</span> <span class="nx">min</span><span class="o">:</span> <span class="mi">3000</span><span class="p">,</span> <span class="nx">max</span><span class="o">:</span> <span class="mi">99999</span><span class="p">,</span> <span class="nx">label</span><span class="o">:</span> <span class="s1">'3k+'</span> <span class="p">},</span></span></span><span class="line"><span class="ln">133</span><span class="cl">        <span class="p">],</span></span></span><span class="line"><span class="ln">134</span><span class="cl">        <span class="nx">itemGap</span><span class="o">:</span> <span class="mi">8</span><span class="p">,</span></span></span><span class="line"><span class="ln">135</span><span class="cl">    <span class="p">},</span></span></span><span class="line"><span class="ln">136</span><span class="cl">    <span class="nx">calendar</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln">137</span><span class="cl">        <span class="nx">top</span><span class="o">:</span> <span class="mi">70</span><span class="p">,</span></span></span><span class="line"><span class="ln">138</span><span class="cl">        <span class="nx">left</span><span class="o">:</span> <span class="mi">40</span><span class="p">,</span></span></span><span class="line"><span class="ln">139</span><span class="cl">        <span class="nx">bottom</span><span class="o">:</span> <span class="mi">45</span><span class="p">,</span> </span></span><span class="line"><span class="ln">140</span><span class="cl">        <span class="nx">right</span><span class="o">:</span> <span class="mi">10</span><span class="p">,</span></span></span><span class="line"><span class="ln">141</span><span class="cl">        <span class="nx">cellSize</span><span class="o">:</span> <span class="s1">'auto'</span><span class="p">,</span></span></span><span class="line"><span class="ln">142</span><span class="cl">        <span class="nx">orient</span><span class="o">:</span> <span class="s1">'horizontal'</span><span class="p">,</span></span></span><span class="line"><span class="ln">143</span><span class="cl">        <span class="nx">range</span><span class="o">:</span> <span class="nx">year</span><span class="p">,</span></span></span><span class="line"><span class="ln">144</span><span class="cl">        <span class="nx">itemStyle</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln">145</span><span class="cl">          <span class="nx">color</span><span class="o">:</span> <span class="s1">'#202020'</span><span class="p">,</span> <span class="c1">// Base grid color</span></span></span><span class="line"><span class="ln">146</span><span class="cl"><span class="c1"></span>          <span class="nx">borderWidth</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span></span></span><span class="line"><span class="ln">147</span><span class="cl">          <span class="nx">borderType</span><span class="o">:</span> <span class="s1">'solid'</span><span class="p">,</span></span></span><span class="line"><span class="ln">148</span><span class="cl">          <span class="nx">borderColor</span><span class="o">:</span> <span class="s1">'#1A1A1A'</span><span class="p">,</span> <span class="c1">// Grid lines</span></span></span><span class="line"><span class="ln">149</span><span class="cl"><span class="c1"></span>        <span class="p">},</span></span></span><span class="line"><span class="ln">150</span><span class="cl">        <span class="nx">yearLabel</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln">151</span><span class="cl">            <span class="nx">show</span><span class="o">:</span> <span class="kc">false</span> <span class="c1">// Included year in title</span></span></span><span class="line"><span class="ln">152</span><span class="cl"><span class="c1"></span>        <span class="p">},</span></span></span><span class="line"><span class="ln">153</span><span class="cl">        <span class="nx">splitLine</span><span class="o">:</span> <span class="p">{</span>  <span class="c1">// Month-splitting lines</span></span></span><span class="line"><span class="ln">154</span><span class="cl"><span class="c1"></span>          <span class="nx">show</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// Set to false for a more GitHub look</span></span></span><span class="line"><span class="ln">155</span><span class="cl"><span class="c1"></span>          <span class="nx">lineStyle</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln">156</span><span class="cl">            <span class="nx">color</span><span class="o">:</span> <span class="s2">"#A1A1A1"</span><span class="p">,</span></span></span><span class="line"><span class="ln">157</span><span class="cl">            <span class="nx">width</span><span class="o">:</span> <span class="mf">0.5</span><span class="p">,</span></span></span><span class="line"><span class="ln">158</span><span class="cl">            <span class="nx">type</span><span class="o">:</span> <span class="s1">'dotted'</span><span class="p">,</span></span></span><span class="line"><span class="ln">159</span><span class="cl">            <span class="nx">opacity</span><span class="o">:</span> <span class="mf">0.8</span></span></span><span class="line"><span class="ln">160</span><span class="cl">            <span class="p">}</span></span></span><span class="line"><span class="ln">161</span><span class="cl">        <span class="p">},</span></span></span><span class="line"><span class="ln">162</span><span class="cl">        <span class="nx">monthLabel</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln">163</span><span class="cl">            <span class="nx">show</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span></span></span><span class="line"><span class="ln">164</span><span class="cl">            <span class="nx">formatter</span><span class="o">:</span> <span class="s1">'{nameMap}'</span><span class="p">,</span></span></span><span class="line"><span class="ln">165</span><span class="cl">            <span class="nx">fontSize</span><span class="o">:</span> <span class="mi">12</span><span class="p">,</span></span></span><span class="line"><span class="ln">166</span><span class="cl">            <span class="nx">color</span><span class="o">:</span> <span class="s1">'#A1A1A1'</span><span class="p">,</span></span></span><span class="line"><span class="ln">167</span><span class="cl">            <span class="nx">fontWeight</span><span class="o">:</span> <span class="mi">400</span><span class="p">,</span></span></span><span class="line"><span class="ln">168</span><span class="cl">            <span class="nx">silent</span><span class="o">:</span> <span class="kc">true</span></span></span><span class="line"><span class="ln">169</span><span class="cl">        <span class="p">},</span></span></span><span class="line"><span class="ln">170</span><span class="cl">        <span class="nx">dayLabel</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln">171</span><span class="cl">          <span class="nx">show</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span></span></span><span class="line"><span class="ln">172</span><span class="cl">          <span class="nx">firstDay</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>  <span class="c1">// Monday start, or 0 for Sunday</span></span></span><span class="line"><span class="ln">173</span><span class="cl"><span class="c1"></span>          <span class="nx">color</span><span class="o">:</span> <span class="s1">'#A1A1A1'</span><span class="p">,</span></span></span><span class="line"><span class="ln">174</span><span class="cl">          <span class="nx">fontSize</span><span class="o">:</span> <span class="mi">10</span><span class="p">,</span></span></span><span class="line"><span class="ln">175</span><span class="cl">          <span class="nx">fontWeight</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span></span></span><span class="line"><span class="ln">176</span><span class="cl">          <span class="nx">silent</span><span class="o">:</span> <span class="kc">true</span></span></span><span class="line"><span class="ln">177</span><span class="cl">        <span class="p">}</span></span></span><span class="line"><span class="ln">178</span><span class="cl">    <span class="p">},</span></span></span><span class="line"><span class="ln">179</span><span class="cl">    <span class="nx">series</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln">180</span><span class="cl">        <span class="nx">type</span><span class="o">:</span> <span class="s1">'heatmap'</span><span class="p">,</span></span></span><span class="line"><span class="ln">181</span><span class="cl">        <span class="nx">coordinateSystem</span><span class="o">:</span> <span class="s1">'calendar'</span><span class="p">,</span></span></span><span class="line"><span class="ln">182</span><span class="cl">        <span class="nx">data</span><span class="o">:</span> <span class="nx">filterDataByYear</span><span class="p">(</span><span class="nx">year</span><span class="p">),</span></span></span><span class="line"><span class="ln">183</span><span class="cl">        <span class="nx">label</span><span class="o">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln">184</span><span class="cl">            <span class="nx">show</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span></span></span><span class="line"><span class="ln">185</span><span class="cl">        <span class="p">}</span></span></span><span class="line"><span class="ln">186</span><span class="cl">    <span class="p">}</span></span></span><span class="line"><span class="ln">187</span><span class="cl">    <span class="p">};</span></span></span><span class="line"><span class="ln">188</span><span class="cl">  <span class="p">}</span></span></span><span class="line"><span class="ln">189</span><span class="cl">    <span class="nb">window</span><span class="p">.</span><span class="nx">changeYear</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">year</span><span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln">190</span><span class="cl">        <span class="kd">let</span> <span class="nx">option</span> <span class="o">=</span> <span class="nx">createOption</span><span class="p">(</span><span class="nx">year</span><span class="p">);</span></span></span><span class="line"><span class="ln">191</span><span class="cl">        <span class="nx">myChart</span><span class="p">.</span><span class="nx">setOption</span><span class="p">(</span><span class="nx">option</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span></span></span><span class="line"><span class="ln">192</span><span class="cl">    <span class="p">}</span></span></span><span class="line"><span class="ln">193</span><span class="cl"></span></span><span class="line"><span class="ln">194</span><span class="cl">    <span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'resize'</span><span class="p">,</span> <span class="p">()</span> <span class="p">=></span> <span class="nx">myChart</span><span class="p">.</span><span class="nx">resize</span><span class="p">());</span> <span class="c1">// Resize listener</span></span></span><span class="line"><span class="ln">195</span><span class="cl"><span class="c1"></span></span></span><span class="line"><span class="ln">196</span><span class="cl">    <span class="nx">myChart</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">params</span><span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln">197</span><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">params</span><span class="p">.</span><span class="nx">componentType</span> <span class="o">===</span> <span class="s1">'series'</span><span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln">198</span><span class="cl">        <span class="kr">const</span> <span class="nx">post</span> <span class="o">=</span> <span class="nx">dataMap</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">params</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span></span></span><span class="line"><span class="ln">199</span><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">post</span><span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln">200</span><span class="cl">          <span class="kr">const</span> <span class="nx">link</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">origin</span> <span class="o">+</span> <span class="nx">post</span><span class="p">.</span><span class="nx">link</span><span class="p">;</span></span></span><span class="line"><span class="ln">201</span><span class="cl">          <span class="nb">window</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">link</span><span class="p">,</span> <span class="s1">'_blank'</span><span class="p">).</span><span class="nx">focus</span><span class="p">();</span></span></span><span class="line"><span class="ln">202</span><span class="cl">        <span class="p">}</span></span></span><span class="line"><span class="ln">203</span><span class="cl">      <span class="p">}</span></span></span><span class="line"><span class="ln">204</span><span class="cl">    <span class="p">});</span></span></span><span class="line"><span class="ln">205</span><span class="cl"></span></span><span class="line"><span class="ln">206</span><span class="cl">  <span class="c1">// Initialize with the current year with Hugo's now.Format or manually `changeYear('2020')`</span></span></span><span class="line"><span class="ln">207</span><span class="cl"><span class="c1"></span>  <span class="nx">changeYear</span><span class="p">(</span><span class="s1">'{{ now.Format "2006" }}'</span><span class="p">);</span></span></span><span class="line"><span class="ln">208</span><span class="cl">  <span class="p">});</span></span></span><span class="line"><span class="ln">209</span><span class="cl"></span></span><span class="line"><span class="ln">210</span><span class="cl"><span class="c1">// // Uncomment line below to access the JSON dataMap in browser console with`JSON.stringify([window.__dataMapSnapshot])`. Only works when in localhost mode or when the word debug is in the URL like `?debug`</span></span></span><span class="line"><span class="ln">211</span><span class="cl"><span class="c1">//   if (location.search.includes('debug') || location.hostname === 'localhost') {</span></span></span><span class="line"><span class="ln">212</span><span class="cl"><span class="c1">//   window.__dataMapSnapshot = Object.freeze(Object.fromEntries(dataMap));</span></span></span><span class="line"><span class="ln">213</span><span class="cl"><span class="c1">// }</span></span></span><span class="line"><span class="ln">214</span><span class="cl"><span class="c1"></span><span class="p"></</span><span class="nt">script</span><span class="p">></span></span></span><span class="line"><span class="ln">215</span><span class="cl">{{ end }}</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h4 id="remarks-1">Remarks<a href="#remarks-1" class="h-anchor" title="Permalink to #Remarks"></a></h4>   <ul><li>The first 4 lines are Hugo-specific template jargon. It ensures the script only generates data points from posts not pages and setting a hard-coded “beginning date”. Line 4 ensures the whole partial only runs on the post archive page.</li><li>Line 6 loads the ECharts JS library. Read <a href="https://burgeonlab.com/blog/github-style-heatmap-calendar-widget-visualizing-hugo/#step-2-importing-the-echart-javascript-library">step 2</a> for more info.</li><li>Line 8-9 adds the related CSS file for the chart.</li><li>The main script steps are:<ul><li>A dataMap using Hugo range loops is run on Hugo build, storing data for each post in JSON format (I never post more than once a day; I don’t think it will work with multiple posts per day).</li><li>The following two helper functions, <code>filterDataByYear</code> and <code>getDotCount</code> builds the array of the date + word count per year and counts the number of total days with posts in a year, respectively.</li><li>Line 45 -46 ensures an element called <code>id="heatmap"</code> exists on the page before the script runs. The renderer can be <a href="https://echarts.apache.org/handbook/en/best-practices/canvas-vs-svg/" target="_blank" class="ext-link" rel="noopener noreferrer">canvas or svg</a>, and as per the documentation, both have its merits. Try both to see which works better for you.</li><li>The resize handler was something I had to add because of some glitches when viewing the chart on mobile devices or narrow viewports.</li><li>Line 57-188 configures the chart’s visual. Read the <a href="https://echarts.apache.org/en/option.html#title" target="_blank" class="ext-link" rel="noopener noreferrer">chart config documentation</a> to see what you can change. I have used the following objects:<ul><li>title<ul><li>The title includes the <code>year</code> and <code>dotCount</code></li></ul></li><li>tooltip</li><li>visualMap<ul><li>I made <code>visualMap-piecewise. pieces</code> labels <code>false</code> because it looks cluttered with the actual group values.</li></ul></li><li>calendar</li><li>series</li></ul></li><li><code>changeYear</code> is set as a global function so that buttons can change the displayed year on the chart.</li><li><code>addEventListener('resize')</code> tells EChart to recalculate the chart layout when the window changes size.</li><li>Line 196-204 adds interactivity by making the day squares clickable, opening the relevant post on a new tab</li><li>Line 210-213 are for debugging, it creates a read-only, plain-object snapshot of the data while in <code>hugo server</code> mode.</li></ul></li></ul><h2 id="step-2-importing-the-echart-javascript-library">Step 2: Importing the EChart JavaScript Library<a href="#step-2-importing-the-echart-javascript-library" class="h-anchor" title="Permalink to #Step 2: Importing the EChart JavaScript Library"></a></h2>   <p>In order for the config in <code>heatmap-data.html</code> to be read and rendered into the heatmap calendar, we need to use the EChart JS Library. You can use it via a CDN (third-party reliance) or only generate the <a href="https://echarts.apache.org/handbook/en/basics/download#online-customization" target="_blank" class="ext-link" rel="noopener noreferrer">necessary code</a> needed for our GitHub contribution style calendar using the <a href="https://echarts.apache.org/en/builder.html" target="_blank" class="ext-link" rel="noopener noreferrer">Online EChart Builder</a>.</p><p>I have gone down the customized route as I prefer local dependencies. On the Online Builder page, tick the following components and then click download.</p><ul><li>Chart: Heatmap</li><li>Coordinate Systems: Calendar</li><li>Component: Title, Tooltip, VisualMap</li><li>Others: SVG Renderer (if you are using svg instead of canvas), Code Compression</li></ul><p>Place the generated <code>echarts.min.js</code> into your <code>static/js</code> folder. The JS file is linked at the beginning of the <code>heatmap-data.html</code> partial with:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html"><span class="line"><span class="ln">1</span><span class="cl"><span class="p"><</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">"{{ "</span><span class="err">/</span><span class="na">js</span><span class="err">/</span><span class="na">echarts</span><span class="err">.</span><span class="na">min</span><span class="err">.</span><span class="na">js</span><span class="err">"</span> <span class="err">|</span> <span class="na">relURL</span> <span class="err">}}"</span> <span class="na">defer</span><span class="p">></</span><span class="nt">script</span><span class="p">></span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>If you are using it from a <a href="https://echarts.apache.org/handbook/en/basics/download#use-from-cdn" target="_blank" class="ext-link" rel="noopener noreferrer">CDN</a> instead, choose a source, e.g., jsdelivr:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html"><span class="line"><span class="ln">1</span><span class="cl"><span class="p"><</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">" https://cdn.jsdelivr.net/npm/echarts@6.0.0/dist/echarts.min.js "</span><span class="p">></</span><span class="nt">script</span><span class="p">></span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h2 id="step-3-customize-appearance">Step 3: Customize Appearance<a href="#step-3-customize-appearance" class="h-anchor" title="Permalink to #Step 3: Customize Appearance"></a></h2>   <h3 id="heatmapcss">heatmap.css<a href="#heatmapcss" class="h-anchor" title="Permalink to #heatmap.css"></a></h3>          <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">.</span><span class="nc">heatmap-wrapper</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="k">margin</span><span class="p">:</span> <span class="mf">0.5</span><span class="kt">rem</span> <span class="mi">0</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="k">overflow-x</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span>  <span class="c">/* Scrollbar only when needed */</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="k">overflow-y</span><span class="p">:</span> <span class="kc">hidden</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="n">scrollbar-width</span><span class="p">:</span> <span class="kc">thin</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="n">scrollbar-color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="kc">yellow</span><span class="p">)</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">light</span><span class="o">-</span><span class="kc">grey</span><span class="p">);</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="k">padding</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="k">box-sizing</span><span class="p">:</span> <span class="kc">border-box</span><span class="p">;</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">.</span><span class="nc">heatmap</span> <span class="p">{</span></span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="k">width</span><span class="p">:</span> <span class="mi">48</span><span class="kt">em</span><span class="p">;</span>  <span class="c">/* Fixed width for mobile to prevent squashing */</span></span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="k">min-width</span><span class="p">:</span> <span class="mi">48</span><span class="kt">em</span><span class="p">;</span>  <span class="c">/* Ensure minimum width on mobile */</span></span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="k">height</span><span class="p">:</span> <span class="mi">210</span><span class="kt">px</span><span class="p">;</span></span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="k">box-sizing</span><span class="p">:</span> <span class="kc">border-box</span><span class="p">;</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="p">@</span><span class="k">media</span> <span class="nt">screen</span> <span class="nt">and</span> <span class="o">(</span><span class="nt">min-width</span><span class="o">:</span> <span class="nt">48em</span><span class="o">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="p">.</span><span class="nc">heatmap</span> <span class="p">{</span></span></span><span class="line"><span class="ln">19</span><span class="cl">      <span class="k">min-width</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="ln">20</span><span class="cl">      <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span> </span></span><span class="line"><span class="ln">21</span><span class="cl">      <span class="k">max-width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span> </span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="p">}</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="p">.</span><span class="nc">heatmap-year-buttons</span> <span class="p">{</span></span></span><span class="line"><span class="ln">25</span><span class="cl">  <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span></span></span><span class="line"><span class="ln">26</span><span class="cl">  <span class="k">display</span><span class="p">:</span> <span class="k">grid</span><span class="p">;</span></span></span><span class="line"><span class="ln">27</span><span class="cl">  <span class="k">box-sizing</span><span class="p">:</span> <span class="kc">border-box</span><span class="p">;</span></span></span><span class="line"><span class="ln">28</span><span class="cl">  <span class="k">gap</span><span class="p">:</span> <span class="mf">0.5</span><span class="kt">rem</span><span class="p">;</span></span></span><span class="line"><span class="ln">29</span><span class="cl">  <span class="k">margin</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span></span></span><span class="line"><span class="ln">30</span><span class="cl">  <span class="k">grid-auto-flow</span><span class="p">:</span> <span class="kc">column</span><span class="p">;</span> </span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="k">justify-content</span><span class="p">:</span> <span class="kc">inherit</span><span class="p">;</span></span></span><span class="line"><span class="ln">32</span><span class="cl">  <span class="k">direction</span><span class="p">:</span> <span class="kc">rtl</span><span class="p">;</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="p">.</span><span class="nc">heatmap-year-buttons</span> <span class="nt">button</span> <span class="p">{</span></span></span><span class="line"><span class="ln">35</span><span class="cl">  <span class="k">border-radius</span><span class="p">:</span> <span class="mf">0.5</span><span class="kt">rem</span><span class="p">;</span></span></span><span class="line"><span class="ln">36</span><span class="cl">  <span class="k">background-color</span><span class="p">:</span> <span class="mh">#141414</span><span class="p">;</span></span></span><span class="line"><span class="ln">37</span><span class="cl">  <span class="k">color</span><span class="p">:</span> <span class="mh">#DBDBDB</span><span class="p">;</span></span></span><span class="line"><span class="ln">38</span><span class="cl">  <span class="k">cursor</span><span class="p">:</span> <span class="kc">pointer</span><span class="p">;</span></span></span><span class="line"><span class="ln">39</span><span class="cl">  <span class="k">padding</span><span class="p">:</span> <span class="mf">0.5</span><span class="kt">rem</span> <span class="mi">0</span><span class="p">;</span></span></span><span class="line"><span class="ln">40</span><span class="cl">  <span class="k">margin</span><span class="p">:</span> <span class="mf">0.5</span><span class="kt">rem</span> <span class="mi">0</span><span class="p">;</span></span></span><span class="line"><span class="ln">41</span><span class="cl">  <span class="k">font-size</span><span class="p">:</span> <span class="mf">0.833</span><span class="kt">rem</span><span class="p">;</span></span></span><span class="line"><span class="ln">42</span><span class="cl">  <span class="k">font-family</span><span class="p">:</span> <span class="s2">"monaspacekrypton"</span><span class="p">,</span> <span class="kc">monospace</span><span class="p">;</span></span></span><span class="line"><span class="ln">43</span><span class="cl">  <span class="k">font-weight</span><span class="p">:</span> <span class="mi">600</span><span class="p">;</span></span></span><span class="line"><span class="ln">44</span><span class="cl">  <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="c">/* COLORS FOR LIGHT & DARK THEMES */</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="nt">html</span><span class="o">[</span><span class="nt">data-theme</span><span class="o">=</span><span class="s2">"dark"</span><span class="o">]</span> <span class="p">.</span><span class="nc">heatmap</span> <span class="p">{</span></span></span><span class="line"><span class="ln">48</span><span class="cl">  <span class="k">background-color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">bg</span><span class="o">-</span><span class="kc">color</span><span class="p">);</span></span></span><span class="line"><span class="ln">49</span><span class="cl">  <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">dotted</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="kc">yellow</span><span class="p">);</span></span></span><span class="line"><span class="ln">50</span><span class="cl">  <span class="k">border-radius</span><span class="p">:</span> <span class="mf">0.5</span><span class="kt">rem</span><span class="p">;</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="nt">html</span><span class="o">[</span><span class="nt">data-theme</span><span class="o">=</span><span class="s2">"dark"</span><span class="o">]</span> <span class="p">.</span><span class="nc">heatmap-year-buttons</span> <span class="nt">button</span> <span class="p">{</span></span></span><span class="line"><span class="ln">53</span><span class="cl">  <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#E64814</span><span class="p">;</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="nt">html</span><span class="o">[</span><span class="nt">data-theme</span><span class="o">=</span><span class="s2">"dark"</span><span class="o">]</span> <span class="p">.</span><span class="nc">heatmap-year-buttons</span> <span class="nt">button</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span></span></span><span class="line"><span class="ln">56</span><span class="cl">  <span class="k">border-color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="kc">red</span><span class="p">);</span></span></span><span class="line"><span class="ln">57</span><span class="cl">  <span class="k">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">button</span><span class="o">-</span><span class="n">state</span><span class="o">-</span><span class="kc">color</span><span class="p">);</span></span></span><span class="line"><span class="ln">58</span><span class="cl">  <span class="k">transition</span><span class="p">:</span> <span class="mf">0.2</span><span class="kt">s</span> <span class="kc">ease</span><span class="p">;</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="nt">html</span><span class="o">[</span><span class="nt">data-theme</span><span class="o">=</span><span class="s2">"dark"</span><span class="o">]</span> <span class="p">.</span><span class="nc">heatmap-wrapper</span> <span class="p">{</span></span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="n">scrollbar-color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="kc">yellow</span><span class="o">-</span><span class="n">opacity</span><span class="p">)</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">bg</span><span class="o">-</span><span class="kc">color</span><span class="p">);</span></span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="nt">html</span><span class="o">[</span><span class="nt">data-theme</span><span class="o">=</span><span class="s2">"light"</span><span class="o">]</span> <span class="p">.</span><span class="nc">heatmap</span> <span class="p">{</span></span></span><span class="line"><span class="ln">64</span><span class="cl">  <span class="k">background-color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">light</span><span class="o">-</span><span class="kc">grey</span><span class="p">);</span></span></span><span class="line"><span class="ln">65</span><span class="cl">  <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="kc">black</span><span class="p">);</span></span></span><span class="line"><span class="ln">66</span><span class="cl">  <span class="k">border-radius</span><span class="p">:</span> <span class="mf">0.5</span><span class="kt">rem</span><span class="p">;</span></span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="nt">html</span><span class="o">[</span><span class="nt">data-theme</span><span class="o">=</span><span class="s2">"light"</span><span class="o">]</span> <span class="p">.</span><span class="nc">heatmap-wrapper</span> <span class="p">{</span></span></span><span class="line"><span class="ln">69</span><span class="cl">  <span class="n">scrollbar-color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">special</span><span class="o">-</span><span class="n">bg</span><span class="p">)</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">bg</span><span class="o">-</span><span class="kc">color</span><span class="p">);</span></span></span><span class="line"><span class="ln">70</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">71</span><span class="cl"><span class="nt">html</span><span class="o">[</span><span class="nt">data-theme</span><span class="o">=</span><span class="s2">"light"</span><span class="o">]</span> <span class="p">.</span><span class="nc">heatmap-year-buttons</span> <span class="nt">button</span> <span class="p">{</span></span></span><span class="line"><span class="ln">72</span><span class="cl">  <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="kc">black</span><span class="p">);</span></span></span><span class="line"><span class="ln">73</span><span class="cl">  <span class="k">background-color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">light</span><span class="o">-</span><span class="kc">grey</span><span class="p">);</span></span></span><span class="line"><span class="ln">74</span><span class="cl">  <span class="k">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">dark</span><span class="o">-</span><span class="kc">grey</span><span class="p">);</span></span></span><span class="line"><span class="ln">75</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">76</span><span class="cl"><span class="nt">html</span><span class="o">[</span><span class="nt">data-theme</span><span class="o">=</span><span class="s2">"light"</span><span class="o">]</span> <span class="p">.</span><span class="nc">heatmap-year-buttons</span> <span class="nt">button</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span></span></span><span class="line"><span class="ln">77</span><span class="cl">  <span class="k">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">special</span><span class="o">-</span><span class="n">bg</span><span class="p">);</span></span></span><span class="line"><span class="ln">78</span><span class="cl">  <span class="k">transition</span><span class="p">:</span> <span class="mf">0.2</span><span class="kt">s</span> <span class="kc">ease</span><span class="p">;</span></span></span><span class="line"><span class="ln">79</span><span class="cl"><span class="p">}</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p><strong>Update</strong>: There’s a new <a href="https://echarts.apache.org/handbook/en/basics/release-note/v6-upgrade-guide/" target="_blank" class="ext-link" rel="noopener noreferrer">major upgrade for v6.0</a> that has new features like supporting dark mode and colour themes, so try to use the correct method from the documentation. If I get the time, I might investigate…</p><p>The final part of the equation is the CSS. Add it to your <code>assets/css</code> folder for <a href="https://gohugo.io/hugo-pipes/" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo Pipes</a> processing; otherwise, put it in <code>static/css</code>. I have tweaked it so it looks and interacts well on both mobile and desktop; but it looks only so-so on the light theme. Using CSS to change the colours for dark and light mode is not the best/correct implementation.</p><p>Because I made this calendar heatmap before the upgrade, my method is very makeshift—the text and most of the calendar parts have hard-coded colours in the config and doesn’t respect the current theme the site is set to. I managed to make a “light theme” version by using some CSS, but it’s not the right way to do it. If you have implemented colour themes / dark mode on your EChart v6.0+, please leave a comment to share how you did it!</p><p>The rest of the CSS should make the calendar responsive and look good on all devices, with a horizontal scroll bar appearing only when it becomes too narrow to show the full year. I hope it works for you too. Please leave a message if it’s not working as expected.</p><h2 id="limitations">Limitations<a href="#limitations" class="h-anchor" title="Permalink to #Limitations"></a></h2>   <p>There are two issues / concerns I could think of about my guide:</p><ul><li>Generating the JSON might not work well for blogs with a lot of posts—have to limit the date range I think</li><li>The colour themes are not integrated correctly as of the latest v6.0 update</li></ul><h2 id="conclusion">Conclusion<a href="#conclusion" class="h-anchor" title="Permalink to #Conclusion"></a></h2>   <p>I don’t know what it is about the GitHub calendar, but it was something I was always drawn to as a GitHub noob. But times have changed—I’m a SourceHut girl now! Although I  have kept my GitHub account for contributing to projects still on there; I wouldn’t consider using it to host my content. Now, I don’t have to feel too nostalgic about the <a href="https://burgeonlab.com/blog/migrate-github-pages-to-sourcehut-bunny/#finding-a-github-alternative">downfall of GitHub</a>!</p><p>Making my first calendar heatmap and visualizing blog data was a fun and somewhat educational project—the resulting interactive calendar looks really cool and functional too (in my opinion). Leave a message below or <a href="https://burgeonlab.com/contact/">contact</a> me if you have any issues setting yours up, I’ll try my best to help. And I’d really appreciate if anyone spots any issues with the code—will update it right away!</p><p>Please show some <a href="https://burgeonlab.com/support/">support</a> if you found my detailed guide useful; and I’d love to see your customizations!</p><div class="footnotes"><hr><ol><li id="fn:1"><p>They wrote a <a href="https://blog.douchi.space/hugo-blog-heatmap" target="_blank" class="ext-link" rel="noopener noreferrer">guide</a> on it, but I had difficulty understanding it as I don’t know any tech terms in Chinese, and sadly the translated version is a bit confusing. <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/subscribe/</id><link rel="alternate" href="https://burgeonlab.com/subscribe/"/><title type="html">Web Syndication: Subscribe to RSS or Atom Feed</title><published>2025-09-21T21:28:16Z</published><updated>2025-09-21T21:28:16Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/subscribe/og_img_subscribe.webp"/><summary type="html">Explaining basics of web syndication feeds for websites. Two feeds are available: a RSS summary feed and an Atom full-text feed. Fit your reading preferences and get BurgeonLab.com blog updates easily.</summary><content type="html"><![CDATA[<h2 id="tldr">TL;DR<a href="#tldr" class="h-anchor" title="Permalink to #TL;DR"></a></h2>   <p>Jump directly to my option of <a href="https://burgeonlab.com/subscribe/#two-feed-options">two feed links</a>. To learn about web feeds in general and how I use them, read on!</p><h2 id="what-is-web-syndication">What is Web Syndication<a href="#what-is-web-syndication" class="h-anchor" title="Permalink to #What is Web Syndication"></a></h2>   <p>Being able to consume Internet content using a feed reader (aka newsreader or news aggregator client) of your own choosing is a flexible and convenient way of reading digital media. Usually this consists of HTML content from blogs or websites, podcasts are also supported. It is an open standard and allows for people to “keep tabs on” blogs/sites without signing up for <em>another</em> email newsletter or entering centralized services.</p><h2 id="how-to-add-a-news-source">How to Add a News Source<a href="#how-to-add-a-news-source" class="h-anchor" title="Permalink to #How to Add a News Source"></a></h2>   <p>To add a source (be it a site you want to follow, a podcast you like to get updates on, etc) into your client so you can get notified when it publishes new content; you first have to locate the source’s feed URL. It can usually be found at the header or footer of a site with an orange   <span class="inline-icon"></span> logo or terms like “Feed”, “RSS”, “Subscribe”. If it is not obvious, I like to use a <a href="https://github.com/mratmeyer/rsslookup" target="_blank" class="ext-link" rel="noopener noreferrer">FOSS</a> tool called <a href="https://www.rsslookup.com/" target="_blank" class="ext-link" rel="noopener noreferrer">RSS Lookup</a> to find the feed of a site.</p><p>Once you’ve amassed a collection of sources, you can always export the <code>.OPML</code> <a href="https://en.wikipedia.org/wiki/OPML" target="_blank" class="ext-link" rel="noopener noreferrer">file</a> for backup or if you want to change clients. This is the list of all your web feeds.</p><h2 id="readers-i-personally-use">Readers I Personally Use<a href="#readers-i-personally-use" class="h-anchor" title="Permalink to #Readers I Personally Use"></a></h2>   <p>I mainly read feeds on my phone (Android) and not on my desktop (macOS), so my subscriptions are all on <a href="https://github.com/spacecowboy/Feeder" target="_blank" class="ext-link" rel="noopener noreferrer">Feeder</a>. I export the <code>.OPML</code> occasionally to update my desktop client—<a href="https://github.com/yang991178/fluent-reader" target="_blank" class="ext-link" rel="noopener noreferrer">Fluent Reader</a>. Unfortunately it’s deprecated (last update was October 2023), so I wouldn’t recommend it to someone who wants a macOS RSS reader. My recommendation would be <a href="https://netnewswire.com/" target="_blank" class="ext-link" rel="noopener noreferrer">NetNewsWire</a> which supports macOS and iOS.</p><p>If you’re into self-hosting, there’s quite a few good RSS readers/news aggregators you can consider the likes of <a href="https://github.com/miniflux/miniflux" target="_blank" class="ext-link" rel="noopener noreferrer">MiniFlux</a>, <a href="https://github.com/FreshRSS/FreshRSS" target="_blank" class="ext-link" rel="noopener noreferrer">FreshRSS</a> (which are the main two I keep seeing people recommend). I have MiniFlux on a Docker container on my Raspberry Pi 4B DietPi home server, but I never got round setting it up—which is why I’m still relying on single device reading with Feeder.</p><h2 id="feed-formats">Feed Formats<a href="#feed-formats" class="h-anchor" title="Permalink to #Feed Formats"></a></h2>   <p>The main format of web feeds are <code>.XML</code> based. <a href="https://en.wikipedia.org/wiki/Atom_%28web_standard%29" target="_blank" class="ext-link" rel="noopener noreferrer">Atom</a> and <a href="https://en.wikipedia.org/wiki/RSS" target="_blank" class="ext-link" rel="noopener noreferrer">RSS</a> formats are the most well known. Atom is the newer/younger one (released in 2005) compared to its rival, RSS, which was announced in 1999, 26 years ago. Both are widely supported with all clients, with RSS having a slight edge in compatibility with older/simpler readers.</p><p>As a consumer/reader of feeds, Atom feeds allows for more complex/richer content to be shown, as well as supporting updated timestamps on posts compared to RSS feeds. Whereas the advantages of subscribing to an RSS feed means compatibility is almost always at 100%, but that’s about it. It may have a simpler structure, but as a consumer, I think Atom feeds wins out.</p><h2 id="hugo-integration">Hugo Integration<a href="#hugo-integration" class="h-anchor" title="Permalink to #Hugo Integration"></a></h2>   <p>As a web dev’s standpoint, I think both has its pros and cons, which is why I decided to integrate both in my Hugo blog. RSS templates was already supported in my theme/Hugo’s backend, despite having validation issues. I actually wrote about <a href="https://burgeonlab.com/blog/about-rss-feeds/">fixing my theme’s RSS template</a> and making my first contributions to a GitHub project as a result! It was a very nice learning process, but I digress.</p><p>Hugo does not automatically output Atom feeds, so I managed to add my own custom configs and template to output a second feed with full-text instead of just summaries. I have to iron a few small issues still; like post bundle cover images not showing up properly (default blog cover image works). The main features of the Atom feed has been validated and is working well otherwise.</p><h2 id="two-feed-options">Two Feed Options<a href="#two-feed-options" class="h-anchor" title="Permalink to #Two Feed Options"></a></h2>   <p>On BurgeonLab.com, you have a choice of two feeds:</p><p><a href="/index.xml">  <span class="inline-icon"></span> <strong>1. RSS 2.0 Feed</strong> (Summary)</a></p><p>Quick overview of posts with a summary and the option to click through to read the full post on the website.</p><p><a href="/feed.xml">  <span class="inline-icon"></span> <strong>2. Atom Feed</strong> (Full-text)</a></p><p>Full-text feed allows you to read the whole post (text, images, code blocks) within the reader app, <strong>with the exception of</strong> posts with Mermaid diagrams which requires loading in a web browser to render content correctly.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup></p><h2 id="conclusion">Conclusion<a href="#conclusion" class="h-anchor" title="Permalink to #Conclusion"></a></h2>   <p>While feeds provide convenience to the reader, as someone who has put in <a href="https://burgeonlab.com/changelog/">time and effort</a> designing my blog to look “nice” (in my opinion) and work smoothly, I would of course appreciate it immensely if you visit and read my posts on the site directly! I hope offering a full-text feed won’t reduce my site’s visitor count as I do enjoy seeing which posts get more <a href="https://burgeonlab.com/colophon/#web-analytics">traffic</a>, which in turn gives me ideas as to what to write next; and also to rank my Top 5 section on the home page.</p><p>Thanks for reading and subscribing to my blog,</p><p>Naty</p><div class="footnotes"><hr><ol><li id="fn:1"><p>I have automated inserting a message at the top of the content to warn if a post has <code>mermaid = true</code> in the front matter. This message then contains a link to open the post in the browser for correct rendering. <em>(JavaScript components like Mermaid diagrams or ECharts (e.g. on my <a href="https://burgeonlab.com/blog/">Posts Archive page</a>) require a browser to work.)</em> <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/podcast-interview-about-being-a-blogger/</id><link rel="alternate" href="https://burgeonlab.com/blog/podcast-interview-about-being-a-blogger/"/><title type="html">Response to The Worst Community Report Podcast: Blogging</title><published>2025-09-19T22:21:43Z</published><updated>2025-09-19T22:21:43Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/podcast-interview-about-being-a-blogger/og_img_024.webp"/><summary type="html">This blog shares my personal reflections on key blogging topics inspired by &amp;lsquo;The Worst Community Report&amp;rsquo; podcast interview questions. I discuss my site&amp;rsquo;s name choice, writing motivations, social media habits, writing setup, and publishing process. I also explore ideas about smaller web communities and end with some recommendations just like in the podcast episode.</summary><content type="html"><![CDATA[            <p><i>[Note: This post contains Mermaid diagram(s) which do not work in RSS feed readers. Please <a href='https://burgeonlab.com/blog/podcast-interview-about-being-a-blogger/'>load this page</a> in a web browser to render content correctly, thank you!]</i></p><hr>            <h2 id="preface">Preface<a href="#preface" class="h-anchor" title="Permalink to #Preface"></a></h2>   <p>Recently, I listened to a podcast <a href="https://creators.spotify.com/pod/profile/brent-zeimen/episodes/Oh-No-Im-The-Old-Man-Again-Feat--Cassie--Joel--and-Cameron-e374bim" target="_blank" class="ext-link" rel="noopener noreferrer">episode “Oh No I’m The Old Man Again” (2025/08/21)</a> from <a href="https://anchor.fm/s/105981164/podcast/rss" target="_blank" class="ext-link" rel="noopener noreferrer">The Worst Community Report</a>, where @joelchrono was a guest speaker. It was thought-provoking and stimulating learning about other people’s experience with blogging. I thought I’d make a blog out of the interview questions that the host, <a href="https://tk-web.top/" target="_blank" class="ext-link" rel="noopener noreferrer">TK</a>, went over with <a href="https://joelchrono.xyz/" target="_blank" class="ext-link" rel="noopener noreferrer">Joel</a>, <a href="https://cassie.ink/" target="_blank" class="ext-link" rel="noopener noreferrer">Cassie</a>, and <a href="https://thingle.pika.page/" target="_blank" class="ext-link" rel="noopener noreferrer">Cameron</a>.</p><h2 id="interview-questions">Interview Questions<a href="#interview-questions" class="h-anchor" title="Permalink to #Interview Questions"></a></h2>   <h3 id="q1-why-did-you-pick-your-site-name-and-tell-us-a-bit-about-your-blog">Q1. Why did you pick your site name and tell us a bit about your blog?<a href="#q1-why-did-you-pick-your-site-name-and-tell-us-a-bit-about-your-blog" class="h-anchor" title="Permalink to #Q1. Why did you pick your site name and tell us a bit about your blog?"></a></h3>   <p>I think I have too many user handles and domain names—three in total because I originally thought it would be a good idea to have separate “online identities” for my main hobbies. I have one for photography (@APERTURE2IRIS), one for this tech-focused blog (@BurgeonLab), and lastly, a general username that arose from my Instagram account (@eclecticpassions). After hearing how Cassie likes buying domain names and has a few herself, I sort of feel “better” 😆.</p><p>My current writing endeavour is focused on <a href="https://burgeonlab.com" target="_blank" class="ext-link" rel="noopener noreferrer">https://burgeonlab.com</a>, which is my first attempt at static site generators (I chose <a href="https://gohugo.io/" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo</a> and I love it; glad TK and Cassie are also enjoying it). This is my most “successful” blog to date—with 24 blog posts so far as of September 2025. I was never able to keep up with posting consistently on any one platform<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup> in the past, so I hope this time the habit will stick; allowing me to become a “sustainable blogger”.</p><p>This static site came about after being inspired by the many cool personal sites and weblogs of Fedizens I discovered on my Mastodon instance (<a href="https://fosstodon.org/@fosstodon" target="_blank" class="ext-link" rel="noopener noreferrer">Fosstodon.org</a>). I had to learn quite a bit to get started as I was unfamiliar with even the basics of Git, online repositories, SSGs, CSS, hosting, CDN, page speed best practices, etc. But gosh, it was so worth it! I am really proud and happy with how this blog turned out and of all the skills I’ve accumulated from creating and maintaining this blog.</p><p>I still have <a href="https://eclecticpassions.net" target="_blank" class="ext-link" rel="noopener noreferrer">https://eclecticpassions.net</a> which is using WordPress.org (ugh I know). It sparks zero joy when using it, and if I do get the time one of these days, I’ll try to either convert that into a Hugo static site or use a licence I got of <a href="https://simplystatic.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Simply Static</a> I bought during a flash sale.</p><p>As for the last domain <a href="https://aperture2iris.com" target="_blank" class="ext-link" rel="noopener noreferrer">https://aperture2iris.com</a>, reserved for my photography hobby—it is still “under construction” because I have not sorted out my huge library of photos after abandoning the Adobe-subscription-bound-price-hiking ship that is Lightroom Classic. I’ve only ever used it for <strong>all</strong> my photographic experimentation (been a user since Lightroom version 3 when they were still being sold in retail boxes!) and it has been a bumpy ride to get used to alternatives… but that is for another post!</p><h3 id="q2-what-was-the-initial-impetus-or-event-that-triggered-you-to-start-writing-on-the-internet">Q2. What was the initial impetus or event that triggered you to start writing on the Internet?<a href="#q2-what-was-the-initial-impetus-or-event-that-triggered-you-to-start-writing-on-the-internet" class="h-anchor" title="Permalink to #Q2. What was the initial impetus or event that triggered you to start writing on the Internet?"></a></h3>   <p>Excluding my teenage experimentations, I would say the thing that triggered me to long-form writing is the character limit on Instagram. When I got into the fountain pen hobby in college, I created an IG account to share photos of my pens and stationery. I started building a small following which was quite exciting; but it wasn’t long before Meta acquired and ruined it with ads and making it a video-first platform. I started investigating blogging when I kept hitting the character limit; and at that time, I wasn’t aware of SSGs, which is why I went with self-hosted WordPress. The setup and learning process was a drag (I seem to have learnt how to become an amateur WebDev instead of a writer/blogger), and then life got in the way… So, I haven’t actually fulfilled my original hopes to write about fountain pens, and later, speciality coffee on my general WP blog.</p><p>Nowadays, I’m driven to write more routinely on this blog because I enjoy the process of creating in Markdown, using an IDE (VSCodium), and continuing to learn how to build and customize my site with Hugo. I absolutely adore <a href="https://www.markdownguide.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Markdown</a>—I’ve been using it many years long before this recent blogging craze. I highly recommend checking it out as it is much faster (<a href="https://burgeonlab.com/blog/retext-markdown-editor-how-to-install-python-apps-on-mac/#markdown-beginners">amongst other advantages of Markdown</a>) than using a word processor. This syntax language has really propelled me to using a tool like Hugo.</p><p>The place of most inspiration for me was actually on <a href="https://joinmastodon.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Mastodon</a>, where I have been lucky enough to meet genuinely nice, talented people who have motivated me to write and create a static personal site that is unique. <a href="https://burgeonlab.com/blog/hosting-static-sites-with-github-pages-or-bunny-net/">Recently</a>, I made a big change of moving this site from GitHub Pages’s “free hosting” to a paid (very budget friendly) static hosting on <a href="https://bunny.net?ref=k4vc3x5108" target="_blank" class="ext-link" rel="noopener noreferrer">Bunny.net</a> (affiliate link) because of the enshittification process happening at GitHub… I also stopped using GitHub repos (switched to <a href="https://sourcehut.org/" target="_blank" class="ext-link" rel="noopener noreferrer">SourceHut</a>) for my blog’s source code and deployment (CI/CD). It was something I would never have dreamed of accomplishing if you had asked blogging-newbie me three years ago—just goes to show what experimentation and self-learning can do!</p><p>The final trigger that makes me want to focus on putting content on my own site/s is because I’m done with my content being locked within walled-off gardens. I hope to one day gather all my online posts and have them all in one place—or at least in places that I have control over, where I could migrate to other services without losing my content.</p><h3 id="q3-do-you-use-any-of-the-other-modern-social-platforms-blueskythreadsinstagrametc">Q3. Do you use any of the other modern social platforms? (Bluesky/Threads/Instagram/etc)<a href="#q3-do-you-use-any-of-the-other-modern-social-platforms-blueskythreadsinstagrametc" class="h-anchor" title="Permalink to #Q3. Do you use any of the other modern social platforms? (Bluesky/Threads/Instagram/etc)"></a></h3>   <p>I never got into microblogging platforms until discovering Mastodon in mid-2023—so the whole Twitter/X debacle didn’t affect me. I tried out Bluesky after getting an invitation from a friend, but I didn’t like it for some reason. Maybe because it felt too similar to Twitter/X?  It felt like stepping into another centralized platform, despite its claims of the open-source framework, ATProto. I think in the end, I believe in <a href="https://activitypub.rocks/" target="_blank" class="ext-link" rel="noopener noreferrer">ActivityPub</a> more with its longer history and popularity.</p><p>I have been part of Reddit starting from 2012 (~8000karma, 400+ contributions) but I went cold turkey when my absolute favourite Android client, <a href="https://www.reddit.com/r/redditisfun/" target="_blank" class="ext-link" rel="noopener noreferrer">Reddit is Fun (RIF)</a>, died during the API changes in mid-2023 (which I realize coincides with me starting my journey on the Fediverse with Mastodon and Lemmy!). As for Discord… it is a weird one for me; I never clicked with it, and it’s doomed to be enshittified (as TK mentioned). Anyhow, it’s nice to hop on there occasionally to chat with strangers or get some support for apps/services that use that as their main channel.</p><p>Instagram was probably the first place I actually met most of my “online friends”. I connected with people who shared similar hobbies (stationery and coffee) and it was lovely at first: the growth and connecting with cool like-minded people. However, the app eventually became more and more obtrusive and infuriating to use, slowing down its growth to a grinding halt for my account (which was photography-based, not video). Maybe the algorithm hates me, but I have been stuck at 1555 followers for a long time (years) and I got fed up.</p><p>I actually did not install IG on my new Android phone in 2025. It was only within the last month when I installed it back as a Work app in the Android Work profile (via <a href="https://f-droid.org/en/packages/com.oasisfeng.island.fdroid/" target="_blank" class="ext-link" rel="noopener noreferrer">Insular</a> as recommended by Joel). I’m still thinking how I should proceed with it as I <strong>do not</strong> want any Meta apps on my main Android phone any more. I remember trying Vero for a while a few years ago, but I didn’t find any audience there (anyone using it?). Then there is PixelFed which is the most similar to IG, but I’ve yet to investigate… Lastly, I’m new to <a href="https://matrix.to/#/@burgeon:tchncs.de" target="_blank" class="ext-link" rel="noopener noreferrer">Matrix</a>. (Say hi and send me a message!)</p><h3 id="q4-what-is-your-writing-setup-like-what-process-do-you-go-through-to-get-a-post-published">Q4. What is your writing setup like? What process do you go through to get a post published?<a href="#q4-what-is-your-writing-setup-like-what-process-do-you-go-through-to-get-a-post-published" class="h-anchor" title="Permalink to #Q4. What is your writing setup like? What process do you go through to get a post published?"></a></h3>   <p>I usually write my posts on my M1 Mac Mini at home running <a href="https://vscodium.com/" target="_blank" class="ext-link" rel="noopener noreferrer">VSCodium</a>, with a few plugins to make it pleasing to the eye—like the <a href="https://github.com/rainglow/vscode" target="_blank" class="ext-link" rel="noopener noreferrer">Rainglow colour themes</a> and add extra functionality like syntax highlighting and spell check (my full list of extensions can be found in the <a href="https://burgeonlab.com/colophon/#about-this-site">/colophon</a>). Making my own <a href="https://gohugo.io/content-management/archetypes/" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo archetypes</a> (Hugo’s nomenclature for new content templates) was super handy when creating a new post or page, because the front matter is ready to be filled (I fill it usually at the end, right before publishing), with some metadata pre-filled/automatically generated on creation.</p><p>I rely heavily on <a href="https://espanso.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Espanso</a>, a privacy-first, FOSS text expander when writing in Hugo on macOS. For example, to add an image using Hugo’s shortcode system: <code>{{<img src="image-in-post-bundle.webp" alt="ALTTEXT" caption="CAPTION">}}</code>. By setting a trigger on Espanso, I just have to type <code>:hugoimg</code> to expand to the full shortcode. <strong>This has been a life changer for me.</strong></p><p>Here are the Espanso triggers I am using,<sup id="fnref:2"><a href="#fn:2" class="footnote-ref">2</a></sup> located at <code>/Users/username/Library/Application Support/espanso/match/base.yml</code>:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="w">  </span>- <span class="nt">trigger</span><span class="p">:</span><span class="w"> </span><span class="s2">":hugoimg"</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="nt">replace</span><span class="p">:</span><span class="w"> </span><span class="s2">"{{<img src=\"TITLE.webp\" alt=\"ALTTEXT\" caption=\"CAPTION\">}}"</span><span class="w"></span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="nt">force_clipboard</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"></span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span>- <span class="nt">trigger</span><span class="p">:</span><span class="w"> </span><span class="s2">":hugopost"</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">replace</span><span class="p">:</span><span class="w"> </span><span class="s2">"hugo new content posts/2025/02x-name/index.md"</span><span class="w"></span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">force_clipboard</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"></span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">  </span>- <span class="nt">trigger</span><span class="p">:</span><span class="w"> </span><span class="s2">":hugopage"</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="nt">replace</span><span class="p">:</span><span class="w"> </span><span class="s2">"hugo new content pages/page_name/index.md"</span><span class="w"></span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="nt">force_clipboard</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"></span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">  </span>- <span class="nt">trigger</span><span class="p">:</span><span class="w"> </span><span class="s2">":hugoserver"</span><span class="w"> </span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="nt">replace</span><span class="p">:</span><span class="w"> </span><span class="s2">"hugo server --gc --cleanDestinationDir -D --bind 192.168.1.123 --baseURL http://192.168.1.123"</span><span class="w"></span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="nt">force_clipboard</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"></span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">  </span>- <span class="nt">trigger</span><span class="p">:</span><span class="w"> </span><span class="s2">":hugoicon"</span><span class="w"> </span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="nt">replace</span><span class="p">:</span><span class="w"> </span><span class="s2">"{{< icon \"icon_name\" >}}"</span><span class="w"></span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="nt">force_clipboard</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"></span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">  </span>- <span class="nt">trigger</span><span class="p">:</span><span class="w"> </span><span class="s2">":hugolink"</span><span class="w"></span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="nt">replace</span><span class="p">:</span><span class="w"> </span><span class="s2">"({{< ref \"/posts/2025/0x_title/index.md\">}})"</span><span class="w"></span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="nt">force_clipboard</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>Sometimes, I write drafts on my Android phone when I’m out and about. This is done with <a href="https://github.com/massivemadness/Squircle-CE" target="_blank" class="ext-link" rel="noopener noreferrer">Squircle CE</a> paired with <a href="https://github.com/ViscousPot/GitSync" target="_blank" class="ext-link" rel="noopener noreferrer">GitSync</a>. Originally, I used <a href="https://github.com/catpuppyapp/PuppyGit" target="_blank" class="ext-link" rel="noopener noreferrer">PuppyGit</a> as my Git client on Android, but unfortunately it doesn’t work with SourceHut’s git repos (after I <a href="https://burgeonlab.com/blog/migrate-github-pages-to-sourcehut-bunny/">migrated my blog’s source code and hosting off GitHub</a>). Squircle CE does provide Git syncing functions—but only after the repo has been cloned <em>manually</em> onto the device. This app duo has been great and makes my Hugo blog very portable as changes can be made even when I’m not at my desk.</p><p>My last steps before publishing looks like this:</p><pre class="mermaid">%%{init: { 'themeCSS': '.node rect, .node circle, .node ellipse, .node polygon { stroke: #FFB800; stroke-width: 1px; rx: 5px; ry: 5px;}' }}%%flowchart TDA["Final manual proofread in Hugo server mode"] --> F["Complete post/SEO metadata, create an Open Graph image"] --> B["Change draft=false", git push for deployment on SourceHut Builds] --> C["Write accompanying Mastodon post and paste in URL"] --> D["Copy Mastodon's postID, add to front matter to enable Mastodon comments, git push a second time"] --> E["Completed post! ✅"] style E fill:#FFB800, color:#000000</pre> <p>Like Cassie and TK, I too use <a href="https://obsidian.md/" target="_blank" class="ext-link" rel="noopener noreferrer">Obsidian</a>—but instead of using it directly in my blogging workflow, it’s my PKM (personal knowledge management) app. I don’t write my blogs in there, but I do have ongoing notes about the technical/web development aspects of using Hugo and WordPress, and how-tos for my own reference. I also keep my blog to-do and post ideas in Obsidian with the help of the very powerful <a href="https://github.com/blacksmithgu/obsidian-dataview" target="_blank" class="ext-link" rel="noopener noreferrer">Dataview plugin</a>. And finally, although I haven’t bought any new fountain pens for many years, as a fountain pen ex-fanatic and stationery lover, I do enjoy occasionally scribbling on paper my ideas and rough plans for my blog posts.</p><h3 id="q5-re-smaller-web-and-community">Q5. Re: Smaller web and community<a href="#q5-re-smaller-web-and-community" class="h-anchor" title="Permalink to #Q5. Re: Smaller web and community"></a></h3>   <p>Ever since learning about concept of IndieWeb and the Small Web in 2025, I have been endlessly fascinated by the amount of unique and interesting content put out by genuine people (my RSS reader is way more varied now with less “commercial” content). I really feel the Internet is no longer the place it used to be; I started feeling the toxic nature and effects of social media on mental health, seeing the extreme polarization of views online is quite scary, and the countless online trolls with ruthless and obnoxious comments makes me feel rather sad about the person on the other side of the computer. Not to mention the privacy-attacking, all-encompassing aims of Big Tech companies who want you addicted on their apps. It’s really a drain physically and mentally—I am glad I did my research and equipped myself with the knowledge and have adjusted my habits accordingly.</p><p>Using Mastodon was a breath of fresh air to say the least. I hope I continue to connect with cool people and help promote, in my own small way, owning our own content and not falling victim of Big Tech’s agenda.</p><h3 id="q6-share-some-recommendations">Q6. Share some recommendations?<a href="#q6-share-some-recommendations" class="h-anchor" title="Permalink to #Q6. Share some recommendations?"></a></h3>   <h4 id="maps">Maps<a href="#maps" class="h-anchor" title="Permalink to #Maps"></a></h4>   <p>I don’t have Google Maps installed on my phone any more. This means I am relying on <a href="https://www.openstreetmap.org/#map=11/22.3733/114.0827" target="_blank" class="ext-link" rel="noopener noreferrer">OpenStreetMap</a> now via Organic Maps on Android. It has been fun contributing to my local area, and I hope it will become a viable alternative for where I’m based. I also use <a href="https://f-droid.org/en/packages/page.ooooo.geoshare/" target="_blank" class="ext-link" rel="noopener noreferrer">Geo Share</a> for opening Google / Apple Map links in Organic Maps instead.</p><p>Related to maps, a little while back, I exported all my tracked GPS data like walks/hikes over the years using different tracking services or devices, and also my cycling routes when I used to cycle in Ireland. I discovered a neat site to view all this <code>.GPX</code> data called <a href="https://gpx.studio/" target="_blank" class="ext-link" rel="noopener noreferrer">gpx.studio</a>. On the Mac, there is <a href="https://sourceforge.net/projects/gpxsee/" target="_blank" class="ext-link" rel="noopener noreferrer">GPXSee</a> which is a viewer that also supports <code>.KML</code> and <code>.TCX</code>, which were the other formats that was exported.</p><h4 id="blog-features">Blog Features<a href="#blog-features" class="h-anchor" title="Permalink to #Blog Features"></a></h4>                                <a href="/blog/podcast-interview-about-being-a-blogger/burgeonlab-heatmap.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/podcast-interview-about-being-a-blogger/burgeonlab-heatmap.webp" alt="Screenshot of a GitHub style contribution heatmap calendar but for posts based on word count/length of post on a Hugo blog.">    </a>       <p>Early September, after my 10+ day holiday in Bangkok, I returned home to feel a bit disconnected with my Hugo blog… So I decided to tackle a featured I always wanted: a GitHub style contribution heatmap calendar but for posts based on word count/length of post. After testing two different heatmap JavaScript libraries I found the one, <a href="https://echarts.apache.org/" target="_blank" class="ext-link" rel="noopener noreferrer">ECharts</a>, that does what I want! It is the next post I’m writing, so keep an eye for that if you’re interested in adding one to your Hugo blog! If you want a working preview—see it in action on the <a href="https://burgeonlab.com/blog/">Posts Archive</a> page.</p><p>I also added a new <a href="https://burgeonlab.com/contact/">/contact</a> page with a contact form! This should be handy and fast if someone wants to reach out without the fuss. I’ve been using <a href="https://formspark.io/" target="_blank" class="ext-link" rel="noopener noreferrer">FormSpark</a> for my other blogs too, and they’ve been simple and good! (Not sponsored, just a happy customer!) This time instead of using <a href="https://www.cloudflare.com/en-gb/application-services/products/turnstile/" target="_blank" class="ext-link" rel="noopener noreferrer">Cloudflare’s Turnstile</a> spam bot protection, I opted to try <a href="https://botpoison.com/" target="_blank" class="ext-link" rel="noopener noreferrer">BotPoison</a> alongside the form. I think it is a very streamlined service, EU-based (Belgium), and super easy to set up! I’ll likely write a post too on this soon.</p><p>Lastly, I updated my navigation bar with a CSS-only dropdown menu! It uses a hidden checkbox to activate. I’m considering making a hamburger menu for the mobile view but I haven’t had the time yet. The wrapped version of the header will do for now on mobile. If you have any feedback on it, I’m all ears!</p><h4 id="f1-fantasy">F1 Fantasy<a href="#f1-fantasy" class="h-anchor" title="Permalink to #F1 Fantasy"></a></h4>   <p>As some of you may know from my Mastodon feed, I’m into <a href="https://fantasy.formula1.com/en/how-to-play" target="_blank" class="ext-link" rel="noopener noreferrer">F1 Fantasy</a> for the past three years; but I have been a F1 fan since 2010s! There is a <a href="https://fantasy.formula1.com/en/leagues/join/P3DUAWMCY10" target="_blank" class="ext-link" rel="noopener noreferrer">FediF1 League</a> run by <a href="https://fosstodon.org/@rob@rstokes.uk" target="_blank" class="ext-link" rel="noopener noreferrer">Rob Stokes</a>. Highly recommend you to join in—the more, the merrier! I’m quite a competitive soul and I love a good game. However, I think it’s a bit of a miracle that I’m at the top of the “Fedi F1” and “Hong Kong F1 Fantasy” Leagues’ leaderboard after 16 Grands Prix (my team name is <strong>TifosiTime_HKG</strong>).</p>                             <a href="/blog/podcast-interview-about-being-a-blogger/f1fantasy-rank.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/podcast-interview-about-being-a-blogger/f1fantasy-rank.webp" alt="Screenshot of TifosiTime_HKG team 1 rankings in public leagues.">    </a>                 <p>With only eight races to go this season and the constructor’s championship near decided, the excitement for me personally has gone down slightly. The driver’s championship though! Let’s hope there is a few more action-packed weekends. If you ever want to start a H2H (Head 2 Head Private) League with me and have some fun, reach out!</p><div class="footnotes"><hr><ol><li id="fn:1"><p>I’ve used the likes of Xanga, LiveJournal, WordPress.com, Tumblr. None of them lasted and I wish I knew about Markdown and SSGs when I was younger… <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li><li id="fn:2"><p>There is a <a href="https://github.com/espanso/espanso/issues/675" target="_blank" class="ext-link" rel="noopener noreferrer">bug</a> with VSCodium and Espanso, which is why all the triggers used inside VSCodium has an extra line <code>force_clipboard: true</code>. Also, all quote marks (") needs to be escaped (<code>\</code> backslash) for it to work. Try using a yaml validator to double-check if it’s not working. <a href="#fnref:2" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/multi-device-blogging-with-hugo/</id><link rel="alternate" href="https://burgeonlab.com/blog/multi-device-blogging-with-hugo/"/><title type="html">Quick Setup: Second Device (Linux) For Blogging With Hugo</title><published>2025-08-21T17:48:00Z</published><updated>2025-08-21T17:48:00Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/multi-device-blogging-with-hugo/og_img_023.webp"/><summary type="html">A quick rundown of setting up a multi-device Hugo workflow, and accessing your static site files on three different systems (macOS, Android, Linux). I go through some practical tips, app recommendations, and config/commands to make cross-device blogging straightforward.</summary><content type="html"><![CDATA[<h2 id="multi-device-blogging-workflow">Multi-Device Blogging Workflow<a href="#multi-device-blogging-workflow" class="h-anchor" title="Permalink to #Multi-Device Blogging Workflow"></a></h2>   <p>I like and appreciate being able to access my <a href="https://gohugo.io/" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo</a> blog from multiple devices. I mainly write on my Mac Mini; but have recently overhauled my Android workflow; plus I configured my less-used MacBook Air 13" (2012) running on Linux Mint to be able to access and deploy this site. Will try to keep this one short and sweet! 😉</p><h3 id="android-setup">Android Setup<a href="#android-setup" class="h-anchor" title="Permalink to #Android Setup"></a></h3>   <h4 id="git-client">Git Client<a href="#git-client" class="h-anchor" title="Permalink to #Git Client"></a></h4>   <p>I originally used <a href="https://github.com/catpuppyapp/PuppyGit" target="_blank" class="ext-link" rel="noopener noreferrer">PuppyGit</a>, but unfortunately it doesn’t work with SourceHut’s git repo SSH clone URL format <code>git@git.sr.ht:~username/repo_name</code>. So I had to switch to <a href="https://github.com/ViscousPot/GitSync" target="_blank" class="ext-link" rel="noopener noreferrer">GitSync</a> which also works great and supports sr.ht. (I <a href="https://burgeonlab.com/blog/hosting-static-sites-with-github-pages-or-bunny-net/">moved</a> my Hugo blog source code from <a href="https://burgeonlab.com/blog/migrate-github-pages-to-sourcehut-bunny/">GitHub to sr.ht</a> recently.)</p> <blockquote class="alert alert-tip">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Tip      </p>  <p>Remember to add <code>author.name</code> and <code>author.email</code> to your Git client and use SSH keys whenever possible.</p><p>And when cloning, do a shallow clone to reduce the size of the repo by skipping the large commit history being downloaded onto your mobile device:</p><p><code>git clone --depth=1 <repository-url></code></p></blockquote> <h4 id="ide">IDE<a href="#ide" class="h-anchor" title="Permalink to #IDE"></a></h4>   <p>I have tried <a href="https://github.com/Acode-Foundation/Acode" target="_blank" class="ext-link" rel="noopener noreferrer">ACode</a> and <a href="https://github.com/massivemadness/Squircle-CE" target="_blank" class="ext-link" rel="noopener noreferrer">Squircle CE</a>. They’re both good, see which suits your needs more! Working in an IDE means there’s syntax highlighting and other features like code-completion. One can use just a Markdown editor like <a href="https://github.com/gsantner/markor" target="_blank" class="ext-link" rel="noopener noreferrer">Markor</a> to write and edit blog posts.</p><h3 id="linux-setup">Linux Setup<a href="#linux-setup" class="h-anchor" title="Permalink to #Linux Setup"></a></h3>   <p>My second computer is running Linux Mint but it is barely used, so I had to start from scratch in terms of installing all the tools I use in my Hugo workflow. Here is a brief rundown of what I did to get it all working:</p><ul><li><p>Create a new SSH key for this device and add the <code>.pub</code> key into <a href="https://meta.sr.ht/keys" target="_blank" class="ext-link" rel="noopener noreferrer">https://meta.sr.ht/keys</a> (or your hosted Git repo provider)</p></li><li><p><code>git clone</code> the website source files</p></li><li><p>I have my Hugo theme installed as <a href="https://git-scm.com/book/en/v2/Git-Tools-Submodules" target="_blank" class="ext-link" rel="noopener noreferrer">Git Submodules</a>, so I have to run <code>git submodule update --init --recursive</code> to download the theme files, then run <code>git submodule update --remote --merge</code> when I want to update the theme.</p></li><li><p>Install <code>git</code>, <code>go</code>, and <code>hugo</code>. I originally installed Hugo with <code>apt</code> but the version was really out of date (like lower than v.0.100) and the site failed to build. So I recommend installing via <a href="https://snapcraft.io/hugo" target="_blank" class="ext-link" rel="noopener noreferrer">Snap</a> (I should have followed the <a href="https://gohugo.io/installation/linux/" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo Linux install instructions</a>).</p></li><li><p>Setup basic Git config:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">1</span><span class="cl">git config --global user.name <span class="s2">"Name"</span></span></span><span class="line"><span class="ln">2</span><span class="cl">git config --global user.email <span class="s2">"name@example.com"</span><span class="sb">`</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div></li><li><p>Try running <code>hugo server</code> and you should be able to work on your site!</p></li></ul><h4 id="ide-setup">IDE Setup<a href="#ide-setup" class="h-anchor" title="Permalink to #IDE Setup"></a></h4>   <p>I’m using <a href="https://vscodium.com/" target="_blank" class="ext-link" rel="noopener noreferrer">VSCodium</a> on Linux as well, but I was unable to get <code>zsh</code> to run in the integrated terminal, it only ran <code>sh</code>, while in the Terminal <em>outside</em> VSCodium, <code>which zsh</code> works and shows the path. So here’s how I got it to work:</p><ul><li>Ensure <code>zsh</code> is installed on the system.</li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">1</span><span class="cl">sudo apt update</span></span><span class="line"><span class="ln">2</span><span class="cl">sudo apt install zsh -y</span></span><span class="line"><span class="ln">3</span><span class="cl">zsh --version</span></span><span class="line"><span class="ln">4</span><span class="cl">which zsh <span class="c1"># Shows a valid path, meaning it is installed correctly</span></span></span><span class="line"><span class="ln">5</span><span class="cl">chsh -s <span class="k">$(</span>which zsh<span class="k">)</span> <span class="c1"># Make it the default shell</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>Because I used Flatpak to install VSCodium, it is sandboxed and doesn’t have access to the system shells like <code>/usr/bin/zsh</code>. To overcome this I had to do some research and found this workaround:</p><ul><li><p>Install a utility called <code>host-spawn</code> to help bridge the sandboxed/isolated app to the host system shells</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">1</span><span class="cl">sudo apt install flatpak-xdg-utils</span></span><span class="line"><span class="ln">2</span><span class="cl">flatpak override --user --filesystem<span class="o">=</span>host com.vscodium.codium</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div></li><li><p>After installing <code>host-spawn</code>, go into VSCodium settings (<code>.json</code> mode) and set the shell profile to be:</p></li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json"><span class="line"><span class="ln">1</span><span class="cl"><span class="s2">"terminal.integrated.profiles.linux"</span><span class="err">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="nt">"zsh"</span><span class="p">:</span> <span class="p">{</span></span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="nt">"path"</span><span class="p">:</span> <span class="s2">"/app/bin/host-spawn"</span><span class="p">,</span></span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="nt">"args"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"zsh"</span><span class="p">],</span></span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="nt">"icon"</span><span class="p">:</span> <span class="s2">"terminal-bash"</span><span class="p">,</span></span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="nt">"overrideName"</span><span class="p">:</span> <span class="kc">true</span></span></span><span class="line"><span class="ln">7</span><span class="cl">  <span class="p">}</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p">}</span><span class="err">,</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="s2">"terminal.integrated.defaultProfile.linux"</span><span class="err">:</span> <span class="s2">"zsh"</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><ul><li>Close and restart VSCodium to see the <code>zsh</code> running in the integrated terminal!</li></ul><h2 id="closing-remarks">Closing Remarks<a href="#closing-remarks" class="h-anchor" title="Permalink to #Closing Remarks"></a></h2>   <p>I really love Hugo and static sites in general. You might think this is very technical to set up, but once you get the hang of the basics of Git, Markdown, and using an IDE—I think you too, will take this over using something like the WordPress Web UI any day of the week!</p><p>Blog on.</p>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/migrate-github-pages-to-sourcehut-bunny/</id><link rel="alternate" href="https://burgeonlab.com/blog/migrate-github-pages-to-sourcehut-bunny/"/><title type="html">GitHub Pages Alternative: SourceHut Builds + Bunny.net</title><published>2025-08-14T01:10:38Z</published><updated>2025-08-14T01:10:38Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/migrate-github-pages-to-sourcehut-bunny/og_img_022.webp"/><summary type="html">Learn how to replace GitHub Pages with SourceHut Builds, Bunny Storage and Bunny CDN. Set up a CI static site deployment workflow replacing GitHub Actions, with tools like SFTP, Rclone, and Curl using SourceHut&amp;rsquo;s CI platform running on Alpine Linux.</summary><content type="html"><![CDATA[            <p><i>[Note: This post contains Mermaid diagram(s) which do not work in RSS feed readers. Please <a href='https://burgeonlab.com/blog/migrate-github-pages-to-sourcehut-bunny/'>load this page</a> in a web browser to render content correctly, thank you!]</i></p><hr>            <h2 id="finding-a-github-alternative">Finding a GitHub Alternative<a href="#finding-a-github-alternative" class="h-anchor" title="Permalink to #Finding a GitHub Alternative"></a></h2>   <p>I remember the first time using GitHub and feeling slightly intimidated by the complexity; but at the same time, also intrigued by all the cool projects I found there. This was before <a href="https://techcrunch.com/2022/10/26/four-years-after-being-acquired-by-microsoft-github-keeps-doing-its-thing/" target="_blank" class="ext-link" rel="noopener noreferrer">Microsoft bought GitHub</a>. I only started delving deeper into the platform when I started using static site generators in 2023 or so, trying out their free hosting offered with GitHub Pages.</p><p>Recently, I’ve become more comfortable using GitHub (likely because I learnt more about Git versioning). I even managed to contribute to a few FOSS projects last month with pull requests, which was a personally delightful moment! But just as I found familiarity, I’m leaving it as my web host for this blog (using GitHub Pages & GitHub Actions) and for hosting my Git repositories and blog source code.</p><p>For someone who cares about:</p><ul><li>open-source community</li><li>online privacy</li><li>dodging vendor lock-in with Big Tech</li><li>avoiding AI infusing into every possible nook and cranny</li><li><em>and</em> breaking free from the “<a href="https://en.wikipedia.org/wiki/Enshittification" target="_blank" class="ext-link" rel="noopener noreferrer">enshittification</a>” cycle;</li></ul><p>I did my research on GitHub alternatives and decided to migrate to SourceHut (AKA sr.ht).</p><h2 id="what-is-sourcehut">What is SourceHut<a href="#what-is-sourcehut" class="h-anchor" title="Permalink to #What is SourceHut"></a></h2>   <p><a href="https://sourcehut.org/" target="_blank" class="ext-link" rel="noopener noreferrer">SourceHut</a> is a collection of development tools presented on a minimalist platform, currently in alpha, that is focused on and prioritizes the open source community.</p><p>From SourceHut’s home page:</p><ul><li>Absolutely no tracking or advertising</li><li>All features work without JavaScript</li><li>No AI features whatsoever</li><li>Many features work without an account</li><li>100% free and open source software</li><li>Supports hundreds of free software projects</li></ul><p>The GitHub alternatives that made my shortlist and suited my needs included <a href="https://codeberg.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Codeberg</a> (which doesn’t seem to support hosting private code repos that aren’t open licenced), and self-hosting <a href="https://forgejo.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Forgejo</a> / <a href="https://about.gitea.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Gitea</a>. <a href="https://about.gitlab.com/" target="_blank" class="ext-link" rel="noopener noreferrer">GitLab</a> is a no go for those in Hong Kong because they have <a href="https://about.gitlab.com/pricing/faq-jihu/" target="_blank" class="ext-link" rel="noopener noreferrer">licensed</a> the brand to JiHu, a Chinese company that I’m not familiar with. Migrating to these platforms would have been easier as their look and feel are more similar to GitHub..</p><p>But I decided to use SourceHut because:</p><ol><li>Their manifesto and the creator behind SourceHut (<a href="https://drewdevault.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Drew Devault</a>) seems very committed to the open source community</li><li>They are based in Europe</li><li>I ended up liking their minimal, “nerdy looking”, text-based, no frills user interface after browsing around</li><li>It’s snappy</li><li>Features are modular and lightweight</li><li>Yay, no AI!</li></ol><h3 id="features-of-sourcehutthe-hackers-forge">Features of SourceHut—The Hacker’s Forge<a href="#features-of-sourcehutthe-hackers-forge" class="h-anchor" title="Permalink to #Features of SourceHut—The Hacker’s Forge"></a></h3>   <p>SourceHut’s features are all modular and almost all are <a href="https://sourcehut.org/pricing/" target="_blank" class="ext-link" rel="noopener noreferrer">free</a> during their alpha stage. They have made it clear payment will be required from <a href="https://sourcehut.org/alpha-details/#payment-will-be-required-later" target="_blank" class="ext-link" rel="noopener noreferrer">beta onwards</a>. The feature I need to replace GitHub Pages with is their continuous integration (CI/CD) platform called Builds (<a href="https://builds.sr.ht/" target="_blank" class="ext-link" rel="noopener noreferrer">builds.sr.ht</a>). It is one of two features (the other being <a href="https://sourcehut.org/blog/2021-11-29-announcing-the-chat.sr.ht-public-beta/" target="_blank" class="ext-link" rel="noopener noreferrer">chat.sr.ht</a>, a hosted IRC bouncer service) that requires a SourceHut subscription. I paid for the <em>Amateur hacker tier</em> for $20 USD annually. If you have the means to <a href="https://man.sr.ht/installation.md" target="_blank" class="ext-link" rel="noopener noreferrer">self-host</a>, SourceHut also supports that—but I’m quite happy to support this platform.</p>                             <a href="/blog/migrate-github-pages-to-sourcehut-bunny/022_srht_git.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/migrate-github-pages-to-sourcehut-bunny/022_srht_git.webp" alt="Screenshot of BurgeonLab.com Hugo source code on SourceHut's Git hosted repository.">    </a>                  <blockquote class="alert alert-tip">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Tip      </p>  <p>One cool feature of SourceHut’s Builds is the ability to <a href="https://man.sr.ht/builds.sr.ht/build-ssh.md" target="_blank" class="ext-link" rel="noopener noreferrer">SSH into</a> the build’s virtual environment within a short window (10 minutes) if a build fails. Handy for troubleshooting!</p></blockquote> <h3 id="sourcehuts-disadvantages">SourceHut’s Disadvantages<a href="#sourcehuts-disadvantages" class="h-anchor" title="Permalink to #SourceHut&rsquo;s Disadvantages"></a></h3>   <p>A few downsides relevant to my use-case are that the site feels a bit rough around the edges, especially the <a href="https://man.sr.ht/" target="_blank" class="ext-link" rel="noopener noreferrer">documentation</a>. I read from the blog, there has been some financial troubles and a <a href="https://sourcehut.org/blog/2024-06-04-status-and-plans/" target="_blank" class="ext-link" rel="noopener noreferrer">big DDOS</a> that brought everything to a halt in early 2024. However, I hope that the infrastructure improves and that the platform gains popularlity in the near future. I’ve noticed that it’s more difficult to discover and save FOSS projects on <a href="https://sr.ht/projects" target="_blank" class="ext-link" rel="noopener noreferrer">SourceHut</a> than say on GitHub with its star and lists system. If you like the social aspect of GitHub, you might not like the simplicity or “lack of features” of SourceHut (but this point can go both ways).</p><p>Therefore, do some research before switching to SourceHut, as it is indeed <strong>quite different</strong> to the alternatives—especially regarding its contribution model, which is email-based with a patch workflow instead of the more common GitHub-type pull request workflow. I think for most people, <a href="https://codeberg.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Codeberg</a> would be a better bet.</p><p>Here are some pages that talk about Sourcehut I found insightful:</p><ul><li><a href="https://yotam.net/posts/sourcehut-build/" target="_blank" class="ext-link" rel="noopener noreferrer">What Makes Sourcehut CI So Good</a></li><li><a href="https://splitcells.net/net/splitcells/network/community/blog/articles/2024-07-07-migration-from-sourhut-to-codeberg.html" target="_blank" class="ext-link" rel="noopener noreferrer">SourceHut to Coderberg Migration</a></li><li><a href="https://cadence.moe/blog/2022-07-03-git-forge-opinions-github-gitlab-gitea-sourcehut" target="_blank" class="ext-link" rel="noopener noreferrer">Git forge opinions: GitHub, GitLab, Gitea, Sourcehut</a></li><li><a href="https://davideisinger.com/journal/migrating-from-github-to-sourcehut/" target="_blank" class="ext-link" rel="noopener noreferrer">Migrating from GitHub to SourceHut</a></li><li><a href="https://chupson.dev/blog/sourcehut-the-hackers-forge/" target="_blank" class="ext-link" rel="noopener noreferrer">SourceHut — the hacker’s forge</a></li></ul><h2 id="step-by-step-migration-guide-hugo-static-site-ci-deployment">Step-by-step Migration Guide: Hugo Static Site CI Deployment<a href="#step-by-step-migration-guide-hugo-static-site-ci-deployment" class="h-anchor" title="Permalink to #Step-by-step Migration Guide: Hugo Static Site CI Deployment"></a></h2>   <p>Here is a closer look at the practical aspects of replacing GitHub to two different services in one move! I made an overview diagram<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup> showing the drop-in replacements for GitHub’s services.</p><pre class="mermaid">%%{init: { 'themeCSS': '.node rect, .node circle, .node ellipse, .node polygon { stroke: #FFB800; stroke-width: 1px; rx: 5px; ry: 5px;}' }}%%flowchart TD  GitHub["GitHub"] -.-x A["Git repo"] & B["GitHub Actions (CI/CD)"] & C["GitHub Pages (free hosting)"]  A --> D["git.sr.ht"]  B --> E["builds.sr.ht (CI/CD)"]  C --> F["Bunny.net Storage (budget-friendly hosting)"]  D --> G["Git repo (private) with Hugo source code"]  G --> E  E -->|builds.yml| H["Hugo Build & Deploy public folder to Bunny"]  H -->|via rclone and SFTP| F  F -->|via Bunny.net CDN| I["Static Hugo blog is up and running 🎉"]   style E stroke-width:3px,stroke:#FFB800  style D stroke-width:3px,stroke:#FFB800  style F stroke-width:3px,stroke:#FFB800  style G stroke:#FFB800  style H stroke:#FFB800  style A color:#E64814,stroke:#E64814,stroke-width:4px  style B color:#E64814,stroke:#E64814,stroke-width:4px  style C color:#E64814,stroke:#E64814,stroke-width:4px  style GitHub color:#E64814,stroke:#E64814,stroke-width:4px  linkStyle 3 stroke:#FFB800,stroke-width:2px  linkStyle 5 stroke:#FFB800,stroke-width:2px  linkStyle 6 stroke:#FFB800,stroke-width:2px  linkStyle 7 stroke:#FFB800,stroke-width:2px  linkStyle 4 stroke:#FFB800,stroke-width:2px  linkStyle 8 stroke:#FFB800,stroke-width:2px  linkStyle 9 stroke:#FFB800,stroke-width:2px  linkStyle 10 stroke:#FFB800,stroke-width:2px  style I fill:#FFB800,color:#000000,stroke:#FFB800</pre> <h3 id="step-1-transfer-git-repository-from-github-to-sourcehut">Step 1: Transfer Git Repository From Github to SourceHut<a href="#step-1-transfer-git-repository-from-github-to-sourcehut" class="h-anchor" title="Permalink to #Step 1: Transfer Git Repository From Github to SourceHut"></a></h3>   <ol><li><p>Register for a free <a href="https://meta.sr.ht/register" target="_blank" class="ext-link" rel="noopener noreferrer">SourceHut account</a>. If you’re using SourceHut with their Builds service, you will have to <a href="https://sourcehut.org/pricing/" target="_blank" class="ext-link" rel="noopener noreferrer">subscribe</a> to one of the tiers (they all offer the same services). But if you are just using their hosted git repository service, you don’t have to pay.</p></li><li><p>Add your device’s <a href="https://meta.sr.ht/keys" target="_blank" class="ext-link" rel="noopener noreferrer">.pub SSH key</a> to your account.</p></li><li><p>Create your first Git repository on <a href="https://git.sr.ht/" target="_blank" class="ext-link" rel="noopener noreferrer">git.sr.ht</a>. For <code>private</code> repositories, copy the read/write clone URL for the next step  (e.g. <code>git@git.sr.ht:~username/your_blog</code>). Set default branch to <code>master</code> (some prefer <code>main</code> instead).</p></li><li><p>Go to your local Hugo directory (or other SSGs like Jekyll, 11ty) and remove the old Git origin, replacing it with the new SourceHut origin, and confirm the remote is set correctly:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">1</span><span class="cl">git remote remove origin </span></span><span class="line"><span class="ln">2</span><span class="cl">git remote add origin git@git.sr.ht:~username/your_blog</span></span><span class="line"><span class="ln">3</span><span class="cl">git remote -v</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div></li><li><p>Remove GitHub related files in your Hugo directory like these:</p><ul><li><code>cname</code></li><li><code>.github/workflows/pages.yml</code></li><li>remove <code>.github/</code> from <code>.gitignore</code> list</li></ul></li><li><p>Clear the Git cache with <code>git rm -r --cached .github</code></p></li><li><p>Push the local repo to the newly set remote origin with <code>git push -u origin master</code>.</p></li><li><p>Your static site source code has now migrated from GitHub over to the new Git forge at git.sr.ht.</p></li></ol><h3 id="step-2-replace-github-actions-pagesyml-with-sourcehut-buildyml">Step 2: Replace GitHub Actions (pages.yml) with SourceHut (build.yml)<a href="#step-2-replace-github-actions-pagesyml-with-sourcehut-buildyml" class="h-anchor" title="Permalink to #Step 2: Replace GitHub Actions (pages.yml) with SourceHut (build.yml)"></a></h3>   <p>The <a href="https://man.sr.ht/builds.sr.ht/manifest.md" target="_blank" class="ext-link" rel="noopener noreferrer">manifest file</a>, <code>build.yml</code>, is the <a href="https://man.sr.ht/builds.sr.ht/" target="_blank" class="ext-link" rel="noopener noreferrer">SourceHut equivalent</a> of <code>pages.yml</code> from GitHub Actions. Instead of pushing the<code>/public</code> Hugo folder to the GitHub Pages repo (username.github.io) for free static site hosting, I have set the workflow to push the static files to Bunny.net for hosting using Bunny Storage.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref">2</a></sup></p> <blockquote>  <p><strong>DISCLAIMER</strong>:</p><p><em>I am not an expert in automated workflows or cybersecurity by any means. I only achieved this workflow through trial and error. I’ve tried my best to research and ensure everything remains secure, but the code I share below may not be the best or safest method.</em></p><p><em>If you spot any red flags or places for improvement, I’d appreciate it a lot if you can let me know by <a href="https://burgeonlab.com/blog/migrate-github-pages-to-sourcehut-bunny/#comments">leaving me a message</a>, thanks!</em></p></blockquote> <p>To do this, we need to store some Bunny.net <a href="https://man.sr.ht/builds.sr.ht/#secrets" target="_blank" class="ext-link" rel="noopener noreferrer">secrets</a> in SourceHut Builds’ secrets manager so we could use them in the deployment process/manifest file.</p> <blockquote class="alert alert-warning">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Warning      </p>  <p>Never hardcode any passwords, API keys, credentials, access tokens, etc into CI/CD workflows! Always use the platform’s secret manager to store the sensitive info. Read this article for more information about <a href="https://man.sr.ht/tutorials/builds.sr.ht/using-build-secrets.md" target="_blank" class="ext-link" rel="noopener noreferrer">secrets in SourceHut manifests.</a></p></blockquote> <h4 id="storing-secrets-in-sourcehut-builds">Storing Secrets in SourceHut Builds<a href="#storing-secrets-in-sourcehut-builds" class="h-anchor" title="Permalink to #Storing Secrets in SourceHut Builds"></a></h4>   <p>I will assume you already have a Bunny.net account (they do offer a generous 14 day trial without credit card)<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref">2</a></sup> and have a Storage zone<sup id="fnref:3"><a href="#fn:3" class="footnote-ref">3</a></sup> and CDN pull zone set up (with the CDN origin set to the Storage Zone in question). Add these four Bunny secrets to the <a href="https://builds.sr.ht/secrets" target="_blank" class="ext-link" rel="noopener noreferrer">secrets manager</a>:</p><ul><li><p><code>BUNNYNET_API_KEY</code></p><ul><li>Bunny main account API key, accessed by clicking on your Account icon (top of left sidebar) > API key</li></ul></li><li><p><code>BUNNYNET_PULL_ZONE_ID</code></p><ul><li>Numeric Pull Zone ID, accessed from the menu button on the top right corner of the CDN > Pull zone dashboard</li></ul></li><li><p><code>BUNNYNET_SFTP_PASS</code></p><ul><li>Bunny Storage zone password (not the read-only password), accessed from the Storage > “FTP & API access” dashboard <strong>(read extra step below)</strong></li></ul></li><li><p><code>BUNNYNET_SFTP_USER</code></p><ul><li>Bunny Storage zone username, accessed from the Storage > “FTP & API access” dashboard</li></ul></li></ul> <blockquote class="alert alert-note">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Note      </p>  <p>There’s one extra <strong>compulsory</strong> step required for the Builds manifest to work later when using <code>BUNNYNET_SFTP_PASS</code> with <code>rclone</code>.</p><p>Instead of pasting the Bunny Storage read/write password directly into the SourceHut Secrets manager page, paste the output of:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">1</span><span class="cl">rclone obscure <paste-bunny-storage-password-without-brackets></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>into the Secret field for <code>BUNNYNET_SFTP_PASS</code> when creating the SFTP password secret.</p><p>Tip: Run this locally in Terminal. Install <code>rclone</code> if it’s not <a href="https://rclone.org/install/" target="_blank" class="ext-link" rel="noopener noreferrer">installed</a> yet with <code>brew install rclone</code> (for those who are using <a href="https://brew.sh/" target="_blank" class="ext-link" rel="noopener noreferrer">Homebrew</a>).</p></blockquote> <p>After noting down the four credentials at Bunny.net, go back to SourceHut to add the secrets. This was a bit confusing for me at first, but these settings worked for me:</p>                             <a href="/blog/migrate-github-pages-to-sourcehut-bunny/022_srht_secrets.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/migrate-github-pages-to-sourcehut-bunny/022_srht_secrets.webp" alt="Screenshot of secrets in builds Sourcehut.com.">    </a>       <ul><li><p><strong>Name</strong>: I used capitals and descriptive names, but you can name the secrets anything you want. The generated UUID can be used to recall the secrets, but for the sake of clarity, I am using a meaningful name like <code>BUNNY_SFTP_USER</code>.</p></li><li><p><strong>Secret</strong>: Paste the secure credential here, ensuring there are no extra spaces, quotes, line breaks, etc.</p></li><li><p><strong>Secret Type</strong>: Choose <code>File</code>.</p></li><li><p><strong>Path</strong>: I used a generic path like <code>/tmp/SECRET_ONE</code>. This is where the file will be stored in the VM, during the building process.</p></li><li><p><strong>Mode</strong>: This is the permissions mode, AKA <a href="https://chmodcommand.com/chmod-600/" target="_blank" class="ext-link" rel="noopener noreferrer">chmod</a>. <code>600</code> ensures only the build user can read/write the secret file.</p></li><li><p><strong>File</strong>: Leave blank, no need to upload an actual file.</p></li></ul><h3 id="step-3-sourcehut-builds-manifest-template">Step 3: SourceHut Builds Manifest Template<a href="#step-3-sourcehut-builds-manifest-template" class="h-anchor" title="Permalink to #Step 3: SourceHut Builds Manifest Template"></a></h3>   <p>Here is my <code>builds.yml</code> for your reference. I added some comments directly in the .yml for clarity, but here are some important notes I suggest reading before using the template:</p><ul><li><p>Tailor the packages list for your own needs, e.g., no need for <code>npm</code> if Node.js is not in your workflow.</p></li><li><p>If the Git repo is set to private mode, do the following:</p><ul><li>Use the SourceHut repository SSH clone URL, not the HTTPS URL</li><li>A SSH Key secret is required for <a href="https://man.sr.ht/builds.sr.ht/private-repos.md" target="_blank" class="ext-link" rel="noopener noreferrer">builds.sr.ht to access git.sr.hr private repos</a></li></ul></li></ul><h4 id="build">Build<a href="#build" class="h-anchor" title="Permalink to #Build"></a></h4>   <ul><li><p>The <strong>build</strong> section is straightforward; it generates the blog by running the <a href="https://gohugo.io/getting-started/usage/#build-your-site" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo build</a> command.</p><ul><li>The next command may not be required in your case, but because I didn’t integrate Pagefind’s indexing process into the theme itself, I have to run <code>npx -y pagefind --site public</code> every time after Hugo finishes building the site.</li></ul></li></ul><h4 id="deploy">Deploy<a href="#deploy" class="h-anchor" title="Permalink to #Deploy"></a></h4>   <ul><li><p>The <strong>deploy</strong> section involves more steps:</p><ol><li><p>Set the path to the secrets</p></li><li><p>Set a new variable called CONFIG_FILE. This will be our temporary (with limited access) rclone.conf file, used to connect to Bunny Storage. The <code>.XXXXXX</code> is a random suffix generator added to create a unique filename, preventing filename collisions.</p></li><li><p>Add a trap to delete the CONFIG_FILE and remove the secret variables on exit (or earlier, even, if there is a failure).</p></li><li><p>Add a loop to check every secret’s existence; if any are missing, it prints which one and exits with failure so the build stops early.</p></li><li><p>Read secrets and make them accessible for creating <code>rclone.conf</code>.</p> <blockquote class="alert alert-tip">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Tip      </p>  <p>It is good practice to rotate (regenerate) secrets frequently, use the lowest possible permission level, and reduce their exposure to different steps. I haven’t been able to get SourceHut (file type) secrets to auto-inject into the build environment (which is probably the safest way). If you know of a better/safer method, please let me know!<sup id="fnref:4"><a href="#fn:4" class="footnote-ref">4</a></sup></p></blockquote> </li><li><p>Notes about <code>rclone.conf</code>:<sup id="fnref:5"><a href="#fn:5" class="footnote-ref">5</a></sup></p><ul><li><p>Use port 22 for SFTP (not port 21 for FTP)</p></li><li><p>Find the <code>host</code> in the Bunny Storage dashboard or check the list on <a href="https://docs.bunny.net/reference/storage-api#storage-endpoints" target="_blank" class="ext-link" rel="noopener noreferrer">Bunny Storage docs</a>.</p></li><li><p><code>cat > "$CONFIG_FILE" <<-EOF</code> is the command to create the <code>rclone.conf</code> file with multi-line, indented text within our script. Ensure there’s a dash before <code>EOF</code>, as it tells the shell to remove the leading tab indents from the input. The .conf file <strong>will not work</strong> if this dash is not present!</p></li><li><p><code>use-agent</code> and <code>key_use_agent</code> are both set to <code>false</code> to prevent rclone trying to connect using SSH key authentication.</p></li></ul></li><li><p>Now for the actual <code>rclone sync</code> command—this probably took the longest for me to debug, but the solution is simple!</p><ul><li><p>We need to work around Bunny’s limitation of <a href="https://support.bunny.net/hc/en-us/articles/360020400891-I-am-unable-to-rename-files-using-FTP" target="_blank" class="ext-link" rel="noopener noreferrer">inability to rename files through FTP</a>. Rclone, by default, uploads files by <a href="https://rclone.org/docs/#inplace" target="_blank" class="ext-link" rel="noopener noreferrer">renaming them temporarily</a> with a <code>.partial</code> suffix during the transfer. This would cause the sync to Bunny fail. By using the <code>--inplace</code> flag, <code>rclone</code> will upload directly without renaming files.</p></li><li><p><code>rclone sync blog-name/public/ bunny:</code> means sync from <code>source to destination</code>, source being <code>blog-name/public/</code> and destination being <code>bunny:</code> (Bunny Storage’s root directory).</p></li><li><p>The <code>-sftp-set-modtime=false</code> flag is recommended by Bunny.net customer support. <code>--progress</code> is useful for monitoring or use <code>--stats-one-line</code> for a more minimal log output. Read about <a href="https://rclone.org/commands/rclone_sync/#sync-options" target="_blank" class="ext-link" rel="noopener noreferrer">other rclone flags</a> like <a href="https://rclone.org/docs/#checkers-n" target="_blank" class="ext-link" rel="noopener noreferrer">–checkers</a> or <a href="https://rclone.org/docs/#transfers-n" target="_blank" class="ext-link" rel="noopener noreferrer">–transfers</a>.</p></li></ul></li><li><p>Last step is to clear the cache at Bunny CDN so the newly synced static site files are reflected online using <code>curl</code>.</p></li></ol></li></ul> <blockquote class="alert alert-important">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Important      </p>  <p>Make sure you are <strong>not revealing your secrets</strong> in logs or output, as this means your credentials are compromised/leaked to the public. <strong>Read <a href="https://man.sr.ht/builds.sr.ht/#keeping-your-secrets-a-secret" target="_blank" class="ext-link" rel="noopener noreferrer">this section</a> in the builds.sr.ht documentation carefully</strong> and disable logging (xtrace) by using <code>set +x</code> when reading or using secrets!</p></blockquote> <h4 id="template">Template<a href="#template" class="h-anchor" title="Permalink to #Template"></a></h4>   <p>Remember to update <code>blog-name</code> (twice) to your own project’s directory, use your git source URL, secret UUIDs, and correct hostname in the <code>rclone.conf</code> when using the template!</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml"><span class="line"><span class="ln">  1</span><span class="cl"><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">alpine/latest</span><span class="w"></span></span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="w"></span><span class="nt">packages</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="w">  </span>- <span class="l">hugo</span><span class="w"></span></span></span><span class="line"><span class="ln">  4</span><span class="cl"><span class="w">  </span>- <span class="l">git</span><span class="w"> </span><span class="c"># Hugo submodule updates</span><span class="w"></span></span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="w">  </span>- <span class="l">npm</span><span class="w"> </span><span class="c"># Pagefind</span><span class="w"></span></span></span><span class="line"><span class="ln">  6</span><span class="cl"><span class="w">  </span>- <span class="l">rclone</span><span class="w"> </span><span class="c"># Deployment to Bunny Storage</span><span class="w"></span></span></span><span class="line"><span class="ln">  7</span><span class="cl"><span class="w">  </span>- <span class="l">openssh-client</span><span class="w"> </span><span class="c"># SFTP connection to Bunny Storage</span><span class="w"></span></span></span><span class="line"><span class="ln">  8</span><span class="cl"><span class="w">  </span>- <span class="l">curl</span><span class="w"> </span><span class="c"># Bunny CDN cache purge</span><span class="w"></span></span></span><span class="line"><span class="ln">  9</span><span class="cl"><span class="w"></span><span class="nt">sources</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln"> 10</span><span class="cl"><span class="w">  </span>- <span class="l">git@git.sr.ht:~username/repo_name</span><span class="w"></span></span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="w"></span><span class="nt">secrets</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln"> 12</span><span class="cl"><span class="w">  </span>- <span class="l">10f134e5-7889-48hd-a34k-bf179h5d2eq0</span><span class="w"> </span><span class="c"># Paste your SSH Key UUID for private repo access by Builds</span><span class="w"></span></span></span><span class="line"><span class="ln"> 13</span><span class="cl"><span class="w">  </span>- <span class="l">BUNNYNET_API_KEY</span><span class="w"></span></span></span><span class="line"><span class="ln"> 14</span><span class="cl"><span class="w">  </span>- <span class="l">BUNNYNET_PULL_ZONE_ID </span><span class="w"></span></span></span><span class="line"><span class="ln"> 15</span><span class="cl"><span class="w">  </span>- <span class="l">BUNNYNET_SFTP_PASS </span><span class="w"></span></span></span><span class="line"><span class="ln"> 16</span><span class="cl"><span class="w">  </span>- <span class="l">BUNNYNET_SFTP_USER</span><span class="w"></span></span></span><span class="line"><span class="ln"> 17</span><span class="cl"><span class="w"></span><span class="nt">tasks</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln"> 18</span><span class="cl"><span class="w">  </span>- <span class="nt">build</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd"></span></span></span><span class="line"><span class="ln"> 19</span><span class="cl"><span class="sd">      #!/bin/sh</span></span></span><span class="line"><span class="ln"> 20</span><span class="cl"><span class="sd">      set -e</span></span></span><span class="line"><span class="ln"> 21</span><span class="cl"><span class="sd">      echo "🚀 Starting build..."</span></span></span><span class="line"><span class="ln"> 22</span><span class="cl"><span class="sd">      </span></span></span><span class="line"><span class="ln"> 23</span><span class="cl"><span class="sd">      # Change directory to Hugo site's root</span></span></span><span class="line"><span class="ln"> 24</span><span class="cl"><span class="sd">      cd blog-name</span></span></span><span class="line"><span class="ln"> 25</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln"> 26</span><span class="cl"><span class="sd">      echo "🚀 Building Hugo site..."</span></span></span><span class="line"><span class="ln"> 27</span><span class="cl"><span class="sd">      hugo --gc --cleanDestinationDir --minify</span></span></span><span class="line"><span class="ln"> 28</span><span class="cl"><span class="sd">      echo "✅  Hugo build complete."</span></span></span><span class="line"><span class="ln"> 29</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln"> 30</span><span class="cl"><span class="sd">      echo "🚀 Building Pagefind database..."</span></span></span><span class="line"><span class="ln"> 31</span><span class="cl"><span class="sd">      npx -y pagefind --site public</span></span></span><span class="line"><span class="ln"> 32</span><span class="cl"><span class="sd">      echo "✅  Pagefind build complete."</span><span class="w"></span></span></span><span class="line"><span class="ln"> 33</span><span class="cl"><span class="w"></span></span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="w">  </span>- <span class="nt">deploy</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd"></span></span></span><span class="line"><span class="ln"> 35</span><span class="cl"><span class="sd">      #!/bin/sh</span></span></span><span class="line"><span class="ln"> 36</span><span class="cl"><span class="sd">      set -e</span></span></span><span class="line"><span class="ln"> 37</span><span class="cl"><span class="sd">      echo "🚀 Starting deployment..."</span></span></span><span class="line"><span class="ln"> 38</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln"> 39</span><span class="cl"><span class="sd">      # Load secret file locations</span></span></span><span class="line"><span class="ln"> 40</span><span class="cl"><span class="sd">      BUNNYNET_SFTP_PASS_PATH=/tmp/bunnynet_sftp_pass</span></span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="sd">      BUNNYNET_SFTP_USER_PATH=/tmp/bunnynet_sftp_user</span></span></span><span class="line"><span class="ln"> 42</span><span class="cl"><span class="sd">      BUNNYNET_API_KEY_PATH=/tmp/bunnynet_api_key</span></span></span><span class="line"><span class="ln"> 43</span><span class="cl"><span class="sd">      BUNNYNET_PULL_ZONE_ID_PATH=/tmp/bunnynet_pull_zone_id</span></span></span><span class="line"><span class="ln"> 44</span><span class="cl"><span class="sd">      </span></span></span><span class="line"><span class="ln"> 45</span><span class="cl"><span class="sd">      echo "🚀 Creating temporary rclone.conf..."</span></span></span><span class="line"><span class="ln"> 46</span><span class="cl"><span class="sd">      CONFIG_FILE="$(mktemp /tmp/rclone.conf.XXXXXX)"</span></span></span><span class="line"><span class="ln"> 47</span><span class="cl"><span class="sd">      # Make CONFIG_FILE only readable by build user</span></span></span><span class="line"><span class="ln"> 48</span><span class="cl"><span class="sd">      chmod 600 "$CONFIG_FILE"</span></span></span><span class="line"><span class="ln"> 49</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln"> 50</span><span class="cl"><span class="sd">      # Ensure cleanup on exit even if the script errors out (remove temp config, unset secret variables)</span></span></span><span class="line"><span class="ln"> 51</span><span class="cl"><span class="sd">      cleanup() {</span></span></span><span class="line"><span class="ln"> 52</span><span class="cl"><span class="sd">        set +x</span></span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="sd">        rm -f "$CONFIG_FILE"</span></span></span><span class="line"><span class="ln"> 54</span><span class="cl"><span class="sd">        unset SFTP_PASS SFTP_USER API_KEY PULL_ZONE_ID</span></span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="sd">      }</span></span></span><span class="line"><span class="ln"> 56</span><span class="cl"><span class="sd">      trap cleanup EXIT</span></span></span><span class="line"><span class="ln"> 57</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln"> 58</span><span class="cl"><span class="sd">      # Fail early if any secret file is missing to avoid writing incomplete config</span></span></span><span class="line"><span class="ln"> 59</span><span class="cl"><span class="sd">      for p in "$BUNNYNET_SFTP_PASS_PATH" "$BUNNYNET_SFTP_USER_PATH" "$BUNNYNET_API_KEY_PATH" "$BUNNYNET_PULL_ZONE_ID_PATH"; do</span></span></span><span class="line"><span class="ln"> 60</span><span class="cl"><span class="sd">        [ -f "$p" ] || { echo "Missing expected secret file: $p" >&2; exit 1; }</span></span></span><span class="line"><span class="ln"> 61</span><span class="cl"><span class="sd">      done</span></span></span><span class="line"><span class="ln"> 62</span><span class="cl"><span class="sd">      echo "✅ Secrets loaded successfully."</span></span></span><span class="line"><span class="ln"> 63</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln"> 64</span><span class="cl"><span class="sd">      # Read secrets with xtrace logging disabled and create rclone.conf</span></span></span><span class="line"><span class="ln"> 65</span><span class="cl"><span class="sd">      set +x</span></span></span><span class="line"><span class="ln"> 66</span><span class="cl"><span class="sd">      SFTP_USER="$(< "$BUNNYNET_SFTP_USER_PATH")"</span></span></span><span class="line"><span class="ln"> 67</span><span class="cl"><span class="sd">      SFTP_PASS="$(< "$BUNNYNET_SFTP_PASS_PATH")"</span></span></span><span class="line"><span class="ln"> 68</span><span class="cl"><span class="sd">      API_KEY="$(< "$BUNNYNET_API_KEY_PATH")"</span></span></span><span class="line"><span class="ln"> 69</span><span class="cl"><span class="sd">      PULL_ZONE_ID="$(< "$BUNNYNET_PULL_ZONE_ID_PATH")"</span></span></span><span class="line"><span class="ln"> 70</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln"> 71</span><span class="cl"><span class="sd">      cat > "$CONFIG_FILE" <<-EOF</span></span></span><span class="line"><span class="ln"> 72</span><span class="cl"><span class="sd">      [bunny]</span></span></span><span class="line"><span class="ln"> 73</span><span class="cl"><span class="sd">      type = sftp</span></span></span><span class="line"><span class="ln"> 74</span><span class="cl"><span class="sd">      host = storage.bunnycdn.com</span></span></span><span class="line"><span class="ln"> 75</span><span class="cl"><span class="sd">      port = 22</span></span></span><span class="line"><span class="ln"> 76</span><span class="cl"><span class="sd">      user = ${SFTP_USER}</span></span></span><span class="line"><span class="ln"> 77</span><span class="cl"><span class="sd">      pass = ${SFTP_PASS}</span></span></span><span class="line"><span class="ln"> 78</span><span class="cl"><span class="sd">      use_agent = false</span></span></span><span class="line"><span class="ln"> 79</span><span class="cl"><span class="sd">      key_use_agent = false</span></span></span><span class="line"><span class="ln"> 80</span><span class="cl"><span class="sd">      EOF</span></span></span><span class="line"><span class="ln"> 81</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln"> 82</span><span class="cl"><span class="sd">      # Remove secret variables from shell after creating rclone.conf</span></span></span><span class="line"><span class="ln"> 83</span><span class="cl"><span class="sd">      unset SFTP_USER SFTP_PASS</span></span></span><span class="line"><span class="ln"> 84</span><span class="cl"><span class="sd">      echo "✅ Temporary rclone.conf created."</span></span></span><span class="line"><span class="ln"> 85</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln"> 86</span><span class="cl"><span class="sd">      # Deploy to Bunny using rclone</span></span></span><span class="line"><span class="ln"> 87</span><span class="cl"><span class="sd">      echo "🚀 Deploying to Bunny Storage..."</span></span></span><span class="line"><span class="ln"> 88</span><span class="cl"><span class="sd">      {</span></span></span><span class="line"><span class="ln"> 89</span><span class="cl"><span class="sd">        # Enable xtrace for rclone command only</span></span></span><span class="line"><span class="ln"> 90</span><span class="cl"><span class="sd">        set -x</span></span></span><span class="line"><span class="ln"> 91</span><span class="cl"><span class="sd">        rclone --config "$CONFIG_FILE" sync blog-name/public/ bunny: --progress --inplace --sftp-set-modtime=false</span></span></span><span class="line"><span class="ln"> 92</span><span class="cl"><span class="sd">        set +x</span></span></span><span class="line"><span class="ln"> 93</span><span class="cl"><span class="sd">      }</span></span></span><span class="line"><span class="ln"> 94</span><span class="cl"><span class="sd">      echo "✅ Deployment complete."</span></span></span><span class="line"><span class="ln"> 95</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln"> 96</span><span class="cl"><span class="sd">      # Purge BunnyCDN Cache</span></span></span><span class="line"><span class="ln"> 97</span><span class="cl"><span class="sd">      echo "🚀 Purging BunnyCDN cache..."</span></span></span><span class="line"><span class="ln"> 98</span><span class="cl"><span class="sd">      set +x</span></span></span><span class="line"><span class="ln"> 99</span><span class="cl"><span class="sd">      curl -fsS -X POST "https://api.bunny.net/pullzone/${PULL_ZONE_ID}/purgeCache" \</span></span></span><span class="line"><span class="ln">100</span><span class="cl"><span class="sd">          -H "AccessKey: ${API_KEY}"</span></span></span><span class="line"><span class="ln">101</span><span class="cl"><span class="sd">      echo "✅ BunnyCDN cache purged."</span></span></span><span class="line"><span class="ln">102</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln">103</span><span class="cl"><span class="sd">      # Cleanup is handled by trap above</span></span></span><span class="line"><span class="ln">104</span><span class="cl"><span class="sd">      echo "🧹 Cleanup scheduled on exit complete."</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h3 id="step-4-bunnynet-configuration-bunny-storage-and-bunny-cdn">Step 4: Bunny.net Configuration: Bunny Storage and Bunny CDN<a href="#step-4-bunnynet-configuration-bunny-storage-and-bunny-cdn" class="h-anchor" title="Permalink to #Step 4: Bunny.net Configuration: Bunny Storage and Bunny CDN"></a></h3>   <p>The static site files inside the /public folder should now all be uploaded to the root of the Bunny Storage zone.</p><ul><li>Go to Delivery > CDN > <a href="https://dash.bunny.net/cdn" target="_blank" class="ext-link" rel="noopener noreferrer">Add Pull Zone</a>.</li><li>Select Origin type to be the Storage zone with the files pushed from SourceHut.</li><li>Tick “Force SSL” on the General > Hostnames page and also on General > Origin > “Verify origin SSL certificate”</li><li>Add a custom hostname if you have a custom domain. You’ll need to go to your registrar and add a CNAME<sup id="fnref:6"><a href="#fn:6" class="footnote-ref">6</a></sup> pointing to your <code>storage-zone-name.b-cdn.net</code>. Tick “Force SSL” when it’s been added.</li></ul>                             <a href="/blog/migrate-github-pages-to-sourcehut-bunny/022_bunny_dash.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/migrate-github-pages-to-sourcehut-bunny/022_bunny_dash.webp" alt="Screenshot of Bunny.net usage statistic dashboard view.">    </a>                 <h2 id="conclusion">Conclusion<a href="#conclusion" class="h-anchor" title="Permalink to #Conclusion"></a></h2>                                <a href="/blog/migrate-github-pages-to-sourcehut-bunny/022_stat_vs.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/migrate-github-pages-to-sourcehut-bunny/022_stat_vs.webp" alt="ALTTEXT">    </a>                 <p>Your static site should now be:</p><ul><li>Served to readers faster (based on my own speed test comparisons) than when hosting on GitHub Pages</li><li>You’ve gained the ability to control security headers, cache, and other CDN-related settings (without relying on a giant like Cloudflare CDN).</li><li>It relies on EU-based services that respect privacy.</li></ul><p>Thanks for reading my guide on how to switch from hosting a static site from GitHub Pages to SourceHut and Bunny Storage! I hope you found this migration guide useful. I think it’s a good time to leave or rely less on Microsoft’s GitHub, especially after reading the news that <a href="https://arstechnica.com/gadgets/2025/08/github-will-be-folded-into-microsoft-proper-as-ceo-steps-down/" target="_blank" class="ext-link" rel="noopener noreferrer">Thomas Dohmke will official step down as GitHub CEO</a>.</p><p>This is the most thorough guide I’ve written. If you benefited from it, I’d appreciate it a lot if you would consider supporting me by <a href="https://bunny.net/?ref=k4vc3x5108?ref=k4vc3x5108" target="_blank" class="ext-link" rel="noopener noreferrer">using my Bunny.net affiliate link</a> (which will directly support the hosting costs of this blog), or buying me a coffee with the links at the bottom of the page. Thanks!</p><p>And let me know how your migration went by leaving a comment, I’d love to hear from you.</p><div class="footnotes"><hr><ol><li id="fn:1"><p>My first time using <a href="https://github.com/mermaid-js/mermaid" target="_blank" class="ext-link" rel="noopener noreferrer">Mermaid</a> diagrams. It’s <a href="https://mermaid.live" target="_blank" class="ext-link" rel="noopener noreferrer">so cool</a>; plus my Hugo theme, <a href="https://hugo-theme-anubis2.netlify.app/" target="_blank" class="ext-link" rel="noopener noreferrer">Anubis2</a> supports it! <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li><li id="fn:2"><p>If you’re interested in <a href="https://bunny.net/?ref=k4vc3x5108" target="_blank" class="ext-link" rel="noopener noreferrer">trying Bunny.net’s services</a>, consider using my affiliate link to show your support. I also found a promo code that extends the trial to over a month; but I’m not sure how long it will last: <code>JSMASTERY</code>. <a href="#fnref:2" class="footnote-backref">↩︎</a> <a href="#fnref1:2" class="footnote-backref">↩︎</a></p></li><li id="fn:3"><p>Note that when creating a Storage zone on Bunny.net, “GEO replication” can only be added, not removed. So start off with less! <a href="#fnref:3" class="footnote-backref">↩︎</a></p></li><li id="fn:4"><p><a href="https://trufflesecurity.com/blog/secrets-leak-in-ci-cd" target="_blank" class="ext-link" rel="noopener noreferrer">Good post</a> about security of secrets in CI/CD workflows. <a href="#fnref:4" class="footnote-backref">↩︎</a></p></li><li id="fn:5"><p>Originally, I wanted Rclone to connect directly to Bunny Storage with an API, without SFTP, using a built-in configuration. However, Rclone (version 1.70.3) does not currently support Bunny.net in its backend, which is why I’m using SFTP instead. <a href="#fnref:5" class="footnote-backref">↩︎</a></p></li><li id="fn:6"><p>Read about Bunny’s take on <a href="https://bunny.net/blog/how-aname-dns-records-affect-cdn-routing/" target="_blank" class="ext-link" rel="noopener noreferrer">ANAME records</a>. <a href="#fnref:6" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/uses/</id><link rel="alternate" href="https://burgeonlab.com/uses/"/><title type="html">Uses: Dependable Services and Tools I Rely On</title><published>2025-08-06T16:33:38Z</published><updated>2025-09-29T00:00:00Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/uses/og_img_uses.webp"/><summary type="html">Discover the tech, gear, and essentials Naty relies on; from hardware, software, and services to daily tools like bags, coffee gear, EDC, watches, and more.</summary><content type="html"><![CDATA[<h2 id="what-is-uses">What Is /uses?<a href="#what-is-uses" class="h-anchor" title="Permalink to #What Is /uses?"></a></h2>   <p>Inspired by the many examples of /uses from <a href="https://slashpages.net/#uses" target="_blank" class="ext-link" rel="noopener noreferrer">Slash Pages</a>, I decided to make my own! It’s a list of all the stuff I personally use. In the future, I plan to add some context and photos to my favourite items.</p><h2 id="tech-hardware">Tech Hardware<a href="#tech-hardware" class="h-anchor" title="Permalink to #Tech Hardware"></a></h2>   <ul><li><strong>Current Phone</strong>: Nothing Phone 3a, 12GB RAM, 256GB, in blue</li><li><strong>Cameras</strong>:<ul><li>Ricoh GR IIIx</li><li>Canon 6D</li><li>Osmo Pocket 1</li><li>Other camera gear on my <a href="https://www.aperture2iris.com/gear" target="_blank" class="ext-link" rel="noopener noreferrer">photography blog</a></li></ul></li><li><strong>Tablet</strong>: iPad Mini 6gen 256GB</li><li><strong>e-Reader</strong>: Kobo Aura One LE (2017)</li><li><strong>Wearable</strong>: Garmin Vivosport</li><li><strong>Security</strong>: Yubico Security Key C NFC, PortaPow (UK) Data Blockers (AKA USB condoms)</li><li><strong>Desktop</strong>: Mac Mini M1 (2020), 16GB RAM, 1TB SSD</li><li><strong>Laptop</strong>: Macbook Air 13" (2012), 8GB RAM, 512GB SSD (max spec) running Linux Mint</li><li><strong>Homelab server</strong>: Raspberry Pi 4B 8GB</li><li><strong>Peripherals</strong>:<ul><li><strong>Keyboards</strong>:<ul><li>Owlab Spring Keyboard</li><li>OLKB Planck rev.4</li><li>Leopold FC660M</li></ul></li><li>Dell Ultrasharp U2415</li><li>Xiaomi Monitor Light Bar 30769</li><li>BenQ ZOWIE EC1-B Mouse</li><li>Kensington Orbit Trackball</li><li>DockCase DPR81C 8 in 1 USB-C Hub</li><li><strong>Audio</strong>:<ul><li>Harman Kardon SoundSticks II Speakers <em>(2004—This is probably the oldest piece of tech I use daily that is still running strong!)</em></li><li>Samson Meteorite (USB condenser) Microphone</li><li><strong>DAC/Amps</strong>:<ul><li>Leckerton UHA-4 Slimline USB DAC/Amp</li><li>E1DA PowerDac V2.1</li><li>EarStudio ES100 V1</li></ul></li><li><strong>Headphones</strong>:<ul><li>Massdrop x Sennheiser HD 6XX</li><li>Beyerdynamic T51p</li><li>Koss PortaPro</li><li>Audiosense DT 200 IEMs</li><li>Moondrop Space Travel TWS</li></ul></li></ul></li><li><strong>Storage</strong>:<ul><li>ACASIS TBU405 Pro 40Gbps Enclosure with Samsung 980 Pro 2TB</li><li>Samsung T3 1TB</li><li>SanDisk Professional G-Drive 4TB (7200RPM Ultrastar 3.5" HDD)</li><li>Seagate IronWolf Pro NAS 8TB (7200RPM 3.5" HDD)</li></ul></li></ul></li></ul><h2 id="tech-software">Tech Software<a href="#tech-software" class="h-anchor" title="Permalink to #Tech Software"></a></h2>   <h3 id="macos">macOS<a href="#macos" class="h-anchor" title="Permalink to #macOS"></a></h3>   <ul><li><strong>Browser</strong>: <a href="https://zen-browser.app/" target="_blank" class="ext-link" rel="noopener noreferrer">Zen</a>, <a href="https://librewolf.net" target="_blank" class="ext-link" rel="noopener noreferrer">LibreWolf</a>, and waiting for <a href="https://ladybird.org/" target="_blank" class="ext-link" rel="noopener noreferrer">LadyBird</a></li><li><strong>Extensions</strong>: <a href="https://ublockorigin.com/" target="_blank" class="ext-link" rel="noopener noreferrer">uBlock Origin</a>, <a href="https://decentraleyes.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Decentraleyes</a>, <a href="https://darkreader.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Dark Reader</a></li><li><strong>Email client</strong>: <a href="https://www.thunderbird.net/en-US/thunderbird/all/" target="_blank" class="ext-link" rel="noopener noreferrer">Thunderbird</a></li><li><strong>Notes</strong>: <a href="https://obsidian.md/" target="_blank" class="ext-link" rel="noopener noreferrer">Obsidian</a></li><li><strong>To-do</strong>: <a href="https://super-productivity.com/" target="_blank" class="ext-link" rel="noopener noreferrer">SuperProductivity</a></li><li><strong>Music</strong>: Self-hosted <a href="https://www.navidrome.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Navidrome</a> with <a href="https://github.com/jeffvli/feishin" target="_blank" class="ext-link" rel="noopener noreferrer">Feishin</a></li><li><strong>Markdown/text editor</strong>: <a href="https://github.com/retext-project/retext" target="_blank" class="ext-link" rel="noopener noreferrer">ReText</a></li><li><strong>IDE</strong>: <a href="https://vscodium.com/" target="_blank" class="ext-link" rel="noopener noreferrer">VSCodium</a></li><li><strong>RSS</strong>: <a href="https://github.com/spacecowboy/Feeder" target="_blank" class="ext-link" rel="noopener noreferrer">FluentReader</a>, <a href="https://netnewswire.com/" target="_blank" class="ext-link" rel="noopener noreferrer">NetNewsWire</a></li><li><strong>Gif recorder</strong>: <a href="https://www.cockos.com/licecap/" target="_blank" class="ext-link" rel="noopener noreferrer">Licecap</a></li><li><strong>Text expander</strong>: <a href="https://espanso.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Espanso</a></li><li><strong>Dictation</strong>: <a href="https://tryvoiceink.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Voiceink</a></li><li><strong>Spotlight alternative</strong>: <a href="https://sol.ospfranco.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Sol</a></li><li><strong>Multi-device sync</strong>: <a href="https://kdeconnect.kde.org/" target="_blank" class="ext-link" rel="noopener noreferrer">KDE Connect</a>, <a href="https://syncthing.net/" target="_blank" class="ext-link" rel="noopener noreferrer">Syncthing</a></li><li><strong>Networking</strong>: <a href="https://objective-see.org/products/lulu.html" target="_blank" class="ext-link" rel="noopener noreferrer">LuLu</a>, <a href="https://tailscale.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Tailscale</a></li><li><strong>Android file manager</strong>: <a href="https://openmtp.ganeshrvel.com/" target="_blank" class="ext-link" rel="noopener noreferrer">OpenMTP</a></li><li><strong>Package manager</strong>: <a href="https://brew.sh/" target="_blank" class="ext-link" rel="noopener noreferrer">Homebrew</a></li><li><strong>Terminal</strong>: Apple Terminal with <a href="https://github.com/fastfetch-cli/fastfetch" target="_blank" class="ext-link" rel="noopener noreferrer">fastfetch</a>, <a href="https://ohmyz.sh/" target="_blank" class="ext-link" rel="noopener noreferrer">Oh My Zsh</a>, <a href="https://github.com/romkatv/powerlevel10k" target="_blank" class="ext-link" rel="noopener noreferrer">powerlevel10k</a> Zsh theme</li><li><strong>Backups</strong>: <a href="https://bombich.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Carbon Copy Cloner</a></li><li><strong>Remote desktop</strong>: <a href="https://www.nomachine.com/" target="_blank" class="ext-link" rel="noopener noreferrer">NoMachine</a></li><li><strong>LLMs</strong>: <a href="https://ollama.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Ollama</a></li><li><strong>Photography apps</strong>: Post-processing software on my <a href="https://aperture2iris.com/gear/#post-processing-software" target="_blank" class="ext-link" rel="noopener noreferrer">photography blog</a></li></ul><h3 id="android">Android<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup><a href="#android" class="h-anchor" title="Permalink to #Android1"></a></h3>   <ul><li><strong>App store</strong>: <a href="https://f-droid.org/packages/org.fdroid.basic/" target="_blank" class="ext-link" rel="noopener noreferrer">F-Droid Basic</a>, <a href="https://accrescent.app/" target="_blank" class="ext-link" rel="noopener noreferrer">Accrescent</a>, Play Store</li><li><strong>Browser</strong>: <a href="https://gitlab.com/ironfox-oss/IronFox" target="_blank" class="ext-link" rel="noopener noreferrer">IronFox</a>, <a href="https://github.com/uazo/cromite" target="_blank" class="ext-link" rel="noopener noreferrer">Cromite</a></li><li><strong>Email client</strong>: <a href="https://email.faircode.eu/" target="_blank" class="ext-link" rel="noopener noreferrer">FairEmail</a></li><li><strong>SMS</strong>: <a href="https://github.com/octoshrimpy/quik" target="_blank" class="ext-link" rel="noopener noreferrer">QUIK</a></li><li><strong>Messages</strong>: <a href="https://signal.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Signal</a>, <a href="https://web.telegram.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Telegram</a>, <a href="https://shop.threema.ch/en" target="_blank" class="ext-link" rel="noopener noreferrer">Threema</a>, <a href="https://matrix.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Matrix (Element X)</a></li><li><strong>2FA</strong>: <a href="https://getaegis.app/" target="_blank" class="ext-link" rel="noopener noreferrer">Aegis</a></li><li><strong>Launcher</strong>: <a href="https://pearlauncher.github.io/about.html" target="_blank" class="ext-link" rel="noopener noreferrer">Pear Launcher</a></li><li><strong>System cleaner</strong>: <a href="https://github.com/d4rken-org/sdmaid-se" target="_blank" class="ext-link" rel="noopener noreferrer">SD Maid SE</a></li><li><strong>Firewall</strong>: <a href="https://netguard.me/" target="_blank" class="ext-link" rel="noopener noreferrer">NetGuard</a></li><li><strong>Reddit</strong>: <a href="https://github.com/QuantumBadger/RedReader" target="_blank" class="ext-link" rel="noopener noreferrer">RedReader</a></li><li><strong>Lemmy</strong>: <a href="https://github.com/LemmyNet/jerboa" target="_blank" class="ext-link" rel="noopener noreferrer">Jerboa</a></li><li><strong>Music</strong>: <a href="https://www.navidrome.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Navidrome</a> via <a href="https://www.symfonium.app/" target="_blank" class="ext-link" rel="noopener noreferrer">Symfonium</a>, <a href="https://github.com/Pittvandewitt/Wavelet" target="_blank" class="ext-link" rel="noopener noreferrer">Wavelet</a></li><li><strong>Dictionary</strong>: <a href="https://www.wordwebsoftware.com/android/WordWeb.html" target="_blank" class="ext-link" rel="noopener noreferrer">WordWeb</a></li><li><strong>Markdown/text editor</strong>: <a href="https://github.com/gsantner/markor" target="_blank" class="ext-link" rel="noopener noreferrer">Markor</a></li><li><strong>Git client</strong>: <a href="https://github.com/ViscousPot/GitSync" target="_blank" class="ext-link" rel="noopener noreferrer">GitSync</a> or <a href="https://github.com/catpuppyapp/PuppyGit" target="_blank" class="ext-link" rel="noopener noreferrer">PuppyGit</a></li><li><strong>IDE</strong>: <a href="https://github.com/massivemadness/Squircle-CE" target="_blank" class="ext-link" rel="noopener noreferrer">Squircle CE</a></li><li><strong>Mastodon</strong>: <a href="https://tusky.app/" target="_blank" class="ext-link" rel="noopener noreferrer">Tusky</a></li><li><strong>UnifiedPush</strong>: <a href="https://unifiedpush.org/users/distributors/sunup/" target="_blank" class="ext-link" rel="noopener noreferrer">Sunup</a></li><li><strong>Reading</strong>: <a href="https://libbyapp.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Libby</a>, <a href="http://koreader.rocks/" target="_blank" class="ext-link" rel="noopener noreferrer">KOReader</a></li><li><strong>Habit tracker</strong>: <a href="https://github.com/FriesI23/mhabit" target="_blank" class="ext-link" rel="noopener noreferrer">Table Habit</a></li><li><strong>Tasks</strong>: <a href="https://tasks.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Tasks.org</a></li><li><strong>Video</strong>: <a href="https://github.com/InfinityLoop1308/PipePipe" target="_blank" class="ext-link" rel="noopener noreferrer">PipePipe</a></li><li><strong>RSS</strong>: <a href="https://github.com/spacecowboy/Feeder" target="_blank" class="ext-link" rel="noopener noreferrer">Feeder</a></li></ul><h3 id="home-lab--self-hosted">Home Lab / Self-hosted<a href="#home-lab--self-hosted" class="h-anchor" title="Permalink to #Home Lab / Self-hosted"></a></h3>   <ul><li><strong>OS</strong>: <a href="https://dietpi.com/" target="_blank" class="ext-link" rel="noopener noreferrer">DietPi</a></li><li><strong>Dashboard</strong>: <a href="https://github.com/bastienwirtz/homer" target="_blank" class="ext-link" rel="noopener noreferrer">Homer</a></li><li><strong>eBooks</strong>: <a href="https://calibre-ebook.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Calibre</a></li><li><strong>DNS</strong>: <a href="https://pi-hole.net/" target="_blank" class="ext-link" rel="noopener noreferrer">Pi-Hole</a></li><li><strong>Music</strong>: <a href="https://www.navidrome.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Navidrome</a></li><li><strong>RSS</strong>: <a href="https://miniflux.app/" target="_blank" class="ext-link" rel="noopener noreferrer">Miniflux</a></li><li><strong>Containers</strong>: <a href="https://github.com/portainer/portainer" target="_blank" class="ext-link" rel="noopener noreferrer">Portainer</a></li></ul><h2 id="online-services">Online Services<a href="#online-services" class="h-anchor" title="Permalink to #Online Services"></a></h2>    <blockquote>  <p><strong>DISCLAIMER</strong>: This section contains affiliate links.</p></blockquote> <ul><li><strong>Search engine</strong>: <a href="https://duckduckgo.com/" target="_blank" class="ext-link" rel="noopener noreferrer">DuckDuckGo</a>, <a href="https://www.startpage.com/" target="_blank" class="ext-link" rel="noopener noreferrer">StartPage</a></li><li><strong>DNS</strong>: <a href="https://quad9.net/" target="_blank" class="ext-link" rel="noopener noreferrer">Quad9</a></li><li><strong>Cloud storage</strong>: <a href="https://koofr.eu/" target="_blank" class="ext-link" rel="noopener noreferrer">Koofr</a>, <a href="https://www.pcloud.com/" target="_blank" class="ext-link" rel="noopener noreferrer">pCloud</a>, <a href="https://tresorit.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Tresorit</a>, <a href="https://www.dropbox.com/referrals/AAAnPKJh0rf2-zsJnKaYzsn25tV8eRQpEVI?src=global9" target="_blank" class="ext-link" rel="noopener noreferrer">Dropbox</a> (all with <a href="https://cryptomator.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Cryptomator</a>)</li><li><strong>Email</strong>: <a href="https://app.tuta.com/signup?ref=Y2xsTkR4cFE&s=0" target="_blank" class="ext-link" rel="noopener noreferrer">Tuta</a>, <a href="https://proton.me/" target="_blank" class="ext-link" rel="noopener noreferrer">Proton</a>, Gmail</li><li><strong>Calendar</strong>: <a href="https://app.tuta.com/signup?ref=Y2xsTkR4cFE&s=0" target="_blank" class="ext-link" rel="noopener noreferrer">Tuta Calendar</a></li><li><strong>Password manager</strong>: <a href="https://bitwarden.com" target="_blank" class="ext-link" rel="noopener noreferrer">Bitwarden</a></li><li><strong>Git forge</strong>: <a href="https://sourcehut.org/" target="_blank" class="ext-link" rel="noopener noreferrer">SourceHut</a> (Leaving GitHub <del>in progress</del> <a href="https://burgeonlab.com/blog/migrate-github-pages-to-sourcehut-bunny/">completed</a>!)</li><li><strong>Domain registrar</strong>: <a href="https://porkbun.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Porkbun</a>, <a href="https://www.ovhcloud.com/en/" target="_blank" class="ext-link" rel="noopener noreferrer">OVH</a></li><li><strong>Static website hosting</strong>: <a href="https://bunny.net?ref=k4vc3x5108" target="_blank" class="ext-link" rel="noopener noreferrer">Bunny.net Storage</a></li><li><strong>WordPress hosting</strong>: <a href="https://hostinger.com?REFERRALCODE=NATYSREF" target="_blank" class="ext-link" rel="noopener noreferrer">Hostinger WP Starter</a></li><li><strong>CDN</strong>: <a href="https://bunny.net?ref=k4vc3x5108" target="_blank" class="ext-link" rel="noopener noreferrer">Bunny CDN</a></li><li><strong>VPN</strong>: <a href="https://mullvad.net/" target="_blank" class="ext-link" rel="noopener noreferrer">Mullvad</a>, <a href="https://www.privateinternetaccess.com/pages/buy-a-vpn/1218buyavpn?invite=U2FsdGVkX1-ZBFREM72o0GzEoACYfdkLcbg-sw3matU%2CWUIKZel6Gqq66DparGnLiMbFTZg" target="_blank" class="ext-link" rel="noopener noreferrer">Private Internet Access</a></li></ul><h2 id="edc-every-day-carry">EDC (Every Day Carry)<a href="#edc-every-day-carry" class="h-anchor" title="Permalink to #EDC (Every Day Carry)"></a></h2>   <p>Here’s some of my favourite brands and specific items.</p><ul><li><strong>Bags</strong>: Gregory, Mystery Ranch, Timbuk2</li><li><strong>Glasses</strong>: Silhouette</li><li><strong>Shoes</strong>: I’m a big proponent of barefoot shoes, been wearing the classic <a href="https://www.camper.com/en_US/content/peu-icons" target="_blank" class="ext-link" rel="noopener noreferrer">Camper Peu</a> design for maybe 20 years already! Nowadays, I’m wearing Vivobarefoot.</li><li><strong>Apparel</strong>: Favourite brand is Patagonia, hands down.</li><li><strong>Water bottle</strong>: Anything from Zojirushi.</li><li><strong>Coffee cup</strong>: KeepCup Brew Cork</li><li><strong>Hand cream</strong>: This is my absolute non-replaceable skincare product: La Roche-Posay Cicaplast Mains.</li><li><strong>Wallet</strong>: The <a href="https://www.kickstarter.com/projects/fireti/omega-compact-solid-titanium-wallet" target="_blank" class="ext-link" rel="noopener noreferrer">FIRETI Omega Ti</a> used to be my favourite, but it’s a bit heavy (and the company no longer exists). A very lightweight wallet I also like is the Micro size from Paperwallet.</li><li><strong>Hardware security keys</strong>: I use the baseline Security Key Series from <a href="https://www.yubico.com/quiz/" target="_blank" class="ext-link" rel="noopener noreferrer">Yubico</a>.</li></ul><div class="footnotes"><hr><ol><li id="fn:1"><p>More info in the <a href="https://burgeonlab.com/blog/hardening-my-new-android-phone/">post about my Android setup</a>. <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/hosting-static-sites-with-github-pages-or-bunny-net/</id><link rel="alternate" href="https://burgeonlab.com/blog/hosting-static-sites-with-github-pages-or-bunny-net/"/><title type="html">Leaving GitHub Pages for Bunny.net: My New Static Site Host</title><published>2025-08-03T16:21:00Z</published><updated>2025-08-05T22:15:00Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/hosting-static-sites-with-github-pages-or-bunny-net/og_img_021.webp"/><summary type="html">I share my experience moving away from GitHub Pages to Bunny.net for static blog hosting, prioritizing privacy and budget. Comparing load speeds and using Bunny&amp;rsquo;s FTP options. Codeberg and SourceHut are considered as alternatives to GitHub.</summary><content type="html"><![CDATA[<h2 id="update-migration-success">Update: Migration Success<a href="#update-migration-success" class="h-anchor" title="Permalink to #Update: Migration Success"></a></h2>   <p>Happy to share that I killed two (and possibly three) birds with one stone; my Hugo is now on sr.ht (SourceHut)!</p><ul><li>Subscribed to SourceHut’s <a href="https://sourcehut.org/pricing/" target="_blank" class="ext-link" rel="noopener noreferrer">Amateur Hacker tier</a> to access <a href="http://builds.sr.ht/" target="_blank" class="ext-link" rel="noopener noreferrer">builds.sr.ht</a>, a CI/CD service similar to GitHub Action</li><li>Moved my Hugo source code from GitHub to <a href="https://git.sr.ht/" target="_blank" class="ext-link" rel="noopener noreferrer">SourceHut’s Git hosting</a> service (hurray <a href="https://fosstodon.org/tags/GiveUpGitHub" target="_blank" class="ext-link" rel="noopener noreferrer">#GiveUpGitHub</a>)</li><li>Wrote up a working <code>build.yml</code> (equivalent to GitHub <code>pages.yml</code>) that deploys Hugo static files in <code>/public</code> to Bunny.net’s Storage/hosting + CDN</li></ul><p>I’m working on a detailed post about the move from GitHub to SourceHut and Bunny.net. But I’ve been really impressed with Bunny’s performance upgrades (as you can read in the blog below) and their customer support was responsive. In fact, this Hugo blog is now running on Bunny.net Edge severs, and I’m still within their trial period using their free $20 credit!</p><p>If you want to test Bunny out for their CDN or hosting (which are the only two services I’ve tested so far), or their other services; you can use my affiliate link: <a href="https://bunny.net?ref=k4vc3x5108" target="_blank" class="ext-link" rel="noopener noreferrer">Try Bunny.net</a>. If you sign up and become a paying user, I may earn a small commission, which helps support this blog. I’ll be sharing more details about my experience with setting up Bunny Storage with SourceHut, rclone, and SFTP <del>in an upcoming post</del> <a href="https://burgeonlab.com/blog/migrate-github-pages-to-sourcehut-bunny/">in this post</a>.</p><p>This is the first deployment from builds.sr.ht! <a href="https://sr.ht/~eclecticpassions" target="_blank" class="ext-link" rel="noopener noreferrer"><img src="https://builds.sr.ht/~eclecticpassions.svg" alt="builds.sr.ht status"></a></p><p>End of update.</p><hr><h2 id="preface">Preface<a href="#preface" class="h-anchor" title="Permalink to #Preface"></a></h2>    <blockquote>  <p>“When a product is free, it’s because you are the product.”</p></blockquote> <p>This long-standing quote I’ve read many years ago has been at the forefront of my mind lately. There are so many online services that are free outright, or have free tiers, but what <em>price</em> are we really <em>paying</em>? We are paying with our digital footprint, personal data, and also our time and attention. There’s more—but that’s for another post. I am more aware and conscious of the services I’m using nowadays and trying to slowly migrate away from “Big Tech”, especially those from the US. I’ve already worked on this ongoing “migration project” with email, cloud storage, etc, so I’m excited to tackle this area (code repositories, hosting, CDN) next.</p><h3 id="moving-away-from-big-data">Moving Away From Big Data<a href="#moving-away-from-big-data" class="h-anchor" title="Permalink to #Moving Away From Big Data"></a></h3>   <p>Currently, BurgeonLab.com is hosted on GitHub Pages for <em>free</em>. In this post, I will be sharing my experience of moving away from Microsoft’s GitHub Pages free hosting. After leaving GitHub for hosting, I’ll be migrating repos to another Git versioning platform next (I’m leaning towards SourceHut). Switching to static site hosting is the first step for me in preparing to ditch GitHub in my main blogging workflow, for a better, more privacy-friendly alternative like <a href="https://codeberg.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Codeberg</a>, <a href="https://sourcehut.org/" target="_blank" class="ext-link" rel="noopener noreferrer">SourceHut</a> or self-hosting <a href="https://forgejo.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Forgejo</a>, <a href="https://gitea.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Gitea</a>, etc.</p> <blockquote class="alert alert-tip">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Tip      </p>  <p>If you want a guide on transferring your Hugo blog deployment from GitHub to an alternative like Codeberg, I highly recommend this post by <a href="https://hachyderm.io/@theaitch" target="_blank" class="ext-link" rel="noopener noreferrer">@theaitch</a> on his <a href="https://www.codedge.de/posts/hugo-with-codeberg-bunnycdn/" target="_blank" class="ext-link" rel="noopener noreferrer">blog</a>!</p></blockquote> <h2 id="static-blog-hosting">Static Blog Hosting<a href="#static-blog-hosting" class="h-anchor" title="Permalink to #Static Blog Hosting"></a></h2>   <p>I have a multi-year WordPress starter deal on Hostinger with unused resources, which is why my Hugo photography blog (under construction)  is hosted there along with my WordPress general blog. But I have already decided I won’t be paying the renewal prices when this plan expires next year. This is why I am looking for an alternative solution that is reliable, budget-friendly now in advance.</p> <blockquote class="alert alert-note">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Note      </p>  <p>I don’t have <strong>any</strong> bad experience with Hostinger, they’re actually really good for their introductory price (I bought a 48-month plan during Black Friday) and their customer support has always been really fast and extensive. For someone starting out, like me back in the day, I would highly recommend them. Their hPanel (dashboard interface) is really comprehensive and I felt at ease even though I was a first-timer.</p><p>I am changing hosts because I want a more budget solution for static site hosting only (no need for a dynamic WP plan because I want to swap out WordPress CMS in the future too).</p><p>Here’s a referral code if you do decide to try out <a href="https://hostinger.com?REFERRALCODE=NATYSREF" target="_blank" class="ext-link" rel="noopener noreferrer">Hostinger</a> (affiliate link, 20% discount). I may earn a commission if you become a paid user which helps support this blog.</p></blockquote> <p>My hosting requirements:</p><ul><li>Fast site performance and low latency/load times</li><li>EU-based</li><li>Has ability to change security headers like Content-Security-Policy and set cache durations with Cache-Control header</li><li>Budget friendly</li></ul><p>I did consider going all manual by getting my first VPS/root server, but I still haven’t found one that suits me <strong>and</strong> doesn’t ask for a whole load of personal information.</p><p>Static site web hosting is plentiful but I heard of good things from Bunny.net with its CDN service (I already had them in my list to swap out another service—Cloudflare). What I didn’t know was they offer a product called <a href="https://bunny.net/storage/ssd/" target="_blank" class="ext-link" rel="noopener noreferrer">Bunny Storage</a> which is pay-as-you-go with a $1 minimum monthly payment. It sounds good on paper, so I’m currently on their 14-day trial to do some comparisons.</p><h2 id="speed-comparison">Speed Comparison<a href="#speed-comparison" class="h-anchor" title="Permalink to #Speed Comparison"></a></h2>   <p>I’m using <a href="https://tools.pingdom.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Pingdom</a> to test the page load speed.</p><h3 id="hostinger--cloudflare-cdn-vs-bunnynet">Hostinger + Cloudflare CDN vs Bunny.net<a href="#hostinger--cloudflare-cdn-vs-bunnynet" class="h-anchor" title="Permalink to #Hostinger + Cloudflare CDN vs Bunny.net"></a></h3>                                <a href="/blog/hosting-static-sites-with-github-pages-or-bunny-net/021_photoblog.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hosting-static-sites-with-github-pages-or-bunny-net/021_photoblog.webp" alt="Screenshot comparing speed test of two different hosts.">    </a>                 <p>Bunny.net seems to win out big time <strong>(76.7% quicker)</strong> on this comparison, but it could be the different CDNs used that is causing the load time discrepancy.</p><h3 id="github-pages-vs-bunnynet">GitHub Pages vs Bunny.net<a href="#github-pages-vs-bunnynet" class="h-anchor" title="Permalink to #GitHub Pages vs Bunny.net"></a></h3>                                <a href="/blog/hosting-static-sites-with-github-pages-or-bunny-net/021_techblog.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hosting-static-sites-with-github-pages-or-bunny-net/021_techblog.webp" alt="Screenshot comparing speed test of two different hosts.">    </a>                 <p>In terms of comparing between Bunny.net and the more limited GitHub Pages hosting, Bunny.net has also won this round by <strong>31.5% reduction</strong>  in load time.</p><h2 id="bunnynet-technicalities">Bunny.net Technicalities<a href="#bunnynet-technicalities" class="h-anchor" title="Permalink to #Bunny.net Technicalities"></a></h2>   <p>Because I haven’t gotten round to moving my source repos from GitHub yet, I’m just testing Bunny.net Storage by dumping the contents of the Hugo <code>/public</code> folder with <a href="https://docs.bunny.net/docs/edge-storage-ftp" target="_blank" class="ext-link" rel="noopener noreferrer">SSH File Transfer Protocol (SFTP)</a> using Mountain Duck.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup></p><p>The settings for SFTP on Mountain Duck (or other FTP clients) for Bunny.net Storage are:</p><table>  <thead>      <tr>          <th>Field</th>          <th>Setting</th>      </tr>  </thead>  <tbody>      <tr>          <td>URL</td>          <td><code>sftp://storage.bunnycdn.com</code></td>      </tr>      <tr>          <td>Server/hostname</td>          <td><code>storage.bunnycdn.com</code><sup id="fnref:2"><a href="#fn:2" class="footnote-ref">2</a></sup></td>      </tr>      <tr>          <td>Port</td>          <td>22 (port 21 is for FTP only, not secure)</td>      </tr>      <tr>          <td>Username</td>          <td><code>Storage Zone Name</code></td>      </tr>      <tr>          <td>Password</td>          <td>Paste the <code>generated password</code> (not the read-only one) in Delivery > Storage > FTP & API access</td>      </tr>      <tr>          <td>SSH Private Key</td>          <td>Select <code>None</code></td>      </tr>  </tbody></table><p>The client will probably ask if you want to trust the destination server’s fingerprint. Bunny.net customer support confirmed its correctness.</p>                             <a href="/blog/hosting-static-sites-with-github-pages-or-bunny-net/021_fingerprint.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hosting-static-sites-with-github-pages-or-bunny-net/021_fingerprint.webp" alt="Screenshot of Mountain Duck FTP client confirming connecting to a new fingerprint.">    </a>       <h2 id="remaining-tasks">Remaining Tasks<a href="#remaining-tasks" class="h-anchor" title="Permalink to #Remaining Tasks"></a></h2>   <p>I have to work out a few remaining jobs before the move is complete:</p><ul><li><input checked="" disabled="" type="checkbox"> Set DNS and custom domain name settings</li><li><input checked="" disabled="" type="checkbox"> Update the CI/CD blog workflow with alternatives to GitHub Actions or Webhooks</li><li><input checked="" disabled="" type="checkbox"> Since my blogs are not under open licence, I don’t think I can use Codeberg. Will investigate SourceHut as I’m leaning towards it more (nice <a href="https://blog.strus.guru/2022/04/going-sourcehut/" target="_blank" class="ext-link" rel="noopener noreferrer">SourceHut overview post by Marcin</a> and <a href="https://adol.pw/2022/06/04/why-sourcehut/" target="_blank" class="ext-link" rel="noopener noreferrer">captainepoch’s vouch for Sourcehut</a>; although both posts are from 2022).</li><li><input checked="" disabled="" type="checkbox"> Read about GZIP <a href="https://support.bunny.net/hc/en-us/articles/115001200111-Which-MIME-types-does-BunnyCDN-support-for-compression" target="_blank" class="ext-link" rel="noopener noreferrer">compression</a></li></ul><p><del>Will update the post when this blog is officially hosted on Bunny.net and source code on SourceHut!</del> Check the <a href="https://burgeonlab.com/blog/hosting-static-sites-with-github-pages-or-bunny-net/">update</a> at the top of the post.</p><div class="footnotes"><hr><ol><li id="fn:1"><p><a href="https://mountainduck.io/" target="_blank" class="ext-link" rel="noopener noreferrer">Mountain Duck</a> is based on <a href="https://cyberduck.io/" target="_blank" class="ext-link" rel="noopener noreferrer">Cyberduck</a> which is FOSS. (There is a CLI version called <a href="https://duck.sh/" target="_blank" class="ext-link" rel="noopener noreferrer">Duck</a>.) Mountain Duck adds proprietary features to mount servers and cloud storage as local drives on macOS. I got this licence many years ago from one of those “bundle” deals but never got round to using it much! <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li><li id="fn:2"><p>Check your primary storage location and use the correct storage zone URL listed in the <a href="https://docs.bunny.net/reference/storage-api#storage-endpoints" target="_blank" class="ext-link" rel="noopener noreferrer">documentation</a>. <a href="#fnref:2" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/retext-code-block-syntax-highlighting-bug/</id><link rel="alternate" href="https://burgeonlab.com/blog/retext-code-block-syntax-highlighting-bug/"/><title type="html">Retext Markdown Editor: Code Block Syntax Highlighting Bug</title><published>2025-08-01T17:47:00Z</published><updated>2025-08-01T17:47:00Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/images/default_post_cover.png"/><summary type="html">Fixed a syntax highlighting bug in ReText with guidance from genAI. Learned Git basics, made a pull request, and got merged.</summary><content type="html"><![CDATA[<h2 id="fixing-my-first-bug">Fixing My First Bug<a href="#fixing-my-first-bug" class="h-anchor" title="Permalink to #Fixing My First Bug"></a></h2>   <p><a href="https://fosstodon.org/@Minty95" target="_blank" class="ext-link" rel="noopener noreferrer">@Minty95</a>, one of my Mastodon followers, has brought to my attention a problem he’s had with <a href="https://github.com/retext-project/retext" target="_blank" class="ext-link" rel="noopener noreferrer">ReText’s</a> syntax highlighting of <a href="https://github.com/retext-project/retext/issues/667" target="_blank" class="ext-link" rel="noopener noreferrer">fenced code blocks</a>. I tried it on my Mac and sure enough, the bug was present. So as a challenge, I tried fixing it. I’m happy to say I managed to troubleshoot the issue, albeit with some guidance from genAI (Gemini-2.0-Flash and Perplexity.AI). This is the first time I fixed some app code! 🥳</p><p>I had to <a href="https://burgeonlab.com/blog/my-first-github-pull-request/">learn how to do a pull request (PR)</a> and some more Git basics (like new branches and setting upstream), but it went smoothly. ReText’s creator <a href="https://github.com/mitya57" target="_blank" class="ext-link" rel="noopener noreferrer">Dmitry</a> gave me clear comments for the code review and then the changes were merged! (Thanks Dmitry for making ReText available for all!)</p><p>A new release has not been announced, so here are the changes I made (more for my own reference).</p><h2 id="issue">Issue<a href="#issue" class="h-anchor" title="Permalink to #Issue"></a></h2>   <p>Backticks and language of fenced code blocks not highlighted properly:</p>                             <a href="/blog/retext-code-block-syntax-highlighting-bug/020_before.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/retext-code-block-syntax-highlighting-bug/020_before.webp" alt="Screenshot of ReText Markdown editor not highlighting backticks correctly.">    </a>       <p>After fix:</p>                             <a href="/blog/retext-code-block-syntax-highlighting-bug/020_fixed.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/retext-code-block-syntax-highlighting-bug/020_fixed.webp" alt="Screenshot of ReText Markdown editor highlighting backticks correctly.">    </a>       <h2 id="fix">Fix<a href="#fix" class="h-anchor" title="Permalink to #Fix"></a></h2>   <p>We need to make changes to three files: the config file, <code>editor.py</code>, and <code>highlighter.py</code>.</p><h3 id="config">Config<a href="#config" class="h-anchor" title="Permalink to #Config"></a></h3>   <p>Add these two parameters under Color Scheme in the ReText configuration file. Choose your own colours! (I have a previous post that covers <a href="https://burgeonlab.com/blog/customize-retext-markdown-editor/">ReText customization</a>.)</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">[ColorScheme]</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="na">codeBlock</span><span class="o">=</span><span class="s">orange</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="na">codeSpans</span><span class="o">=</span><span class="s">yellowgreen</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h3 id="editorpy">Editor.py<a href="#editorpy" class="h-anchor" title="Permalink to #Editor.py"></a></h3>   <p>Add a new line to define the colours of the newly created codeBlock:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python"><span class="line"><span class="ln">1</span><span class="cl"><span class="err">`</span><span class="n">codeBlock</span><span class="err">`</span><span class="p">:</span> <span class="p">{</span><span class="s1">'light'</span><span class="p">:</span> <span class="s1">'#aa6600'</span><span class="p">,</span> <span class="s1">'dark'</span><span class="p">:</span> <span class="s1">'#fe5f01'</span><span class="p">}</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>Here’s a truncated snippet:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">colors</span> <span class="o">=</span> <span class="p">{</span></span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="c1"># Editor</span></span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="s1">'marginLine'</span><span class="p">:</span>           <span class="p">{</span><span class="s1">'light'</span><span class="p">:</span> <span class="s1">'#dcd2dc'</span><span class="p">,</span> <span class="s1">'dark'</span><span class="p">:</span> <span class="s1">'#3daee9'</span><span class="p">},</span></span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="s1">'currentLineHighlight'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'light'</span><span class="p">:</span> <span class="s1">'#ffffc8'</span><span class="p">,</span> <span class="s1">'dark'</span><span class="p">:</span> <span class="s1">'#31363b'</span><span class="p">},</span></span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="c1"># Highlighter</span></span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="s1">'htmlComments'</span><span class="p">:</span>         <span class="p">{</span><span class="s1">'light'</span><span class="p">:</span> <span class="s1">'#a0a0a4'</span><span class="p">,</span> <span class="s1">'dark'</span><span class="p">:</span> <span class="s1">'#b0b0aa'</span><span class="p">},</span></span></span><span class="line"><span class="ln">7</span><span class="cl">    <span class="s1">'codeSpans'</span><span class="p">:</span>            <span class="p">{</span><span class="s1">'light'</span><span class="p">:</span> <span class="s1">'#505050'</span><span class="p">,</span> <span class="s1">'dark'</span><span class="p">:</span> <span class="s1">'#afafaf'</span><span class="p">},</span></span></span><span class="line"><span class="ln">8</span><span class="cl">    <span class="s1">'codeBlock'</span><span class="p">:</span>            <span class="p">{</span><span class="s1">'light'</span><span class="p">:</span> <span class="s1">'#aa6600'</span><span class="p">,</span> <span class="s1">'dark'</span><span class="p">:</span> <span class="s1">'#fe5f01'</span><span class="p">},</span> <span class="c1"># Add this line</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h3 id="highlighterpy">Highlighter.py<a href="#highlighterpy" class="h-anchor" title="Permalink to #Highlighter.py"></a></h3>   <p>Add two lines around line 25, below <code>from ReText.editor import getColor</code>:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">reFencedCodeStart</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">'^(`{3,})(\w+)?\s*$'</span><span class="p">)</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">reFencedCodeEnd</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">'^`{3,}\s*$'</span><span class="p">)</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>Secondly, add this new block after <code>def highlightBlock(self, text):</code> around line 157:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python"><span class="line"><span class="ln"> 1</span><span class="cl">    <span class="k">def</span> <span class="nf">highlightBlock</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">text</span><span class="p">):</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl">        <span class="c1"># If inside fenced code block (not the fence line)</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl">        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">docType</span> <span class="o">==</span> <span class="s1">'Markdown'</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">previousBlockState</span><span class="p">()</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl">            <span class="n">fmt</span> <span class="o">=</span> <span class="n">QTextCharFormat</span><span class="p">()</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl">            <span class="n">fmt</span><span class="o">.</span><span class="n">setFontItalic</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"></span></span><span class="line"><span class="ln"> 7</span><span class="cl">            <span class="c1"># Check if this line closes fenced block</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl">            <span class="k">if</span> <span class="n">reFencedCodeEnd</span><span class="o">.</span><span class="k">match</span><span class="p">(</span><span class="n">text</span><span class="p">):</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl">                <span class="c1"># Fence closes: highlight fence line in yellowgreen (codeSpans)</span></span></span><span class="line"><span class="ln">10</span><span class="cl">                <span class="n">fmt</span><span class="o">.</span><span class="n">setForeground</span><span class="p">(</span><span class="n">getColor</span><span class="p">(</span><span class="s1">'codeSpans'</span><span class="p">))</span>  <span class="c1"># fence color yellowgreen</span></span></span><span class="line"><span class="ln">11</span><span class="cl">                <span class="bp">self</span><span class="o">.</span><span class="n">setFormat</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">text</span><span class="p">),</span> <span class="n">fmt</span><span class="p">)</span></span></span><span class="line"><span class="ln">12</span><span class="cl">                <span class="bp">self</span><span class="o">.</span><span class="n">setCurrentBlockState</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span></span></span><span class="line"><span class="ln">13</span><span class="cl">            <span class="k">else</span><span class="p">:</span></span></span><span class="line"><span class="ln">14</span><span class="cl">                <span class="c1"># Inside fenced code: highlight in orange (codeBlock)</span></span></span><span class="line"><span class="ln">15</span><span class="cl">                <span class="n">fmt</span><span class="o">.</span><span class="n">setForeground</span><span class="p">(</span><span class="n">getColor</span><span class="p">(</span><span class="s1">'codeBlock'</span><span class="p">))</span>  <span class="c1"># code block content color orange</span></span></span><span class="line"><span class="ln">16</span><span class="cl">                <span class="bp">self</span><span class="o">.</span><span class="n">setFormat</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">text</span><span class="p">),</span> <span class="n">fmt</span><span class="p">)</span></span></span><span class="line"><span class="ln">17</span><span class="cl">                <span class="bp">self</span><span class="o">.</span><span class="n">setCurrentBlockState</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></span></span><span class="line"><span class="ln">18</span><span class="cl">            <span class="k">return</span></span></span><span class="line"><span class="ln">19</span><span class="cl"></span></span><span class="line"><span class="ln">20</span><span class="cl">        <span class="c1"># If this line is a fence opening line (3+ backticks with optional lang)</span></span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">docType</span> <span class="o">==</span> <span class="s1">'Markdown'</span> <span class="ow">and</span> <span class="n">reFencedCodeStart</span><span class="o">.</span><span class="k">match</span><span class="p">(</span><span class="n">text</span><span class="p">):</span></span></span><span class="line"><span class="ln">22</span><span class="cl">            <span class="n">fmt</span> <span class="o">=</span> <span class="n">QTextCharFormat</span><span class="p">()</span></span></span><span class="line"><span class="ln">23</span><span class="cl">            <span class="n">fmt</span><span class="o">.</span><span class="n">setForeground</span><span class="p">(</span><span class="n">getColor</span><span class="p">(</span><span class="s1">'codeSpans'</span><span class="p">))</span>  <span class="c1"># fence color yellowgreen</span></span></span><span class="line"><span class="ln">24</span><span class="cl">            <span class="n">fmt</span><span class="o">.</span><span class="n">setFontItalic</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span></span></span><span class="line"><span class="ln">25</span><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">setFormat</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">text</span><span class="p">),</span> <span class="n">fmt</span><span class="p">)</span></span></span><span class="line"><span class="ln">26</span><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">setCurrentBlockState</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></span></span><span class="line"><span class="ln">27</span><span class="cl">            <span class="k">return</span></span></span><span class="line"><span class="ln">28</span><span class="cl"></span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="c1"># Otherwise, normal highlighting for text outside fenced blocks</span></span></span><span class="line"><span class="ln">30</span><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">setCurrentBlockState</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/my-first-github-pull-request/</id><link rel="alternate" href="https://burgeonlab.com/blog/my-first-github-pull-request/"/><title type="html">Making My First Pull Request on GitHub</title><published>2025-07-21T22:26:22Z</published><updated>2025-07-21T22:26:22Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/my-first-github-pull-request/og_img_019.webp"/><summary type="html">This post breaks down the pull request process into simple steps, perfect for Git beginners. I share my experience making my first contributions, including setting up your repository, working on a forked branch, and submitting a pull request. Plus, get maintenance tips to keep your GitHub repo clean and organized.</summary><content type="html"><![CDATA[<h2 id="making-my-first-contributions">Making My First Contributions<a href="#making-my-first-contributions" class="h-anchor" title="Permalink to #Making My First Contributions"></a></h2>   <p>My first thought of contributing to an open source project began when I first started using Hugo. Having added extra features and making improvements to the theme base code (<a href="https://github.com/hugo-theme-anubis2/" target="_blank" class="ext-link" rel="noopener noreferrer">Anubis2</a>), I realised some of them can be a PR (<a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests" target="_blank" class="ext-link" rel="noopener noreferrer">pull request</a>) so the rest of the users of the theme can benefit. I never got round to doing it as it seemed a bit daunting as a Git newbie.</p>                             <a href="/blog/my-first-github-pull-request/new_contributor.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/my-first-github-pull-request/new_contributor.webp" alt="Screenshot of the changelog in Anubis2 hugo theme repo with a mention of a new contributor.">    </a>                 <h3 id="what-prompted-me">What Prompted Me<a href="#what-prompted-me" class="h-anchor" title="Permalink to #What Prompted Me"></a></h3>   <p>Two things triggered my desire to take the first step in creating PRs:</p><ul><li>The friendliness and encouragement from <a href="https://github.com/Junyi-99" target="_blank" class="ext-link" rel="noopener noreferrer">Junyi</a>, the theme’s dev/creator, who suggested that I make my first PR for the fixes I made in the <code>RSS.xml</code> template.</li><li>One of my Mastodon followers, <a href="https://fosstodon.org/@Minty95" target="_blank" class="ext-link" rel="noopener noreferrer">@Minty95</a>, has shared with me a problem he’s had with <a href="https://github.com/retext-project/retext" target="_blank" class="ext-link" rel="noopener noreferrer">ReText’s</a> syntax highlighting of fenced code blocks. I managed to fix the bug and really want to share it with other users of this app. It’s more complex than the Hugo theme changes, so I’m still working on it…</li></ul>                             <a href="/blog/my-first-github-pull-request/github_pull_shark.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/my-first-github-pull-request/github_pull_shark.webp" alt="Screenshot of github profile page with pull shark achievement badge.">    </a>                 <h3 id="merge-success">Merge Success<a href="#merge-success" class="h-anchor" title="Permalink to #Merge Success"></a></h3>   <p>After reading the basic procedures and best practices, it didn’t seem too bad; so I tried out a few PRs with the hugo-theme-anubis2 project and I am happy to say, all three PRs I submitted were merged with the project’s main branch! 🥳</p>                             <a href="/blog/my-first-github-pull-request/merged_pull_requests.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/my-first-github-pull-request/merged_pull_requests.webp" alt="Screenshot of commit history in github.com with merges of pull requests.">    </a>       <p>I’m especially pleased with the <a href="https://github.com/hugo-theme-anubis2/hugo-theme-anubis2/pull/105" target="_blank" class="ext-link" rel="noopener noreferrer">RSS validation fix</a> because the before and after is significant.</p><h2 id="creating-a-pull-request-step-by-step-guide">Creating a Pull Request: Step-by-Step Guide<a href="#creating-a-pull-request-step-by-step-guide" class="h-anchor" title="Permalink to #Creating a Pull Request: Step-by-Step Guide"></a></h2>   <p>Here’s my simple notes of what I did to create my first pull request on <a href="https://github.com/" target="_blank" class="ext-link" rel="noopener noreferrer">GitHub.com</a>. Hope after this short tutorial, you will find it less intimidating to contribute to projects you like! <strong>I will assume you have some basic knowledge about Git and using Terminal commands.</strong></p><h3 id="initial-repository-setup">Initial Repository Setup<a href="#initial-repository-setup" class="h-anchor" title="Permalink to #Initial Repository Setup"></a></h3>   <ul><li><p>Fork (duplicate) a project/repository into your own GitHub account by going to the project’s repository page and click the “Fork” button.</p></li><li><p>Clone (download) a copy of the forked project onto your local machine using the remote URL (HTTPS or SSH)<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup>: <code>git clone <remoteURL></code></p></li></ul>                             <a href="/blog/my-first-github-pull-request/remote_url.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/my-first-github-pull-request/remote_url.webp" alt="Screenshot of github.com clone URL with SSH.">    </a>       <ul><li><p>Once the project is downloaded onto your local machine, open it in an IDE like VS Codium (my personal preference) and open a Terminal window. <code>cd</code> into the newly cloned repo.</p></li><li><p>To sync remote changes from the original project’s repo onto the local fork, we configure an upstream remote for the original repo (AKA upstream).</p><ul><li><p>Copy the original project’s remoteURL</p></li><li><p>Check the current remote with <code>git remote -v</code>, it will show something like this:</p></li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">1</span><span class="cl">> git remote -v</span></span><span class="line"><span class="ln">2</span><span class="cl">origin <git@github.com>:your-username/projectname.git <span class="o">(</span>fetch<span class="o">)</span></span></span><span class="line"><span class="ln">3</span><span class="cl">origin <git@github.com>:your-username/projectname.git <span class="o">(</span>push<span class="o">)</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><ul><li><p>Add upstream remote:<br><code>git remote add upstream git@github.com:some-developer/projectname.git</code></p></li><li><p>Check remote again with <code>git remote -v</code>. The <code>origin</code> should point to the fork, and <code>upstream</code> should point to the original repo.</p></li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">1</span><span class="cl">> git remote -v</span></span><span class="line"><span class="ln">2</span><span class="cl">origin git@github.com:your-username/projectname.git <span class="o">(</span>fetch<span class="o">)</span></span></span><span class="line"><span class="ln">3</span><span class="cl">origin git@github.com:your-username/projectname.git <span class="o">(</span>push<span class="o">)</span></span></span><span class="line"><span class="ln">4</span><span class="cl">upstream git@github.com:some-developer/projectname.git <span class="o">(</span>fetch<span class="o">)</span></span></span><span class="line"><span class="ln">5</span><span class="cl">upstream git@github.com:some-developer/projectname.git <span class="o">(</span>push<span class="o">)</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div></li><li><p>To sync your fork, we need to fetch(download) the latest changes from the upstream repo</p><ul><li><p>First make sure you’re on the <code>master</code>(sometimes called <code>main</code>) branch:</p><p><code>git checkout master</code></p></li><li><p>Fetch updates from the remote (on GitHub.com) project’s repo:</p><p><code>git fetch upstream</code></p></li><li><p>Merge(combine) the changes from the upstream repo into the local repo:</p><p><code>git merge upstream/master</code></p></li></ul></li></ul><h3 id="work-on-a-local-forked-branch">Work On a Local Forked Branch<a href="#work-on-a-local-forked-branch" class="h-anchor" title="Permalink to #Work On a Local Forked Branch"></a></h3>   <ul><li><p>This is the setup portion done! The local forked repo can now be updated as required. We can start working on our contribution, suggestion, new feature, bug fix, etc. To do this we must create a new branch.</p><ul><li><p>Check what branch we’re currently on: <code>git branch</code></p></li><li><p>Create a new branch with a descriptive and meaningful name, describing what your proposed change will do, e.g. NewFeature: <code>git checkout -b NewFeature</code></p></li><li><p>Make the changes. Once completed, stage the changes and add commit messages:<br><code>git add .</code> and <code>git commit -m "Add new feature."</code></p></li><li><p>Now push the changes in the NewFeature branch back into your forked repo on GitHub.com.<br><code>git push -u origin NewFeature</code><sup id="fnref:2"><a href="#fn:2" class="footnote-ref">2</a></sup></p></li></ul> <blockquote class="alert alert-tip">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Tip      </p>  <p>If you want to automatically set the upstream on pushes of new branches, use global Git config setting:<code>git config --global push.autoSetupRemote true</code></p></blockquote> </li></ul><h3 id="actual-pull-request">Actual Pull Request<a href="#actual-pull-request" class="h-anchor" title="Permalink to #Actual Pull Request"></a></h3>   <ul><li>Finally, go back to GitHub.com to check the pushed changes in <strong>your</strong> forked repo. There will be a banner near the top notifying us of the recent new branch being pushed. Click “Compare & pull request”.</li></ul>                             <a href="/blog/my-first-github-pull-request/new_push%20.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/my-first-github-pull-request/new_push%20.webp" alt="Screenshot of github showing new push to forked repo, ready for compare & pull request.">    </a>       <ul><li><p>On the “Comparing changes” page, there is a small bar at the top, with a left pointing arrow in the middle, if not click “compare across forks”.</p><ul><li>The <strong>right</strong> of the arrow is the <strong>head</strong>, which is the NewFeature branch in your forked repo that you just pushed.</li><li>The <strong>left</strong> of the arrow is the <strong>base</strong>, which is the original project’s main/master branch.</li></ul>                                                       <a href="/blog/my-first-github-pull-request/github_pr_create.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">        <img src="/blog/my-first-github-pull-request/github_pr_create.webp" alt="Screenshot of the pull request ">      </a>           <ul><li>Fill in the title and description of your changes. You can see the <code>diff</code> view (differences between the old and new files) at the bottom of the page. Click “Create pull request” once you’re satisfied with the changes.</li></ul>                                                       <a href="/blog/my-first-github-pull-request/diff_view.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">        <img src="/blog/my-first-github-pull-request/diff_view.webp" alt="Screenshot of diff view on github.com.">      </a>           </li><li><p>Wait patiently for the devs to review your code, discuss and revise when necessary with the maintainers. If changes are required, just do the work locally, commit them, and push it to your fork. The PR will update itself.</p></li><li><p>Each project has their own rules or guidelines for making PRs, so check if there’s a <code>CONTRIBUTING.md</code> before creating one for a smooth experience.</p></li></ul><h3 id="maintenance-tips">Maintenance Tips<a href="#maintenance-tips" class="h-anchor" title="Permalink to #Maintenance Tips"></a></h3>   <ul><li><p>It is good practice to delete branches that have successfully merged with the base repo, and the PR completed. This helps reduce confusion, keeps things tidy, and prevents us from working on old branches accidentally.</p><ul><li><p>To delete a local branch, make sure you’re not actively on that branch; therefore, switch to a branch like master/main: <code>git checkout master</code></p></li><li><p>Delete the finished/completed/merged branch with: <code>git branch -d <branch-name></code></p></li><li><p>If you prefer the GUI method you can “View all branches” and delete your branches in the list view.</p></li></ul>                                                                                       <a href="/blog/my-first-github-pull-request/old_branches.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">          <img src="/blog/my-first-github-pull-request/old_branches.webp" alt="Screenshot of accessing all branches in a repo on github.com.">        </a>                                                                                                      <a href="/blog/my-first-github-pull-request/delete_branches.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">          <img src="/blog/my-first-github-pull-request/delete_branches.webp" alt="Screenshot of all the branches in a repo on github.com.">        </a>               </li><li><p>If for some reason, you have no future contribution plans to a project, you can consider deleting the fork off your GitHub repo. This is especially if it is consuming your GitHub resources (e.g. Action minutes, storage, etc).</p></li><li><p>If you are planning on future contributions though, you can always archive the fork. This makes it read-only so it doesn’t consume resources. Go to the repo’s settings and find the “Archive this repository”.</p></li></ul><h2 id="conclusion">Conclusion<a href="#conclusion" class="h-anchor" title="Permalink to #Conclusion"></a></h2>   <p>Congrats, you have gone through how to make a pull request! If you’re like me, a newbie to all this, I genuinely hope you feel more confident to creating a PR for your favourite projects. I have now submitted four—but probably have another few to add to my current Hugo theme’s repo (Anubis2). Hopefully the more complex PR with the ReText syntax highlighting bug will go smoothly too. 🤞</p><div class="footnotes"><hr><ol><li id="fn:1"><p>This <a href="https://docs.github.com/en/get-started/git-basics/about-remote-repositories" target="_blank" class="ext-link" rel="noopener noreferrer">documentation</a> explains the different types of remote URLs you can use to access a repo. SSH is generally recommended after you’ve set up SSH keys. <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li><li id="fn:2"><p>The <code>-u</code> flag option sets the upstream tracking branch, so future git push and git pull commands will work without specifying the remote and branch. <a href="#fnref:2" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/customize-retext-markdown-editor/</id><link rel="alternate" href="https://burgeonlab.com/blog/customize-retext-markdown-editor/"/><title type="html">How to Customize ReText: Python Markdown Editor</title><published>2025-07-19T13:31:11Z</published><updated>2025-07-19T13:31:11Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/customize-retext-markdown-editor/retext_solarizeddark.webp"/><summary type="html">Learn how to customize the ReText Markdown editor with this guide. Explore configuration file tweaks, style sheet adjustments for both the WebEngine preview and Markdown editor panes, and how to use Python Markdown extensions for enhanced functionality. Includes code highlighting setup with Pygements.</summary><content type="html"><![CDATA[<h2 id="retext-markdown-editor">ReText Markdown Editor<a href="#retext-markdown-editor" class="h-anchor" title="Permalink to #ReText Markdown Editor"></a></h2>   <p>In this post I’ll run through all the <a href="https://github.com/retext-project/retext" target="_blank" class="ext-link" rel="noopener noreferrer">ReText</a> customization options I know. I feel it’s a bit convoluted at times—perhaps I’m making it more confusing than it should be? Anyway, I hope this guide can help anyone who is new to ReText get started quickly.</p><h2 id="prerequisites">Prerequisites<a href="#prerequisites" class="h-anchor" title="Permalink to #Prerequisites"></a></h2>   <ul><li><a href="https://github.com/retext-project/retext/wiki/Installing-ReText" target="_blank" class="ext-link" rel="noopener noreferrer">Install</a> ReText. I am on macOS 15.5 (Sequoia) and installed ReText v8.1.0, using Method B which I’ve gone over in my <a href="https://burgeonlab.com/blog/retext-markdown-editor-how-to-install-python-apps-on-mac/">ReText installation guide</a>.</li><li>I believe the folder structure and naming conventions are slightly different with Linux, but the main gist should be the same (e.g. <code>retext.ini</code> vs <code>retext.conf</code> for the config file name).</li><li>Therefore, the post will be referring to macOS naming conventions, with ReText running in a Python virtual environment.</li></ul><h2 id="configuration-file">Configuration File<a href="#configuration-file" class="h-anchor" title="Permalink to #Configuration File"></a></h2>   <p>ReText automatically creates a folder and config file upon first run in <code>'/Users/username/.config/ReText project/Retext.ini'</code>. This location may vary, so double check the config location by opening ReText > Preferences. At the top of the window there should be a path indicating the configuration file location.</p>                             <a href="/blog/customize-retext-markdown-editor/retext_pref.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/customize-retext-markdown-editor/retext_pref.webp" alt="Screenshot of ReText Preferences window and settings in the Edit Menu.">    </a>                 <p>Open the <code>ReText.ini</code> config file with your favourite text editor. This is where the settings made in the GUI are saved, but I found that some settings can only be added by changing the config file directly. Yours may look slightly different; just know the two main sections are <code>[General]</code> and <code>[ColorScheme]</code>. All the possible setting names are listed in the <a href="https://github.com/retext-project/retext/blob/master/configuration.md" target="_blank" class="ext-link" rel="noopener noreferrer">configuration wiki page</a> on the project repo for reference. For example, the <code>editorFont</code> and <code>font</code> lines were added automatically after setting the fonts in the GUI Edit menu.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">[General]</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="na">autoSave</span><span class="o">=</span><span class="s">true</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="na">defaultPreviewState</span><span class="o">=</span><span class="s">live-preview</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="na">editorFont</span><span class="o">=</span><span class="s">"Source Code Pro,14,-1,5,400,0,0,0,0,0,0,0,0,0,0,1,Regular"</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="na">font</span><span class="o">=</span><span class="s">"Inter,16,-1,5,400,0,0,0,0,0,0,0,0,0,0,1,Regular"</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="na">handleWebLinks</span><span class="o">=</span><span class="s">true</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="na">lineNumbersEnabled</span><span class="o">=</span><span class="s">true</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="na">markdownDefaultFileExtension</span><span class="o">=</span><span class="s">.md</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="na">openLastFilesOnStartup</span><span class="o">=</span><span class="s">true</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="na">tabWidth</span><span class="o">=</span><span class="s">2</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="na">paperSize</span><span class="o">=</span><span class="s">A4</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="na">rightMargin</span><span class="o">=</span><span class="s">80</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="na">rightMarginWrap</span><span class="o">=</span><span class="s">true</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="na">saveWindowGeometry</span><span class="o">=</span><span class="s">true</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="na">tabBarAutoHide</span><span class="o">=</span><span class="s">true</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="na">useWebEngine</span><span class="o">=</span><span class="s">true</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="na">windowTitleFullPath</span><span class="o">=</span><span class="s">true</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="na">wideCursor</span><span class="o">=</span><span class="s">true</span></span></span><span class="line"><span class="ln">19</span><span class="cl"></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">[ColorScheme]</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="na">codeSpans</span><span class="o">=</span><span class="s">red</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="na">htmlTags</span><span class="o">=</span><span class="s">teal</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="na">htmlStrings</span><span class="o">=</span><span class="s">skyblue</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="na">htmlComments</span><span class="o">=</span><span class="s">pink</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="na">markdownHeaders</span><span class="o">=</span><span class="s">orange</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="na">markdownLinks</span><span class="o">=</span><span class="s">limegreen</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="na">whitespaceOnEnd</span><span class="o">=</span><span class="s">grey</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="na">blockquotes</span><span class="o">=</span><span class="s">yellow</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="na">marginLine</span><span class="o">=</span><span class="s">orange</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="na">lineNumberArea</span><span class="o">=</span><span class="s">pink</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="na">lineNumberAreaText</span><span class="o">=</span><span class="s">black</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>Here’s the output of the <code>[ColorScheme]</code> settings. I’ve made each setting<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup> a different colour for easy reference.</p>                             <a href="/blog/customize-retext-markdown-editor/retext_colorscheme.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/customize-retext-markdown-editor/retext_colorscheme.webp" alt="Screenshot of what the color scheme settings change in the editor view of ReText.">    </a>       <p>Most are self-explanatory like <code>htmlTags</code>. To clarify, <code>marginLine</code> color, which I’ve set as orange in this demonstration (you can see a thin orange line on the left panel, <strong>AKA Editor Pane</strong>, near the right margin), is the <code>rightMargin</code> line setting’s colour. I have set it to =80 under <code>[General]</code>. This is because 80 to 100 characters is the general recommendation for the printable width of an A4 piece of paper.</p><p>I have used plain English colours for easier understanding of what each setting does; but you can use hex codes for colours like <code>codeSpans=#04D5FA</code> for greater colour customization. <strong>Hex codes must be capitalized/upper case!</strong></p> <blockquote class="alert alert-note">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Note      </p>  <p>If you try this out this config yourself, you may be wondering why strike-through, sub-bullets and the checkboxes in Markdown aren’t rendering on the right panel, <strong>AKA Preview Pane</strong>, like in my screenshot.</p><p>Go to the Menu bar > Edit > ‘Use WebEngine (Chromium) renderer’ and keep that on.</p><p>Depending on what Python packages are present in your venv or system, you may need to install additional Python Markdown extensions. Please refer to the <a href="https://burgeonlab.com/#markdown-featuresusing-extensions">Extensions section</a> below for more information.</p></blockquote> <h2 id="more-customization-powerstyle-sheets">More Customization Power—Style Sheets<a href="#more-customization-powerstyle-sheets" class="h-anchor" title="Permalink to #More Customization Power—Style Sheets"></a></h2>   <p>You may be wondering, other than the basic colours of Markdown syntax in the Editor Pane, can anything else be changed? The answer is yes!</p><p>There are two CSS files you can use to customize different parts of ReText:</p><ol><li>styleSheet - Used to edit the Preview Pane (right) exclusively, just like in websites.</li><li>appStyleSheet - Used to edit the ReText interface itself and the Editor Pane (left).</li></ol><p>Both settings should be added under <code>[General]</code> in the config <code>ReText.ini</code> file, with file path to their respective stylesheet. Remember to use quotes if there are spaces in the file path, for example:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">[General]</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="na">appStyleSheet</span><span class="o">=</span><span class="s">"/Users/username/.config/ReText project/retext_interface.css"</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="na">styleSheet</span><span class="o">=</span><span class="s">"/Users/username/.config/ReText project/retext-preview.css"</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h3 id="example-of-retext-previewcss">Example of retext-preview.css<a href="#example-of-retext-previewcss" class="h-anchor" title="Permalink to #Example of retext-preview.css"></a></h3>   <p>Here’s a simple CSS of some beige text on a warm grey with yellow links for the right panel. Remember, you can change the font-family and font-size directly in the GUI menu bar > Edit > ‘Change editor font’ and ‘Change preview font’, which is saved in the <code>retext.ini</code> config file. If changes in the CSS is not seen after restarting, try adding <code>!important</code> to override default styles.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">body</span> <span class="p">{</span></span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="k">color</span><span class="p">:</span> <span class="mh">#D6C7AB</span><span class="p">;</span></span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="k">background-color</span><span class="p">:</span> <span class="mh">#191916</span><span class="p">;</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">5</span><span class="cl"></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nt">a</span> <span class="p">{</span></span></span><span class="line"><span class="ln">7</span><span class="cl">    <span class="k">color</span><span class="p">:</span><span class="mh">#fdc200</span><span class="p">;</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p">}</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>Here is the Preview Pane after restarting the app.</p>                             <a href="/blog/customize-retext-markdown-editor/retext_csspreview.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/customize-retext-markdown-editor/retext_csspreview.webp" alt="Screenshot of customized preview panel of Retext Markdown editor.">    </a>       <p>The stylesheet can also be set in the GUI Preferences window.</p>                             <a href="/blog/customize-retext-markdown-editor/retext_prefcss.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/customize-retext-markdown-editor/retext_prefcss.webp" alt="Screenshot of Retext Markdown editor stylesheet setting">    </a>       <p>If you are not sure what the CSS selector is for the part you want to change, simply go to the Menu bar > Edit > ‘View HTML code’. A window will pop up with the Markdown (Editor Pane) contents rendered as HTML. You can then target the selector in the stylesheet.</p>                             <a href="/blog/customize-retext-markdown-editor/retext_viewhtml.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/customize-retext-markdown-editor/retext_viewhtml.webp" alt="Screenshot of View HTML window in Retext Markdown editor.">    </a>       <h3 id="example-of-retext-interfacecss">Example of retext-interface.css<a href="#example-of-retext-interfacecss" class="h-anchor" title="Permalink to #Example of retext-interface.css"></a></h3>   <p>Now, onto the second stylesheet that you can use to customize ReText. This is mainly for controlling the whole ReText interface window and also the left Editor Pane. It is based on the Qt framework and these are the selectors I’ve discovered so far that works in ReText. Use this <a href="https://doc.qt.io/qt-6/stylesheet-reference.html" target="_blank" class="ext-link" rel="noopener noreferrer">Qt style sheet reference</a> to get the selector names for editable sections.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref">2</a></sup></p> <blockquote class="alert alert-note">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Note      </p>  <p>Some of the colour options of the interface or Editor Pane are directly accessible in the <code>[ColorScheme]</code> of the config file mentioned above; e.g. <code>lineNumberArea=pink</code> and <code>lineNumberAreaText=black</code></p></blockquote>        <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c">/*RETEXT INTERFACE—MIDDLE SPLIT BAR*/</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nt">QSplitter</span><span class="p">::</span><span class="nd">handle</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="k">background</span><span class="p">:</span> <span class="kc">fuchsia</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c">/*RETEXT INTERFACE—BORDER*/</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nt">QMainWindow</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="k">background</span><span class="p">:</span> <span class="kc">yellow</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">10</span><span class="cl"></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c">/*LEFT PANEL QTextEdit */</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nt">ReTextEdit</span> <span class="p">{</span> </span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="k">background-color</span><span class="p">:</span> <span class="kc">darkolivegreen</span><span class="p">;</span> </span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="k">color</span><span class="p">:</span> <span class="kc">white</span><span class="p">;</span> <span class="c">/*LEFT PANEL TEXT COLOR*/</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">16</span><span class="cl"></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c">/*LEFT PANEL SCROLL BAR*/</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="nt">QScrollBar</span> <span class="p">{</span> </span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="k">background</span><span class="p">:</span> <span class="kc">aqua</span><span class="p">;</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="p">}</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>Here is what each CSS selector is doing with the colour coded example:</p>                             <a href="/blog/customize-retext-markdown-editor/retext_editorview.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/customize-retext-markdown-editor/retext_editorview.webp" alt="Screenshot of Retext interface being edited by the CSS appstylesheet setting.">    </a>       <h2 id="markdown-featuresusing-extensions">Markdown Features—Using Extensions<a href="#markdown-featuresusing-extensions" class="h-anchor" title="Permalink to #Markdown Features—Using Extensions"></a></h2>   <p>After customizing with stylesheets and the configuration file, there are other features/settings we can set in ReText using <a href="https://github.com/retext-project/retext/wiki/Markdown-extensions" target="_blank" class="ext-link" rel="noopener noreferrer">Python-Markdown extensions</a>.</p><p>Here is a page with all the possible <a href="https://github.com/Python-Markdown/markdown/wiki/Third-Party-Extensions" target="_blank" class="ext-link" rel="noopener noreferrer">Python Markdown extensions</a>; I am currently using these:</p><h3 id="pymdown-collection">PyMdown Collection<a href="#pymdown-collection" class="h-anchor" title="Permalink to #PyMdown Collection"></a></h3>   <ul><li><a href="https://github.com/facelessuser/pymdown-extensions" target="_blank" class="ext-link" rel="noopener noreferrer">pymdown-extensions</a><ul><li><a href="https://facelessuser.github.io/pymdown-extensions/extensions/tasklist/" target="_blank" class="ext-link" rel="noopener noreferrer">pymdownx.tasklist</a></li><li><a href="https://facelessuser.github.io/pymdown-extensions/extensions/tilde/" target="_blank" class="ext-link" rel="noopener noreferrer">pymdownx.tilde</a></li><li><a href="https://facelessuser.github.io/pymdown-extensions/extensions/betterem/" target="_blank" class="ext-link" rel="noopener noreferrer">pymdownx.betterem</a></li><li><a href="https://facelessuser.github.io/pymdown-extensions/extensions/inlinehilite/" target="_blank" class="ext-link" rel="noopener noreferrer">pymdownx.inlinehilite</a></li><li><a href="https://facelessuser.github.io/pymdown-extensions/extensions/superfences/" target="_blank" class="ext-link" rel="noopener noreferrer">pymdownx.superfences</a></li></ul></li></ul><h3 id="other-extensions">Other Extensions<a href="#other-extensions" class="h-anchor" title="Permalink to #Other Extensions"></a></h3>   <ul><li><a href="https://github.com/radude/mdx_truly_sane_lists" target="_blank" class="ext-link" rel="noopener noreferrer">mdx_truly_sane_lists</a></li><li><a href="https://github.com/adamb70/mdx-breakless-lists" target="_blank" class="ext-link" rel="noopener noreferrer">mdx_breakless_lists</a></li><li><a href="https://github.com/Python-Markdown/markdown/blob/master/docs/extensions/code_hilite.md" target="_blank" class="ext-link" rel="noopener noreferrer">mdx_codehilite</a></li></ul><h2 id="how-to-install-extensions">How To Install Extensions<a href="#how-to-install-extensions" class="h-anchor" title="Permalink to #How To Install Extensions"></a></h2>   <p>You have the option to using these Markdown extensions for <a href="https://github.com/retext-project/retext/wiki/Markdown-extensions#enabling-extensions-for-a-particular-document" target="_blank" class="ext-link" rel="noopener noreferrer">single files</a> or globally. I prefer global extensions so we will add a <code>markdown-extensions.txt</code> file in <code>/Users/username/.config/markdown-extensions.txt</code>, with each extension in its own single line. Alternatively, you can add the extensions you want in the GUI Preferences window, separated with a comma. The list will reflect in the same file.</p>                             <a href="/blog/customize-retext-markdown-editor/retext_extpref.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/customize-retext-markdown-editor/retext_extpref.webp" alt="Screenshot of ReText Preference window with Python extensions.">    </a>        <blockquote class="alert alert-tip">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Tip      </p>  <p>A file with a <code>.</code> (period) prefix means it is hidden. To toggle hidden files on macOS, press Command + Shift + Period.</p></blockquote>        <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt"><span class="line"><span class="ln">1</span><span class="cl">pymdownx.tasklist</span></span><span class="line"><span class="ln">2</span><span class="cl">pymdownx.tilde</span></span><span class="line"><span class="ln">3</span><span class="cl">pymdownx.superfences</span></span><span class="line"><span class="ln">4</span><span class="cl">pymdownx.betterem</span></span><span class="line"><span class="ln">5</span><span class="cl">pymdownx.inlinehilite</span></span><span class="line"><span class="ln">6</span><span class="cl">mdx_truly_sane_lists</span></span><span class="line"><span class="ln">7</span><span class="cl">mdx_breakless_lists</span></span><span class="line"><span class="ln">8</span><span class="cl">mdx_codehilite</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>Each Python extension will have it’s own install command. So if you installed ReText in a venv like I did:</p><ul><li>Open Terminal and activate the venv with <code>source /path/to/retext/venv/bin/activate</code>.</li><li><code>pip list</code> to show currently installed packages.</li><li><code>pip install package-name</code> to install the extension you require.</li><li>For example, for <code>mdx_truly_sane_lists</code>, you can install using <a href="https://pypi.org/project/mdx-truly-sane-lists/" target="_blank" class="ext-link" rel="noopener noreferrer">PyPi</a> <code>pip install mdx_truly_sane_lists</code> or directly from Git using <code>pip install git+git://github.com/radude/mdx_truly_sane_lists</code>.</li><li>Do this for each of the extensions you added to ReText, deactivate the venv when you’re done with <code>deactivate</code> and restart ReText.app.</li><li>The new features from the added extensions should be activated. If not, double check Menu > Edit > ‘Use WebEngine (Chromium) renderer’ is ticked. Make sure the actual syntax is correct, e.g. 2 space tab indents vs 4 space tab indents to nested bullet points makes a difference!</li></ul> <blockquote class="alert alert-note">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Note      </p>  <p>You may sometimes see <code>pip3</code> instead of <code>pip</code> in commands. Which do you use? From my understanding, because we are inside a Python virtual environment, the <code>pip</code> inside it will be linked to the Python interpreter which was used to create the venv in the first place (usually python3 like <code>python3 -m venv /path/to/venv/retext-env</code>). So the package installer is going to be <code>pip3</code> in this case (the same as <code>pip</code>).</p><p>Therefore, there is no need to specify the version number unless we’re using the package installer <strong>outside</strong> venvs or on systems where multiple versions of Python are installed.</p></blockquote> <h3 id="pygmentscode-highlighting">Pygments—Code Highlighting<a href="#pygmentscode-highlighting" class="h-anchor" title="Permalink to #Pygments—Code Highlighting"></a></h3>   <p>A feature that is quite popular is code syntax highlighting. This requires the <a href="https://pypi.org/project/Pygments/" target="_blank" class="ext-link" rel="noopener noreferrer">Pygments</a> package to be installed and <code>mdx_codehilite</code> to be in the ReText extension list. To use Pygments, first install it with the same method described in the previous paragraph.</p><ul><li><code>pip install Pygments</code></li><li><code>pygmentize -L styles</code> to list all the support styles or visit the <a href="https://pygments.org/styles/" target="_blank" class="ext-link" rel="noopener noreferrer">website</a>.</li><li>Choose the style you like and generate a css file in your current directory: <code>pygmentize -S solarized-dark -f html > pygments.css</code></li><li>Open the generated <code>pygments.css</code>, copy and paste its contents into <code>retext-preview.css</code> (our ReText styleSheet).</li><li>These colours are only for code highlighting. If you want the rest of your Preview Pane to match the Pygments theme, you can try to customize the main font and background colours to complement the theme.</li></ul><p>For example, I have used the <code>solarized-dark</code> Pygments theme.</p>                             <a href="/blog/customize-retext-markdown-editor/retext_solarizeddark.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/customize-retext-markdown-editor/retext_solarizeddark.webp" alt="Screenshot of Retext with Pygments Solarized-dark theme in Chromium Webengine preview pane.">    </a>              <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css"><span class="line"><span class="ln">  1</span><span class="cl"><span class="c">/*MAIN PREVIEW PANE SETTINGS*/</span></span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="nt">body</span> <span class="p">{</span></span></span><span class="line"><span class="ln">  3</span><span class="cl">  <span class="k">color</span><span class="p">:</span> <span class="mh">#85a7b2</span><span class="p">;</span></span></span><span class="line"><span class="ln">  4</span><span class="cl">  <span class="k">background-color</span><span class="p">:</span> <span class="mh">#171e20</span><span class="p">;</span></span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">  6</span><span class="cl"></span></span><span class="line"><span class="ln">  7</span><span class="cl"><span class="nt">a</span> <span class="p">{</span></span></span><span class="line"><span class="ln">  8</span><span class="cl">    <span class="k">color</span><span class="p">:</span><span class="mh">#CB4B16</span><span class="p">;</span></span></span><span class="line"><span class="ln">  9</span><span class="cl">    <span class="k">font-weight</span><span class="p">:</span><span class="mi">800</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 10</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln"> 11</span><span class="cl"></span></span><span class="line"><span class="ln"> 12</span><span class="cl"><span class="nt">h1</span><span class="o">,</span> <span class="nt">h2</span><span class="o">,</span> <span class="nt">h3</span><span class="o">,</span> <span class="nt">h4</span><span class="o">,</span> <span class="nt">h5</span><span class="o">,</span> <span class="nt">h6</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 13</span><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#8ecadc</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 14</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln"> 15</span><span class="cl"><span class="nt">pre</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 16</span><span class="cl">    <span class="k">background-color</span><span class="p">:</span> <span class="mh">#181e17</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 17</span><span class="cl">    <span class="k">padding</span><span class="p">:</span> <span class="mf">0.4</span><span class="kt">rem</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 18</span><span class="cl">    <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#2AA198</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 19</span><span class="cl">    <span class="k">border-radius</span><span class="p">:</span> <span class="mf">0.5</span><span class="kt">rem</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 20</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln"> 21</span><span class="cl"><span class="nt">p</span> <span class="nt">code</span><span class="o">,</span> <span class="nt">li</span> <span class="nt">code</span><span class="o">,</span> <span class="nt">ul</span> <span class="nt">code</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 22</span><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 23</span><span class="cl">    <span class="k">background-color</span><span class="p">:</span> <span class="mh">#181e17</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 24</span><span class="cl">    <span class="k">padding</span><span class="p">:</span> <span class="mi">0</span> <span class="mf">0.2</span><span class="kt">rem</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 25</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln"> 26</span><span class="cl"></span></span><span class="line"><span class="ln"> 27</span><span class="cl"><span class="c">/*GENERATED WITH pygmentize -S solarized-dark -f html > pygments.css FOR CODE SYNTAX HIGHLIGHTING*/</span></span></span><span class="line"><span class="ln"> 28</span><span class="cl"><span class="nt">pre</span> <span class="p">{</span> <span class="k">line-height</span><span class="p">:</span> <span class="mi">125</span><span class="kt">%</span><span class="p">;</span> <span class="p">}</span></span></span><span class="line"><span class="ln"> 29</span><span class="cl"><span class="nt">td</span><span class="p">.</span><span class="nc">linenos</span> <span class="p">.</span><span class="nc">normal</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#586e75</span><span class="p">;</span> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#073642</span><span class="p">;</span> <span class="k">padding-left</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span> <span class="k">padding-right</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span> <span class="p">}</span></span></span><span class="line"><span class="ln"> 30</span><span class="cl"><span class="nt">span</span><span class="p">.</span><span class="nc">linenos</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#586e75</span><span class="p">;</span> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#073642</span><span class="p">;</span> <span class="k">padding-left</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span> <span class="k">padding-right</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span> <span class="p">}</span></span></span><span class="line"><span class="ln"> 31</span><span class="cl"><span class="nt">td</span><span class="p">.</span><span class="nc">linenos</span> <span class="p">.</span><span class="nc">special</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#000000</span><span class="p">;</span> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#ffffc0</span><span class="p">;</span> <span class="k">padding-left</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span> <span class="k">padding-right</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span> <span class="p">}</span></span></span><span class="line"><span class="ln"> 32</span><span class="cl"><span class="nt">span</span><span class="p">.</span><span class="nc">linenos</span><span class="p">.</span><span class="nc">special</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#000000</span><span class="p">;</span> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#ffffc0</span><span class="p">;</span> <span class="k">padding-left</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span> <span class="k">padding-right</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span> <span class="p">}</span></span></span><span class="line"><span class="ln"> 33</span><span class="cl"><span class="p">.</span><span class="nc">hll</span> <span class="p">{</span> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#073642</span> <span class="p">}</span></span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="p">.</span><span class="nc">c</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#586E75</span><span class="p">;</span> <span class="k">font-style</span><span class="p">:</span> <span class="kc">italic</span> <span class="p">}</span> <span class="c">/* Comment */</span></span></span><span class="line"><span class="ln"> 35</span><span class="cl"><span class="p">.</span><span class="nc">err</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span><span class="p">;</span> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#DC322F</span> <span class="p">}</span> <span class="c">/* Error */</span></span></span><span class="line"><span class="ln"> 36</span><span class="cl"><span class="p">.</span><span class="nc">esc</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span> <span class="p">}</span> <span class="c">/* Escape */</span></span></span><span class="line"><span class="ln"> 37</span><span class="cl"><span class="p">.</span><span class="nc">g</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span> <span class="p">}</span> <span class="c">/* Generic */</span></span></span><span class="line"><span class="ln"> 38</span><span class="cl"><span class="p">.</span><span class="nc">k</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#859900</span> <span class="p">}</span> <span class="c">/* Keyword */</span></span></span><span class="line"><span class="ln"> 39</span><span class="cl"><span class="p">.</span><span class="nc">l</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span> <span class="p">}</span> <span class="c">/* Literal */</span></span></span><span class="line"><span class="ln"> 40</span><span class="cl"><span class="p">.</span><span class="nc">n</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span> <span class="p">}</span> <span class="c">/* Name */</span></span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="p">.</span><span class="nc">o</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#586E75</span> <span class="p">}</span> <span class="c">/* Operator */</span></span></span><span class="line"><span class="ln"> 42</span><span class="cl"><span class="p">.</span><span class="nc">x</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span> <span class="p">}</span> <span class="c">/* Other */</span></span></span><span class="line"><span class="ln"> 43</span><span class="cl"><span class="p">.</span><span class="nc">p</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span> <span class="p">}</span> <span class="c">/* Punctuation */</span></span></span><span class="line"><span class="ln"> 44</span><span class="cl"><span class="p">.</span><span class="nc">ch</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#586E75</span><span class="p">;</span> <span class="k">font-style</span><span class="p">:</span> <span class="kc">italic</span> <span class="p">}</span> <span class="c">/* Comment.Hashbang */</span></span></span><span class="line"><span class="ln"> 45</span><span class="cl"><span class="p">.</span><span class="nc">cm</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#586E75</span><span class="p">;</span> <span class="k">font-style</span><span class="p">:</span> <span class="kc">italic</span> <span class="p">}</span> <span class="c">/* Comment.Multiline */</span></span></span><span class="line"><span class="ln"> 46</span><span class="cl"><span class="p">.</span><span class="nc">cp</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#D33682</span> <span class="p">}</span> <span class="c">/* Comment.Preproc */</span></span></span><span class="line"><span class="ln"> 47</span><span class="cl"><span class="p">.</span><span class="nc">cpf</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#586E75</span> <span class="p">}</span> <span class="c">/* Comment.PreprocFile */</span></span></span><span class="line"><span class="ln"> 48</span><span class="cl"><span class="p">.</span><span class="nc">c1</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#586E75</span><span class="p">;</span> <span class="k">font-style</span><span class="p">:</span> <span class="kc">italic</span> <span class="p">}</span> <span class="c">/* Comment.Single */</span></span></span><span class="line"><span class="ln"> 49</span><span class="cl"><span class="p">.</span><span class="nc">cs</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#586E75</span><span class="p">;</span> <span class="k">font-style</span><span class="p">:</span> <span class="kc">italic</span> <span class="p">}</span> <span class="c">/* Comment.Special */</span></span></span><span class="line"><span class="ln"> 50</span><span class="cl"><span class="p">.</span><span class="nc">gd</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#DC322F</span> <span class="p">}</span> <span class="c">/* Generic.Deleted */</span></span></span><span class="line"><span class="ln"> 51</span><span class="cl"><span class="p">.</span><span class="nc">ge</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span><span class="p">;</span> <span class="k">font-style</span><span class="p">:</span> <span class="kc">italic</span> <span class="p">}</span> <span class="c">/* Generic.Emph */</span></span></span><span class="line"><span class="ln"> 52</span><span class="cl"><span class="p">.</span><span class="nc">ges</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span><span class="p">;</span> <span class="k">font-weight</span><span class="p">:</span> <span class="kc">bold</span><span class="p">;</span> <span class="k">font-style</span><span class="p">:</span> <span class="kc">italic</span> <span class="p">}</span> <span class="c">/* Generic.EmphStrong */</span></span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="p">.</span><span class="nc">gr</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#DC322F</span> <span class="p">}</span> <span class="c">/* Generic.Error */</span></span></span><span class="line"><span class="ln"> 54</span><span class="cl"><span class="p">.</span><span class="nc">gh</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span><span class="p">;</span> <span class="k">font-weight</span><span class="p">:</span> <span class="kc">bold</span> <span class="p">}</span> <span class="c">/* Generic.Heading */</span></span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="p">.</span><span class="nc">gi</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#859900</span> <span class="p">}</span> <span class="c">/* Generic.Inserted */</span></span></span><span class="line"><span class="ln"> 56</span><span class="cl"><span class="p">.</span><span class="nc">go</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span> <span class="p">}</span> <span class="c">/* Generic.Output */</span></span></span><span class="line"><span class="ln"> 57</span><span class="cl"><span class="p">.</span><span class="nc">gp</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span><span class="p">;</span> <span class="k">font-weight</span><span class="p">:</span> <span class="kc">bold</span> <span class="p">}</span> <span class="c">/* Generic.Prompt */</span></span></span><span class="line"><span class="ln"> 58</span><span class="cl"><span class="p">.</span><span class="nc">gs</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span><span class="p">;</span> <span class="k">font-weight</span><span class="p">:</span> <span class="kc">bold</span> <span class="p">}</span> <span class="c">/* Generic.Strong */</span></span></span><span class="line"><span class="ln"> 59</span><span class="cl"><span class="p">.</span><span class="nc">gu</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span><span class="p">;</span> <span class="k">text-decoration</span><span class="p">:</span> <span class="kc">underline</span> <span class="p">}</span> <span class="c">/* Generic.Subheading */</span></span></span><span class="line"><span class="ln"> 60</span><span class="cl"><span class="p">.</span><span class="nc">gt</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Generic.Traceback */</span></span></span><span class="line"><span class="ln"> 61</span><span class="cl"><span class="p">.</span><span class="nc">kc</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Keyword.Constant */</span></span></span><span class="line"><span class="ln"> 62</span><span class="cl"><span class="p">.</span><span class="nc">kd</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Keyword.Declaration */</span></span></span><span class="line"><span class="ln"> 63</span><span class="cl"><span class="p">.</span><span class="nc">kn</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#CB4B16</span> <span class="p">}</span> <span class="c">/* Keyword.Namespace */</span></span></span><span class="line"><span class="ln"> 64</span><span class="cl"><span class="p">.</span><span class="nc">kp</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#859900</span> <span class="p">}</span> <span class="c">/* Keyword.Pseudo */</span></span></span><span class="line"><span class="ln"> 65</span><span class="cl"><span class="p">.</span><span class="nc">kr</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#859900</span> <span class="p">}</span> <span class="c">/* Keyword.Reserved */</span></span></span><span class="line"><span class="ln"> 66</span><span class="cl"><span class="p">.</span><span class="nc">kt</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#B58900</span> <span class="p">}</span> <span class="c">/* Keyword.Type */</span></span></span><span class="line"><span class="ln"> 67</span><span class="cl"><span class="p">.</span><span class="nc">ld</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span> <span class="p">}</span> <span class="c">/* Literal.Date */</span></span></span><span class="line"><span class="ln"> 68</span><span class="cl"><span class="p">.</span><span class="nc">m</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.Number */</span></span></span><span class="line"><span class="ln"> 69</span><span class="cl"><span class="p">.</span><span class="nc">s</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.String */</span></span></span><span class="line"><span class="ln"> 70</span><span class="cl"><span class="p">.</span><span class="nc">na</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span> <span class="p">}</span> <span class="c">/* Name.Attribute */</span></span></span><span class="line"><span class="ln"> 71</span><span class="cl"><span class="p">.</span><span class="nc">nb</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Builtin */</span></span></span><span class="line"><span class="ln"> 72</span><span class="cl"><span class="p">.</span><span class="nc">nc</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Class */</span></span></span><span class="line"><span class="ln"> 73</span><span class="cl"><span class="p">.</span><span class="nc">no</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Constant */</span></span></span><span class="line"><span class="ln"> 74</span><span class="cl"><span class="p">.</span><span class="nc">nd</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Decorator */</span></span></span><span class="line"><span class="ln"> 75</span><span class="cl"><span class="p">.</span><span class="nc">ni</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Entity */</span></span></span><span class="line"><span class="ln"> 76</span><span class="cl"><span class="p">.</span><span class="nc">ne</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Exception */</span></span></span><span class="line"><span class="ln"> 77</span><span class="cl"><span class="p">.</span><span class="nc">nf</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Function */</span></span></span><span class="line"><span class="ln"> 78</span><span class="cl"><span class="p">.</span><span class="nc">nl</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Label */</span></span></span><span class="line"><span class="ln"> 79</span><span class="cl"><span class="p">.</span><span class="nc">nn</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Namespace */</span></span></span><span class="line"><span class="ln"> 80</span><span class="cl"><span class="p">.</span><span class="nc">nx</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span> <span class="p">}</span> <span class="c">/* Name.Other */</span></span></span><span class="line"><span class="ln"> 81</span><span class="cl"><span class="p">.</span><span class="nc">py</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span> <span class="p">}</span> <span class="c">/* Name.Property */</span></span></span><span class="line"><span class="ln"> 82</span><span class="cl"><span class="p">.</span><span class="nc">nt</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Tag */</span></span></span><span class="line"><span class="ln"> 83</span><span class="cl"><span class="p">.</span><span class="nc">nv</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Variable */</span></span></span><span class="line"><span class="ln"> 84</span><span class="cl"><span class="p">.</span><span class="nc">ow</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#859900</span> <span class="p">}</span> <span class="c">/* Operator.Word */</span></span></span><span class="line"><span class="ln"> 85</span><span class="cl"><span class="p">.</span><span class="nc">pm</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span> <span class="p">}</span> <span class="c">/* Punctuation.Marker */</span></span></span><span class="line"><span class="ln"> 86</span><span class="cl"><span class="p">.</span><span class="nc">w</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#839496</span> <span class="p">}</span> <span class="c">/* Text.Whitespace */</span></span></span><span class="line"><span class="ln"> 87</span><span class="cl"><span class="p">.</span><span class="nc">mb</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.Number.Bin */</span></span></span><span class="line"><span class="ln"> 88</span><span class="cl"><span class="p">.</span><span class="nc">mf</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.Number.Float */</span></span></span><span class="line"><span class="ln"> 89</span><span class="cl"><span class="p">.</span><span class="nc">mh</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.Number.Hex */</span></span></span><span class="line"><span class="ln"> 90</span><span class="cl"><span class="p">.</span><span class="nc">mi</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.Number.Integer */</span></span></span><span class="line"><span class="ln"> 91</span><span class="cl"><span class="p">.</span><span class="nc">mo</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.Number.Oct */</span></span></span><span class="line"><span class="ln"> 92</span><span class="cl"><span class="p">.</span><span class="nc">sa</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.String.Affix */</span></span></span><span class="line"><span class="ln"> 93</span><span class="cl"><span class="p">.</span><span class="nc">sb</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.String.Backtick */</span></span></span><span class="line"><span class="ln"> 94</span><span class="cl"><span class="p">.</span><span class="nc">sc</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.String.Char */</span></span></span><span class="line"><span class="ln"> 95</span><span class="cl"><span class="p">.</span><span class="nc">dl</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.String.Delimiter */</span></span></span><span class="line"><span class="ln"> 96</span><span class="cl"><span class="p">.</span><span class="nc">sd</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#586E75</span> <span class="p">}</span> <span class="c">/* Literal.String.Doc */</span></span></span><span class="line"><span class="ln"> 97</span><span class="cl"><span class="p">.</span><span class="nc">s2</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.String.Double */</span></span></span><span class="line"><span class="ln"> 98</span><span class="cl"><span class="p">.</span><span class="nc">se</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.String.Escape */</span></span></span><span class="line"><span class="ln"> 99</span><span class="cl"><span class="p">.</span><span class="nc">sh</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.String.Heredoc */</span></span></span><span class="line"><span class="ln">100</span><span class="cl"><span class="p">.</span><span class="nc">si</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.String.Interpol */</span></span></span><span class="line"><span class="ln">101</span><span class="cl"><span class="p">.</span><span class="nc">sx</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.String.Other */</span></span></span><span class="line"><span class="ln">102</span><span class="cl"><span class="p">.</span><span class="nc">sr</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#CB4B16</span> <span class="p">}</span> <span class="c">/* Literal.String.Regex */</span></span></span><span class="line"><span class="ln">103</span><span class="cl"><span class="p">.</span><span class="nc">s1</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.String.Single */</span></span></span><span class="line"><span class="ln">104</span><span class="cl"><span class="p">.</span><span class="nc">ss</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.String.Symbol */</span></span></span><span class="line"><span class="ln">105</span><span class="cl"><span class="p">.</span><span class="nc">bp</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Builtin.Pseudo */</span></span></span><span class="line"><span class="ln">106</span><span class="cl"><span class="p">.</span><span class="nc">fm</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Function.Magic */</span></span></span><span class="line"><span class="ln">107</span><span class="cl"><span class="p">.</span><span class="nc">vc</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Variable.Class */</span></span></span><span class="line"><span class="ln">108</span><span class="cl"><span class="p">.</span><span class="nc">vg</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Variable.Global */</span></span></span><span class="line"><span class="ln">109</span><span class="cl"><span class="p">.</span><span class="nc">vi</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Variable.Instance */</span></span></span><span class="line"><span class="ln">110</span><span class="cl"><span class="p">.</span><span class="nc">vm</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#268BD2</span> <span class="p">}</span> <span class="c">/* Name.Variable.Magic */</span></span></span><span class="line"><span class="ln">111</span><span class="cl"><span class="p">.</span><span class="nc">il</span> <span class="p">{</span> <span class="k">color</span><span class="p">:</span> <span class="mh">#2AA198</span> <span class="p">}</span> <span class="c">/* Literal.Number.Integer.Long */</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h2 id="conclusion">Conclusion<a href="#conclusion" class="h-anchor" title="Permalink to #Conclusion"></a></h2>   <p>There’s lots of flexibility and functionality to ReText, and I am happy to say it has not crashed once on me (yet, touch wood). I have replaced Mac’s <a href="https://support.apple.com/en-gb/guide/textedit/welcome/mac" target="_blank" class="ext-link" rel="noopener noreferrer">TextEdit</a> with ReText, and I tend to use it on single Markdown files instead of a collection of them (like in <a href="https://obsidian.md/" target="_blank" class="ext-link" rel="noopener noreferrer">Obsidian</a>—my main PKM app). I really love the customizability; and using Markdown instead of plaintext is just a million times better!</p><h2 id="further-reading">Further Reading<a href="#further-reading" class="h-anchor" title="Permalink to #Further Reading"></a></h2>   <ul><li><a href="https://github.com/retext-project/retext/wiki/FAQ" target="_blank" class="ext-link" rel="noopener noreferrer">ReText’s official FAQ page</a></li><li><a href="https://python-markdown.github.io/extensions/" target="_blank" class="ext-link" rel="noopener noreferrer">Python Markdown Extensions</a></li></ul><div class="footnotes"><hr><ol><li id="fn:1"><p>There is a reason why I have not included an example of <code>codeSpan</code> in Markdown. There is a bug with the syntax highlighting of fenced code blocks with ReText. I have sorted it and in the process of doing a pull request. <del>Will update this section</del> Update: <a href="https://burgeonlab.com/blog/retext-code-block-syntax-highlighting-bug/">PR completed and bug fixed</a>. FYI, the <code>codeSpan=red</code> will lead to backticks (```) being red in the Editor pane. <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li><li id="fn:2"><p>It can be a bit hit-or-miss finding the right selectors (for someone with no Qt background like me). I hope the ones I have in my example stylesheet is enough. If you know any, I’ll add to it! <a href="#fnref:2" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blogroll/</id><link rel="alternate" href="https://burgeonlab.com/blogroll/"/><title type="html">Blogroll: Worthwhile Sites and Blogs</title><published>2025-07-16T20:40:18Z</published><updated>2025-07-16T20:40:18Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blogroll/og_img_blogroll.webp"/><summary type="html">A curated collection of Naty&amp;rsquo;s favourite websites and creators, and inspiring blogs.</summary><content type="html"><![CDATA[<p>Welcome to my blogroll, a common <a href="https://slashpages.net/" target="_blank" class="ext-link" rel="noopener noreferrer">slash page</a>. It’s a curated collection of sites or creators that I follow and like (in no particular order).</p><p>Will be updating the page as I discover more cool people and websites!<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup></p><h2 id="recommend-websites">Recommend Websites<a href="#recommend-websites" class="h-anchor" title="Permalink to #Recommend Websites"></a></h2>   <ul><li><a href="https://www.privacyguides.org/en/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.privacyguides.org/en/</a></li><li><a href="https://privsec.dev/" target="_blank" class="ext-link" rel="noopener noreferrer">https://privsec.dev/</a></li><li><a href="https://100daystooffload.com/" target="_blank" class="ext-link" rel="noopener noreferrer">https://100daystooffload.com/</a></li><li><a href="https://awesome-selfhosted.net/" target="_blank" class="ext-link" rel="noopener noreferrer">https://awesome-selfhosted.net/</a></li><li><a href="https://offa.github.io/android-foss/" target="_blank" class="ext-link" rel="noopener noreferrer">https://offa.github.io/android-foss/</a></li></ul><h2 id="sites-i-follow">Sites I Follow<a href="#sites-i-follow" class="h-anchor" title="Permalink to #Sites I Follow"></a></h2>   <h3 id="personal">Personal<a href="#personal" class="h-anchor" title="Permalink to #Personal"></a></h3>   <ul><li><a href="https://joelchrono.xyz/" target="_blank" class="ext-link" rel="noopener noreferrer">https://joelchrono.xyz/</a></li><li><a href="https://shellsharks.com/" target="_blank" class="ext-link" rel="noopener noreferrer">https://shellsharks.com/</a></li><li><a href="https://kevquirk.com/" target="_blank" class="ext-link" rel="noopener noreferrer">https://kevquirk.com/</a></li><li><a href="https://www.adamsdesk.com/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.adamsdesk.com/</a></li><li><a href="https://baty.net/" target="_blank" class="ext-link" rel="noopener noreferrer">https://baty.net/</a></li><li><a href="https://unixdigest.com/" target="_blank" class="ext-link" rel="noopener noreferrer">https://unixdigest.com/</a></li><li><a href="https://blog.ktz.me/author/alex/" target="_blank" class="ext-link" rel="noopener noreferrer">https://blog.ktz.me/author/alex/</a></li><li><a href="https://fortelabs.com/blog/" target="_blank" class="ext-link" rel="noopener noreferrer">https://fortelabs.com/blog/</a></li></ul><h3 id="organizations">Organizations<a href="#organizations" class="h-anchor" title="Permalink to #Organizations"></a></h3>   <ul><li><a href="https://dietpi.com/blog/" target="_blank" class="ext-link" rel="noopener noreferrer">https://dietpi.com/blog/</a></li><li><a href="https://alternativeto.net/news/all/" target="_blank" class="ext-link" rel="noopener noreferrer">https://alternativeto.net/news/all/</a></li><li><a href="https://www.xda-developers.com/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.xda-developers.com/</a></li><li><a href="https://terminaltrove.com/totw.xml" target="_blank" class="ext-link" rel="noopener noreferrer">https://terminaltrove.com/totw.xml</a></li></ul><h2 id="podcasts">Podcasts<a href="#podcasts" class="h-anchor" title="Permalink to #Podcasts"></a></h2>   <ul><li><a href="https://2.5admins.com/" target="_blank" class="ext-link" rel="noopener noreferrer">https://2.5admins.com/</a></li><li><a href="https://surveillancereport.tech/" target="_blank" class="ext-link" rel="noopener noreferrer">https://surveillancereport.tech/</a></li><li><a href="https://selfhosted.show/" target="_blank" class="ext-link" rel="noopener noreferrer">https://selfhosted.show/</a></li></ul><h2 id="video-creators-i-enjoy-watching">Video Creators I Enjoy Watching<a href="#video-creators-i-enjoy-watching" class="h-anchor" title="Permalink to #Video Creators I Enjoy Watching"></a></h2>   <h3 id="tech">Tech<a href="#tech" class="h-anchor" title="Permalink to #Tech"></a></h3>   <ul><li><a href="https://odysee.com/@NaomiBrockwell:4/" target="_blank" class="ext-link" rel="noopener noreferrer">https://odysee.com/@NaomiBrockwell:4/</a></li><li><a href="https://odysee.com/@RobBraxmanTech:6/" target="_blank" class="ext-link" rel="noopener noreferrer">https://odysee.com/@RobBraxmanTech:6/</a></li><li><a href="https://odysee.com/@AllThingsSecured:9/" target="_blank" class="ext-link" rel="noopener noreferrer">https://odysee.com/@AllThingsSecured:9/</a></li><li><a href="https://odysee.com/@techlore:3/" target="_blank" class="ext-link" rel="noopener noreferrer">https://odysee.com/@techlore:3/</a></li><li><a href="https://www.youtube.com/@rejectconvenience" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@rejectconvenience</a></li><li><a href="https://www.youtube.com/@NetworkChuck" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@NetworkChuck</a></li><li><a href="https://www.youtube.com/@Fireship/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@Fireship/</a></li><li><a href="https://www.youtube.com/@JeffGeerling/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@JeffGeerling/</a></li><li><a href="https://www.youtube.com/@leepspvideo/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@leepspvideo/</a></li><li><a href="https://www.youtube.com/@NovaspiritTech/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@NovaspiritTech/</a> (RIP Don)</li></ul><h3 id="design">Design<a href="#design" class="h-anchor" title="Permalink to #Design"></a></h3>   <ul><li><a href="https://www.youtube.com/@nevertoosmall/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@nevertoosmall/</a></li><li><a href="https://www.youtube.com/@DearModern/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@DearModern/</a></li><li><a href="https://www.youtube.com/@ScottYuJan" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@ScottYuJan</a></li></ul><h3 id="health">Health<a href="#health" class="h-anchor" title="Permalink to #Health"></a></h3>   <ul><li><a href="https://www.youtube.com/@athleanx/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@athleanx/</a></li><li><a href="https://www.youtube.com/@HybridCalisthenics/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@HybridCalisthenics/</a></li><li><a href="https://www.youtube.com/@MovementbyDavid/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@MovementbyDavid/</a></li><li><a href="https://www.youtube.com/@BobandBrad/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@BobandBrad/</a></li></ul><h3 id="miscellaneous">Miscellaneous<a href="#miscellaneous" class="h-anchor" title="Permalink to #Miscellaneous"></a></h3>   <ul><li><a href="https://www.youtube.com/@jameshoffmann/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@jameshoffmann/</a></li><li><a href="https://www.youtube.com/@jessesteahouse/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@jessesteahouse/</a></li><li><a href="https://www.youtube.com/@PanTheOrganizer/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@PanTheOrganizer/</a></li><li><a href="https://www.youtube.com/@ClearviewDriving/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@ClearviewDriving/</a></li><li><a href="https://www.youtube.com/@SamuelStreetlife/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@SamuelStreetlife/</a></li><li><a href="https://www.youtube.com/@tanner.leatherstein/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@tanner.leatherstein/</a></li><li><a href="https://www.youtube.com/@TeddyBaldassarre" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@TeddyBaldassarre</a></li><li><a href="https://www.youtube.com/@uyenninh/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.youtube.com/@uyenninh/</a></li></ul><div class="footnotes"><hr><ol><li id="fn:1"><p>My bookmark setup is currently a scattered mess. Plus I have a very bad case of “a million browser tabs opened” syndrome. Once I get the bookmarks and RSS workflow implemented, I am certain there will be more added. Some are just lost in the system right now… 😵💫 <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/add-caption-to-wordpress-featured-image-block/</id><link rel="alternate" href="https://burgeonlab.com/blog/add-caption-to-wordpress-featured-image-block/"/><title type="html">Add Captions to Featured Images Without Plugins on WordPress</title><published>2025-07-14T12:23:01Z</published><updated>2025-07-14T12:23:01Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/add-caption-to-wordpress-featured-image-block/og_img_017.webp"/><summary type="html">Add featured image captions in WordPress with the Code Snippets plugin &amp;amp; a simple shortcode. No theme editing needed.</summary><content type="html"><![CDATA[<h2 id="issue">Issue<a href="#issue" class="h-anchor" title="Permalink to #Issue"></a></h2>   <p>A friend of mine created his first <a href="https://lens-wanderings.com/" target="_blank" class="ext-link" rel="noopener noreferrer">blog</a> with WordPress.org and wanted to have captions for the <a href="https://wordpress.org/documentation/article/post-featured-image-block/" target="_blank" class="ext-link" rel="noopener noreferrer">featured image block</a>. He uses the default WordPress <a href="https://wordpress.org/themes/twentytwentyfive/" target="_blank" class="ext-link" rel="noopener noreferrer">Twenty-Twenty-Five</a> theme but he couldn’t find a way to turn it on; captions were only showing in gallery blocks and image blocks.</p><p>Here’s a simple step-by-step guide for anyone who wants to add image descriptions or captions to their WordPress featured images (without downloading <em>yet another</em> job-specific plugin).</p><h2 id="solution">Solution<a href="#solution" class="h-anchor" title="Permalink to #Solution"></a></h2>   <ol><li><p>Download the <a href="https://en-gb.wordpress.org/plugins/code-snippets/" target="_blank" class="ext-link" rel="noopener noreferrer">Code Snippets</a> plugin (I highly recommend this plugin—I’m using the free version, but it is plenty powerful for all sorts of customizations. <strong>It can do the job of many plugins.</strong>)</p></li><li><p>Activate Code Snippets (Plugins > Activate), go to Snippets (left sidebar) > Add New > make sure you’re on the Functions (PHP) tab</p></li><li><p>Give it a memorable name (snippet title), e.g.: “Shortcode: Show Featured Image Caption [featured_image_caption]"</p></li><li><p>Add the following code:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">function</span> <span class="nf">display_featured_image_caption</span><span class="p">()</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="k">if</span> <span class="p">(</span> <span class="nx">has_post_thumbnail</span><span class="p">()</span> <span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl">        <span class="nv">$thumb_id</span> <span class="o">=</span> <span class="nx">get_post_thumbnail_id</span><span class="p">();</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="nv">$caption</span> <span class="o">=</span> <span class="nx">wp_get_attachment_caption</span><span class="p">(</span> <span class="nv">$thumb_id</span> <span class="p">);</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="k">if</span> <span class="p">(</span> <span class="nv">$caption</span> <span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl">            <span class="k">return</span> <span class="s1">'<div class="featured-image-caption">'</span> <span class="o">.</span> <span class="nx">esc_html</span><span class="p">(</span> <span class="nv">$caption</span> <span class="p">)</span> <span class="o">.</span> <span class="s1">'</div>'</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="p">}</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="p">}</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">return</span> <span class="s1">''</span><span class="p">;</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">11</span><span class="cl"></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">function</span> <span class="nf">featured_image_caption_shortcode</span><span class="p">()</span> <span class="p">{</span></span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="k">return</span> <span class="nx">display_featured_image_caption</span><span class="p">();</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">add_shortcode</span><span class="p">(</span> <span class="s1">'featured_image_caption'</span><span class="p">,</span> <span class="s1">'featured_image_caption_shortcode'</span> <span class="p">);</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div></li><li><p>Select Location > “Only run on site front end”</p></li><li><p>Add some notes for future reference in the Description field, e.g.:</p> <blockquote>  <p>css selector: .featured-image-captionshortcode used to activate snippet: [featured_image_caption]</p></blockquote> </li><li><p>Click “Save and Activate”</p>                                                                                       <a href="/blog/add-caption-to-wordpress-featured-image-block/code_snippet_settings.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">          <img src="/blog/add-caption-to-wordpress-featured-image-block/code_snippet_settings.webp" alt="Screenshot of Code Snippet settings for adding php code to show image caption for featured image on WordPress">        </a>               </li><li><p>Now go to Appearance > Editor > Single Post template</p></li><li><p>Find your “Featured Image” block, add a “Shortcode” block beneath it</p></li><li><p>In the Shortcode block field, type with the square brackets: [featured_image_caption]</p>                                                                                       <a href="/blog/add-caption-to-wordpress-featured-image-block/shortcode_wp.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">          <img src="/blog/add-caption-to-wordpress-featured-image-block/shortcode_wp.webp" alt="Screenshot of the WordPress block editor, with a shortcode block added beneath the featured image block.">        </a>               </li><li><p>Save template, clear cache, force refresh and visit a post. You should see the caption appear if that image used has the caption box filled in the Media Library view.</p>                                                                                       <a href="/blog/add-caption-to-wordpress-featured-image-block/wp_media_lib.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">          <img src="/blog/add-caption-to-wordpress-featured-image-block/wp_media_lib.webp" alt="Screenshot of WordPress Media Library page, where the caption, description, altext settings are located.">        </a>               </li><li><p>To edit the look (CSS) of the caption, use the CSS selector as mentioned above in step 6. Read about <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/figcaption" target="_blank" class="ext-link" rel="noopener noreferrer">figure captions on Mozilla developer  documentations</a>. For example:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">.</span><span class="nc">featured-image-caption</span> <span class="p">{</span></span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="k">font-size</span><span class="p">:</span> <span class="mf">0.85</span><span class="kt">rem</span><span class="p">;</span></span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="k">font-weight</span><span class="p">:</span> <span class="mi">300</span><span class="p">;</span></span></span><span class="line"><span class="ln">4</span><span class="cl">  <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span></span></span><span class="line"><span class="ln">5</span><span class="cl">  <span class="k">padding</span><span class="p">:</span> <span class="mf">0.5</span><span class="kt">rem</span> <span class="mi">0</span><span class="p">;</span></span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="k">background-color</span><span class="p">:</span> <span class="nb">rgba</span><span class="p">(</span><span class="mi">89</span><span class="p">,</span> <span class="mi">89</span><span class="p">,</span> <span class="mi">89</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">);</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="p">}</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div></li></ol><h2 id="conclusion">Conclusion<a href="#conclusion" class="h-anchor" title="Permalink to #Conclusion"></a></h2>   <p>If you’re interested in reading about the differences between captions, alt text and descriptions within the WordPress Media Library window, I think <a href="https://wpwinners.com/guides/alt-text-vs-captions-vs-descriptions-differences/" target="_blank" class="ext-link" rel="noopener noreferrer">this</a> blog post describes it quite well. And here’s a good guide for <a href="https://www.stylemanual.gov.au/content-types/images/alt-text-captions-and-titles-images" target="_blank" class="ext-link" rel="noopener noreferrer">how to write good image metadata</a> by the Australian Government.</p><p>Hope this helps!</p>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/hugo-tips-and-tricks/</id><link rel="alternate" href="https://burgeonlab.com/blog/hugo-tips-and-tricks/"/><title type="html">Hugo (Static Site Generator) CMS: Tips and Tricks</title><published>2025-07-12T21:20:53Z</published><updated>2025-10-04T00:00:00Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/hugo-tips-and-tricks/og_img_016.webp"/><summary type="html">A collection of notes and learnings from using Hugo as a primary CMS. This post will be updated periodically.</summary><content type="html"><![CDATA[<p>Here are some notes of things I’ve learnt while using Hugo as my main CMS. I will be updating this post as I discover new tips and tricks. Hope it helps!</p><h2 id="hugo-templates">Hugo Templates<a href="#hugo-templates" class="h-anchor" title="Permalink to #Hugo Templates"></a></h2>   <h3 id="minify">Minify<a href="#minify" class="h-anchor" title="Permalink to #Minify"></a></h3>          <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">minify</span><span class="p">]</span></span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="nx">disableHTML</span> <span class="p">=</span> <span class="kc">false</span></span></span><span class="line"><span class="ln">3</span><span class="cl"></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">[</span><span class="nx">outputFormats</span><span class="p">.</span><span class="nx">HTML</span><span class="p">]</span></span></span><span class="line"><span class="ln">5</span><span class="cl">  <span class="nx">mediaType</span> <span class="p">=</span> <span class="s2">"text/html"</span></span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="nx">isPlainText</span> <span class="p">=</span> <span class="kc">false</span></span></span><span class="line"><span class="ln">7</span><span class="cl">  <span class="nx">isHTML</span> <span class="p">=</span> <span class="kc">true</span></span></span><span class="line"><span class="ln">8</span><span class="cl">  <span class="nx">noUgly</span> <span class="p">=</span> <span class="kc">true</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>To have more readable HTML output, don’t use <code>--minify</code> in the build pipeline, or check if you have minification set to true in hugo.toml (config) and set HTML outputs.</p>                             <a href="/blog/hugo-tips-and-tricks/minify.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hugo-tips-and-tricks/minify.webp" alt="Comparison between non-minified and minified HTML code.">    </a>                 <h3 id="whitespace-trimming">Whitespace Trimming<a href="#whitespace-trimming" class="h-anchor" title="Permalink to #Whitespace Trimming"></a></h3>   <ul><li>Use whitespace control <code>{{-</code> and <code>-}}</code> to remove unwanted extra blank lines when writing templates in Go. For example, template blocks like <code>{{ if }}</code>, <code>{{ range }}</code>, <code>{{ else }}</code> will include all the lines surrounding them in the output. This means when viewing the HTML like in “View Page Source”,<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup> there will be lots of empty lines.</li></ul>                             <a href="/blog/hugo-tips-and-tricks/whitespace.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hugo-tips-and-tricks/whitespace.webp" alt="ALTTEXT">    </a>                 <ul><li><p><code>{{-</code> will remove all the whitespace and new lines left of the action, and vice versa for <code>-}}</code>. I do this for all <code>if</code>, <code>else if</code>, <code>end</code>, <code>range</code>, <code>with</code>, and <code>block</code>. But don’t do it for all curly brackets to prevent breaking layouts, as sometimes new lines and spaces are necessary for HTML formatting. Always test after changing.</p></li><li><p>For <code>{{ end }}</code>, I tend to add the trim to <em>left of the action</em> like <code>{{- end }}</code> because it is closing a block, is on its own line, and I want the next item to start on a new line. As for partials, use selectively—it may trim too much, so try to fix whitespace within the partial itself.</p></li></ul><h3 id="comments">Comments<a href="#comments" class="h-anchor" title="Permalink to #Comments"></a></h3>   <ul><li>I used to use HTML comment syntax like: <code><!-- This is a comment  --></code> but I realise that they would appear in HTML output. So I’ve switched to Hugo comment syntax like: <code>{{/* Another comment */}}</code></li></ul><h3 id="custom-templates">Custom Templates<a href="#custom-templates" class="h-anchor" title="Permalink to #Custom Templates"></a></h3>   <ul><li><p>If you’ve read how to make customizations to your Hugo theme, you probably came across the instruction not to modify the theme files directly; but make use of <a href="https://gohugo.io/templates/new-templatesystem-overview/" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo’s lookup order</a>.</p></li><li><p>Any file with the same name in the project’s <code>layouts</code> directory will override the one in <code>/themes/theme_name/layouts</code>. This is to prevent your changes overwritten during theme upgrades.</p></li><li><p>I also suggest reading how to use <a href="https://gohugo.io/templates/types/" target="_blank" class="ext-link" rel="noopener noreferrer">blocks and partials</a> to incorporate your own theme customizations as it will make code maintenance easier in the long run.</p></li></ul><h2 id="hugo-front-matter">Hugo Front Matter<a href="#hugo-front-matter" class="h-anchor" title="Permalink to #Hugo Front Matter"></a></h2>   <ul><li><p>Having a <code>slug</code> <a href="https://gohugo.io/content-management/urls/#slug" target="_blank" class="ext-link" rel="noopener noreferrer">parameter</a> in the front matter is useful as it prevents Hugo from creating the url based on the title, which can cause 404 errors if you decide to rename the folder/file.</p></li><li><p>If you need to <a href="https://gohugo.io/content-management/urls/#aliases" target="_blank" class="ext-link" rel="noopener noreferrer">redirect old URLs</a>, use the handy <code>aliases</code> parameter to point to the new URL.</p></li><li><p>I use TOML for my front matter, and I recommend <code>"</code> double quotes as it’s more reliable and less error prone than using <code>'</code> single quotes. For example:</p></li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="err">+++</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">draft</span> <span class="p">=</span> <span class="kc">true</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">type</span> <span class="p">=</span> <span class="s2">"post"</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nx">date</span> <span class="p">=</span> <span class="s2">"{{ .Date }}"</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nx">lastmod</span> <span class="p">=</span> <span class="s2">""</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">title</span> <span class="p">=</span> <span class="s2">"{{ replace .File.ContentBaseName "</span><span class="nx">-</span><span class="s2">" "</span> <span class="s2">" | title }}"</span> </span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="nx">slug</span> <span class="p">=</span> <span class="s2">"post-slug"</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">"160 char meta description"</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="err">+++</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h2 id="internal-links">Internal Links<a href="#internal-links" class="h-anchor" title="Permalink to #Internal Links"></a></h2>   <p>When linking internal, pages or posts, a simple Markdown link pointing to a relative file path will work fine if you won’t be changing the target folder name or directory structure.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown"><span class="line"><span class="ln">1</span><span class="cl">Page link: [<span class="nt">About page</span>](<span class="na">/pages/about</span>)</span></span><span class="line"><span class="ln">2</span><span class="cl">Post link: [<span class="nt">A post</span>](<span class="na">/posts/2025/001-hugo-tips/index.md</span>)</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h3 id="shortcodes">Shortcodes<a href="#shortcodes" class="h-anchor" title="Permalink to #Shortcodes"></a></h3>    <blockquote class="alert alert-note">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Note      </p>  <p>ref Shortcodes are obsolete, please skip to the <a href="https://burgeonlab.com/blog/hugo-tips-and-tricks/#render-hooks">render hook</a> section!</p></blockquote> <p>In the beginning, I used Hugo’s <code>ref</code> <a href="https://gohugo.io/shortcodes/ref/" target="_blank" class="ext-link" rel="noopener noreferrer">shortcode</a> to generate the link dynamically like this example screenshot below. It will generate the correct permalink on build based on Hugo’s content structure and front matter settings—namely <code>slug</code> or <code>url</code>.</p>                             <a href="/blog/hugo-tips-and-tricks/link_shortcode.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hugo-tips-and-tricks/link_shortcode.webp" alt="ALTTEXT">    </a>                  <blockquote class="alert alert-tip">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Tip      </p>  <p>To get the right syntax highlighting in VS Codium for Hugo shortcodes, I recommend the <a href="https://github.com/kofuk/vscode-hugo-utils.git" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo Utilities</a> extension.</p></blockquote> <p>Hugo provides <code>ref</code> and <code>relref</code> shortcodes which generates absolute or relative URLs respectively. The advantages of using Hugo shortcode for generating links are:</p><ul><li>Hugo validates that the target exists; warning you on build that the link is broken, therefore reducing broken links on your site</li><li>Shortcodes are useful for multilingual sites as it can add language prefixes to the permalink</li><li>Using shortcodes means nothing is hardcoded, e.g., you decide to change your slug, the link will not break as Hugo will read the updated front matter to generate the new permalink</li></ul><p>But after a while, with about 20 of these ref shortcodes scattered in my Markdown content, I decided it was maybe not such a good idea to internally link with a non-portable, Hugo-specific method. Let’s say one day I decide to switch to another static site generator, or even, just wanting to browse my content locally in a Markdown parser, these clunky links, will not work.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown"><span class="line"><span class="ln">1</span><span class="cl">[<span class="nt">Link</span>](<span class="na">{{< ref "/posts/2025/001-post-title/index.md" >}}</span>)</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>And guess what, I didn’t even know, but according to the docs, the <code>ref</code> shortcode is already depreciated and obsolete for some time (since v0.123). It is now recommended to use <a href="https://gohugo.io/render-hooks/links/#embedded" target="_blank" class="ext-link" rel="noopener noreferrer">Embedded Link Render Hooks</a>.</p><h3 id="render-hooks">Render Hooks<a href="#render-hooks" class="h-anchor" title="Permalink to #Render Hooks"></a></h3>   <p>So what are Hugo render hooks? From my understanding, they are like templates for generating how links are rendered in HTML from Markdown content. You can customize and add custom rules to make the links have features that you otherwise wouldn’t get in plain Markdown links, e.g., adding attributes like <code>class</code>, <code>rel</code>, <code>target</code>, or validating if a link target exists, etc. In other words, it can do all that of the <code>ref</code> shortcode and more without cluttering the Markdown files with Hugo shortcode syntax; by using standard Markdown link syntax. I think it’s pretty powerful.</p><p>I have now replaced all <code>ref</code> shortcodes with standard looking Markdown link syntax. In the link render hook template (<code>/_layouts/_markup/render-link.html</code>), there are these features, doing more than the original shortcode:</p><ul><li><p>Opens external links (not internal ones) in new tabs with <code>target="_blank" rel="noopener noreferrer"</code> and adds an inline SVG to indicate it is an external link</p></li><li><p>Generates the correct permalink using the front matter</p></li><li><p>Warns if the target doesn’t exist on build</p></li><li><p>Has the option to add link title (i.e. tooltip) with this Markdown syntax:</p><p><code>[Topic](/posts/topic "Read more about topic")</code></p></li></ul><h3 id="shortcode-vs-render-hook">Shortcode vs Render Hook<a href="#shortcode-vs-render-hook" class="h-anchor" title="Permalink to #Shortcode vs Render Hook"></a></h3>   <table>  <thead>      <tr>          <th>Feature</th>          <th>ref Shortcodes</th>          <th>Render Hooks</th>      </tr>  </thead>  <tbody>      <tr>          <td>Lock-in Risk</td>          <td>High (Hugo-specific syntax)</td>          <td>Low (Markdown is standard syntax, only the hook is Hugo-specific)</td>      </tr>      <tr>          <td>Link Validation</td>          <td>Validates target page exists</td>          <td>Can add logic to template</td>      </tr>      <tr>          <td>Broken Link Risk</td>          <td>Low (build-time validation)</td>          <td>Low (with validation logic)</td>      </tr>      <tr>          <td>Dynamic URL Support</td>          <td>Resolves to permalink or slug</td>          <td>Resolves via template (e.g., .Permalink)</td>      </tr>      <tr>          <td>Multilingual Support</td>          <td>Handles language prefixes</td>          <td>Can add logic to template</td>      </tr>      <tr>          <td>Ease of Use</td>          <td>Complex syntax</td>          <td>Simple input; but complex initial template setup</td>      </tr>      <tr>          <td>Status (as of 2025)</td>          <td>Obsolete, not recommended</td>          <td>Recommended in official docs</td>      </tr>      <tr>          <td>Customization</td>          <td>Limited to shortcode params</td>          <td>Highly customizable via template</td>      </tr>  </tbody></table><h2 id="to-be-continued">To Be Continued<a href="#to-be-continued" class="h-anchor" title="Permalink to #To Be Continued"></a></h2>   <p>Consider this post work in progress.</p><div class="footnotes"><hr><ol><li id="fn:1"><p>To see page source with word wrap in Firefox, go to <code>about:config</code> and switch <code>view_source.wrap_long_lines</code> to <code>true</code>. <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/hugo-and-wordpress-open-graph-meta-tags/</id><link rel="alternate" href="https://burgeonlab.com/blog/hugo-and-wordpress-open-graph-meta-tags/"/><title type="html">Open Graph Meta Tags on Hugo and WordPress Blogs</title><published>2025-07-08T23:02:47Z</published><updated>2025-07-14T00:00:00Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/hugo-and-wordpress-open-graph-meta-tags/ecp_og.webp"/><summary type="html">This post explains how to properly configure Open Graph meta tags to enhance your blog&#39;s appearance when shared on social media. It goes over setup tips for Hugo themes and WordPress plugins, common pitfalls like firewall blocks, and best practices for images and descriptions. Useful for making visually engaging social previews.</summary><content type="html"><![CDATA[<h2 id="initial-encounter">Initial Encounter<a href="#initial-encounter" class="h-anchor" title="Permalink to #Initial Encounter"></a></h2>   <p>I previously never shared my blog posts on social media, so I didn’t know those “link cards” that pop up when a URL is shared are called Open Graph and had to be set up correctly on the site for it to be generated. I first noticed the sad looking links when I was scrolling on Mastodon and when I started sharing my own posts there.</p>                             <a href="/blog/hugo-and-wordpress-open-graph-meta-tags/no_og_img.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hugo-and-wordpress-open-graph-meta-tags/no_og_img.webp" alt="Screenshot of a Mastodon toot.">    </a>                                              <a href="/blog/hugo-and-wordpress-open-graph-meta-tags/mastodon_no_og.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hugo-and-wordpress-open-graph-meta-tags/mastodon_no_og.webp" alt="Screenshot of Mastodon posts without Open Graph images">    </a>                 <h2 id="make-web-pages-look-good-on-socials">Make Web Pages Look Good on Socials<a href="#make-web-pages-look-good-on-socials" class="h-anchor" title="Permalink to #Make Web Pages Look Good on Socials"></a></h2>   <p><a href="https://ogp.me/" target="_blank" class="ext-link" rel="noopener noreferrer">Open Graph</a> (OG) meta tags are used to control how a website appears when shared on social media platforms. Instead of just having a plain text URL; an image and other details regarding the link will be generated from the tags in the website’s <code><head></code>. This includes details like the site name, title, description, and most importantly an image, making the card much more visually pleasing and engaging.</p><p>With OG tags set up correctly, an image (thumbnail if portrait, or full-size if landscape), site name, page or post title, description, and a link to your Fediverse account (Mastodon) will be shown.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup></p>                             <a href="/blog/hugo-and-wordpress-open-graph-meta-tags/og_img_working.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hugo-and-wordpress-open-graph-meta-tags/og_img_working.webp" alt="Screenshot of thumbnail OG image in a Mastodon share.">    </a>                                              <a href="/blog/hugo-and-wordpress-open-graph-meta-tags/og_with_desc.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hugo-and-wordpress-open-graph-meta-tags/og_with_desc.webp" alt="Screenshot of full width OG image in a Mastodon share.">    </a>                 <h2 id="hugo-open-graph-setup">Hugo: Open Graph Setup<a href="#hugo-open-graph-setup" class="h-anchor" title="Permalink to #Hugo: Open Graph Setup"></a></h2>   <p>I run two Hugo themes (<a href="https://github.com/hugo-theme-anubis2/" target="_blank" class="ext-link" rel="noopener noreferrer">Anubis2</a> and <a href="https://github.com/nunocoracao/blowfish" target="_blank" class="ext-link" rel="noopener noreferrer">Blowfish</a>). Blowfish seems to have all the Open Graph tags working out of the box, you just need to set up the config correctly by including <code>defaultSocialImage = "path/to/a/static/image.webp"</code> in <code>config/_default/params.toml</code> as the fallback image for when there is no featured image set. The <a href="https://blowfish.page/samples/thumbnail_sample/" target="_blank" class="ext-link" rel="noopener noreferrer">featured image</a> in the <a href="https://gohugo.io/content-management/page-bundles/" target="_blank" class="ext-link" rel="noopener noreferrer">page bundle</a> will automatically become the <code>og:image</code> if present.</p><p>As for Anubis2, there was no pre-existing theme code that rendered an Open Graph image; therefore, I added the following custom snippet into the head.html template:</p><h3 id="html-snippet">HTML Snippet<a href="#html-snippet" class="h-anchor" title="Permalink to #HTML Snippet"></a></h3>   <p>The logic <a href="https://burgeonlab.com/blog/about-rss-feeds/#rss-images">reuses</a> the cover parameter, used for the RSS feed, as the <code>og:image</code>.</p><ol><li>Declare and initialize a variable, <code>$ogImage</code></li><li>Find if there’s a parameter in the front matter called <code>cover</code>, if so use that, if not use the fallback image</li><li>use the <code>.Kind</code> Hugo variable to dynamically set <code>og:type</code> to be either article or website depending if it’s a <code>page</code> (blog posts, articles) or <code>home</code> for website</li><li>Use a <code>cover_alttext</code> parameter in the front matter to determine the alt text <a href="https://ogp.me/#structured" target="_blank" class="ext-link" rel="noopener noreferrer">structured property</a> to <code>og:image</code></li></ol>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">{{</span> <span class="err">$</span><span class="nx">ogImage</span> <span class="o">:=</span> <span class="s">""</span> <span class="p">}}</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="p">{{</span><span class="cm">/* 1. Check for cover image in front matter */</span><span class="p">}}</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="p">{{</span> <span class="k">if</span> <span class="p">.</span><span class="nx">Params</span><span class="p">.</span><span class="nx">cover</span> <span class="p">}}</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="p">{{</span><span class="cm">/* Use cover image if present */</span><span class="p">}}</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="p">{{</span><span class="cm">/* Use resource from the page bundle */</span><span class="p">}}</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="p">{{</span> <span class="err">$</span><span class="nx">resource</span> <span class="o">:=</span> <span class="p">.</span><span class="nx">Resources</span><span class="p">.</span><span class="nx">GetMatch</span> <span class="p">.</span><span class="nx">Params</span><span class="p">.</span><span class="nx">cover</span> <span class="p">}}</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="p">{{</span> <span class="k">if</span> <span class="err">$</span><span class="nx">resource</span> <span class="p">}}</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl">      <span class="p">{{</span> <span class="err">$</span><span class="nx">ogImage</span> <span class="p">=</span> <span class="err">$</span><span class="nx">resource</span><span class="p">.</span><span class="nx">Permalink</span> <span class="p">|</span> <span class="nx">absURL</span> <span class="p">}}</span></span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="p">{{</span> <span class="k">else</span> <span class="p">}}</span></span></span><span class="line"><span class="ln">11</span><span class="cl">      <span class="p">{{</span><span class="cm">/* If resource not found in page bundle, use default fallback image in static/ */</span><span class="p">}}</span></span></span><span class="line"><span class="ln">12</span><span class="cl">      <span class="p">{{</span> <span class="err">$</span><span class="nx">ogImage</span> <span class="p">=</span> <span class="s">"/images/default_post_cover.png"</span> <span class="p">|</span> <span class="nx">absURL</span> <span class="p">}}</span></span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="p">{{</span> <span class="nx">end</span> <span class="p">}}</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">{{</span> <span class="k">else</span> <span class="p">}}</span></span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="p">{{</span><span class="cm">/* 2. If no cover image  set, use the default image */</span><span class="p">}}</span></span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="p">{{</span> <span class="err">$</span><span class="nx">ogImage</span> <span class="p">=</span> <span class="s">"/images/default_post_cover.png"</span> <span class="p">|</span> <span class="nx">absURL</span> <span class="p">}}</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="p">{{</span> <span class="nx">end</span> <span class="p">}}</span></span></span><span class="line"><span class="ln">18</span><span class="cl"></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p"><</span><span class="nx">meta</span> <span class="nx">property</span><span class="p">=</span><span class="s">"og:site_name"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"{{ .Site.Title }}"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="p"><</span><span class="nx">meta</span> <span class="nx">property</span><span class="p">=</span><span class="s">"og:url"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"{{ .Permalink }}"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">{{</span> <span class="k">if</span> <span class="nx">eq</span> <span class="p">.</span><span class="nx">Kind</span> <span class="s">"page"</span> <span class="p">}}</span></span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="p"><</span><span class="nx">meta</span> <span class="nx">property</span><span class="p">=</span><span class="s">"og:type"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"article"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="p">{{</span> <span class="k">else</span> <span class="p">}}</span></span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="p"><</span><span class="nx">meta</span> <span class="nx">property</span><span class="p">=</span><span class="s">"og:type"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"website"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="p">{{</span> <span class="nx">end</span> <span class="p">}}</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="p"><</span><span class="nx">meta</span> <span class="nx">property</span><span class="p">=</span><span class="s">"og:title"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"{{ .Title }}"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p"><</span><span class="nx">meta</span> <span class="nx">property</span><span class="p">=</span><span class="s">"og:locale"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"en_GB"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="p"><</span><span class="nx">meta</span> <span class="nx">property</span><span class="p">=</span><span class="s">"og:description"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"{{ with .Params.description }}{{ . }}{{ end }}"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="p"><</span><span class="nx">meta</span> <span class="nx">property</span><span class="p">=</span><span class="s">"og:image"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"{{ $ogImage }}"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="p"><</span><span class="nx">meta</span> <span class="nx">property</span><span class="p">=</span><span class="s">"og:image:width"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"1200"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="p"><</span><span class="nx">meta</span> <span class="nx">property</span><span class="p">=</span><span class="s">"og:image:height"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"630"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="p">{{</span> <span class="nx">with</span> <span class="p">.</span><span class="nx">Params</span><span class="p">.</span><span class="nx">cover_alttext</span> <span class="p">}}</span></span></span><span class="line"><span class="ln">33</span><span class="cl">  <span class="p"><</span><span class="nx">meta</span> <span class="nx">property</span><span class="p">=</span><span class="s">"og:image:alt"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"{{ . }}"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="p">{{</span> <span class="nx">end</span> <span class="p">}}</span></span></span><span class="line"><span class="ln">35</span><span class="cl"></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="p"><</span><span class="nx">meta</span> <span class="nx">name</span><span class="p">=</span><span class="s">"twitter:card"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"summary_large_image"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="p"><</span><span class="nx">meta</span> <span class="nx">property</span><span class="p">=</span><span class="s">"twitter:url"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"{{ .Permalink }}"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="p"><</span><span class="nx">meta</span> <span class="nx">name</span><span class="p">=</span><span class="s">"twitter:title"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"{{ .Title }}"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="p"><</span><span class="nx">meta</span> <span class="nx">name</span><span class="p">=</span><span class="s">"twitter:image"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"{{ $ogImage }}"</span> <span class="o">/</span><span class="p">></span></span></span><span class="line"><span class="ln">40</span><span class="cl"></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="p"><</span><span class="nx">meta</span> <span class="nx">name</span><span class="p">=</span><span class="s">"fediverse:creator"</span> <span class="nx">content</span><span class="p">=</span><span class="s">"@username@instance"</span> <span class="o">/</span><span class="p">></span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h2 id="wordpress-open-graph-setup">WordPress: Open Graph Setup<a href="#wordpress-open-graph-setup" class="h-anchor" title="Permalink to #WordPress: Open Graph Setup"></a></h2>   <p>I use the <a href="https://www.seopress.org/wordpress-seo-plugins/free/" target="_blank" class="ext-link" rel="noopener noreferrer">SEOPress (Free)</a> plugin and, having tested several in the past, find SEOPress the most minimalistic and lightweight. Most SEO plugins have Open Graph settings built-in so it is quite handy. Just turn on the feature, fill in the details, and you’re off.</p>                             <a href="/blog/hugo-and-wordpress-open-graph-meta-tags/wp_seo_settings.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hugo-and-wordpress-open-graph-meta-tags/wp_seo_settings.webp" alt="Screenshot of SEOPress Open Graph settings page.">    </a>       <p>The other Open Graph tags like <code>og:site-title</code> and <code>og:description</code> are found in the “Titles & Metas” tab of SEOPress.</p><h3 id="firewall-settings">Firewall Settings<a href="#firewall-settings" class="h-anchor" title="Permalink to #Firewall Settings"></a></h3>   <p>I ran into an issue where <code>og:image</code> wouldn’t load, and it took me a while to realize it was caused by the <a href="https://www.wordfence.com/help/firewall/rate-limiting/#enable-rate-limiting" target="_blank" class="ext-link" rel="noopener noreferrer">rate limiting</a> setting I had in <a href="https://www.wordfence.com/products/wordfence-free/" target="_blank" class="ext-link" rel="noopener noreferrer">WordFence</a> firewall options. So if you can’t see your image load correctly, check your firewall or any advanced blocking settings in your security plugin.</p><h2 id="general-tips-for-open-graph-tags">General Tips for Open Graph Tags<a href="#general-tips-for-open-graph-tags" class="h-anchor" title="Permalink to #General Tips for Open Graph Tags"></a></h2>   <ul><li>Use recommended size: 1200px x 630px for <code>og:image</code></li><li>Ensure the image is accessible on HTTPS</li><li>Use accessible format: JPG, WebP, or PNG</li><li>Purge all caches (e.g., LiteSpeed, Cloudflare, Opcode) or re-scrape with a checker to see changes.</li><li>Write an <code>og:title</code> that is under 60 characters</li><li>Keep the <code>og:description</code> around 155-160 characters</li></ul><h3 id="open-graph-validators--checkers">Open Graph Validators / Checkers<a href="#open-graph-validators--checkers" class="h-anchor" title="Permalink to #Open Graph Validators / Checkers"></a></h3>   <p>Use tools like these to see if your page is generating the meta tags correctly.</p><ul><li><a href="https://www.opengraph.xyz/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.opengraph.xyz/</a></li><li><a href="https://opengraph.dev/" target="_blank" class="ext-link" rel="noopener noreferrer">https://opengraph.dev/</a></li><li><a href="https://orcascan.com/tools/open-graph-validator/" target="_blank" class="ext-link" rel="noopener noreferrer">https://orcascan.com/tools/open-graph-validator/</a></li><li><a href="https://www.opengraph.io/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.opengraph.io/</a></li></ul>                             <a href="/blog/hugo-and-wordpress-open-graph-meta-tags/ecp_og.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hugo-and-wordpress-open-graph-meta-tags/ecp_og.webp" alt="Screenshot of a Open Graph validator showing outputs on different social media platforms.">    </a>                 <h3 id="recommended-guides">Recommended Guides<a href="#recommended-guides" class="h-anchor" title="Permalink to #Recommended Guides"></a></h3>   <ul><li><a href="https://developers.facebook.com/docs/sharing/webmasters/" target="_blank" class="ext-link" rel="noopener noreferrer">https://developers.facebook.com/docs/sharing/webmasters/</a></li><li><a href="https://ogp.me/" target="_blank" class="ext-link" rel="noopener noreferrer">https://ogp.me/</a></li><li><a href="https://w3things.com/blog/open-graph-meta-tags/" target="_blank" class="ext-link" rel="noopener noreferrer">https://w3things.com/blog/open-graph-meta-tags/</a></li><li><a href="https://www.opengraphpreview.com/blog/understanding-open-graph-meta-tags-ultimate-guide" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.opengraphpreview.com/blog/understanding-open-graph-meta-tags-ultimate-guide</a></li></ul><h3 id="free-open-graph-generators">Free Open Graph Generators<a href="#free-open-graph-generators" class="h-anchor" title="Permalink to #Free Open Graph Generators"></a></h3>   <p>Found two tools that are really cool to use. Credits to <a href="https://github.com/Mr-Sunglasses." target="_blank" class="ext-link" rel="noopener noreferrer">Kanishk Pachauri</a> for <a href="https://github.com/Mr-Sunglasses/ogplayground/" target="_blank" class="ext-link" rel="noopener noreferrer">OGPlayground</a> and <a href="https://github.com/EddyVinck/" target="_blank" class="ext-link" rel="noopener noreferrer">Eddy Vinck</a> for <a href="https://ogimagemaker.com/" target="_blank" class="ext-link" rel="noopener noreferrer">OG Image Maker</a>.</p><p>I’m happy to help if you have any issues implementing this on your Hugo or WordPress blog. Just leave a comment below!</p><div class="footnotes"><hr><ol><li id="fn:1"><p>I noticed the og:description tag disappears when the fediverse:creator tag is added. I have <a href="https://github.com/mastodon/mastodon/issues/35270" target="_blank" class="ext-link" rel="noopener noreferrer">submitted a suggestion</a> and awaiting a response. Update: Apparently it’s been suggested before (it didn’t come up in my initial search) but I’m not sure if they’re willing to reconsider it… <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/cloudflare-web-analytics-rum-injected-tracking-beacon-script-into-my-sites/</id><link rel="alternate" href="https://burgeonlab.com/blog/cloudflare-web-analytics-rum-injected-tracking-beacon-script-into-my-sites/"/><title type="html">Cloudflare Auto Injected Tracking Scripts To My Sites</title><published>2025-06-30T15:03:26Z</published><updated>2025-06-30T15:03:26Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/cloudflare-web-analytics-rum-injected-tracking-beacon-script-into-my-sites/cloudflareinsights_errors.webp"/><summary type="html">Discover how Cloudflare&amp;rsquo;s auto-injection of tracking scripts led to privacy concerns. Learn how to disable Cloudflare Web Analytics (Real User Monitoring) and explore privacy-first alternatives for CDN and DNS. Services like HE.net, Bunny.net, Fastly, and others are being considered. Share your favorite Cloudflare alternatives!</summary><content type="html"><![CDATA[<h2 id="preface">Preface<a href="#preface" class="h-anchor" title="Permalink to #Preface"></a></h2>   <p>Two of my three domains (not <em>BurgeonLab.com</em>) use Cloudflare DNS nameservers. When I first started out my <a href="https://burgeonlab.com/blog/blogging-with-hugo-and-wordpress/">blogging and web hosting journey</a>, I didn’t know anything about domains, DNS, CDNs, etc. At the time, I believed that using Cloudflare DNS, compared to my domain registrar’s default DNS, would significantly improve several areas, such as:</p><ul><li>Page Rules and caching options</li><li>Fast global CDN</li><li>Free SSL/TLS certs</li><li>CNAME flattening</li><li>Supports many DNS record types (over 200 records per zone)</li><li>DDoS protection and WAF (web application firewall)</li><li>DNSSEC protection</li><li>Fast, reliable, and secure DNS hosting and management</li></ul><h2 id="the-problem">The Problem<a href="#the-problem" class="h-anchor" title="Permalink to #The Problem"></a></h2>                                <a href="/blog/cloudflare-web-analytics-rum-injected-tracking-beacon-script-into-my-sites/cloudflareinsights_errors.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/cloudflare-web-analytics-rum-injected-tracking-beacon-script-into-my-sites/cloudflareinsights_errors.webp" alt="Screenshot of browser console showing multiple errors related to cloudflareinsights like Cross-Origin Request Blocked and CORS request did not succeed.">    </a>                 <p>I have not had any big issues with using Cloudflare, until today, when I noticed errors in the browser console related to <code>https://static.cloudflareinsights.com/beacon.min.js/*</code>.I found this strange because I hadn’t added any scripts other than <a href="https://burgeonlab.com/colophon/#web-analytics">GoatCounter</a>. I have not worked on these other two sites recently, so I only just noticed these errors. I do not remember seeing them before though. The errors would disappear if I turn off “<a href="https://librewolf.net/docs/faq/#what-is-enhanced-tracking-protection" target="_blank" class="ext-link" rel="noopener noreferrer">Enhanced Tracking Protection</a>”.</p><p>After some rummaging, I realized that Cloudflare has decided to <strong>auto inject</strong> a tracking script for their own “Cloudflare Web Analytics” which is also known as “<a href="https://developers.cloudflare.com/rules/configuration-rules/settings/#disable-real-user-monitoring-rum" target="_blank" class="ext-link" rel="noopener noreferrer">Real User Monitoring (RUM)</a>” into my two sites! Seriously… 😒 They claim it is a <em>privacy-first analytics</em> tools, but I am skeptical with that kind of behaviour…</p>                             <a href="/blog/cloudflare-web-analytics-rum-injected-tracking-beacon-script-into-my-sites/cloudflare_webanalytics.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/cloudflare-web-analytics-rum-injected-tracking-beacon-script-into-my-sites/cloudflare_webanalytics.webp" alt="Screenshot of Web Analytics on Cloudflare dashboard.">    </a>                 <h2 id="the-fix">The Fix<a href="#the-fix" class="h-anchor" title="Permalink to #The Fix"></a></h2>   <ol><li>Log in to your Cloudflare dashboard at the account level (not per site level)</li><li>Go to Analytics & Logs > Web Analytics</li><li>Click Manage site for each of your sites on Cloudflare</li><li>Select Disable > Update</li></ol>                             <a href="/blog/cloudflare-web-analytics-rum-injected-tracking-beacon-script-into-my-sites/cloudflare_managesite.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/cloudflare-web-analytics-rum-injected-tracking-beacon-script-into-my-sites/cloudflare_managesite.webp" alt="Screenshot of Cloudflare disable RUM.">    </a>       <h2 id="whats-next">What’s Next<a href="#whats-next" class="h-anchor" title="Permalink to #What&rsquo;s Next"></a></h2>   <p>Now that I’ve been “in the game” for a while, I know there are definitely some trade-offs and disadvantages to using a “giant”, US-based, and free service like Cloudflare. A year or two ago, I stopped using their DNS resolver on all my devices and switched to <a href="https://docs.quad9.net/" target="_blank" class="ext-link" rel="noopener noreferrer">Quad9</a>. I wasn’t aware of as much data privacy back then, but now I am. Therefore, I’m considering finding alternatives that are more privacy-conscious for my websites. It’s still early days, but my preliminary searches haven’t shown any similar all-in-one alternatives.</p><h3 id="cloudflare-alternatives">Cloudflare Alternatives<a href="#cloudflare-alternatives" class="h-anchor" title="Permalink to #Cloudflare Alternatives"></a></h3>   <p>Here are a few services I came across that I need to research. I think it will be easier to split the two main services I’m using currently using (CDN and DNS) between two providers.</p><ul><li><a href="https://dns.he.net/" target="_blank" class="ext-link" rel="noopener noreferrer">HE.net</a></li><li>Bunny.net (<a href="https://bunny.net/pricing/dns/" target="_blank" class="ext-link" rel="noopener noreferrer">DNS</a> and <a href="https://bunny.net/pricing/cdn/" target="_blank" class="ext-link" rel="noopener noreferrer">CDN</a>)</li><li><a href="https://www.fastly.com/pricing" target="_blank" class="ext-link" rel="noopener noreferrer">Fastly</a></li><li><a href="https://www.quic.cloud/cdn-costs/" target="_blank" class="ext-link" rel="noopener noreferrer">Quic.Cloud</a> (Mainly for WordPress + Litespeed)</li><li><a href="https://www.keycdn.com/pricing" target="_blank" class="ext-link" rel="noopener noreferrer">KeyCDN</a></li><li><a href="https://www.ibm.com/products/ns1-connect#free-developer" target="_blank" class="ext-link" rel="noopener noreferrer">IMB NS1 Connect</a></li><li><a href="https://1984.hosting/product/freedns/" target="_blank" class="ext-link" rel="noopener noreferrer">1984.hosting</a></li></ul><p>If you are using a Cloudflare alternative, please share it! Thanks.</p><p><strong>P.S. TIL:</strong> P.S. TIL: I’ve decided to add a new series called “<a href="https://burgeonlab.com/series/today-i-learnt/">Today I Learnt</a>” to the blog.</p>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/blogging-with-hugo-and-wordpress/</id><link rel="alternate" href="https://burgeonlab.com/blog/blogging-with-hugo-and-wordpress/"/><title type="html">My Blogging Pursuit with Hugo and WordPress</title><published>2025-06-25T00:10:00Z</published><updated>2025-06-25T00:10:00Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/blogging-with-hugo-and-wordpress/3_blogs.webp"/><summary type="html">Dive into my blogging journey across Hugo, WordPress, and learning about IndieWeb. I reflect on webrings, RSS, and the pros of static site generators. Discover why I&#39;m shifting focus to BurgeonLab as my main blog, overcoming challenges of managing multiple blogs, and embracing the joy of customizing themes, writing offline, and exploring IndieWeb principles.</summary><content type="html"><![CDATA[<h2 id="widening-my-horizons">Widening My Horizons<a href="#widening-my-horizons" class="h-anchor" title="Permalink to #Widening My Horizons"></a></h2>   <p>Ever since joining the Fosstodon.org instance on Mastodon as my main “Fediverse residence” in mid-2023, I have discovered many amazing personal sites, weblogs, and online representations from skilled and interesting tech-oriented people people.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup> I have subsequently taken a lot of interest in building a blog and developing skills in my growing <a href="https://burgeonlab.com/about/#technology-stack">tech stack</a>, so that I can make BurgeonLab my “main home” on the World Wide Web.</p><p>Now that I have dabbled in Hugo for almost two years, I am feeling way more comfortable with customizing themes to my liking and adding little features here and there. But I still have a lot I want to do with BurgeonLab; which is why I decided to write this post to reflect a bit on my blogging journey thus far, and the challenges of managing multiple blogs.</p><h2 id="discovering-webring-communities">Discovering Webring Communities<a href="#discovering-webring-communities" class="h-anchor" title="Permalink to #Discovering Webring Communities"></a></h2>   <p><strong>Update</strong>: In Q3 2025, I took my first step and joined 512KB.club with my site clocking in at 440kB <em>(uncompressed, as of 2025/10/02)</em>. I also joined <a href="https://darktheme.club/" target="_blank" class="ext-link" rel="noopener noreferrer">Darktheme.club</a> and <a href="https://edleeman.co.uk/cookie-zero/" target="_blank" class="ext-link" rel="noopener noreferrer">CookieZero</a>! I’m quite happy with the web badge (88x31) collection…</p><p>While browsing the websites of people I’ve come across on Fosstodon, I noticed many were part of <a href="https://en.wikipedia.org/wiki/Webring" target="_blank" class="ext-link" rel="noopener noreferrer">webrings</a>. This word vaguely triggered old memories of going on the Internet as a kid in the 90s (cue that <a href="https://freesound.org/people/wtermini/sounds/546450/" target="_blank" class="ext-link" rel="noopener noreferrer">modem sound</a>). I had no idea these still existed…oh, my ignorance! After refreshing my knowledge on the current webring situation, I found a site that has a pretty extensive and up to date <a href="https://brisray.com/web/webring-list.htm" target="_blank" class="ext-link" rel="noopener noreferrer">webring directory</a> curated by Ray at <a href="https://brisray.com/" target="_blank" class="ext-link" rel="noopener noreferrer">brisray.com</a>. <em>Thanks Ray!</em> Here are a few I’m interested in checking out, in no particular order:</p><ul><li><a href="https://cs.sjoy.lol/" target="_blank" class="ext-link" rel="noopener noreferrer">CSS JOY Webring</a> - For those who like to mess with CSS</li><li><a href="https://meta-ring.hedy.dev/" target="_blank" class="ext-link" rel="noopener noreferrer">Meta Ring</a> - For those who craft their site and write about it in a /meta or /colophon</li><li><a href="https://geekring.net/" target="_blank" class="ext-link" rel="noopener noreferrer">Geekring</a> - For geeks with non-commercial sites</li><li><a href="https://fediring.net/" target="_blank" class="ext-link" rel="noopener noreferrer">Fediring</a> - For fedizens with personal sites</li><li><a href="https://baccyflap.com/noai/" target="_blank" class="ext-link" rel="noopener noreferrer">No AI Webring</a> - For sites without AI content</li><li><a href="https://netloop.netlify.app/" target="_blank" class="ext-link" rel="noopener noreferrer">Netloop</a> - For anyone with personal sites</li><li><a href="https://webmasterwebring.netlify.app/" target="_blank" class="ext-link" rel="noopener noreferrer">Webmaster Webring</a> - For anyone with websites</li><li><a href="https://octo-ring.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Octo Ring</a> - For GitHub users</li><li><a href="https://webring.bucketfish.me/" target="_blank" class="ext-link" rel="noopener noreferrer">Bucket Webring</a> - For cool people who like to make things</li></ul><p>Many webrings have a requirement that the sites have to be hard-coded but I think some allow for static sites by generators; I’ll need to double check. I don’t feel ready to join any yet, but it’s definitely something I am working towards.</p><h3 id="indieweb">IndieWeb<a href="#indieweb" class="h-anchor" title="Permalink to #IndieWeb"></a></h3>   <p>As for <a href="https://indieweb.org/" target="_blank" class="ext-link" rel="noopener noreferrer">IndieWeb</a> (I am still wrapping my head around it); from what I understand, it’s a movement/collective focused on how one’s content shouldn’t be locked in a proprietary platform. By using independent, self-owned websites to share content without using centralized platforms; ultimately, we can avoid <a href="https://en.wikipedia.org/wiki/Enshittification" target="_blank" class="ext-link" rel="noopener noreferrer">enshittification</a>. IndieWeb promotes personal identity and content ownership which I vouch for.</p> <blockquote>  <p>“When you post something on the web, it should belong to you, not a corporation. Too many companies have gone out of business and lost all of their users’ data. By joining the IndieWeb, your content stays yours and in your control.”</p><p>— <em><a href="https://indieweb.org/" target="_blank" class="ext-link" rel="noopener noreferrer">https://indieweb.org/</a></em></p></blockquote> <p>There seem to be multiple components of IndieWeb and I suggest reading this recent (2024) detailed guide written by <a href="https://bacardi55.io/2024/01/29/indieweb-adventure-first-steps/" target="_blank" class="ext-link" rel="noopener noreferrer">bacardi55</a> and an older one (2018) by <a href="https://www.amitgawande.com/blog/2018/indiewebify-your-hugo-website" target="_blank" class="ext-link" rel="noopener noreferrer">Amit</a> which can explain it better than I can.</p><p>Some CMS (content management systems) have built-in <a href="https://spec.indieweb.org/" target="_blank" class="ext-link" rel="noopener noreferrer">IndieWeb features</a>:</p><ul><li><a href="https://indieweb.org/microformats" target="_blank" class="ext-link" rel="noopener noreferrer">Microformats</a>—a type of HTML markup that identify structured data used by IndieWeb</li><li><a href="https://www.w3.org/TR/webmention/" target="_blank" class="ext-link" rel="noopener noreferrer">Webmentions</a>—receiving notifications when a URL is mentioned/links to it</li><li>POSSE (i.e. “Publish (on your) Own Site, Syndicate Elsewhere”, allows siloing (cross-posting) content to other social platforms from your own site. Here’s a <a href="https://news.ycombinator.com/item?id=35636052" target="_blank" class="ext-link" rel="noopener noreferrer">discussion about it on Hacker News</a>)</li><li><a href="https://indieweb.org/IndieAuth" target="_blank" class="ext-link" rel="noopener noreferrer">IndieAuth</a></li></ul><p>Here are a few Hugo<sup id="fnref:2"><a href="#fn:2" class="footnote-ref">2</a></sup> themes that has IndieWeb features:</p><ul><li><a href="https://github.com/brianreumere/plague" target="_blank" class="ext-link" rel="noopener noreferrer">Plague</a></li><li><a href="https://git.sr.ht/~bacardi55/MinIndie" target="_blank" class="ext-link" rel="noopener noreferrer">MinIndie</a></li><li><a href="https://github.com/victoriadrake/hugo-theme-quint" target="_blank" class="ext-link" rel="noopener noreferrer">Quint</a></li><li><a href="https://github.com/victoriadrake/neofeed-theme" target="_blank" class="ext-link" rel="noopener noreferrer">Neofeed</a></li><li><a href="https://github.com/AngeloStavrow/indigo" target="_blank" class="ext-link" rel="noopener noreferrer">Indigo</a> (last update in 2020)</li></ul><p>Further research is needed for me to decide if I am heading down the IndieWeb route. There’s an insightful post by <a href="https://chrismcleod.dev/blog/some-words-on-webmentions/" target="_blank" class="ext-link" rel="noopener noreferrer">Chris Mcleod</a> that talks about the possible downsides of using features like Microformats and possible privacy issues with Webmentions.</p><h2 id="multiple-blog-situation">Multiple Blog Situation<a href="#multiple-blog-situation" class="h-anchor" title="Permalink to #Multiple Blog Situation"></a></h2>                                <a href="/blog/blogging-with-hugo-and-wordpress/3_blogs.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/blogging-with-hugo-and-wordpress/3_blogs.webp" alt="Screenshot of three blog sites owned by Naty.">    </a>                 <p>In the opening paragraph, I mentioned that this site is set to become my main blog, but it didn’t start out that way; and I’m not entirely sure if it will remain so! This realization only hit me about a month ago after I made some significant changes to the two blogs I’m currently managing.</p><p>For quite a while, I had been dividing my social media accounts for various purposes: one for general content (eclecticpassions) and another dedicated to my photography<sup id="fnref:3"><a href="#fn:3" class="footnote-ref">3</a></sup> (APERTURE2IRIS). It’s only now, with the addition of this tech-focused blog (BurgeonLab), that I’ve come to see splitting up my primary interests (general topics, photography, technology) into separate handles might have been a mistake. Now I’m juggling multiple accounts and domain names without knowing what the best approach is. Maybe one day I’ll consolidate everything into one central location?</p><h2 id="recent-blog-fever">Recent Blog Fever<a href="#recent-blog-fever" class="h-anchor" title="Permalink to #Recent Blog Fever"></a></h2>   <p>What inspired me to make significant updates to my two blogs was when my friend chose to launch his own photography blog. He experimented with various content management systems, but I couldn’t persuade him to go for a static site generator; he preferred something user-friendly with a WYSIWYG GUI editor that’s widely used and didn’t require coding skills. In the end, he opted for hosted WordPress. To my surprise, I found that I could help him out by answering most of his questions about WordPress! It turns out all my experimentation paid off after all!</p><h3 id="wordpress-beginnings">WordPress Beginnings<a href="#wordpress-beginnings" class="h-anchor" title="Permalink to #WordPress Beginnings"></a></h3>   <p>My first “proper” blog (i.e. using my own domain instead of a third-party platform like Tumblr or WordPress.<em>com</em>) was established when I snagged a multi-year, managed WordPress hosting plan from Hostinger a few years ago during Black Friday. I got a <a href="https://www.instagram.com/eclecticpassions/" target="_blank" class="ext-link" rel="noopener noreferrer">domain</a> name that matched my <a href="https://www.instagram.com/eclecticpassions/" target="_blank" class="ext-link" rel="noopener noreferrer">Instagram</a> handle since that is where I’d built up most of my social following. I originally wanted to bridge my short-form content on Instagram (as I often hit their character limit) into longer, more comprehensive posts; and possibly trying out affiliate marketing as I have always liked researching and comparing products.</p><p>As I explored all things blogs, I started learning about not only WordPress; but also SEO, page speed optimization, keyword research, and website best practices, etc. The list of things to learn was never ending, which overwhelmed me when what I really wanted to do was just write content. In the end, I have not gone through with that plan at all and I regret to say there’s not a lot of content there. I blame myself for choosing the wrong CMS and I dug myself into the WordPress rabbit hole.</p><h3 id="hugo-saves-the-day">Hugo Saves the Day<a href="#hugo-saves-the-day" class="h-anchor" title="Permalink to #Hugo Saves the Day"></a></h3>   <p>What did happen was I learnt how the WordPress backend works, DNS, caching, best WordPress plugins (for my use case), getting posts ranked on search engines and optimizing for fast page speed scores, etc. Learning all this made me realise I should have went directly to a static site generator (SSG) like Hugo at the start! In my experience, SSGs are easier to manage, faster than dynamic sites, more secure, and lightweight to list a few pros. Sure, there is a slightly steeper learning curve, but it gave me a chance to learn a lot of other skills like using an IDE (VS Codium), being more comfortable with using the terminal, understanding the basics of Git/versioning, using languages like HTML, CSS, JSON, TOML, YAML, and GO.</p><p>Which is why I think I am now leaning towards making BurgeonLab my main blog as I find a lot more joy when messing around with Hugo, being able to write offline on my device of choice, using my IDE or Markdown editor instead of the horrid UI/UX and clunky in-browser WordPress editor. It’s just very unsatisfactory when I am writing there; I can’t describe it</p><h2 id="last-of-the-blog-trio">Last of the Blog Trio<a href="#last-of-the-blog-trio" class="h-anchor" title="Permalink to #Last of the Blog Trio"></a></h2>   <p>Finally, the <a href="https://www.aperture2iris.com" target="_blank" class="ext-link" rel="noopener noreferrer">photography blog</a> was born out of a need to replace my Adobe Portfolio site. I’ve been meaning to publish it earlier this year but it has been delayed. I will try to prioritize it next as I have worked on the other two enough, <em>for now</em>! With a lot more Hugo experience than the beginning of the year, I am confident it’ll be a pretty painless job, especially with a more developed theme like <a href="https://blowfish.page" target="_blank" class="ext-link" rel="noopener noreferrer">Blowfish</a>. Really looking forward to writing about photography topics like transitioning from Adobe Lightroom to open source photography software (<a href="https://www.darktable.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Darktable</a>, <a href="https://www.digikam.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Digikam</a>) and setting up a portfolio section all within Hugo.</p><p>So do you also have multiple blogs? How do you manage it? Will the pursuit of the perfect blogging setup ever be final?<sup id="fnref:4"><a href="#fn:4" class="footnote-ref">4</a></sup> Let me know what your views are!</p><div class="footnotes"><hr><ol><li id="fn:1"><p>I will include a blogroll section in the future! <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li><li id="fn:2"><p>Here’s a <a href="https://indieweb.org/Hugo" target="_blank" class="ext-link" rel="noopener noreferrer">list of people</a> who use Hugo in the IndieWeb community (scroll to “IndieWeb Examples”). <a href="#fnref:2" class="footnote-backref">↩︎</a></p></li><li id="fn:3"><p>Originally my photography domain was used as my portfolio using Adobe Portfolio which came free with my Adobe Photography plan. But because I have decided to leave Adobe and end the subscription, the idea of having a proper photography blog + portfolio static site really appealed to me. <a href="#fnref:3" class="footnote-backref">↩︎</a></p></li><li id="fn:4"><p>I follow another two bloggers, <a href="https://baty.net/posts/2025/09/ah-blogging/" target="_blank" class="ext-link" rel="noopener noreferrer">Jack</a> and <a href="https://kevquirk.com/blog/switching-back-to-jekyll-building-my-own-cms/" target="_blank" class="ext-link" rel="noopener noreferrer">Kev</a> who are often tweaking and experimenting with their CMS and blogging platforms. Check out their site(s)! <a href="#fnref:4" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/changelog/</id><link rel="alternate" href="https://burgeonlab.com/changelog/"/><title type="html">Changelog: Hugo Development and Blog Updates</title><published>2025-06-21T18:32:11Z</published><updated>2025-10-27T00:00:00Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/changelog/og_img_changelog.webp"/><summary type="html">A log of changes to the structure, functionality, and design of BurgeonLab.com.</summary><content type="html"><![CDATA[<p>Want to see what I’ve been tinkering with, <em>instead of writing</em>?<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup> Here’s a list of changes I’ve made to my Hugo site.</p><h2 id="26-2025-11-02">2.6 (2025-11-02)<a href="#26-2025-11-02" class="h-anchor" title="Permalink to #2.6 (2025-11-02)"></a></h2>   <h3 id="style">Style<a href="#style" class="h-anchor" title="Permalink to #Style"></a></h3>   <ul><li>New hidden web badge section at the bottom of the homepage</li><li>New carbon footprint custom web badge with dropdown menu (only HTML+CSS)</li></ul><h3 id="backend">Backend<a href="#backend" class="h-anchor" title="Permalink to #Backend"></a></h3>   <ul><li>New feature: Add JSON-LD structured data (aka schema markup) used by search engines to homepage and other pages</li><li>Create new Hugo section / post type for upcoming Weeknotes:<ul><li>Dedicated Atom feed</li><li>List template</li><li>Archetype template</li></ul></li></ul><h3 id="bugfix">Bugfix<a href="#bugfix" class="h-anchor" title="Permalink to #Bugfix"></a></h3>   <ul><li>Mastodon verification failed, rel=“me” cannot be joined with other attributes</li><li>Jump to top button disappeared—blocked by uOrigin as “cosmetic annoyances” (<a href="https://burgeonlab.com/blog/adding-a-simple-scroll-to-the-top-button-to-your-hugo-site/">updated tutorial</a>)</li><li>Declare <code>@font-face</code> rules only once in <code>critical.css</code> (inline)</li><li>Fix “force reflow” PageSpeed error caused by <code>render-codeblock</code> and <code>copy-code.js</code></li></ul><h2 id="251-2025-10-27">2.5.1 (2025-10-27)<a href="#251-2025-10-27" class="h-anchor" title="Permalink to #2.5.1 (2025-10-27)"></a></h2>   <h3 id="style-1">Style<a href="#style-1" class="h-anchor" title="Permalink to #Style"></a></h3>   <ul><li>Found lightweight, variable font alternatives similar to Monaspace Krypton and Firava. Replace with <a href="https://kodemono.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Kode Mono</a> and <a href="https://fonts.google.com/specimen/Nokora" target="_blank" class="ext-link" rel="noopener noreferrer">Nokora</a>, saving about 200KB</li><li>Fallback to system font stacks</li></ul>                             <a href="/changelog/fontchange.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/changelog/fontchange.webp" alt="Screenshot comparison of the font changes on burgeonlab.com">    </a>                 <h3 id="backend-1">Backend<a href="#backend-1" class="h-anchor" title="Permalink to #Backend"></a></h3>   <ul><li>Update canonical URL to non-www domain instead of www and update redirect rules</li><li>Add <code>cache-control</code> headers</li><li>Add HSTS security response headers, considering preload list submission</li><li>Compress web badges, use <code>.png</code> instead of <code>.webp</code> for <code>og:image</code> as it is more widely supported</li><li>Use CSS only method to fix FOUC</li><li>Improve PageSpeed score to 100% x4 on both mobile and desktop</li><li>Fix all HTML validation errors on <a href="https://validator.w3.org/" target="_blank" class="ext-link" rel="noopener noreferrer">W3C</a></li><li>Fix CLS issues, correct use of preloading resources, inline critical CSS</li></ul><h3 id="features">Features<a href="#features" class="h-anchor" title="Permalink to #Features"></a></h3>   <ul><li>Due to savings with font changes, BurgeonLab.com is now in the <a href="https://github.com/kevquirk/512kb.club/pull/1974" target="_blank" class="ext-link" rel="noopener noreferrer">orange team of 512KB.club</a>, clocking in at 151KB</li></ul><h2 id="v25-2025-10-11">v2.5 (2025-10-11)<a href="#v25-2025-10-11" class="h-anchor" title="Permalink to #v2.5 (2025-10-11)"></a></h2>   <h3 id="features-1">Features<a href="#features-1" class="h-anchor" title="Permalink to #Features"></a></h3>   <ul><li>Add a CSS-only loading <a href="https://codepen.io/designingcoder/pens/popular" target="_blank" class="ext-link" rel="noopener noreferrer">spinning animation</a> for contact form submit button</li><li>Add post counter to <a href="https://burgeonlab.com/blog/">heatmap calendar</a> and move heading above year buttons</li></ul><h3 id="style-2">Style<a href="#style-2" class="h-anchor" title="Permalink to #Style"></a></h3>   <ul><li>Simplify link hover underline CSS animation, stop relying on psudo-elements</li><li>Make buttons more responsive (i.e. full width) on mobile devices</li></ul><h3 id="backend-2">Backend<a href="#backend-2" class="h-anchor" title="Permalink to #Backend"></a></h3>   <ul><li>Switch to <a href="https://w3speedup.com/tools/critical-css-generator/" target="_blank" class="ext-link" rel="noopener noreferrer">W3’s critical css generator</a> instead of <a href="https://www.npmjs.com/package/critical" target="_blank" class="ext-link" rel="noopener noreferrer">npm critical</a></li><li>Fix FOUC bug related to theme-switcher script</li></ul><h2 id="v24-2025-10-06">v2.4 (2025-10-06)<a href="#v24-2025-10-06" class="h-anchor" title="Permalink to #v2.4 (2025-10-06)"></a></h2>   <h3 id="features-2">Features<a href="#features-2" class="h-anchor" title="Permalink to #Features"></a></h3>   <ul><li>Join two new web communities (<a href="https://darktheme.club/" target="_blank" class="ext-link" rel="noopener noreferrer">darktheme.club</a> and <a href="https://edleeman.co.uk/cookie-zero/" target="_blank" class="ext-link" rel="noopener noreferrer">cookiezero</a>)</li><li>Additional web badges at the bottom of the home page</li><li>Add SourceHut builds badge and tooltip with last deployed date</li></ul><h3 id="style-3">Style<a href="#style-3" class="h-anchor" title="Permalink to #Style"></a></h3>   <ul><li>Render hook customized for links and headings:<ul><li>SVG suffix added for external links on hover</li><li>Opens on new tab <strong>only</strong> for external links with security attributes to prevent clickjacking</li><li>New hover response for heading permalinks with simple CSS animation highlight/fade when a heading is clicked</li><li>Heading permalink SVG allows for quick “copy link” (but no auto copy as I didn’t want to rely on JS)</li></ul></li><li>Simplify post info section (i.e. date, revised date, word count, read time)</li></ul><h3 id="backend-3">Backend<a href="#backend-3" class="h-anchor" title="Permalink to #Backend"></a></h3>   <ul><li>Change all internal links from <code>ref</code> shortcodes to standard Markdown with render hook processing</li><li>Update <a href="https://sr.ht/~eclecticpassions/burgeonlab/" target="_blank" class="ext-link" rel="noopener noreferrer">README.md</a> with more information</li><li>Add section indexes to <a href="https://burgeonlab.com/pages/">pages</a> and <a href="https://burgeonlab.com/blog/">posts</a></li><li>New dedicated <a href="https://burgeonlab.com/support/">/support</a> page, add image banners for referral/affiliate links, simplify support message partial with links to this new page</li></ul><h2 id="v23-2025-09-19">v2.3 (2025-09-19)<a href="#v23-2025-09-19" class="h-anchor" title="Permalink to #v2.3 (2025-09-19)"></a></h2>   <h3 id="features-3">Features<a href="#features-3" class="h-anchor" title="Permalink to #Features"></a></h3>   <ul><li>GitHub-style heatmap calendar on <a href="https://burgeonlab.com/blog/">/archive</a> showing days where posts were published, with the colour range being the length of the post (number of words)</li></ul>                             <a href="/changelog/heamap.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/changelog/heamap.webp" alt="Hugo heatmap widget for posts based on word count (github style heatmap)">    </a>       <ul><li><p>Put contact links into a dedicated <a href="https://burgeonlab.com/contact/">/contact</a> page with the addition of a new method: contact form (with <a href="https://formspark.io" target="_blank" class="ext-link" rel="noopener noreferrer">Formspark.io</a>)</p><ul><li>Remov social links from home page, but kept them at the bottom of posts and pages</li></ul></li><li><p>Navigation bar:</p><ul><li>Add a CSS-only submenu (based on the <a href="https://css-tricks.com/the-checkbox-hack/" target="_blank" class="ext-link" rel="noopener noreferrer">hidden checkbox hack/method</a>)</li><li>Add a home icon next to search for usability</li></ul></li></ul><h3 id="backend-4">Backend<a href="#backend-4" class="h-anchor" title="Permalink to #Backend"></a></h3>   <ul><li>Create ATOM feed with full text instead of the existing RSS feed with only short summaries (will keep both for reader to decide which to subscribe to)</li></ul><h2 id="v22-2025-08-13">v2.2 (2025-08-13)<a href="#v22-2025-08-13" class="h-anchor" title="Permalink to #v2.2 (2025-08-13)"></a></h2>   <h3 id="backend-5">Backend<a href="#backend-5" class="h-anchor" title="Permalink to #Backend"></a></h3>   <ul><li><p>Migrate blog source code / Git hosting to SourceHut (from GitHub)</p></li><li><p>Transfer GitHub Pages and GH Action CI/CD workflow to SourceHut Builds</p></li><li><p>Replace free hosting on GitHub Pages (using their included Fastly CDN) to Bunny.net Storage and Bunny CDN</p></li></ul><h3 id="styleformatting">Style/Formatting<a href="#styleformatting" class="h-anchor" title="Permalink to #Style/Formatting"></a></h3>   <ul><li>New post-cards design, simplified border and delineate the area with post information</li></ul>                             <a href="/changelog/new_postcard.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/changelog/new_postcard.webp" alt="Screenshot comparison of post cards getting a clearer marking for post-info.">    </a>       <ul><li>Improve site logo readability (thanks <a href="https://www.adamsdesk.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Adam</a> for the tip on changing the bottom text right-side-up) and new design for default Open Graph image</li></ul>                             <a href="/changelog/new_og_logo.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/changelog/new_og_logo.webp" alt="Screenshot comparison of the old and new Open Graph OG image and also BurgeonLab logo.">    </a>       <ul><li>Add badges section and support-me section to home page</li></ul><h3 id="speed-optimization">Speed Optimization<a href="#speed-optimization" class="h-anchor" title="Permalink to #Speed Optimization"></a></h3>   <ul><li><p>Improve load speed by reducing total number of requests by combining CSS, JS into bundles</p></li><li><p>Start using <a href="https://github.com/addyosmani/critical" target="_blank" class="ext-link" rel="noopener noreferrer">critical.css</a> for improving render times</p></li></ul><h2 id="v212-2025-07-26">v2.1.2 (2025-07-26)<a href="#v212-2025-07-26" class="h-anchor" title="Permalink to #v2.1.2 (2025-07-26)"></a></h2>   <ul><li><p>Join <a href="https://512kb.club/" target="_blank" class="ext-link" rel="noopener noreferrer">512kb.club </a> at 455kb</p></li><li><p>Tidy up implementation of <a href="https://pagefind.app/docs/installation/" target="_blank" class="ext-link" rel="noopener noreferrer">PageFind</a></p></li><li><p>Improve .<code>png</code> and <code>.webp</code> image compression</p></li><li><p>Remove all series and created new ones with broader subjects or themes, so tags and series don’t overlap in function</p></li></ul><h2 id="v211-2025-06-23">v2.1.1 (2025-06-23)<a href="#v211-2025-06-23" class="h-anchor" title="Permalink to #v2.1.1 (2025-06-23)"></a></h2>   <h3 id="stylingformatting">Styling/Formatting<a href="#stylingformatting" class="h-anchor" title="Permalink to #Styling/Formatting"></a></h3>   <ul><li>Simplify top navigation bar by moving all social link icons to its own section on the home page</li></ul>                             <a href="/changelog/top_nav.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/changelog/top_nav.webp" alt="Screenshot comparison of top navigation header bar on burgeonlab.com.">    </a>                                              <a href="/changelog/find_me.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/changelog/find_me.webp" alt="Screenshot of new social links.">    </a>                 <ul><li>Add <code>box-shadow</code> to the nav bar, post-cards, social-links</li></ul><h3 id="features-4">Features<a href="#features-4" class="h-anchor" title="Permalink to #Features"></a></h3>   <ul><li><p>Create new separate section for pages, link to <a href="https://burgeonlab.com/pages/">/pages</a> in top menu</p></li><li><p>Update all links to have noopener, nofollow and target="_blank"</p></li></ul><h3 id="pages">Pages<a href="#pages" class="h-anchor" title="Permalink to #Pages"></a></h3>   <ul><li>Add custom 404 error page</li></ul><h2 id="v21-2025-06-21">v2.1 (2025-06-21)<a href="#v21-2025-06-21" class="h-anchor" title="Permalink to #v2.1 (2025-06-21)"></a></h2>   <h3 id="stylingformatting-1">Styling/Formatting<a href="#stylingformatting-1" class="h-anchor" title="Permalink to #Styling/Formatting"></a></h3>   <ul><li>Increase color contrast of light & dark themes according to WCAG compliance levels for improved readability and accessibility</li></ul>                             <a href="/changelog/old_colors.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/changelog/old_colors.webp" alt="Screenshot of the blog with old colors.">    </a>                                              <a href="/changelog/new_colors.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/changelog/new_colors.webp" alt="Screenshot of the blog with new colors.">    </a>                 <ul><li>New tree style on <a href="https://burgeonlab.com/blog/">Posts Archive</a> page</li></ul>                             <a href="/changelog/tree_archive.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/changelog/tree_archive.webp" alt="Screenshot of archive page with css tree structure styling.">    </a>                 <ul><li>Streamlined nav bar/header, takes up less vertical space</li></ul>                             <a href="/changelog/combined_header.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/changelog/combined_header.webp" alt="Screenshot of blog header before and after.">    </a>                 <ul><li><p>Footnote indentation convention as per Vancouver/AMA styling, i.e., indent only on second and subsequent lines</p></li><li><p>Passing underline link hover effect from <a href="https://css-tricks.com/css-link-hover-effects/#aa-the-passing-underline-link-hover-effect" target="_blank" class="ext-link" rel="noopener noreferrer">css-tricks</a></p></li></ul><h3 id="features-5">Features<a href="#features-5" class="h-anchor" title="Permalink to #Features"></a></h3>   <ul><li><p>Post comments: Comments can be left on blog posts using a Mastodon/Fediverse account (modified base code from <a href="https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/" target="_blank" class="ext-link" rel="noopener noreferrer">Carl Schwan</a>)</p></li><li><p>Pull recent posts from other blogs: Add script to home page template fetching latest posts from two of my other blogs (Hugo and WordPress)</p></li><li><p>/tags and /series pages: Use <code>display: grid</code> for tags and series links instead of just one long list that is not responsive</p></li><li><p>Remove external dependencies: Host fonts, icons, css, js locally</p></li></ul><h3 id="pages-1">Pages<a href="#pages-1" class="h-anchor" title="Permalink to #Pages"></a></h3>   <ul><li>/colophon, /about, /changelog: Expand upon original /about to separate pages. <a href="https://burgeonlab.com/colophon/">/colophon</a> covers website technicalities, <a href="https://burgeonlab.com/about/">/about</a> covers general information, <a href="https://burgeonlab.com/changelog/">/changelog</a> covers changes to the site over time.</li></ul><h2 id="v201-2025-05-26">v2.0.1 (2025-05-26)<a href="#v201-2025-05-26" class="h-anchor" title="Permalink to #v2.0.1 (2025-05-26)"></a></h2>   <h3 id="styleformatting-1">Style/Formatting<a href="#styleformatting-1" class="h-anchor" title="Permalink to #Style/Formatting"></a></h3>   <ul><li><p>Footer menu: Add links to footer menu with custom icons (Home, RSS feed, Theme switcher)</p></li><li><p>Fonts: Change fonts from <code>.ttf</code> to variable <code>.woff2</code>, add preloading, use minor third scale for headings, switch from Fira Sans to Firava & Inconsolata to Monaspace Krypton</p></li><li><p>Mobile view: Optimize font sizing, margins, word-wrap, etc</p></li></ul><h3 id="bugfix-1">Bugfix<a href="#bugfix-1" class="h-anchor" title="Permalink to #Bugfix"></a></h3>   <ul><li><p>RSS: Fix failed validation, improve intraoperability, optimize feed, add images, correct author details, pubDate error</p></li><li><p>Permalinks: Fix broken links due to changing permalink structure, add explicit slug and alias setting to front matter, add trailing slash in baseURL in <code>hugo.toml</code></p></li><li><p>Favicon: Favicons must be in the root of /static</p></li></ul><h3 id="features-6">Features<a href="#features-6" class="h-anchor" title="Permalink to #Features"></a></h3>   <ul><li><p>Add light mode</p></li><li><p>Pagefind search: Add search functionality with <code>npx -y pagefind --site public</code> on build</p></li><li><p>Post info: Add word count, read time and last mod date</p></li><li><p>Front matter: Add summary to be used in post-cards</p></li></ul><h2 id="v20-2025-04-27">v2.0 (2025-04-27)<a href="#v20-2025-04-27" class="h-anchor" title="Permalink to #v2.0 (2025-04-27)"></a></h2>   <h3 id="styleformatting-2">Style/Formatting<a href="#styleformatting-2" class="h-anchor" title="Permalink to #Style/Formatting"></a></h3>   <ul><li>Customize blockquotes</li></ul><h3 id="features-7">Features<a href="#features-7" class="h-anchor" title="Permalink to #Features"></a></h3>   <ul><li>Add <a href="https://www.goatcounter.com/" target="_blank" class="ext-link" rel="noopener noreferrer">goatcounter</a> analytics</li></ul><div class="footnotes"><hr><ol><li id="fn:1"><p>I know, I know… I really should stop playing with my site and focus on content, but I am having so much fun learning and experimenting with Hugo. I have a bit of temptation to fork my current <a href="https://burgeonlab.com/colophon/#about-this-site">theme</a> and maintain my own theme code—but it’s definitely veering off the track of writing! <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/colophon/</id><link rel="alternate" href="https://burgeonlab.com/colophon/"/><title type="html">Colophon: The Inner Workings of This Blog</title><published>2025-06-17T20:38:18Z</published><updated>2025-10-12T00:00:00Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/colophon/og_img_colophon.webp"/><summary type="html">A rundown of all the resources that was used to create BurgeonLab.com.</summary><content type="html"><![CDATA[<h2 id="tldr">TL;DR<a href="#tldr" class="h-anchor" title="Permalink to #TL;DR"></a></h2>   <p><em>BurgeonLab.com</em> is a static site built with Hugo, hosted on <a href="https://bunny.net/?ref=k4vc3x5108" target="_blank" class="ext-link" rel="noopener noreferrer">Bunny.net</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup> Edge Storage with BunnyCDN, and uses a heavily modded version of the Anubis2 theme. It is built and deployed using SourceHut Builds. No cookies are used. GoatCounter provides basic, privacy-first web analytics. <a href="https://burgeonlab.com/colophon/#about-this-site">Jump to details</a>.</p><h2 id="what-this-page-is-about">What This Page Is About<a href="#what-this-page-is-about" class="h-anchor" title="Permalink to #What This Page Is About"></a></h2>   <p>Only recently did I realize there’s a term that meant “the inner workings / how this site was built”! I was in the process of writing a blog post about the technologies and tools used to build this blog; but now I can actually use the proper term and make it a dedicated page.</p> <blockquote>  <p><strong>Colophon</strong> <em>(noun)</em>: an inscription at the end of a book or manuscript usually with facts about its production.</p><p><em><a href="https://www.merriam-webster.com/dictionary/colophon" target="_blank" class="ext-link" rel="noopener noreferrer">Definition</a> from Merriam-Webster.com Dictionary.</em></p></blockquote> <h2 id="changelog">Changelog<a href="#changelog" class="h-anchor" title="Permalink to #Changelog"></a></h2>   <p>I think it might be cool to see the changes as this blog grows, so I’ve added a separate <a href="https://burgeonlab.com/changelog/">/changelog</a> page.</p><h2 id="about-this-site">About This Site<a href="#about-this-site" class="h-anchor" title="Permalink to #About This Site"></a></h2>   <div class="colophon-table"><table>  <thead>      <tr>          <th>Function</th>          <th>Tool</th>      </tr>  </thead>  <tbody>      <tr>          <td>Generator</td>          <td><a href="https://gohugo.io/" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo </a></td>      </tr>      <tr>          <td>Hugo Theme</td>          <td><a href="https://github.com/hugo-theme-anubis2/hugo-theme-anubis2" target="_blank" class="ext-link" rel="noopener noreferrer">Anubis2</a>, with many custom mods</td>      </tr>      <tr>          <td>Commenting</td>          <td>Integrated with Mastodon (modified code from <a href="https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/" target="_blank" class="ext-link" rel="noopener noreferrer">Carl Schwan</a>)</td>      </tr>      <tr>          <td>Search Function</td>          <td><a href="https://github.com/Pagefind/pagefind" target="_blank" class="ext-link" rel="noopener noreferrer">Pagefind</a></td>      </tr>      <tr>          <td>Hosting</td>          <td><a href="https://bunny.net/?ref=k4vc3x5108" target="_blank" class="ext-link" rel="noopener noreferrer">Bunny.net Storage</a><sup id="fnref1:1"><a href="#fn:1" class="footnote-ref">1</a></sup></td>      </tr>      <tr>          <td>Deployment</td>          <td><a href="https://git-scm.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Git</a> & <a href="https://builds.sr.ht/" target="_blank" class="ext-link" rel="noopener noreferrer">SourceHut Builds CI</a><sup id="fnref:2"><a href="#fn:2" class="footnote-ref">2</a></sup></td>      </tr>      <tr>          <td>Source Code</td>          <td><a href="https://sr.ht/~eclecticpassions/burgeonlab/" target="_blank" class="ext-link" rel="noopener noreferrer">Sourcehut Git Repo</a></td>      </tr>      <tr>          <td>Web Analytics</td>          <td><a href="https://www.goatcounter.com/" target="_blank" class="ext-link" rel="noopener noreferrer">GoatCounter</a></td>      </tr>      <tr>          <td>Static Form</td>          <td><a href="https://formspark.io/" target="_blank" class="ext-link" rel="noopener noreferrer">Formspark</a> with <a href="https://botpoison.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Botpoison</a></td>      </tr>      <tr>          <td>IDE</td>          <td><a href="https://vscodium.com/" target="_blank" class="ext-link" rel="noopener noreferrer">VSCodium</a></td>      </tr>      <tr>          <td>IDE Extensions</td>          <td><a href="https://github.com/tamasfe/taplo.git" target="_blank" class="ext-link" rel="noopener noreferrer">Even Better TOML</a>, <a href="https://github.com/DavidAnson/vscode-markdownlint.git" target="_blank" class="ext-link" rel="noopener noreferrer">Markdownlint</a>, <a href="https://github.com/Skn0tt/markdown-link-expander" target="_blank" class="ext-link" rel="noopener noreferrer">Markdown-link-expander</a>, <a href="https://github.com/davidlday/vscode-languagetool-linter.git" target="_blank" class="ext-link" rel="noopener noreferrer">LanguageTool Linter</a>, <a href="https://github.com/streetsidesoftware/vscode-spell-checker.git" target="_blank" class="ext-link" rel="noopener noreferrer">Code Spell Checker</a>, <a href="https://github.com/DonJayamanne/gitHistoryVSCode.git" target="_blank" class="ext-link" rel="noopener noreferrer">Git History</a>, <a href="https://github.com/kofuk/vscode-hugo-utils.git" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo Utilities</a>, <a href="https://github.com/prettier/prettier-vscode.git" target="_blank" class="ext-link" rel="noopener noreferrer">Prettier</a>, <a href="https://github.com/redhat-developer/vscode-yaml.git" target="_blank" class="ext-link" rel="noopener noreferrer">YAML</a></td>      </tr>      <tr>          <td>Optimizations</td>          <td><a href="https://formulae.brew.sh/formula/webp" target="_blank" class="ext-link" rel="noopener noreferrer">webp conversion</a>, <a href="https://imagemagick.org/script/composite.php" target="_blank" class="ext-link" rel="noopener noreferrer">Watermarking</a>, <a href="https://www.seobility.net/en/" target="_blank" class="ext-link" rel="noopener noreferrer">Seobility</a>, <a href="https://github.com/csstools/normalize.css/" target="_blank" class="ext-link" rel="noopener noreferrer">Normalize.css</a>, <a href="https://w3speedup.com/tools/critical-css-generator/" target="_blank" class="ext-link" rel="noopener noreferrer">Critical.css</a>, <a href="https://github.com/cure53/DOMPurify" target="_blank" class="ext-link" rel="noopener noreferrer">DOMPurify</a>, <a href="https://github.com/svg/svgo" target="_blank" class="ext-link" rel="noopener noreferrer">SVGO</a><sup id="fnref:3"><a href="#fn:3" class="footnote-ref">3</a></sup></td>      </tr>      <tr>          <td>Referrer Policy</td>          <td>strict-origin-when-cross-origin<sup id="fnref:4"><a href="#fn:4" class="footnote-ref">4</a></sup></td>      </tr>      <tr>          <td>Colour Theme</td>          <td>Blog theme (dark mode) inspired by <a href="https://rainglow.io/#rebellion-contrast" target="_blank" class="ext-link" rel="noopener noreferrer">Rainglow (Rebellion Contrast)</a>, syntax highlighter set to <a href="https://swapoff.org/chroma/playground/" target="_blank" class="ext-link" rel="noopener noreferrer">gruvbox</a></td>      </tr>      <tr>          <td>Typeface</td>          <td>Hosted locally: <a href="https://github.com/githubnext/monaspace#monaspace" target="_blank" class="ext-link" rel="noopener noreferrer">Monaspace Krypton</a> & <a href="https://github.com/hellogreg/firava" target="_blank" class="ext-link" rel="noopener noreferrer">Firava</a></td>      </tr>      <tr>          <td>Icons</td>          <td><a href="https://fontawesome.com/icons" target="_blank" class="ext-link" rel="noopener noreferrer">Font Awesome</a>, <a href="https://tabler.io/icons" target="_blank" class="ext-link" rel="noopener noreferrer">Tabler</a>, <a href="https://simpleicons.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Simple Icons</a></td>      </tr>      <tr>          <td>Badges</td>          <td>My blog badge <img src="/images/burgeonlab_badge.webp" alt="BurgeonLab blog badge."> is created by <a href="https://joelchrono.xyz/" target="_blank" class="ext-link" rel="noopener noreferrer">joelchrono</a>. Some are generated by <a href="https://88x31.datakra.sh/" target="_blank" class="ext-link" rel="noopener noreferrer">88x31px Button Generator</a>, and some are made by me in <a href="https://www.gimp.org/" target="_blank" class="ext-link" rel="noopener noreferrer">GIMP</a></td>      </tr>      <tr>          <td>Open Graph Images</td>          <td><a href="/images/default_post_cover.png">Default OG:image</a> made in GIMP, others are generated with <a href="https://ogimagemaker.com/" target="_blank" class="ext-link" rel="noopener noreferrer">OG Image Maker</a></td>      </tr>  </tbody></table></div><h2 id="web-analytics">Web Analytics<a href="#web-analytics" class="h-anchor" title="Permalink to #Web Analytics"></a></h2>   <p>Online privacy is an important thing for everyone using the Internet. It’s something I care about deeply—so I made sure to use, to the best of my knowledge, the most privacy-friendly / open source technologies on this website.</p>                             <a href="/colophon/goatcounter.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/colophon/goatcounter.webp" alt="Screenshot of web analytics on burgeonlab.com using Goatcounter.com.">    </a>                 <p>I use <a href="https://www.goatcounter.com/" target="_blank" class="ext-link" rel="noopener noreferrer">GoatCounter</a> to collect basic analytics about visitors to my site. It is a privacy-friendly and <a href="https://github.com/arp242/goatcounter" target="_blank" class="ext-link" rel="noopener noreferrer">open source</a> analytics platform (alternative to Google Analytics) that does not collect personally identifiable information. It doesn’t track users with unique identifiers and no information is shared with third parties. It collects anonymous data such as:</p><ul><li>Hashed IP addresses (which cannot be reversed to identify individuals)</li><li>Browser User-Agent string</li><li>Screen size</li><li>Referrer and URL visited</li><li>Country/location data (generalized)</li></ul><p>This data helps me understand how many people visit my site and where the top referrers are coming from, so I can improve your experience. GoatCounter <a href="https://www.cookiemetrix.com/display-report/burgeonlab.com/a40269751e6c48696234a30fc8ef8108" target="_blank" class="ext-link" rel="noopener noreferrer">does not require cookie</a> consent under GDPR because it does not track personal data.</p>                                       <a href="/colophon/no_cookie.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">       <img src="/colophon/no_cookie.webp" alt="Screenshot of https://www.cookiemetrix.com/ result with no cookies present on https://burgeonlab.com.">     </a>                     <h2 id="personal-data">Personal Data<a href="#personal-data" class="h-anchor" title="Permalink to #Personal Data"></a></h2>   <p>If you contact me by email or using the  <a href="https://burgeonlab.com/contact/#drop-me-a-line">contact form</a>, I only collect your email address and the message you send so I can reply. I will not share or sell your information to third parties. Same goes for other platforms like Signal, etc. If you’d like your information deleted or have any other questions, please get in touch.</p><h2 id="genai-usage">GenAI Usage<a href="#genai-usage" class="h-anchor" title="Permalink to #GenAI Usage"></a></h2>   <p>AI tools are occasionally used for the following:</p><ul><li>Light proofreading (i.e. grammar, spelling, sentence structure) with LanguageTool<sup id="fnref:5"><a href="#fn:5" class="footnote-ref">5</a></sup></li><li>Generating diagrams/graphics that help illustrate my points with tools like Napkin.AI</li><li>Troubleshooting technical problems or when I get stuck with Hugo’s backend (e.g. customizing layout, partials, shortcodes)</li></ul><p>While these tools help improve the quality of my writing and bring my point across to you more effectively; rest assured that all content and opinions are based my own experience and reflections, and <strong>will never be generated by AI</strong>.</p><h2 id="birth-of-burgeonlab">Birth of BurgeonLab<a href="#birth-of-burgeonlab" class="h-anchor" title="Permalink to #Birth of BurgeonLab"></a></h2>   <p>It’s 2022 and I was fed up after using WordPress for a year with my <a href="https://eclecticpassions.net" target="_blank" class="ext-link" rel="noopener noreferrer">first blog</a>. After re-educating myself about content management systems (CMS), I decided static site generators (SSG) is the way to go. Out of the ones I’ve shortlisted—<a href="https://jekyllrb.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Jekyll</a>, <a href="https://www.11ty.dev/" target="_blank" class="ext-link" rel="noopener noreferrer">Eleventy (11ty)</a>, <a href="https://gohugo.io/" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo</a>, and <a href="https://getpelican.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Pelican</a>—I chose Hugo, and I am so pleased with my decision. With that, my Hugo journey began and this site was born. I have so much fun with Hugo, learning while doing, and writing content in Markdown, locally/offline in VSCodium is just a joy compared to the clunky web-based WordPress editor.</p><h3 id="site-status-as-of-202506">Site Status (As of 2025/06)<a href="#site-status-as-of-202506" class="h-anchor" title="Permalink to #Site Status (As of 2025/06)"></a></h3>   <p>I originally created this site for tech-related topics that I am passionate or learning about; sharing relevant technical notes for others who are interested, acting as a personal logbook for all things technology in my life. But three years in, I have a feeling it could possibly be more than just tech. I plan to evolve it to be something more wide-ranging, perhaps with more personal, non-geeky content in the future. I am currently in the process of “upgrading” my site with additional content (like this /colophon) and improving the look, so expect to see some changes!</p><div class="footnotes"><hr><ol><li id="fn:1"><p>This is an affiliate link. <a href="#fnref:1" class="footnote-backref">↩︎</a> <a href="#fnref1:1" class="footnote-backref">↩︎</a></p></li><li id="fn:2"><p>The base code <strong>used</strong> to be stored in a private GitHub repo which builds and deploys to GitHub Pages via GitHub Actions. I’ve written about the <a href="https://burgeonlab.com/blog/using-github-actions-to-automate-hugo-static-site-deployment-to-github-pages/">workflow</a> before. As of Aug 6 2025, BurgeonLab.com is hosted on <a href="https://bunny.net/?ref=k4vc3x5108" target="_blank" class="ext-link" rel="noopener noreferrer">Bunny.net</a> (affiliate link) Edge Storage with their CDN, and has a similar CI/CD workflow on <a href="https://sourcehut.org/" target="_blank" class="ext-link" rel="noopener noreferrer">SourceHut</a> instead of GitHub. <a href="#fnref:2" class="footnote-backref">↩︎</a></p></li><li id="fn:3"><p>SVG files can run scripts and contain active content, which are security risks if not handled properly. To keep this site safe while using SVGs, I used SVGO to sanitize and remove embedded scripts/event handlers. <a href="#fnref:3" class="footnote-backref">↩︎</a></p></li><li id="fn:4"><p><code><meta name="referrer" content="strict-origin-when-cross-origin"></code> is set inside <code><head></code>.  This policy is privacy and security focused. Learn more about <a href="https://web.dev/articles/referrer-best-practices" target="_blank" class="ext-link" rel="noopener noreferrer">referrer policies</a>. <a href="#fnref:4" class="footnote-backref">↩︎</a></p></li><li id="fn:5"><p><a href="https://languagetool.org/" target="_blank" class="ext-link" rel="noopener noreferrer">LanguageTool</a> is a really awesome, open source and EU-based tool that offers offline, self-hosted options as well as cloud/paid options for Pro functions. I use their offline version but I’m considering paying for more advanced features. Highly recommend them for those looking for an offline yet comprehensive grammar and style checker. <a href="#fnref:5" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/about-rss-feeds/</id><link rel="alternate" href="https://burgeonlab.com/blog/about-rss-feeds/"/><title type="html">Things I Learnt About RSS Feeds</title><published>2025-06-04T19:15:51Z</published><updated>2025-06-04T19:15:51Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/about-rss-feeds/validate_rss.webp"/><summary type="html">RSS feeds are still relevant, so I will share how to validate them and optimize their functionality in WordPress and Hugo. Learn to add featured images, exclude pages, and fix common bugs like invalid XML. With tips on improving RSS descriptions and templates, this guide ensures your blog&amp;rsquo;s feed is reader-friendly and visually appealing.</summary><content type="html"><![CDATA[<h2 id="what-is-rss">What Is RSS<a href="#what-is-rss" class="h-anchor" title="Permalink to #What Is RSS"></a></h2>   <p>RSS stands for Really Simple Syndication. It is a way of subscribing to content on the Internet. This can be a news website, your favourite blog, a <a href="https://castos.com/tools/find-podcast-rss-feed/" target="_blank" class="ext-link" rel="noopener noreferrer">podcast</a>, or even use it to receive newsletters with tools like <a href="https://castos.com/tools/find-podcast-rss-feed/" target="_blank" class="ext-link" rel="noopener noreferrer">Kill the Newsletter</a>.</p><p>To subscribe, you will need a RSS reader. There are many to choose from, and it is not in the scope of this post, but I personally use <a href="https://github.com/spacecowboy/Feeder" target="_blank" class="ext-link" rel="noopener noreferrer">Feeder</a> on Android for years now, <a href="https://github.com/yang991178/fluent-reader" target="_blank" class="ext-link" rel="noopener noreferrer">Fluent Reader</a> on macOS (it has not been updated for a while though, so I might need to look for an alternative). I also have <a href="https://miniflux.app/" target="_blank" class="ext-link" rel="noopener noreferrer">Miniflux</a> (a self-hosted RSS reader) installed using Portainer on my Raspberry Pi homelab but I haven’t set it up yet…</p><h2 id="why-use-rss">Why Use RSS<a href="#why-use-rss" class="h-anchor" title="Permalink to #Why Use RSS"></a></h2>   <p>RSS has been around since 1999. Personally, I’ve been a long-time user, subscribing to sites I want to keep an eye on.</p><p>You might say that email subscriptions can also keep you up to date, but I always try to avoid subscribing to email newsletters as much as possible. RSS feeds offer a clutter-free alternative to email newsletters, which tend to pile up, leading me to mass delete it all—a waste of everyone’s time and resources. It also gives me “invisible stress”, despite filtering them into the newsletter folder for later reading. RSS removes the worry of email overload.</p><p>So if you haven’t turned on RSS for your blog (or haven’t got it set up correctly), please consider doing so, as there are a group of us who use this age-old method of reading content from the web!</p><h2 id="validate-the-rss-feed">Validate The RSS Feed<a href="#validate-the-rss-feed" class="h-anchor" title="Permalink to #Validate The RSS Feed"></a></h2>                                <a href="/blog/about-rss-feeds/validate_rss.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/about-rss-feeds/validate_rss.webp" alt="Screenshot of a RSS feed being validated.">    </a>                 <p>First, ensure the feed.xml is actually generated correctly by checking your RSS feed url with a validator tool. I used <a href="https://www.rssboard.org/rss-validator/" target="_blank" class="ext-link" rel="noopener noreferrer">RSS Board’s RSS Validator</a> and the one from <a href="https://validator.w3.org/feed/" target="_blank" class="ext-link" rel="noopener noreferrer">W3C</a> to good effect.</p><p>My sudden intrigue on the RSS status on my blogs was triggered by one of my new friends/follower who asked me what my RSS link was to BurgeonLab.com. I realised even though I have set it up <em>(or so I thought!)</em>, I forgot to link to it on my page! (Shout-out to <a href="https://joelchrono.xyz/" target="_blank" class="ext-link" rel="noopener noreferrer">joelchrono</a>!)</p><p>But after adding the link to the footer, out of curiosity, I decided to check the feed link with a validator. Lo and behold, the feed was completely broken, i.e. not valid and had a long list of “recommendations” which they say can <em>“improve interoperability with the widest range of feed readers by implementing the recommendations.”</em> To me it was errors/bugs that required fixing as it looked pretty bad.</p> <blockquote class="alert alert-note">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Note      </p>  <p>The main takeaway is to give your feed a check once in a while, especially if you have made changes to the theme files, as sometimes <code>relPermalinks</code>, formatting, or other layout changes can affect how the feed is generated.</p></blockquote> <h2 id="turning-on-rss-for-your-blog">Turning On RSS for Your Blog<a href="#turning-on-rss-for-your-blog" class="h-anchor" title="Permalink to #Turning On RSS for Your Blog"></a></h2>   <p>The two content management systems (CMS) I currently use are WordPress and Hugo.</p><h3 id="wordpress">WordPress<a href="#wordpress" class="h-anchor" title="Permalink to #WordPress"></a></h3>   <p>WP does it for you automatically; just add <code>/feed</code> to the end of your site domain to see the RSS feed. If you don’t see it, go to <code>Settings</code> > <code>Permalinks</code> and just scroll to the bottom and click <code>Save Changes</code> without making any changes. This will flush the permalinks. Then go to your caching plugin and <code>Purge All</code> and <code>Clean All</code> for <code>Database Optimization</code>.</p><p>While researching and fixing all the bugs in the RSS generation with my current Hugo <a href="https://github.com/hugo-theme-anubis2/" target="_blank" class="ext-link" rel="noopener noreferrer">Anubis2</a> theme, I realised WP doesn’t actually generate its RSS feed with images! That’s not good enough! 😅 So here’s a snippet to add to your <code>functions.php</code> to add the featured image of each post into your RSS.</p> <blockquote class="alert alert-tip">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Tip      </p>  <p>One of my favourite WP plugins is <a href="https://en-gb.wordpress.org/plugins/code-snippets/" target="_blank" class="ext-link" rel="noopener noreferrer">Code Snippets</a> by Shea Bunge. I add all my code snippets and mods with it instead of editing theme code directly.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup></p></blockquote>        <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">function</span> <span class="nf">featured_image_in_rss</span><span class="p">(</span><span class="nv">$content</span><span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="k">global</span> <span class="nv">$post</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="k">if</span> <span class="p">(</span> <span class="nx">is_feed</span><span class="p">()</span> <span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="k">if</span> <span class="p">(</span> <span class="nx">has_post_thumbnail</span><span class="p">(</span> <span class="nv">$post</span><span class="o">-></span><span class="na">ID</span> <span class="p">)</span> <span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl">            <span class="nv">$thumbnail_html</span> <span class="o">=</span> <span class="nx">get_the_post_thumbnail</span><span class="p">(</span> <span class="nv">$post</span><span class="o">-></span><span class="na">ID</span><span class="p">,</span> <span class="s1">'medium'</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span> <span class="s1">'style'</span> <span class="o">=></span> <span class="s1">'margin-bottom: 10px;'</span> <span class="p">)</span> <span class="p">);</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"></span></span><span class="line"><span class="ln"> 7</span><span class="cl">            <span class="c1">// Check if  'medium' size exists. If not, try 'large' or 'full'.</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>            <span class="k">if</span> <span class="p">(</span> <span class="k">empty</span><span class="p">(</span> <span class="nv">$thumbnail_html</span> <span class="p">)</span> <span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl">                <span class="nv">$thumbnail_html</span> <span class="o">=</span> <span class="nx">get_the_post_thumbnail</span><span class="p">(</span> <span class="nv">$post</span><span class="o">-></span><span class="na">ID</span><span class="p">,</span> <span class="s1">'large'</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span> <span class="s1">'style'</span> <span class="o">=></span> <span class="s1">'margin-bottom: 10px;'</span> <span class="p">)</span> <span class="p">);</span></span></span><span class="line"><span class="ln">10</span><span class="cl">            <span class="p">}</span></span></span><span class="line"><span class="ln">11</span><span class="cl">            <span class="k">if</span> <span class="p">(</span> <span class="k">empty</span><span class="p">(</span> <span class="nv">$thumbnail_html</span> <span class="p">)</span> <span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln">12</span><span class="cl">                <span class="nv">$thumbnail_html</span> <span class="o">=</span> <span class="nx">get_the_post_thumbnail</span><span class="p">(</span> <span class="nv">$post</span><span class="o">-></span><span class="na">ID</span><span class="p">,</span> <span class="s1">'full'</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span> <span class="s1">'style'</span> <span class="o">=></span> <span class="s1">'margin-bottom: 10px;'</span> <span class="p">)</span> <span class="p">);</span></span></span><span class="line"><span class="ln">13</span><span class="cl">            <span class="p">}</span></span></span><span class="line"><span class="ln">14</span><span class="cl"></span></span><span class="line"><span class="ln">15</span><span class="cl">            <span class="k">if</span> <span class="p">(</span> <span class="o">!</span><span class="k">empty</span><span class="p">(</span> <span class="nv">$thumbnail_html</span> <span class="p">)</span> <span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln">16</span><span class="cl">                <span class="nv">$content</span> <span class="o">=</span> <span class="s1">'<div 15px;">'</span> <span class="o">.</span> <span class="nv">$thumbnail_html</span> <span class="o">.</span> <span class="s1">'</div>'</span> <span class="o">.</span> <span class="nv">$content</span><span class="p">;</span></span></span><span class="line"><span class="ln">17</span><span class="cl">            <span class="p">}</span></span></span><span class="line"><span class="ln">18</span><span class="cl">        <span class="p">}</span></span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="p">}</span></span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="k">return</span> <span class="nv">$content</span><span class="p">;</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">22</span><span class="cl"></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="nx">add_filter</span><span class="p">(</span><span class="s1">'the_excerpt_rss'</span><span class="p">,</span> <span class="s1">'featured_image_in_rss'</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span> <span class="c1">// High priority</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1"></span><span class="nx">add_filter</span><span class="p">(</span><span class="s1">'the_content_feed'</span><span class="p">,</span> <span class="s1">'featured_image_in_rss'</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span></span></span><span class="line"><span class="ln">25</span><span class="cl"></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">function</span> <span class="nf">filter_image_attributes</span><span class="p">(</span> <span class="nv">$attr</span><span class="p">,</span> <span class="nv">$attachment</span><span class="p">,</span> <span class="nv">$size</span> <span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="k">if</span> <span class="p">(</span> <span class="nx">is_feed</span><span class="p">()</span> <span class="p">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln">28</span><span class="cl">        <span class="nx">unset</span><span class="p">(</span> <span class="nv">$attr</span><span class="p">[</span><span class="s1">'decoding'</span><span class="p">]</span> <span class="p">);</span></span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="nx">unset</span><span class="p">(</span> <span class="nv">$attr</span><span class="p">[</span><span class="s1">'sizes'</span><span class="p">]</span> <span class="p">);</span></span></span><span class="line"><span class="ln">30</span><span class="cl">        <span class="nx">unset</span><span class="p">(</span> <span class="nv">$attr</span><span class="p">[</span><span class="s1">'fetchpriority'</span><span class="p">]</span> <span class="p">);</span></span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="p">}</span></span></span><span class="line"><span class="ln">32</span><span class="cl">    <span class="k">return</span> <span class="nv">$attr</span><span class="p">;</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="nx">add_filter</span><span class="p">(</span> <span class="s1">'wp_get_attachment_image_attributes'</span><span class="p">,</span> <span class="s1">'filter_image_attributes'</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">3</span> <span class="p">);</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>Refresh your feed in your reader (or clear cache or re-add the feed to see the update)! I’m really glad I delved into all this RSS stuff because it looks way better now in the RSS reader!</p><h3 id="hugo">Hugo<a href="#hugo" class="h-anchor" title="Permalink to #Hugo"></a></h3>   <p>Now, for the Hugo side of things! I realised, in hindsight, after troubleshooting that many of the issues I had was probably caused by the theme I chose. Because for my other Hugo blog which uses <a href="https://github.com/nunocoracao/blowfish" target="_blank" class="ext-link" rel="noopener noreferrer">Blowfish</a> theme, there seems to be less “recommendations” generated by the validator; and most importantly, at least is was a valid RSS feed to begin with!</p>                             <a href="/blog/about-rss-feeds/rss_errors.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/about-rss-feeds/rss_errors.webp" alt="Screenshot of all the errors while validating a RSS feed.">    </a>                 <p>I will go through a checklist on how I got a “clean” and valid RSS feed with Hugo <em>(in relation to the Anubis2 theme I’m currently using).</em></p><h4 id="setup-hugo-config">Setup Hugo Config<a href="#setup-hugo-config" class="h-anchor" title="Permalink to #Setup Hugo Config"></a></h4>   <ul><li>Enable RSS feed generation by adding <code>"rss"</code> to the sections you want a feed to be created. I prefer to just stick with one feed: <code>home</code>.</li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml"><span class="line"><span class="ln">1</span><span class="cl">    <span class="p">[</span><span class="nx">outputs</span><span class="p">]</span></span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="nx">home</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"html"</span><span class="p">,</span> <span class="s2">"rss"</span><span class="p">]</span></span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="nx">section</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"html"</span><span class="p">]</span></span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="nx">taxonomy</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"html"</span><span class="p">]</span></span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="nx">term</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"html"</span><span class="p">]</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div> <blockquote class="alert alert-tip">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Tip      </p>  <p>The home RSS feed, by default, includes all recent posts and is set up to pull all posts instead of what is <em>visually</em> set to display on your front page (e.g. when your home page doesn’t actually include any recent posts, it will still work).This is because of this line: <code>.IsHome is true, you set $pctx = .Site</code> which means it will generate the feed by considering all the site pages, independent of the home page content.</p></blockquote> <ul><li>The home feed page is found on <code>hugosite.com/index.xml</code>. Remember to add a link to it on your site! I have mine originally next to my social icons in the header, but eventually decided to add it to the footer.</li></ul> <blockquote class="alert alert-note">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Note      </p>  <p><code>Section</code> is the top-level folders inside your <code>/content</code> directory. <code>Taxonomy</code> are the built-in groups like <code>tags</code> and <code>categories</code> (or your own group names). <code>Term</code> is the individual item or tag inside a taxonomy.</p><table>  <thead>      <tr>          <th>RSS Feed Type</th>          <th>Default URL Pattern</th>      </tr>  </thead>  <tbody>      <tr>          <td>Home (site-wide)</td>          <td>/index.xml</td>      </tr>      <tr>          <td>Section</td>          <td>/section/index.xml</td>      </tr>      <tr>          <td>Taxonomy</td>          <td>/categories/index.xml</td>      </tr>      <tr>          <td>Term</td>          <td>/categories/term/index.xml</td>      </tr>  </tbody></table></blockquote> <ul><li><p>To turn off all RSS, add <code>disableKinds = ["rss"]</code></p></li><li><p>To reduce the items in the feed, add <code>[services.rss] limit = 20</code>.</p></li><li><p>While we’re in <code>hugo.toml</code>, ensure these settings are filled in correctly to avoid errors later.</p></li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nx">baseURL</span> <span class="p">=</span> <span class="s2">"https://www.domain.com/"</span> <span class="c"># Ensure there is a trailing slash at the end of your baseURL for permalinks to generate correctly.</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">params</span><span class="p">]</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="nx">copyright</span> <span class="p">=</span> <span class="s2">"(c) 2025 domain.com"</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nx">dateFormat</span> <span class="p">=</span> <span class="s2">"2006-01-02"</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">params</span><span class="p">.</span><span class="nx">author</span><span class="p">]</span> </span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="nx">name</span> <span class="p">=</span>  <span class="s2">"Name"</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="nx">email</span> <span class="p">=</span> <span class="s2">"name@domain.com"</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">[</span><span class="nx">mediaTypes</span><span class="p">]</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="p">[</span><span class="nx">mediaTypes</span><span class="p">.</span><span class="s2">"application/rss+xml"</span><span class="p">]</span></span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="nx">delimiter</span> <span class="p">=</span> <span class="s2">"."</span></span></span><span class="line"><span class="ln">11</span><span class="cl">        <span class="nx">suffixes</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"xml"</span><span class="p">,</span> <span class="s2">"rss"</span><span class="p">]</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">[</span><span class="nx">outputFormats</span><span class="p">]</span></span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="p">[</span><span class="nx">outputFormats</span><span class="p">.</span><span class="nx">RSS</span><span class="p">]</span></span></span><span class="line"><span class="ln">14</span><span class="cl">        <span class="nx">mediaType</span> <span class="p">=</span> <span class="s2">"application/rss+xml"</span></span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="nx">baseName</span> <span class="p">=</span> <span class="s2">"index"</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">[</span><span class="nx">services</span><span class="p">.</span><span class="nx">rss</span><span class="p">]</span></span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="nx">limit</span> <span class="p">=</span> <span class="mi">20</span> <span class="c"># Default if unlimited.</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h4 id="exclude-pages">Exclude Pages<a href="#exclude-pages" class="h-anchor" title="Permalink to #Exclude Pages"></a></h4>   <ul><li>For problem pages (like Pagefind search page) or pages you want to exclude in your RSS feed, add the following to the top of your RSS template, <code>layouts/_default/rss.xml</code>:</li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">{{</span><span class="o">-</span> <span class="err">$</span><span class="nx">pages</span> <span class="o">:=</span> <span class="nf">where</span> <span class="p">(</span><span class="nx">where</span> <span class="err">$</span><span class="nx">pctx</span><span class="p">.</span><span class="nx">RegularPages</span> <span class="s">".Params.disable_feed"</span> <span class="s">"!="</span> <span class="kc">true</span><span class="p">)</span> <span class="s">"Params.hidden"</span> <span class="s">"!="</span> <span class="kc">true</span> <span class="o">-</span><span class="p">}}</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><ul><li>Then add <code>disable_feed = true</code> in the front matter (I use <code>+++</code>, i.e. <code>.toml</code>) of the page you want to exclude.</li></ul><h4 id="optimize-rss-descriptions">Optimize RSS Descriptions<a href="#optimize-rss-descriptions" class="h-anchor" title="Permalink to #Optimize RSS Descriptions"></a></h4>   <ul><li><p>Try to ensure there are no <code><scripts></code>, <code><links></code>, <code><style></code>, etc in RSS descriptions to improve readability and compatibility with different readers. Using a summary with <code>summary = 'Insert post summary here.'</code> in the front matter of post instead of the full content can help, especially if you have table of contents/anchors in your post content.</p></li><li><p><code>plainify</code> strips away all HTML tags like <code>role</code>, <code>aria-hidden</code>, <code>style</code>, to output only plain text. This includes stripping away formatting like <code><p></code> or <code><strong></code>. If you have a lot of formatted text for the description you can try using <code>safeHTML</code> instead of <code>plainify</code>.</p></li><li><p><code>htmlEscape</code> is for fixing special characters like symbols into valid XML syntax. For example, <code>&amp;</code>  is an ampersand (&) or <code>&ldquo;</code> is a quote ("). This fixes the parsing error: <code>undefined entity</code>.</p></li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go"><span class="line"><span class="ln">1</span><span class="cl"><span class="p"><</span><span class="nx">description</span><span class="p">>{{</span> <span class="p">.</span><span class="nx">Summary</span> <span class="p">|</span> <span class="nx">plainify</span> <span class="p">|</span> <span class="nx">htmlEscape</span> <span class="p">}}<</span><span class="o">/</span><span class="nx">description</span><span class="p">></span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h4 id="rss-images">RSS Images<a href="#rss-images" class="h-anchor" title="Permalink to #RSS Images"></a></h4>   <ul><li>Adding a cover or featured image to your RSS posts hugely improves the appeal in RSS readers in my opinion! But it is important to know where you place the images. I will try to explain the three locations Hugo supports.</li></ul>                             <a href="/blog/about-rss-feeds/rss_beforeafter.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/about-rss-feeds/rss_beforeafter.webp" alt="Screenshot of RSS reader Feeder showing BurgeonLab.com's feed before and after adding images to RSS feed.">    </a>                 <ul><li><p>To use an image <strong>inside</strong> your post’s <em>page bundle</em>, i.e. the <a href="https://gohugo.io/content-management/page-bundles/" target="_blank" class="ext-link" rel="noopener noreferrer">directory</a> that holds your particular post’s resources like <code>index.md</code>, <code>img1.webp</code>, add <code>cover = '/img_1.webp'</code> to the front matter.</p></li><li><p>Images inside the same folder as the post/content file are accessible using <code>.Resources.Get</code> and can have Hugo imaging processing like <code>.Fit</code>, <code>.Fill.</code>, <code>.Resize</code>, <code>.Crop</code>. For example: <code>{{ $img := .Resources.Get "cover.webp" }}</code>.</p></li><li><p>To use an image <strong>outside</strong> the <em>page bundle</em>, add it into the <code>/static/images/</code> folder, then add to the front matter: <code>cover = 'images/generic_post_cover.webp'</code>.</p></li><li><p>Images in <code>static/</code> are <strong>not</strong> processed by Hugo, i.e. no image processing like crop can be done. They are referenced by absolute paths (<code>/images/img1.webp</code>).</p></li></ul> <blockquote class="alert alert-tip">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Tip      </p>  <p>There’s one more place to place images in Hugo, which is <code>assets/</code>. These could be images that are used <em>globally</em> around the site or theme images. <a href="https://gohugo.io/content-management/image-processing/" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo imaging processing</a> can be applied. Use <code>resources.Get</code> to reference these.</p><p>The difference between the global function and the page object retrieval is the <code>.</code> before <code>resources</code> and capital <code>R</code>.</p></blockquote> <ul><li>Add this snippet into <code>rss.xml</code> within <code><item></code>. The first part is the conditional check for locating the cover image. If the image is inside the <em>page bundle</em>, use <code>.Resources.GetMatch</code> to fetch the permalink of the image. But if the image is outside the <em>page bundle</em> (i.e. a static image that is not a page resource) and not found, use <code>.Params.cover</code> with <code>absURL</code>.</li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml"><span class="line"><span class="ln"> 1</span><span class="cl">{{ $cover := "" }}</span></span><span class="line"><span class="ln"> 2</span><span class="cl">{{ with $page.Params.cover }}</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    {{ $res := $page.Resources.GetMatch . }}</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    {{ if $res }}</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        {{ $cover = $res.Permalink }}</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    {{ else }}</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        {{ $cover = . | absURL }}</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    {{ end }}</span></span><span class="line"><span class="ln"> 9</span><span class="cl">{{ end }}</span></span><span class="line"><span class="ln">10</span><span class="cl"></span></span><span class="line"><span class="ln">11</span><span class="cl">{{ if $cover }}</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="nt"><media:content</span> <span class="na">url=</span><span class="s">"{{ $cover }}"</span> <span class="na">medium=</span><span class="s">"image"</span> <span class="nt">/></span></span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="nt"><media:title></span>Cover Image<span class="nt"></media:title></span></span></span><span class="line"><span class="ln">14</span><span class="cl">{{ end }}</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><ul><li>Remember to add the Media RSS namespace declaration at the top of the template: <code>xmlns:media="http://search.yahoo.com/mrss/"</code> to the existing XML declaration: <code><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"></code>.</li></ul><h3 id="my-hugo-rss-template">My Hugo RSS Template<a href="#my-hugo-rss-template" class="h-anchor" title="Permalink to #My Hugo RSS Template"></a></h3>   <p>Here is my current RSS template that I’ve modded to my preference and is validating correctly. You can reference it if needed. It is working with <code>hugo v0.147.7+extended+withdeploy</code> and Anubis2 <code>v.1.3.6</code>. There is a <a href="https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/rss.xml" target="_blank" class="ext-link" rel="noopener noreferrer">RSS template</a> provided by Hugo in their GitHub too for reference.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml"><span class="line"><span class="ln"> 1</span><span class="cl">{{- $pctx := . -}}</span></span><span class="line"><span class="ln"> 2</span><span class="cl">{{- if .IsHome -}}</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  {{ $pctx = .Site }}</span></span><span class="line"><span class="ln"> 4</span><span class="cl">{{- end -}}</span></span><span class="line"><span class="ln"> 5</span><span class="cl"></span></span><span class="line"><span class="ln"> 6</span><span class="cl">{{- $pages := where (where $pctx.RegularPages ".Params.disable_feed" "!=" true) "Params.hidden" "!=" true -}}</span></span><span class="line"><span class="ln"> 7</span><span class="cl">{{- $limit := .Site.Config.Services.RSS.Limit -}}</span></span><span class="line"><span class="ln"> 8</span><span class="cl">{{- if ge $limit 1 -}}</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  {{- $pages = $pages | first $limit -}}</span></span><span class="line"><span class="ln">10</span><span class="cl">{{- end -}}</span></span><span class="line"><span class="ln">11</span><span class="cl"></span></span><span class="line"><span class="ln">12</span><span class="cl">  {{- printf "<span class="cp"><?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?></span>" | safeHTML }}</span></span><span class="line"><span class="ln">13</span><span class="cl"></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nt"><rss</span> <span class="na">version=</span><span class="s">"2.0"</span> <span class="na">xmlns:atom=</span><span class="s">"http://www.w3.org/2005/Atom"</span> <span class="na">xmlns:media=</span><span class="s">"http://search.yahoo.com/mrss/"</span><span class="nt">></span></span></span><span class="line"><span class="ln">15</span><span class="cl"></span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="nt"><channel></span></span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="nt"><title></span>{{ with .Site.Params.feedTitle }}{{ . }}{{ else }}{{ .Site.Title }}{{ end }}<span class="nt"></title></span></span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="nt"><link></span>{{ .Site.BaseURL }}<span class="nt"></link></span></span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="nt"><description></span>Recent blog posts on {{ .Site.Title }}<span class="nt"></description></span></span></span><span class="line"><span class="ln">20</span><span class="cl">    {{ with .Site.Params.copyright }}</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="nt"><copyright></span>{{ . }}<span class="nt"></copyright></span>{{ end }}</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="nt"><generator></span>Hugo (https://gohugo.io)<span class="nt"></generator></span></span></span><span class="line"><span class="ln">23</span><span class="cl">    {{ with .Site.LanguageCode }}</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="nt"><language></span>{{.}}<span class="nt"></language></span>{{end}}</span></span><span class="line"><span class="ln">25</span><span class="cl">    {{ with .Site.Params.author.name }}</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="nt"><managingEditor></span>{{ with $.Site.Params.author.email }}{{ . }}{{ with $.Site.Params.author.name }} ({{ . }}){{ end }}{{ end }}<span class="nt"></managingEditor></span>{{ end }}</span></span><span class="line"><span class="ln">27</span><span class="cl">    {{ with .Site.Params.author.name }}</span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="nt"><webMaster></span>{{ with $.Site.Params.author.email }}{{ . }}{{ with $.Site.Params.author.name }} ({{ . }}){{ end }}{{ end }}<span class="nt"></webMaster></span>{{ end }}</span></span><span class="line"><span class="ln">29</span><span class="cl">    {{ if not .Date.IsZero }}</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="nt"><lastBuildDate></span>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 +0800" | safeHTML }}<span class="nt"></lastBuildDate></span>{{ end }}</span></span><span class="line"><span class="ln">31</span><span class="cl">    {{ with .OutputFormats.Get "RSS" }}</span></span><span class="line"><span class="ln">32</span><span class="cl">    {{ printf "<span class="nt"><atom:link</span> <span class="na">href=</span><span class="s">%q</span> <span class="na">rel=</span><span class="s">\"self\"</span> <span class="na">type=</span><span class="s">%q</span> <span class="nt">/></span>" .Permalink .MediaType | safeHTML }}{{ end }}</span></span><span class="line"><span class="ln">33</span><span class="cl">    {{ range $index, $page := $pages }}</span></span><span class="line"><span class="ln">34</span><span class="cl"></span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="nt"><item></span></span></span><span class="line"><span class="ln">36</span><span class="cl">      <span class="nt"><title></span>{{ .Title }}<span class="nt"></title></span></span></span><span class="line"><span class="ln">37</span><span class="cl">      <span class="nt"><link></span>{{ .Permalink }}<span class="nt"></link></span></span></span><span class="line"><span class="ln">38</span><span class="cl">      <span class="nt"><pubDate></span>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 +0800" | safeHTML }}<span class="nt"></pubDate></span></span></span><span class="line"><span class="ln">39</span><span class="cl">      {{ with .Site.Params.author.name }}</span></span><span class="line"><span class="ln">40</span><span class="cl">      <span class="nt"><author></span>{{ with $.Site.Params.author.email }}{{ . }}{{ with $.Site.Params.author.name }} ({{ . }}){{ end }}{{ end }}<span class="nt"></author></span>{{ end }}</span></span><span class="line"><span class="ln">41</span><span class="cl"></span></span><span class="line"><span class="ln">42</span><span class="cl">      {{ $cover := "" }}</span></span><span class="line"><span class="ln">43</span><span class="cl">      {{ with $page.Params.cover }}</span></span><span class="line"><span class="ln">44</span><span class="cl">        {{ $res := $page.Resources.GetMatch . }}</span></span><span class="line"><span class="ln">45</span><span class="cl">          {{ if $res }}</span></span><span class="line"><span class="ln">46</span><span class="cl">            {{ $cover = $res.Permalink }}</span></span><span class="line"><span class="ln">47</span><span class="cl">          {{ else }}</span></span><span class="line"><span class="ln">48</span><span class="cl">            {{ $cover = . | absURL }}</span></span><span class="line"><span class="ln">49</span><span class="cl">          {{ end }}</span></span><span class="line"><span class="ln">50</span><span class="cl">      {{ end }}</span></span><span class="line"><span class="ln">51</span><span class="cl">      </span></span><span class="line"><span class="ln">52</span><span class="cl">      {{ if $cover }}</span></span><span class="line"><span class="ln">53</span><span class="cl">        <span class="nt"><media:content</span> <span class="na">url=</span><span class="s">"{{ $cover }}"</span> <span class="na">medium=</span><span class="s">"image"</span> <span class="nt">/></span></span></span><span class="line"><span class="ln">54</span><span class="cl">        <span class="nt"><media:title></span>Cover Image<span class="nt"></media:title></span></span></span><span class="line"><span class="ln">55</span><span class="cl">      {{ end }}</span></span><span class="line"><span class="ln">56</span><span class="cl"></span></span><span class="line"><span class="ln">57</span><span class="cl">      <span class="nt"><description></span></span></span><span class="line"><span class="ln">58</span><span class="cl">      {{ .Summary | plainify | htmlEscape }}</span></span><span class="line"><span class="ln">59</span><span class="cl">      <span class="nt"></description></span></span></span><span class="line"><span class="ln">60</span><span class="cl">      <span class="nt"><guid</span> <span class="na">isPermaLink=</span><span class="s">"true"</span><span class="nt">></span>{{ .Permalink }}<span class="nt"></guid></span></span></span><span class="line"><span class="ln">61</span><span class="cl">    <span class="nt"></item></span></span></span><span class="line"><span class="ln">62</span><span class="cl">  {{ end }}</span></span><span class="line"><span class="ln">63</span><span class="cl">  <span class="nt"></channel></span></span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="nt"></rss></span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>The template generates a feed that looks like this:</p>                             <a href="/blog/about-rss-feeds/rss_output.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/about-rss-feeds/rss_output.webp" alt="Screenshot of generated XML feed of a Hugo blog">    </a>       <h2 id="summary">Summary<a href="#summary" class="h-anchor" title="Permalink to #Summary"></a></h2>   <p>Working on Hugo’s RSS template gave me a deeper understanding of its backend workings. I now have a solid grasp on how to  customize the RSS template and fix common errors. To know all the required elements and its syntax, read this <a href="https://www.rssboard.org/rss-specification/" target="_blank" class="ext-link" rel="noopener noreferrer">RSS specification guide</a> and this for <a href="https://www.rssboard.org/media-rss/" target="_blank" class="ext-link" rel="noopener noreferrer">Media RSS specs</a>.</p><p>In the end, I actually submitted the RSS template to my Hugo theme’s repo (Anubis2) and the author, <a href="https://github.com/Junyi-99/" target="_blank" class="ext-link" rel="noopener noreferrer">Junyi</a>, actually accept my pull request straight ahead to fix all the RSS validation errors! It was a big achievement for newbie GitHub contributor like me… It was way less intimidating once you’ve done it the first time; so definitely give it a shot if you managed to sort some code out in a FOSS project you like! 🥳</p><p>I hope this post can help you with your RSS feed generation as well, improving your blog traffic and gain more readers! And if you enjoy Hugo-related content, please consider <a href="https://burgeonlab.com/subscribe/">following my blog</a> for more tips and tutorials. 😉</p><div class="footnotes"><hr><ol><li id="fn:1"><p>If you want to a more step-by-step guide on how to use the Code Snippet plugin, check out my post on <a href="https://burgeonlab.com/blog/add-caption-to-wordpress-featured-image-block/">adding a caption to featured images</a>. <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/retext-markdown-editor-how-to-install-python-apps-on-mac/</id><link rel="alternate" href="https://burgeonlab.com/blog/retext-markdown-editor-how-to-install-python-apps-on-mac/"/><title type="html">ReText: Markdown Editor (How to Install Python Apps on Mac)</title><published>2025-05-29T00:31:12Z</published><updated>2025-07-19T09:13:00Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/retext-markdown-editor-how-to-install-python-apps-on-mac/retext_github.webp"/><summary type="html">Explore my love for Markdown editors, starting with Mou and evolving to ReText, a Python-based editor. Learn why Markdown is a powerful tool for writing, its pros and cons, and how to set up ReText on macOS using Python virtual environments (venv). Plus, discover tips like creating a minimal app bundle for easier use of Python apps on a Mac.</summary><content type="html"><![CDATA[<h2 id="my-love-for-markdown-editors">My Love For Markdown Editors<a href="#my-love-for-markdown-editors" class="h-anchor" title="Permalink to #My Love For Markdown Editors"></a></h2>   <p>As a Markdown user for over a decade, I can vividly recall downloading my first Markdown editor, Mou, by Chen Luo<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup>. It was an excellent, lightweight application that made note-taking enjoyable during my college years. I loved the customizability; and typing in Markdown meant I didn’t have to use my trackpad/mouse, speeding up my note taking.</p><p>Last month, I discovered an open source version called <a href="https://github.com/MacDownApp/macdown" target="_blank" class="ext-link" rel="noopener noreferrer">MacDown</a>, but unfortunately it hasn’t been updated since 2020.</p><p>I use Markdown when I’m writing all my notes on Obsidian (which I’ve <a href="https://eclecticpassions.net/blog/how-i-made-a-digital-note-collection-with-obsidian/" target="_blank" class="ext-link" rel="noopener noreferrer">blogged</a> about before) and in <a href="https://vscodium.com/" target="_blank" class="ext-link" rel="noopener noreferrer">VSCodium</a>. I don’t even remember the last time my computer had a word processor installed! A great word processor if you need one is <a href="https://www.libreoffice.org/" target="_blank" class="ext-link" rel="noopener noreferrer">LibreOffice</a>; but I digress.</p><h2 id="markdown-beginners">Markdown Beginners<a href="#markdown-beginners" class="h-anchor" title="Permalink to #Markdown Beginners"></a></h2>   <p>If you’re just starting with Markdown, check out this great tutorial by <a href="https://commonmark.org/help/tutorial/" target="_blank" class="ext-link" rel="noopener noreferrer">Commonmark</a>, which is the main spec of Markdown (There are various dialects or “flavours”, if you will, of Markdown used by different editor/apps like GitHub Markdown). Or read through a detailed guide <a href="https://www.markdownguide.org/" target="_blank" class="ext-link" rel="noopener noreferrer">here</a>.</p><h3 id="why-i-use-markdown">Why I Use Markdown<a href="#why-i-use-markdown" class="h-anchor" title="Permalink to #Why I Use Markdown"></a></h3>   <ul><li>Easy to get started.</li><li>High readability.</li><li>Fast formatting without mouse input.</li><li>Ability to export to other formats; <code>.md to .html</code> or <code>.md to .pdf</code>.</li><li>Many other places support Markdown, including instant messaging apps, forums, etc.</li><li>Minimal appearance in your workspace, i.e., no space-wasting formatting bars in word processors.</li><li>No more proprietary formats! Markdown files are simple plain text and can be opened for years to come.</li></ul><h3 id="disadvantages-of-markdown">Disadvantages of Markdown<a href="#disadvantages-of-markdown" class="h-anchor" title="Permalink to #Disadvantages of Markdown"></a></h3>   <ul><li>As mentioned previously, Markdown is not standardized, so you may need to learn specific syntax for the app you’re using</li></ul><h3 id="a-minimal-markdown-cheatsheet">A Minimal Markdown Cheatsheet<a href="#a-minimal-markdown-cheatsheet" class="h-anchor" title="Permalink to #A Minimal Markdown Cheatsheet"></a></h3>   <p>Here’s a quick Markdown cheatsheet I made with an A.I. tool I’ve been enjoying; <a href="https://www.napkin.ai/" target="_blank" class="ext-link" rel="noopener noreferrer">Napkin.AI</a> (not affiliated).</p>                             <a href="/blog/retext-markdown-editor-how-to-install-python-apps-on-mac/md_cheatsheet.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/retext-markdown-editor-how-to-install-python-apps-on-mac/md_cheatsheet.webp" alt="Markdown basic syntax cheatsheet.">    </a>       <h2 id="retext-python-based-markdown-editor">ReText: Python-Based Markdown Editor<a href="#retext-python-based-markdown-editor" class="h-anchor" title="Permalink to #ReText: Python-Based Markdown Editor"></a></h2>   <p>This week, I was getting the itch to find a plain Markdown editor again, seeing if I could finally find something that could fit the missing hole Mou left.</p>                             <a href="/blog/retext-markdown-editor-how-to-install-python-apps-on-mac/retext_github.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/retext-markdown-editor-how-to-install-python-apps-on-mac/retext_github.webp" alt="Screenshot of ReText Github Readme.">    </a>       <p>I checked the following alternatives, but none were really suitable for me or runs well on macOS (which is still my daily driver OS). If you’re on Linux, I think Ghostwriter and Marknote are both nice options from KDE.</p><ol><li><a href="https://invent.kde.org/office/ghostwriter" target="_blank" class="ext-link" rel="noopener noreferrer">Ghostwriter</a>—No macOS installer</li><li><a href="https://apps.kde.org/en-gb/marknote/" target="_blank" class="ext-link" rel="noopener noreferrer">Marknote</a>—Unstable macOS version</li><li><a href="https://github.com/drl990114/MarkFlowy" target="_blank" class="ext-link" rel="noopener noreferrer">MarkFlowy (alpha)</a>—I didn’t want built-in AI features</li></ol><p>In the end, I decided to try <a href="https://github.com/retext-project/retext" target="_blank" class="ext-link" rel="noopener noreferrer">ReText</a>, a 100% Python compiled project. The appearance can be fully customized using custom CSS and supports more than just Markdown; reStructuredText, Textile and AsciiDoc are also supported. Although it is also a Linux-first application, I found ReText easier to set up and run on macOS than the other two KDE apps. Building apps from source code and managing dependencies is beyond my current skill level.</p><h3 id="some-notes-on-python">Some Notes on Python<a href="#some-notes-on-python" class="h-anchor" title="Permalink to #Some Notes on Python"></a></h3>   <p>Just to make a disclaimer… I have limited Python knowledge from experimenting with <a href="https://jupyter.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Jupyter</a> Notebooks, writing Python automation scripts and using libraries like <a href="https://matplotlib.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Matplotlib</a> to analyse data from my Garmin watch. So I’m not sure if the methods I mention below are correct or best practices. I would love any pointers or correction (<a href="https://fosstodon.org/@eclecticpassions/" target="_blank" class="ext-link" rel="noopener noreferrer">message me</a>) from those who are more fluent with all this!</p><p>Please make your own judgement if it is worth using an app that could lose your work! (So far, the app has been pretty stable, no crashes yet on my Mac Mini M1, Sonoma 15.5.)</p><p>Anyway, one thing I learnt was the importance of using <a href="https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/" target="_blank" class="ext-link" rel="noopener noreferrer">virtual environments</a>, AKA <code>venv</code>, as they keep packages, the Python interpreter, and related files within its own isolated environment.</p> <blockquote class="alert alert-warning">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Warning      </p>  <p>ReText also suggests installing it inside a venv, but <a href="https://github.com/retext-project/retext/wiki/Installing-ReText" target="_blank" class="ext-link" rel="noopener noreferrer">warn</a> about Windows and macOS are in no way officially supported. Use ReText at your own risk.</p></blockquote> <h2 id="how-i-installed-retext-on-macos">How I Installed ReText on macOS<a href="#how-i-installed-retext-on-macos" class="h-anchor" title="Permalink to #How I Installed ReText on macOS"></a></h2>   <p>The gist of my ReText installation guide is to install ReText into a virtual isolated Python environment, create a shell script that activates the venv automatically whenever ReText is run and closes it when the app is quit. It’s a bit of a makeshift way of running ReText I have to admit.</p><h3 id="discovered-a-better-method">Discovered a Better Method<a href="#discovered-a-better-method" class="h-anchor" title="Permalink to #Discovered a Better Method"></a></h3>    <blockquote class="alert alert-update">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Update      </p>  <p>After writing up the steps below, I’ve done some further testing and realise that apps launched in the manner I describe—Method A: Using a Shell Script, launching a GUI app from the terminal, sometimes breaks the macOS app focus and fails to switch to ReText; causing it to become unresponsive intermittently, e.g. showing the Terminal menu instead of the Python app launched.</p><p>So I investigated on how to make it a native macOS application bundle (.app), and I have failed to get it working, <em>without generating other problems</em>, with both <a href="https://sveinbjorn.org/platypus" target="_blank" class="ext-link" rel="noopener noreferrer">Platypus</a> and <a href="https://py2app.readthedocs.io/en/latest/index.html" target="_blank" class="ext-link" rel="noopener noreferrer">Py2app</a>. Platypus worked better than Py2app and was easier to use but it opened another can of worms so I gave up on both.</p><p>After quite a bit of reading and troubleshooting, I found out a minimal app bundle could work to make a <code>ReText.app</code> so macOS can launch ReText properly. Very happy to get it to work in the end! It launches ReText without using the terminal, has its own app icon and no more UI glitches. I am pretty excited. I’ve added this second method below.</p><p>End of update! I’ll leave the Method A in for reference. Scroll down for an alternative, improved <a href="https://burgeonlab.com/#method-b-make-a-minimal-app-bundle">Method B</a>.</p></blockquote> <h3 id="method-a-using-a-shell-script">Method A: Using a Shell Script<a href="#method-a-using-a-shell-script" class="h-anchor" title="Permalink to #Method A: Using a Shell Script"></a></h3>   <p>(A bit janky, prone to UI errors, but can let you test out ReText relatively quickly.)</p><ol><li><p>Make a script (name it anything you want) and set correct permissions (make it executable). We will come back to it in step 4.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">1</span><span class="cl">touch launch_retext.sh</span></span><span class="line"><span class="ln">2</span><span class="cl">chmod +x launch_retext.sh</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div></li><li><p>Create a virtual environment, <code>cd</code> into the path of the venv and use <code>source</code> activate the venv.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">1</span><span class="cl">python3 -m venv /path/to/venv/retext-env</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">cd</span> /path/to/venv/retext-env</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nb">source</span> retext_env/bin/activate</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div> <blockquote class="alert alert-tip">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Tip      </p>  <p>I tend to use the long form install command and not <code>pip3 install ReText</code> or just <code>pip install</code> as I have multiple versions of Python on my Mac and it is the most explicit way to installing packages. It guarantees the correct Python version, works well in virtual environments and avoids PATH issues.</p></blockquote> </li><li><p>Double check if <code>pip</code> is installed in the venv with <code>pip --version</code>. If so, install ReText with <code>python3 -m pip install ReText</code>. Ensure the virtual environment is activated before installing ReText. And remember to use <code>deactivate</code> after installation to stop the venv!</p> <blockquote class="alert alert-important">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Important      </p>  <p>At this point, you can decide to continue with the shell script method or switch to an improved <a href="https://burgeonlab.com/#method-b-make-a-minimal-app-bundle">app bundle method</a> I worked out.</p></blockquote> </li><li><p>Edit the script we made in step 1 with <code>codium launch_retext.sh</code> (My choice of IDE is VS Codium, but <code>nano</code> is good too), and <strong>fill in the correct paths</strong>. Save when done. Note: Because we are installing via <code>pip</code>, the RETEXT_EXECUTABLE will be in the bin directory of the venv directory.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#!/bin/zsh</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cp"></span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"># Set the path to your virtual environment's activation script</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nv">VENV_ACTIVATE</span><span class="o">=</span><span class="s2">"/path/to/venv/retext-env/bin/activate"</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># Set the path to the ReText executable</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nv">RETEXT_EXECUTABLE</span><span class="o">=</span><span class="s2">"path/to/where/retext/is/installed/local/bin/python3"</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># Path to the __main__.py script within the venv</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nv">RETEXT_SCRIPT</span><span class="o">=</span><span class="s2">"path/to/where/retext/is/installed/lib/python3.13/site-packages/ReText/__main__.py"</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nb">echo</span> <span class="s2">"Activating virtual environment: </span><span class="nv">$VENV_ACTIVATE</span><span class="s2">"</span></span></span><span class="line"><span class="ln">11</span><span class="cl"></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># Activate the virtual environment</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nb">source</span> <span class="s2">"</span><span class="nv">$VENV_ACTIVATE</span><span class="s2">"</span></span></span><span class="line"><span class="ln">14</span><span class="cl"></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"># Check if the activation worked</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> -ne <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span></span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="nb">echo</span> <span class="s2">"Failed to activate virtual environment."</span></span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="nb">exit</span> <span class="m">1</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">fi</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div></li><li><p>To make the script more accessible and executable from any directory, there are two options I usually use:</p><ul><li><p>Add the folder of where we saved our script <code>launch_retext.sh</code> to our PATH so that <strong>any</strong> executable script in that directory can be run by its filename alone.</p></li><li><p>Add a shell function for our particular script.</p></li></ul></li></ol><p>Both methods allow the script to be run anywhere, but the shell function option is more explicit and actually more flexible as it can handle more processing steps, e.g. error checking and argument validations.  If you have a lot of scripts, the PATH method is easier.</p><h4 id="how-to-add-script-directory-to-path">How to Add Script Directory to PATH<a href="#how-to-add-script-directory-to-path" class="h-anchor" title="Permalink to #How to Add Script Directory to PATH"></a></h4>   <ul><li><p>To add the script’s directory to your PATH, copy the pathname of the newly created script. Identify which shell you’re using with <code>echo $SHELL</code>. Open the shell configuration file, e.g. <code>.zshrc</code> or <code>.bash_profile</code> in the macOS home directory (~).</p></li><li><p>Add a line near the end of the file: <code>export PATH="$PATH:/location/to/your/scripts"</code>.</p></li><li><p>If you have prompt themes installed like <code>powerlevel10k</code>, <code>zsh-theme</code>, add the aliases or PATH mods <strong>before</strong> theme mods. This ensures are applied and not overwritten by the theme.</p></li><li><p>Save and reload shell with <code>source ~/.zshrc</code> (or the config you edited). Or just close the terminal window and launch a new one.</p></li></ul><h4 id="how-to-add-a-shell-function">How to Add a Shell Function<a href="#how-to-add-a-shell-function" class="h-anchor" title="Permalink to #How to Add a Shell Function"></a></h4>   <ul><li><p>Add this snippet to <code>.zshrc</code>, or to your shell config file, following the same rules as the PATH method regarding where in the config it is added. (The config is read in order, from top to bottom.)</p></li><li><p>I have named my function <code>rtx</code> for fast access, i.e.  less typing to open the app. You can name it anything you want. This is the word you type into terminal to open ReText.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">1</span><span class="cl">rtx<span class="o">()</span> <span class="o">{</span></span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="nb">source</span> <span class="s2">"/location/to/your/scripts/launch_retext.sh"</span> <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="o">}</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div></li><li><p>Next, add this extra snippet to the bottom of <code>launch_retext.sh</code> to allow for ReText to open even if we don’t provide a file for it to open, e.g. <code>rtx first_note.md</code></p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># Check if a file path was provided as an argument</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">if</span> <span class="o">[</span> -z <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span></span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="c1"># If no argument, open ReText with a new, empty file</span></span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="s2">"</span><span class="nv">$RETEXT_EXECUTABLE</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$RETEXT_SCRIPT</span><span class="s2">"</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">else</span></span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="c1"># Run ReText with the specified file</span></span></span><span class="line"><span class="ln">7</span><span class="cl">    <span class="s2">"</span><span class="nv">$RETEXT_EXECUTABLE</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$RETEXT_SCRIPT</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="k">fi</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div></li><li><p>The whole script should now look like this:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#!/bin/zsh</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cp"></span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"># Set the path to your virtual environment's activation script</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nv">VENV_ACTIVATE</span><span class="o">=</span><span class="s2">"/path/to/venv/retext-env/bin/activate"</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># Set the path to the ReText executable</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nv">RETEXT_EXECUTABLE</span><span class="o">=</span><span class="s2">"path/to/where/retext/is/installed/local/bin/python3"</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># Path to the __main__.py script within the venv</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nv">RETEXT_SCRIPT</span><span class="o">=</span><span class="s2">"path/to/where/retext/is/installed/lib/python3.13/site-packages/ReText/__main__.py"</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nb">echo</span> <span class="s2">"Activating virtual environment: </span><span class="nv">$VENV_ACTIVATE</span><span class="s2">"</span></span></span><span class="line"><span class="ln">11</span><span class="cl"></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># Activate the virtual environment</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nb">source</span> <span class="s2">"</span><span class="nv">$VENV_ACTIVATE</span><span class="s2">"</span></span></span><span class="line"><span class="ln">14</span><span class="cl"></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"># Check if the activation worked</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> -ne <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span></span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="nb">echo</span> <span class="s2">"Failed to activate virtual environment."</span></span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="nb">exit</span> <span class="m">1</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">fi</span></span></span><span class="line"><span class="ln">20</span><span class="cl"></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"># Check if a file path was provided as an argument</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">if</span> <span class="o">[</span> -z <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span></span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="c1"># If no argument, open ReText with a new, empty file</span></span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="s2">"</span><span class="nv">$RETEXT_EXECUTABLE</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$RETEXT_SCRIPT</span><span class="s2">"</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">else</span></span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="c1"># Run ReText with the specified file</span></span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="s2">"</span><span class="nv">$RETEXT_EXECUTABLE</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$RETEXT_SCRIPT</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">fi</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div></li><li><p>Remember to run <code>source ~/.zshrc</code> to reload config after making changes.</p></li><li><p>Last step: to open the app, go to Terminal, type the function or script name you have set like <code>rtx</code> or to open a file directly <code>rtx /path/to/a/note.md</code> and you should see this:</p></li></ul>                             <a href="/blog/retext-markdown-editor-how-to-install-python-apps-on-mac/terminal_retext_window.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/retext-markdown-editor-how-to-install-python-apps-on-mac/terminal_retext_window.webp" alt="Screenshot of launching ReText Markdown Editor on a Mac with the terminal.">    </a>       <p>That’s it for Method A! To close ReText, just close the main ReText window and when you go back to the Terminal, you’ll see you still have the venv activated by the visual cue (name of venv). Just closing the Terminal window will close the venv, or type <code>deactivate</code>.</p><p>Let’s get on to the second, perhaps more robust method!</p><h3 id="method-b-make-a-minimal-app-bundle">Method B: Make a Minimal App Bundle<a href="#method-b-make-a-minimal-app-bundle" class="h-anchor" title="Permalink to #Method B: Make a Minimal App Bundle"></a></h3>   <ol><li><p>Follow step 2 (creating a Python virtual environment for ReText) and step 3 (installing ReText with <code>pip</code>) from <a href="https://burgeonlab.com/#method-a-using-a-shell-script">Method A</a>.</p></li><li><p>We will be creating an app bundle with this directory structure. Use <code>mkdir -p ReText.app/Contents/MacOS ReText.app/Contents/Resources</code> to make the folder structure shown below. (To access the files inside the app, right click on the app > Show Package Contents). Put <code>ReText.app</code> into the <code>/Applications</code> folder. We will deal with the <code>Resources/retext.icns</code> in the last step.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text"><span class="line"><span class="ln">1</span><span class="cl">ReText.app</span></span><span class="line"><span class="ln">2</span><span class="cl">└── Contents</span></span><span class="line"><span class="ln">3</span><span class="cl">  ├── Info.plist</span></span><span class="line"><span class="ln">4</span><span class="cl">  ├── MacOS</span></span><span class="line"><span class="ln">5</span><span class="cl">  │   └── ReTextLauncher</span></span><span class="line"><span class="ln">6</span><span class="cl">  └── Resources</span></span><span class="line"><span class="ln">7</span><span class="cl">      └── retext.icns</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div></li><li><p>Create a <code>Info.plist</code> with <code>xml</code> syntax.  <code>CFBundleExecutable</code> will be the name of the executable script and  <code>CFBundleIdentifier</code> can be anything you want (I filled it in with the ReText’s creator details). Make sure the file is using UTF-8 encoding.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp"><?xml version="1.0" encoding="UTF-8"?></span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cp"><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"></span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nt"><plist</span> <span class="na">version=</span><span class="s">"1.0"</span><span class="nt">></span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nt"><dict></span></span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="nt"><key></span>CFBundleExecutable<span class="nt"></key></span></span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="nt"><string></span>ReTextLauncher<span class="nt"></string></span></span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="nt"><key></span>CFBundleIconFile<span class="nt"></key></span></span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="nt"><string></span>retext<span class="nt"></string></span></span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="nt"><key></span>CFBundleIdentifier<span class="nt"></key></span></span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="nt"><string></span>com.DmitryShachnev.ReTextLauncher<span class="nt"></string></span></span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="nt"><key></span>CFBundleInfoDictionaryVersion<span class="nt"></key></span></span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="nt"><string></span>6.0<span class="nt"></string></span></span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="nt"><key></span>CFBundleName<span class="nt"></key></span></span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="nt"><string></span>ReTextLauncher<span class="nt"></string></span></span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="nt"><key></span>CFBundleDisplayName<span class="nt"></key></span></span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="nt"><string></span>ReTextLauncher<span class="nt"></string></span></span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="nt"><key></span>CFBundlePackageType<span class="nt"></key></span></span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="nt"><string></span>APPL<span class="nt"></string></span></span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="nt"><key></span>CFBundleShortVersionString<span class="nt"></key></span></span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="nt"><string></span>1.0<span class="nt"></string></span></span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="nt"><key></span>CFBundleVersion<span class="nt"></key></span></span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="nt"><string></span>1.0<span class="nt"></string></span></span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="nt"><key></span>LSMinimumSystemVersion<span class="nt"></key></span></span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="nt"><string></span>10.10<span class="nt"></string></span></span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="nt"><key></span>NSPrincipalClass<span class="nt"></key></span></span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="nt"><string></span>NSApplication<span class="nt"></string></span></span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="nt"><key></span>NSRequiresAquaSystemAppearance<span class="nt"></key></span></span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="nt"><false/></span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="nt"></dict></span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="nt"></plist></span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div></li><li><p>Like in Method A, step 4, we will create a script in <code>ReText.app/Contents/MacOS/ReTextLauncher</code> to launch ReText in a venv.</p><ul><li>Fill in the correct path for <strong>your</strong> VENV_DIR (the one you created in Method A: Step 2) for the script, the rest should not need to be edited.</li><li>After saving the script, type <code>chmod +x /path/to/ReTextLauncher.app/Contents/MacOS/ReTextLauncher</code> to make it executable.</li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#!/bin/zsh</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cp"></span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"># Set path to the virtual environment</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nv">VENV_DIR</span><span class="o">=</span><span class="s2">"/Users/username/path/to/venv"</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># Path to the virtual environment's activate script</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nv">ACTIVATE_SCRIPT</span><span class="o">=</span><span class="s2">"</span><span class="nv">$VENV_DIR</span><span class="s2">/bin/activate"</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># Path to the ReText executable</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nv">RETEXT_EXECUTABLE</span><span class="o">=</span><span class="s2">"</span><span class="nv">$VENV_DIR</span><span class="s2">/bin/retext"</span></span></span><span class="line"><span class="ln">11</span><span class="cl"></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># PYTHONPATH (change version number, i.e. 3.13, as required)</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nb">export</span> <span class="nv">PYTHONPATH</span><span class="o">=</span><span class="s2">"</span><span class="nv">$VENV_DIR</span><span class="s2">/lib/python3.13/site-packages:</span><span class="nv">$PYTHONPATH</span><span class="s2">"</span></span></span><span class="line"><span class="ln">14</span><span class="cl"></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"># Add the virtual environment's bin directory to the PATH</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="s2">"</span><span class="nv">$VENV_DIR</span><span class="s2">/bin:</span><span class="nv">$PATH</span><span class="s2">"</span></span></span><span class="line"><span class="ln">17</span><span class="cl"></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"># Launch ReText using the retext command</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="s2">"</span><span class="nv">$RETEXT_EXECUTABLE</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div> <blockquote class="alert alert-tip">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Tip      </p>  <p>Sometimes apps fail to run because it’s downloaded outside the Mac App Store or was created manually. It’s due to the new quarantine mechanism. Although I didn’t have to use it on this occasion, I often use it for FOSS apps I get direct from repos online. Always be wary of what you download from the Internet though! Check SHA256 hashes when you can.</p><p>Run <code>xattr -d com.apple.quarantine /path/to/ReTextLauncher.app</code> to remove the quarantine attribute.</p><p>Another cause for errors is the fact this app bundle is not code signed. Code signing is like the seal found in medicine; it is to verify that the contents (app code) is not tampered with. To sign an app for personal use (not distribution), like in this scenario, we can create a self-signed certificate using Keychain Access on the Mac for this.</p><ul><li>Go to Applications > Utilities > Keychain Access > In the menu bar > Keychain Access > Certificate Assistant > Create a Certificate.</li><li>Name it something like “Self-Signed Cert”.</li><li>Identity Type: “Self Signed Root” and check the box “Let me override defaults”, click create.</li><li>Continue with all the default options until the cert is generated.</li><li>Go back to the Terminal and use <code>codesign -s "Self-Signed Cert" -f -v /path/to/ReText.app</code></li></ul></blockquote> </li><li><p>We’re nearly done!</p><ul><li>Grab the <code>.png</code> icon from <code>/path/to/venv/lib/python3.13/site-packages/ReText/icons/retext.png</code> and convert it to <code>.icns</code> using an online converter</li><li>Place the <code>.icns</code> file into <code>ReText.app/Contents/Resources/retext.icns</code></li></ul>                                                                                       <a href="/blog/retext-markdown-editor-how-to-install-python-apps-on-mac/retext_getinfo.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">          <img src="/blog/retext-markdown-editor-how-to-install-python-apps-on-mac/retext_getinfo.webp" alt="Screenshot of the app info window on the ReText bundled app on macOS.">        </a>               </li><li><p>Double click the app to run and close it without going through the Terminal! 🎉</p></li></ol><h2 id="wrap-up">Wrap Up<a href="#wrap-up" class="h-anchor" title="Permalink to #Wrap Up"></a></h2>   <p>This ended up longer than I expected, but I learnt some new tricks here and there…<sup id="fnref:2"><a href="#fn:2" class="footnote-ref">2</a></sup> Hope you did too; and that this tutorial allows Mac users to enjoy this Python-based, Linux-optimized Markdown editor!</p><p><del>I will make another post soon.</del></p><p>Here’s a follow-up guide on <a href="https://burgeonlab.com/blog/customize-retext-markdown-editor/">how to customize ReText</a> like syntax highlighting and using Python Markdown extensions. Learn how to setup the configuration file because I found it slightly unclear to begin with.</p><p>P.S. I made some pretty major changes to the look of this blog last week; which in turn gave me some extra motivation to write! Hope it’s easier on the eyes and looks more polished. 😉</p><div class="footnotes"><hr><ol><li id="fn:1"><p>It no longer exists on the web, but here are two blog posts by others talking about it. <a href="https://tommcfarlin.com/mou-for-mac/" target="_blank" class="ext-link" rel="noopener noreferrer">By Tom</a> and <a href="https://news.macgasm.net/apps/mou-awesome-free-markdown-editor-mac/" target="_blank" class="ext-link" rel="noopener noreferrer">by Andrew</a> <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li><li id="fn:2"><p>Just one of the things I discovered was there’s this CLI tool that generates pretty looking directory trees! <code>brew install tree</code> and run <code>tree</code> in a directory. <a href="#fnref:2" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/using-github-actions-to-automate-hugo-static-site-deployment-to-github-pages/</id><link rel="alternate" href="https://burgeonlab.com/blog/using-github-actions-to-automate-hugo-static-site-deployment-to-github-pages/"/><title type="html">Use GitHub Actions to Automate Hugo Hosting on GitHub Pages</title><published>2025-04-22T19:52:16Z</published><updated>2025-04-22T19:52:16Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/using-github-actions-to-automate-hugo-static-site-deployment-to-github-pages/deployment.webp"/><summary type="html">This guide demonstrates how to automate Hugo site deployment to GitHub Pages using a GitHub Action workflow. It covers prerequisites, the workflow itself, and the basic steps to get started.</summary><content type="html"><![CDATA[<h2 id="preface">Preface<a href="#preface" class="h-anchor" title="Permalink to #Preface"></a></h2>   <p>Occasionally, I need to work on different devices without access to my main computer at home. Being able to pull my Hugo blog “source”, i.e. not the published website files (html, css, etc), onto a remote device, be it another computer or phone, and work on that without interruption is convenient.</p><p>I will show you how I use GitHub to create a source repository (repo) and using GitHub Actions to auto-trigger a workflow (only triggering when the draft status changes),that will publish and deploy the site onto the free hosting GitHub Pages offers to all users.</p><h3 id="prerequisites">Prerequisites<a href="#prerequisites" class="h-anchor" title="Permalink to #Prerequisites"></a></h3>   <p>I’m assuming you already have your <a href="https://gohugo.io/getting-started/quick-start/" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo</a> site locally set up. Create a new account on GitHub if you haven’t done so already. Make a new repo for all the files Hugo uses to build the site. Add a <code>.gitignore</code> file to the root folder with the following to prevent the site files to be synced with this source repo.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt"><span class="line"><span class="ln">1</span><span class="cl">/public/</span></span><span class="line"><span class="ln">2</span><span class="cl">/resources/</span></span><span class="line"><span class="ln">3</span><span class="cl">.DS_Store</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>You can now write wherever you are just by pulling this repo and making changes. Push to the repo to “save” your progress and resume on any device by repeating this process.</p><h2 id="github-action-workflow">GitHub Action Workflow<a href="#github-action-workflow" class="h-anchor" title="Permalink to #GitHub Action Workflow"></a></h2>   <p>Using the free hosting of a site offered on <a href="https://pages.github.com/" target="_blank" class="ext-link" rel="noopener noreferrer">GitHub Pages</a>, we will automate building of the site (Hugo in our case) and deploy it to the GitHub Pages repo using our source repo with a GitHub Action workflow. First, create a new GitHub Pages repo with a repo name in this format: <code>username.github.io</code>. E.g. mine would be <code>eclecticpassions.github.io</code> as eclecticpassions is my GitHub username.</p><h3 id="basic-trigger">Basic Trigger<a href="#basic-trigger" class="h-anchor" title="Permalink to #Basic Trigger"></a></h3>   <p>If you want the Action to run and build the site every time your source repo is updated, you can use this basic workflow<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup>: <code>.github/workflows/pages.yml</code></p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">hugo publish</span><span class="w"></span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w"> </span><span class="c"># Runs on pushes targeting the default branch (i.e. master, in my case)</span><span class="w"></span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span>- <span class="l">master</span><span class="w"> </span><span class="c"># Set the branch name to trigger deployment</span><span class="w"></span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"></span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">  </span><span class="nt">build-deploy</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w"></span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Git checkout</span><span class="w"></span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4 </span><span class="w"></span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">      </span><span class="nt">with</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">          </span><span class="nt">submodules</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">  </span><span class="c"># Fetch Hugo themes ('true' OR 'recursive'). Without this, the site wouldn't build properly, missing index.html etc.</span><span class="w"></span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">          </span><span class="nt">fetch-depth</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">    </span><span class="c">#  0=Fetch entire commit history. 2=Fetch only the latest 2 commits. </span><span class="w"></span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w"></span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Setup Hugo</span><span class="w"></span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peaceiris/actions-hugo@v3</span><span class="w"></span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">      </span><span class="nt">with</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">        </span><span class="nt">hugo-version</span><span class="p">:</span><span class="w"> </span><span class="s1">'latest'</span><span class="w"> </span><span class="c"># Update to the latest Hugo version e.g.'0.145.0' or  use  'latest'</span><span class="w"></span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">        </span><span class="nt">extended</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="c"># Required for CSS to run</span><span class="w"></span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w"></span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build</span><span class="w"></span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">      </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">hugo --minify</span><span class="w"> </span><span class="c">#--enableGitInfo  Uncomment enableGitInfo to show last modified date on posts.</span><span class="w"></span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w"></span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Deploy</span><span class="w"></span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peaceiris/actions-gh-pages@v3</span><span class="w"></span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">      </span><span class="nt">with</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">        </span><span class="nt">personal_token</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.ACTIONS_DEPLOY_KEY }}</span><span class="w"></span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">        </span><span class="nt">external_repository</span><span class="p">:</span><span class="w"> </span><span class="l">username/username.github.io</span><span class="w"> </span><span class="c">#Enter your GitHub Pages repo name</span><span class="w"></span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">        </span><span class="nt">publish_branch</span><span class="p">:</span><span class="w"> </span><span class="l">master</span><span class="w"></span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">        </span><span class="nt">publish_dir</span><span class="p">:</span><span class="w"> </span><span class="l">./public</span><span class="w"></span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">        </span><span class="nt">user_name</span><span class="p">:</span><span class="w"> </span><span class="l">YourName </span><span class="w"></span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">        </span><span class="nt">user_email</span><span class="p">:</span><span class="w"> </span><span class="l">name@domain.com</span><span class="w"></span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">        </span><span class="nt">cname</span><span class="p">:</span><span class="w"> </span><span class="l">www.your-custom-domain-if-you-have-one.com</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>For the <code>personal_token</code> the the last block (Deploy), you need to make a <a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens" target="_blank" class="ext-link" rel="noopener noreferrer">personal access token</a>. I used the <em>Fine-grained token</em> which is more secure.</p>                             <a href="/blog/using-github-actions-to-automate-hugo-static-site-deployment-to-github-pages/fine_grained.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/using-github-actions-to-automate-hugo-static-site-deployment-to-github-pages/fine_grained.webp" alt="Screenshot of permissions required for fine-grained token to work.">    </a>       <p>Now, add it to your GitHub Pages repo > Settings > Secrets and variables > Actions > New repository secret. Name it <code>ACTIONS_DEPLOY_KEY</code> and paste in the token generated in the previous step.</p>                             <a href="/blog/using-github-actions-to-automate-hugo-static-site-deployment-to-github-pages/actions_deploy_key.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/using-github-actions-to-automate-hugo-static-site-deployment-to-github-pages/actions_deploy_key.webp" alt="Screenshot of Github Actions secrets and variables.">    </a>       <h3 id="advanced-trigger">Advanced Trigger<a href="#advanced-trigger" class="h-anchor" title="Permalink to #Advanced Trigger"></a></h3>   <p>If you want it to only build when there is a draft change from true to false (i.e. draft to published), use this workflow<sup id="fnref:2"><a href="#fn:2" class="footnote-ref">2</a></sup> below.</p><p><strong>Things of note:</strong></p><ul><li><code>draft_check</code> checks if the draft status in the post <a href="https://gohugo.io/content-management/front-matter/" target="_blank" class="ext-link" rel="noopener noreferrer">front matter</a> has changed from the previous commit. (I’m using +++, i.e. toml front matter. If you’re using —, yaml, please change the code accordingly.)</li><li><code>if: steps.draft_check.outputs.draft_changed == 'true'</code> condition added to the build and deploy steps so they only run when draft_check is true.</li><li>You should run the basic trigger first so there’s a baseline in the repo. Changes to other files, e.g. theme changes, css, layouts will not be pushed until there is a draft status change thereafter if you use the Advanced Trigger. You can comment out paths, draft_check and if conditions to trigger the build and deploy steps.</li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">hugo publish</span><span class="w"></span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span>- <span class="l">master </span><span class="w"></span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="nt">paths</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span>- <span class="s1">'content/posts/**'</span><span class="w"> </span><span class="c"># Matches any file or folder recursively inside content/posts/, including all subfolders and files like index.md.</span><span class="w"></span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"></span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">  </span><span class="nt">build-deploy</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w"></span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Git checkout</span><span class="w"></span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w"></span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">      </span><span class="nt">with</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">          </span><span class="nt">submodules</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">          </span><span class="nt">fetch-depth</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">    </span><span class="c"># 2=Fetch only the latest 2 commits, needed to compare if draft status has changed in front-matter of previous commit.</span><span class="w"></span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w"></span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Check if draft status changed from true to false </span><span class="w"></span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">      </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">draft_check</span><span class="w"></span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">      </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd"></span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="sd">        # Get list of changed files between previous and current commit</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="sd">        CHANGED_FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }})</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="sd">        echo "Changed files:"</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="sd">        echo "$CHANGED_FILES"</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="sd">        draft_changed=false</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="sd">        get_draft_status() {</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="sd">          local content="$1"</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="sd">          echo "$content" | sed -n '/^+++$/,/^+++$/p' | grep '^draft' | head -1 | awk -F'=' '{print $2}' | tr -d ' "'</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="sd">        }</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="sd">        for file in $CHANGED_FILES; do</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="sd">          # Only check markdown files under content/posts/</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="sd">          case "$file" in</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="sd">            content/posts/**/*.md|content/posts/**/index.md)</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="sd">              echo "Checking draft status in $file"</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="sd">              # Get previous draft value (if file existed before)</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="sd">              PREV_CONTENT=$(git show ${{ github.event.before }}:$file 2>/dev/null || echo "")</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="sd">              CURRENT_CONTENT=$(cat "$file")</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="sd">              PREV_DRAFT=$(get_draft_status "$PREV_CONTENT" || echo "none")</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="sd">              CURRENT_DRAFT=$(get_draft_status "$CURRENT_CONTENT" || echo "none")</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="sd">              echo "Previous draft: $PREV_DRAFT"</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="sd">              echo "Current draft: $CURRENT_DRAFT"</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="sd">              if [[ "$PREV_DRAFT" == "true" && "$CURRENT_DRAFT" == "false" ]]; then</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="sd">                draft_changed=true</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="sd">                break</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="sd">              fi</span></span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="sd">              ;;</span></span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="sd">            *)</span></span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="sd">              # skip other files</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="sd">              ;;</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="sd">          esac</span></span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="sd">        done</span></span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="sd"></span></span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="sd">        echo "draft_changed=$draft_changed" >> $GITHUB_OUTPUT</span><span class="w"></span></span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="w"></span></span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build and Deploy only if draft changed to false</span><span class="w"></span></span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="w">      </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">steps.draft_check.outputs.draft_changed == 'true'</span><span class="w"></span></span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="w">      </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd"></span></span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="sd">          # Your build commands here</span></span></span><span class="line"><span class="ln">69</span><span class="cl"><span class="sd">          echo "Draft status changed! Proceed to build & deploy..."</span><span class="w"></span></span></span><span class="line"><span class="ln">70</span><span class="cl"><span class="w"></span></span></span><span class="line"><span class="ln">71</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Setup Hugo</span><span class="w"></span></span></span><span class="line"><span class="ln">72</span><span class="cl"><span class="w">      </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">steps.draft_check.outputs.draft_changed == 'true'</span><span class="w"></span></span></span><span class="line"><span class="ln">73</span><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peaceiris/actions-hugo@v3</span><span class="w"></span></span></span><span class="line"><span class="ln">74</span><span class="cl"><span class="w">      </span><span class="nt">with</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln">75</span><span class="cl"><span class="w">        </span><span class="nt">hugo-version</span><span class="p">:</span><span class="w"> </span><span class="s1">'latest'</span><span class="w"></span></span></span><span class="line"><span class="ln">76</span><span class="cl"><span class="w">        </span><span class="nt">extended</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"></span></span></span><span class="line"><span class="ln">77</span><span class="cl"><span class="w"></span></span></span><span class="line"><span class="ln">78</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build</span><span class="w"></span></span></span><span class="line"><span class="ln">79</span><span class="cl"><span class="w">      </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">steps.draft_check.outputs.draft_changed == 'true'</span><span class="w"></span></span></span><span class="line"><span class="ln">80</span><span class="cl"><span class="w">      </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">hugo --minify</span><span class="w"></span></span></span><span class="line"><span class="ln">81</span><span class="cl"><span class="w"></span></span></span><span class="line"><span class="ln">82</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Deploy</span><span class="w"></span></span></span><span class="line"><span class="ln">83</span><span class="cl"><span class="w">      </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">steps.draft_check.outputs.draft_changed == 'true'</span><span class="w"></span></span></span><span class="line"><span class="ln">84</span><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peaceiris/actions-gh-pages@v3</span><span class="w"></span></span></span><span class="line"><span class="ln">85</span><span class="cl"><span class="w">      </span><span class="nt">with</span><span class="p">:</span><span class="w"></span></span></span><span class="line"><span class="ln">86</span><span class="cl"><span class="w">        </span><span class="nt">personal_token</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.ACTIONS_DEPLOY_KEY }}</span><span class="w"></span></span></span><span class="line"><span class="ln">87</span><span class="cl"><span class="w">        </span><span class="nt">external_repository</span><span class="p">:</span><span class="w"> </span><span class="l">username/username.github.io</span><span class="w"></span></span></span><span class="line"><span class="ln">88</span><span class="cl"><span class="w">        </span><span class="nt">publish_branch</span><span class="p">:</span><span class="w"> </span><span class="l">master</span><span class="w"></span></span></span><span class="line"><span class="ln">89</span><span class="cl"><span class="w">        </span><span class="nt">publish_dir</span><span class="p">:</span><span class="w"> </span><span class="l">./public</span><span class="w"></span></span></span><span class="line"><span class="ln">90</span><span class="cl"><span class="w">        </span><span class="nt">user_name</span><span class="p">:</span><span class="w"> </span><span class="l">YourName </span><span class="w"></span></span></span><span class="line"><span class="ln">91</span><span class="cl"><span class="w">        </span><span class="nt">user_email</span><span class="p">:</span><span class="w"> </span><span class="l">name@domain.com</span><span class="w"></span></span></span><span class="line"><span class="ln">92</span><span class="cl"><span class="w">        </span><span class="nt">cname</span><span class="p">:</span><span class="w"> </span><span class="l">www.your-custom-domain-if-you-have-one.com</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>My Git client of choice on Android is <a href="https://github.com/catpuppyapp/PuppyGit" target="_blank" class="ext-link" rel="noopener noreferrer">PuppyGit</a> and on iPad is <a href="https://workingcopyapp.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Working Copy</a>.</p><p>That’s it, happy writing! Hope the deployments are successful!</p>                             <a href="/blog/using-github-actions-to-automate-hugo-static-site-deployment-to-github-pages/deployment.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/using-github-actions-to-automate-hugo-static-site-deployment-to-github-pages/deployment.webp" alt="Screenshot of GitHub workflow for building Hugo site and deployment success to GitHub Pages.">    </a>       <div class="footnotes"><hr><ol><li id="fn:1"><p>Check if there are newer @versions of actions: <a href="https://github.com/actions/checkout" target="_blank" class="ext-link" rel="noopener noreferrer">‘actions/checkout’</a>, <a href="https://github.com/peaceiris/actions-hugo" target="_blank" class="ext-link" rel="noopener noreferrer">‘actions-hugo’</a> and <a href="https://github.com/peaceiris/actions-gh-pages" target="_blank" class="ext-link" rel="noopener noreferrer">‘actions-gh-pages’</a>. <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li><li id="fn:2"><p>My Hugo blog folder structure is: <code>blog_name/content/posts/2025/post_identifier/index.md</code>. <a href="#fnref:2" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/hardening-my-new-android-phone/</id><link rel="alternate" href="https://burgeonlab.com/blog/hardening-my-new-android-phone/"/><title type="html">Hardening my New Android Phone</title><published>2025-04-06T22:20:14Z</published><updated>2025-04-06T22:20:14Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/hardening-my-new-android-phone/nothing3a.webp"/><summary type="html">Harden your Android phone by replacing Google apps with FOSS alternatives, minimizing Big Tech and optimizing privacy settings for better security.</summary><content type="html"><![CDATA[<h2 id="new-phone--opportunity-to-start-afresh">New Phone = Opportunity to Start Afresh?<a href="#new-phone--opportunity-to-start-afresh" class="h-anchor" title="Permalink to #New Phone = Opportunity to Start Afresh?"></a></h2>   <p>I’ve always been an Android user since smartphones became the norm. I prefer its openness compared to the walled-off garden of iPhones. My latest phone is a Vivo X70 Pro running its last system update: Android 14 (with three years of software support from Android 11). I was planning to upgrade maybe at the end of 2025, but last month the screen developed the commonly seen <a href="https://www.androidauthority.com/how-to-fix-green-line-issue-on-phone-screen-3342058/" target="_blank" class="ext-link" rel="noopener noreferrer">“green line” hardware defect</a> without any obvious trigger. Having owned over 10 phones, this is the first time I’ve experienced a screen failure! 🫤</p><p>I am excited to decide on a new phone, try to de-Google, and use open source software as much as possible with this new beginning. I will be sharing all the app alternatives I’m using and services I prefer in lieu of Google or other Big Tech conglomerates to improve data privacy and try to take back <em>some</em> control of who we give our data to.</p> <blockquote>  <p><strong>DISCLAIMER</strong>: These are all my own personal preferences and choices after trial and error. I am not sponsored by any of these apps or services. Please do your own research to determine if my recommendations fit your requirements.</p></blockquote> <h2 id="my-priorities">My Priorities<a href="#my-priorities" class="h-anchor" title="Permalink to #My Priorities"></a></h2>   <ul><li>Rely less on Big Tech<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup></li><li>Experiment with apps and services that respect user privacy<sup id="fnref:2"><a href="#fn:2" class="footnote-ref">2</a></sup></li><li>Try to find non-US based services due to unpredictable Trump-era policies<sup id="fnref:3"><a href="#fn:3" class="footnote-ref">3</a></sup></li><li>I will still have a Google account for convenience, but it won’t be linked to services I deem more private and worth protecting; i.e., contacts, calendar, email, etc.</li><li>Allow downloading non-FOSS apps (e.g., banking or local services/government apps) via Google Play instead of Aurora Store.</li></ul><h2 id="my-choicenothing-phone-3a">My Choice—Nothing Phone (3a)<a href="#my-choicenothing-phone-3a" class="h-anchor" title="Permalink to #My Choice—Nothing Phone (3a)"></a></h2>   <p>My budget for a new phone is relatively low—around 3000 HKD (~400 USD)—so I decided to give the Nothing brand a try and got the <a href="https://www.gsmarena.com/nothing_phone_%283a%29-13672.php" target="_blank" class="ext-link" rel="noopener noreferrer">Nothing Phone (3a) 256GB+12GB (blue)</a>, released just in mid-March 2025. I have used the OnePlus 5 before and appreciate what Carl Pei has done so far with the Nothing brand aesthetics and the bloat-free NothingOS experience.</p>                             <a href="/blog/hardening-my-new-android-phone/nothing3a.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hardening-my-new-android-phone/nothing3a.webp" alt="Nothing Phone 3a in blue on a black grid background">    </a>       <p>They claim the 3a series will receive three OS upgrades (Android 15 to 18) and six years of security updates. While this is a little less than competitors like the Samsung Galaxy A series, OnePlus Nord, etc., I am keen on trying out this cool-looking phone with the iconic back glyph lights and the interesting, yet not too intrusive, AI integration with Essential Space. (I’m not super keen on integrating AI into every bit of tech I own, but that’s a topic for another day.)</p><h3 id="the-ideal-smartphone-option-for-the-privacy-conscious">The Ideal Smartphone Option for the Privacy Conscious<a href="#the-ideal-smartphone-option-for-the-privacy-conscious" class="h-anchor" title="Permalink to #The Ideal Smartphone Option for the Privacy Conscious"></a></h3>   <p>If I had no budget constraints and wanted to go all-in on upping my data privacy and security game, I’d get the flagship Google Pixel and use <a href="https://grapheneos.org/" target="_blank" class="ext-link" rel="noopener noreferrer">GrapheneOS</a> (a custom ROM with a fully hardened version of Android for privacy-conscious individuals).</p><p>Now, let’s get started on how I set up my new Android phone!</p><h2 id="first-stepsdo-not-restore-from-google-backup">First Steps–Do Not Restore From Google Backup<a href="#first-stepsdo-not-restore-from-google-backup" class="h-anchor" title="Permalink to #First Steps–Do Not Restore From Google Backup"></a></h2>   <ul><li><p>Try to resist logging into your Google account during the initial onboarding. This prevents any old apps or unnecessary data from cluttering the brand-new phone.</p></li><li><p>Update the phone to the latest version before making changes; it’s often the case that settings and previously switched-off options revert after updates. So might as well do the update first before making changes!</p></li></ul> <blockquote class="alert alert-tip">  <p class="alert-heading">    <span class="alert-icon">            <span class="inline-icon"></span>    </span>          Tip      </p>  <p>Always review all the options in settings, especially privacy-related ones, after system updates to check if they have reverted.</p></blockquote> <ul><li>Using the pre-installed browser, navigate to <a href="https://f-droid.org/docs/Get_F-Droid/#option-2-download-and-install-f-droid-apk" target="_blank" class="ext-link" rel="noopener noreferrer">F-Droid</a> to download the APK. (I prefer the F-Droid <a href="https://f-droid.org/en/packages/org.fdroid.basic/" target="_blank" class="ext-link" rel="noopener noreferrer">Basic client</a>, which you can get after installing F-Droid).</li></ul><h3 id="replacing-default-apps-and-disabling-them">Replacing Default Apps and Disabling Them<a href="#replacing-default-apps-and-disabling-them" class="h-anchor" title="Permalink to #Replacing Default Apps and Disabling Them"></a></h3>   <p>Before downloading <em>all</em> your favourite apps, I suggest downloading the essentials first during setup. That way, you can disable the default Google versions right away in a smaller app list.</p><ol><li><p>Go to Apps > See all apps > Choose apps that won’t be used > clear storage, remove all permissions, and disable background battery usage and background data usage.</p></li><li><p>Disable the unwanted app if uninstall is not available.</p></li><li><p>Select non-Google alternatives in Apps > Default Apps, e.g., QUIK as the default SMS app.</p></li></ol><table>  <thead>      <tr>          <th>Default</th>          <th>Alternative</th>          <th>Link</th>      </tr>  </thead>  <tbody>      <tr>          <td>Google Phone</td>          <td>Fossify Phone</td>          <td><a href="https://www.fossify.org/apps/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.fossify.org/apps/</a></td>      </tr>      <tr>          <td>Google Contacts</td>          <td>Fossify Contacts</td>          <td><a href="https://www.fossify.org/apps/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.fossify.org/apps/</a></td>      </tr>      <tr>          <td>Google SMS/Messages</td>          <td>QUIK SMS</td>          <td><a href="https://f-droid.org/packages/dev.octoshrimpy.quik/" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/packages/dev.octoshrimpy.quik/</a></td>      </tr>      <tr>          <td>Google Chrome</td>          <td>IronFox</td>          <td><a href="https://gitlab.com/ironfox-oss/IronFox" target="_blank" class="ext-link" rel="noopener noreferrer">https://gitlab.com/ironfox-oss/IronFox</a></td>      </tr>      <tr>          <td></td>          <td>Cromite</td>          <td><a href="https://github.com/uazo/cromite" target="_blank" class="ext-link" rel="noopener noreferrer">https://github.com/uazo/cromite</a></td>      </tr>      <tr>          <td>Gmail</td>          <td>FairEmail</td>          <td><a href="https://github.com/M66B/FairEmail" target="_blank" class="ext-link" rel="noopener noreferrer">https://github.com/M66B/FairEmail</a></td>      </tr>      <tr>          <td></td>          <td>Proton Mail</td>          <td><a href="https://protonapps.com/" target="_blank" class="ext-link" rel="noopener noreferrer">https://protonapps.com/</a></td>      </tr>      <tr>          <td></td>          <td>Tuta Mail</td>          <td><a href="https://github.com/tutao/tutanota/releases/tag/tutanota-android-release-277.250414.1" target="_blank" class="ext-link" rel="noopener noreferrer">https://github.com/tutao/tutanota/releases/tag/tutanota-android-release-277.250414.1</a></td>      </tr>      <tr>          <td>Google Calendar</td>          <td>Tuta Calendar</td>          <td><a href="https://github.com/tutao/tutanota/releases/tag/tuta-calendar-android-release-277.250414.0" target="_blank" class="ext-link" rel="noopener noreferrer">https://github.com/tutao/tutanota/releases/tag/tuta-calendar-android-release-277.250414.0</a></td>      </tr>      <tr>          <td></td>          <td>Proton Calendar</td>          <td><a href="https://protonapps.com/" target="_blank" class="ext-link" rel="noopener noreferrer">https://protonapps.com/</a></td>      </tr>      <tr>          <td>Google Tasks</td>          <td>Tasks.org</td>          <td><a href="https://github.com/tasks/tasks" target="_blank" class="ext-link" rel="noopener noreferrer">https://github.com/tasks/tasks</a></td>      </tr>      <tr>          <td></td>          <td>Super Productivity</td>          <td><a href="https://github.com/johannesjo/super-productivity" target="_blank" class="ext-link" rel="noopener noreferrer">https://github.com/johannesjo/super-productivity</a></td>      </tr>      <tr>          <td>Gallery</td>          <td>Aves Libre</td>          <td><a href="https://github.com/deckerst/aves/wiki/App-Versions" target="_blank" class="ext-link" rel="noopener noreferrer">https://github.com/deckerst/aves/wiki/App-Versions</a></td>      </tr>      <tr>          <td>Calculator</td>          <td>Calculator You</td>          <td><a href="https://github.com/forzzzzz/Calculator-You" target="_blank" class="ext-link" rel="noopener noreferrer">https://github.com/forzzzzz/Calculator-You</a></td>      </tr>      <tr>          <td>Google Play</td>          <td>Accrescent</td>          <td><a href="https://accrescent.app/" target="_blank" class="ext-link" rel="noopener noreferrer">https://accrescent.app/</a></td>      </tr>      <tr>          <td></td>          <td>F-Droid Basic</td>          <td><a href="https://f-droid.org/en/packages/org.fdroid.basic/" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/en/packages/org.fdroid.basic/</a></td>      </tr>      <tr>          <td>G-board</td>          <td>Floris Board</td>          <td><a href="https://github.com/florisboard/florisboard" target="_blank" class="ext-link" rel="noopener noreferrer">https://github.com/florisboard/florisboard</a></td>      </tr>      <tr>          <td></td>          <td>Fossify Keyboard</td>          <td><a href="https://www.fossify.org/apps/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.fossify.org/apps/</a></td>      </tr>  </tbody></table><h2 id="android-settings-to-change-for-improved-privacy">Android Settings to Change for Improved Privacy<a href="#android-settings-to-change-for-improved-privacy" class="h-anchor" title="Permalink to #Android Settings to Change for Improved Privacy"></a></h2>   <p>Go through each page in Android Settings and Google Settings to improve privacy. Here are some of my preferences on the Nothing Phone:</p><ul><li>Network & Internet > Internet > Network preferences: Turn off automatically connect to best Wi-Fi, turn on Wi-Fi automatically, notify for public networks, allow WEP networks.</li><li>Network & Internet > Private DNS: Consider using QUAD9 or another DNS provider.-Connected Devices > Connection preferences: Turn off Bluetooth, NFC, and printing when not in use.</li><li>Lock Screen > Privacy: Don’t show notifications at all, turn off quick settings, add contact info and reward on lock screen in case you lose the phone.</li><li>Uninstall bloatware or disable if no uninstall option exists. Choose default apps as mentioned above. Also, review all special app access permissions and disable as many as possible.</li><li>Check accessibility permissions and only allow trusted apps.</li><li>In Security & Privacy: Set a strong password, I prefer alphabets and number/symbols instead of the simple pin or pattern which can be seen easily by onlookers.</li><li>Turn off all Ads settings and review apps in Permission Manager.</li><li>In More Security & Privacy > Turn off: Autofill by Google, Usage and Diagnostics, Android System Intelligence keyboard suggestions, Experience Improvement Programs.</li><li>In Passwords, Passkeys, and Accounts: Choose a password manager of your choice—I chose Bitwarden.</li><li>In Settings > Google: Please follow in-depth guide videos from creators likes <a href="https://www.youtube.com/watch?v=Bi0BXJPbWq8" target="_blank" class="ext-link" rel="noopener noreferrer">Payette Forward</a> or SSD.EFF.ORG’s guide on <a href="https://ssd.eff.org/module/how-to-get-to-know-android-privacy-and-security-settings" target="_blank" class="ext-link" rel="noopener noreferrer">Android Privacy and Security Settings</a>.</li><li>In System: Turn off Backups to Google One. I manually backup personal files (photos, documents, etc) via Syncthing routinely.</li></ul><h2 id="other-apps-i-use-foss-and-non-foss">Other Apps I Use (FOSS and NON-FOSS)<a href="#other-apps-i-use-foss-and-non-foss" class="h-anchor" title="Permalink to #Other Apps I Use (FOSS and NON-FOSS)"></a></h2>   <p>Here’s some more Android apps I like and other notes and quirks about them:</p><ul><li>If you download Bitwarden outside of the Play Store, you will need to manually sync your vault.</li><li>KDE Connect works great with connectivity between macOS and iPadOS.</li><li>Syncthing allows me to have “AirDrop” like functionality with Android + Macs.</li><li>PuppyGit can be used to sync your Obsidian vault, but I use Syncthing instead at the moment (read-mode only). I use PuppyGit to edit this blog on the go by pulling and pushing to my blog’s source repo on GitHub.</li><li><a href="https://github.com/M66B" target="_blank" class="ext-link" rel="noopener noreferrer">Marcel Bokhorst</a>, the developer for NetGuard also makes FairEmail. I really like both apps a lot, and they are must-installs for me!</li><li>If you prefer to use local/offline calendars instead of Tutanota or Proton (the ones I use), try <a href="https://github.com/Etar-Group/Etar-Calendar" target="_blank" class="ext-link" rel="noopener noreferrer">Etar</a>.</li><li>Remember to support the developers who make their apps open source and free! 🫶</li></ul><h3 id="free-open-source-apps">Free Open Source Apps<a href="#free-open-source-apps" class="h-anchor" title="Permalink to #Free Open Source Apps"></a></h3>   <table>  <thead>      <tr>          <th>App</th>          <th>Description</th>          <th>Link</th>      </tr>  </thead>  <tbody>      <tr>          <td>NetGuard</td>          <td>No root firewall</td>          <td><a href="https://f-droid.org/en/packages/eu.faircode.netguard/" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/en/packages/eu.faircode.netguard/</a></td>      </tr>      <tr>          <td>Aegis</td>          <td>2FA</td>          <td><a href="https://f-droid.org/en/packages/com.beemdevelopment.aegis/" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/en/packages/com.beemdevelopment.aegis/</a></td>      </tr>      <tr>          <td>Bitwarden</td>          <td>PW manager</td>          <td><a href="https://bitwarden.com/download/#downloads-mobile" target="_blank" class="ext-link" rel="noopener noreferrer">https://bitwarden.com/download/#downloads-mobile</a></td>      </tr>      <tr>          <td>Image Toolbox</td>          <td>Photo editor</td>          <td><a href="https://f-droid.org/en/packages/ru.tech.imageresizershrinker/" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/en/packages/ru.tech.imageresizershrinker/</a></td>      </tr>      <tr>          <td>KDE Connect</td>          <td>Notification sharing</td>          <td><a href="https://f-droid.org/en/packages/org.kde.kdeconnect_tp/" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/en/packages/org.kde.kdeconnect_tp/</a></td>      </tr>      <tr>          <td>SD Maid 2</td>          <td>System cleaner</td>          <td><a href="https://f-droid.org/en/packages/eu.darken.sdmse/" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/en/packages/eu.darken.sdmse/</a></td>      </tr>      <tr>          <td>Syncthing-Fork</td>          <td>File sync</td>          <td><a href="https://f-droid.org/en/packages/com.github.catfriend1.syncthingandroid/" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/en/packages/com.github.catfriend1.syncthingandroid/</a></td>      </tr>      <tr>          <td>Tusky</td>          <td>Mastodon client</td>          <td><a href="https://f-droid.org/en/packages/com.keylesspalace.tusky/" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/en/packages/com.keylesspalace.tusky/</a></td>      </tr>      <tr>          <td>App Verifier</td>          <td>Verify hash keys</td>          <td><a href="https://github.com/soupslurpr/AppVerifier" target="_blank" class="ext-link" rel="noopener noreferrer">https://github.com/soupslurpr/AppVerifier</a></td>      </tr>      <tr>          <td>Koofr Vault</td>          <td>Cloud storage</td>          <td><a href="https://f-droid.org/en/packages/net.koofr.vault/" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/en/packages/net.koofr.vault/</a></td>      </tr>      <tr>          <td>ElementX</td>          <td>Matrix client</td>          <td><a href="https://f-droid.org/en/packages/io.element.android.x/" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/en/packages/io.element.android.x/</a></td>      </tr>      <tr>          <td>Threema Libre</td>          <td>I.M. app</td>          <td><a href="https://threema.ch/en/faq/libre_installation" target="_blank" class="ext-link" rel="noopener noreferrer">https://threema.ch/en/faq/libre_installation</a></td>      </tr>      <tr>          <td>PuppyGit</td>          <td>Android git client</td>          <td><a href="https://github.com/catpuppyapp/PuppyGit" target="_blank" class="ext-link" rel="noopener noreferrer">https://github.com/catpuppyapp/PuppyGit</a></td>      </tr>      <tr>          <td>KOReader</td>          <td>Ebook reader</td>          <td><a href="https://f-droid.org/en/packages/org.koreader.launcher.fdroid/" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/en/packages/org.koreader.launcher.fdroid/</a></td>      </tr>      <tr>          <td>Pipepipe</td>          <td>Youtube client</td>          <td><a href="https://f-droid.org/en/packages/InfinityLoop1309.NewPipeEnhanced/" target="_blank" class="ext-link" rel="noopener noreferrer">https://f-droid.org/en/packages/InfinityLoop1309.NewPipeEnhanced/</a></td>      </tr>  </tbody></table><h3 id="apps-on-google-play--non-foss">Apps on Google Play / non-FOSS<a href="#apps-on-google-play--non-foss" class="h-anchor" title="Permalink to #Apps on Google Play / non-FOSS"></a></h3>   <table>  <thead>      <tr>          <th>App</th>          <th>Description</th>          <th>Link</th>      </tr>  </thead>  <tbody>      <tr>          <td>Symfonium</td>          <td>Music player (Navidrome compatible)</td>          <td><a href="https://www.symfonium.app/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.symfonium.app/</a></td>      </tr>      <tr>          <td>DeepL</td>          <td>Translation</td>          <td><a href="https://www.deepl.com/en/android-app" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.deepl.com/en/android-app</a></td>      </tr>      <tr>          <td>Obsidian</td>          <td>Notes/PKM</td>          <td><a href="https://obsidian.md/mobile" target="_blank" class="ext-link" rel="noopener noreferrer">https://obsidian.md/mobile</a></td>      </tr>      <tr>          <td>WordWeb</td>          <td>Dictionary</td>          <td><a href="https://www.wordwebsoftware.com/android/WordWeb.html" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.wordwebsoftware.com/android/WordWeb.html</a></td>      </tr>      <tr>          <td>Tresorit</td>          <td>E2EE Cloud storage</td>          <td><a href="https://tresorit.com/download" target="_blank" class="ext-link" rel="noopener noreferrer">https://tresorit.com/download</a></td>      </tr>      <tr>          <td>pCloud</td>          <td>Cloud storage</td>          <td><a href="https://play.google.com/store/apps/details?id=com.pcloud.pcloud" target="_blank" class="ext-link" rel="noopener noreferrer">https://play.google.com/store/apps/details?id=com.pcloud.pcloud</a></td>      </tr>      <tr>          <td>Telegram</td>          <td>Instant messaging</td>          <td><a href="https://play.google.com/store/apps/details?id=org.telegram.messenger" target="_blank" class="ext-link" rel="noopener noreferrer">https://play.google.com/store/apps/details?id=org.telegram.messenger</a></td>      </tr>      <tr>          <td>Signal</td>          <td>Instant messaging</td>          <td><a href="https://play.google.com/store/apps/details?id=org.thoughtcrime.securesms" target="_blank" class="ext-link" rel="noopener noreferrer">https://play.google.com/store/apps/details?id=org.thoughtcrime.securesms</a></td>      </tr>      <tr>          <td>Pear Launcher</td>          <td>Launcher</td>          <td><a href="https://pearlauncher.github.io/about.html" target="_blank" class="ext-link" rel="noopener noreferrer">https://pearlauncher.github.io/about.html</a></td>      </tr>      <tr>          <td>Nova Launcher<sup id="fnref:4"><a href="#fn:4" class="footnote-ref">4</a></sup></td>          <td>Launcher</td>          <td><a href="https://novalauncher.com/" target="_blank" class="ext-link" rel="noopener noreferrer">https://novalauncher.com/</a></td>      </tr>  </tbody></table><h2 id="linking-a-google-account-to-your-android">Linking a Google Account to Your Android<a href="#linking-a-google-account-to-your-android" class="h-anchor" title="Permalink to #Linking a Google Account to Your Android"></a></h2>   <p>Lastly, because I still rely on Google Play to download essential apps like banking and government apps, I suggest linking a new or “bare” Google account to your new Android device. This prevents data aggregation from linking to your existing profile. Also, I keep all Google syncing services off since I have alternatives for everything.</p><p>Hope this helps!</p><p>P.S. Shout-out to <a href="https://social.tchncs.de/@teleclimber" target="_blank" class="ext-link" rel="noopener noreferrer">Olivier</a> for prompting me to write a post about this topic. Follow me on <a href="https://fosstodon.org/@eclecticpassions/" target="_blank" class="ext-link" rel="noopener noreferrer">Mastodon</a> for tech and other musings!</p><div class="footnotes"><hr><ol><li id="fn:1"><p><a href="https://proton.me/blog/european-tech-alternatives" target="_blank" class="ext-link" rel="noopener noreferrer">https://proton.me/blog/european-tech-alternatives</a> <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li><li id="fn:2"><p><a href="https://www.privacyguides.org/articles/2025/03/19/private-european-alternatives/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.privacyguides.org/articles/2025/03/19/private-european-alternatives/</a> <a href="#fnref:2" class="footnote-backref">↩︎</a></p></li><li id="fn:3"><p><a href="https://www.wired.com/story/trump-era-digital-expat/" target="_blank" class="ext-link" rel="noopener noreferrer">https://www.wired.com/story/trump-era-digital-expat/</a> <a href="#fnref:3" class="footnote-backref">↩︎</a></p></li><li id="fn:4"><p>I have been using Nova Launcher since the beginning of my Android journey; but in 2018, they were <a href="https://www.androidpolice.com/nova-launcher-acquired-by-branch/" target="_blank" class="ext-link" rel="noopener noreferrer">acquired by Branch</a>, an analytics company. Since then, I’ve found a similar alternative called <a href="https://pearlauncher.github.io/privacy.html" target="_blank" class="ext-link" rel="noopener noreferrer">Pear Launcher</a> which seems to be privacy-respecting (although not listed as open source) and requires no internet access permission, which is great. I’ve purchased the Pro version to support them. It’s not as stable as Nova but it’ll do for now. <a href="#fnref:4" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/ssh-into-koreader-kobo-ereader-from-macos/</id><link rel="alternate" href="https://burgeonlab.com/blog/ssh-into-koreader-kobo-ereader-from-macos/"/><title type="html">SSH into KOreader (Kobo eReader) from macOS</title><published>2024-08-22T17:43:11Z</published><updated>2024-08-22T17:43:11Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/ssh-into-koreader-kobo-ereader-from-macos/og_img_008.webp"/><summary type="html">Guide to use KOReader on a Kobo eReader with SSH mode to wirelessly transfer ebooks. Includes instructions and config file template.</summary><content type="html"><![CDATA[<h2 id="background">Background<a href="#background" class="h-anchor" title="Permalink to #Background"></a></h2>   <p>I bought my first e-reader (a Kobo Aura One Limited Edition) <a href="https://eclecticpassions.net/blog/refurbished-ereader-kobo-aura-one/" target="_blank" class="ext-link" rel="noopener noreferrer">recently</a> and right away I chose <a href="http://koreader.rocks/" target="_blank" class="ext-link" rel="noopener noreferrer">KOReader</a> as my main software to read and manage my ebooks. KOReader allows for direct USB transfer, Calibre wireless transfer and also SSH. It was easy to use the former two options but I had trouble using SSH to connect to the Kobo using my Mac (on Sonoma 14.6.1) despite following the <a href="https://github.com/koreader/koreader/wiki/SSH" target="_blank" class="ext-link" rel="noopener noreferrer">instructions</a>.</p><h2 id="problem">Problem<a href="#problem" class="h-anchor" title="Permalink to #Problem"></a></h2>   <p>Even with the two “workarounds” listed, I was not able to connect. The two workarounds offered are:</p><ol><li>When scp fails use: <code>cat src_file | ssh -T -x -p 2222 user@ip 'cat > dst_file'</code></li><li>Or use <a href="https://github.com/thomasrebele/lsyncd-workaround" target="_blank" class="ext-link" rel="noopener noreferrer">lysncd-workaround</a> as a scp or rsync replacement for when remote doesn’t support scp or rsync.</li></ol><h2 id="fix">Fix<a href="#fix" class="h-anchor" title="Permalink to #Fix"></a></h2>   <p>It took me some trial and error to get lysncd to work and transfer files over via SSH to the Kobo, but it’s a great alternative to Calibre wireless transfer now that I have it sorted. Here are detailed instructions on how to use lysncd with KOReader SSH mode on a Mac to transfer ebooks (or any other file) to your ereader wirelessly with a CLI.</p><ol><li><p>Install lysncd <code>brew install lsyncd</code>.</p></li><li><p>Make a config file (in lua syntax), call it <code>.no-tty.lsync</code> by <code>touch .no-tty.lsync</code> and then open the file with your preferred text editor - I use VSCodium. Change the language mode to .lua. (I made it a hidden file by adding the dot to the front. It’s up to you.) Paste in the template below, I added some notes to explain the variables.</p></li><li><p>Save the config file in the working directory <code>cd /Users/JohnDoe/Documents/KoboKOReader/.no-tty.lsync</code>.</p></li><li><p>Turn on WiFi and SSH mode (no password if on safe/home network, turn off immediately after done with transfer) in KOReader on the Kobo. Set a unique port number and note down the IP address in the popup window.</p></li><li><p>In your working directory, run <code>sudo lsyncd .no-tty.lsync</code> Remember to type <code>sudo</code> because it doesn’t work and throws an error without it. <code>"Cannot access /dev/fsevents monitor! (1: Operation not permitted)"</code>.</p></li><li><p>If for some reason it is still not working, try giving terminal.app full device access in System Settings > Privacy & Security > Full Disk Access > add Terminal.app. Restart terminal. and repeat step 5.</p></li><li><p>Whatever is in the source directory will be synced over. Once that has completed, click <code>ctrl+c</code> to end the process.</p></li><li><p>Remember to keep WiFi on and do not allow the Kobo to sleep during transfer.</p></li></ol>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua"><span class="line"><span class="ln"> 1</span><span class="cl"></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">settings</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">logfile</span>    <span class="o">=</span> <span class="s2">"/dev/stdout"</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">nodaemon</span>   <span class="o">=</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">-- If you started lsyncd in the foreground (for example, with nodaemon set to true), you can stop it by pressing Ctrl+C in the terminal where it is running. This will send an interrupt signal to lsyncd, causing it to terminate.</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">inotifyMode</span> <span class="o">=</span> <span class="s2">"Polling"</span><span class="p">,</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="p">}</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"> </span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="n">port</span> <span class="o">=</span> <span class="mi">1234</span> <span class="c1">-- Enter port set on KOReader network ssh settings</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="n">remote</span> <span class="o">=</span> <span class="s2">"root@192.169.1.1"</span> <span class="c1">-- Enter ip address for KOReader when you turn on ssh mode</span></span></span><span class="line"><span class="ln">10</span><span class="cl"> </span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="n">ensure_target</span> <span class="o">=</span> <span class="kr">function</span><span class="p">(</span><span class="n">event</span><span class="p">)</span></span></span><span class="line"><span class="ln">12</span><span class="cl">     <span class="kd">local</span> <span class="n">cmd</span> <span class="o">=</span> </span></span><span class="line"><span class="ln">13</span><span class="cl">     <span class="c1">-- connect to server and go to dst dir</span></span></span><span class="line"><span class="ln">14</span><span class="cl">         <span class="s1">'sh -c </span><span class="se">\'</span><span class="s1">ssh -T -x -p '</span><span class="o">..</span><span class="n">port</span><span class="o">..</span><span class="s1">' '</span><span class="o">..</span><span class="n">remote</span><span class="o">..</span><span class="s1">' "sh -c </span><span class="se">\\\"</span><span class="s1">mkdir -p </span><span class="se">\\\\\\\"</span><span class="s1">'</span></span></span><span class="line"><span class="ln">15</span><span class="cl">         <span class="o">..</span><span class="n">event.target</span><span class="o">..</span></span></span><span class="line"><span class="ln">16</span><span class="cl">     <span class="c1">-- write files</span></span></span><span class="line"><span class="ln">17</span><span class="cl">         <span class="s1">'</span><span class="se">\\\\\\\"</span><span class="s1"> </span><span class="se">\\\"</span><span class="s1"> " </span><span class="se">\'</span><span class="s1">'</span></span></span><span class="line"><span class="ln">18</span><span class="cl"> </span></span><span class="line"><span class="ln">19</span><span class="cl">     <span class="n">os.execute</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span></span></span><span class="line"><span class="ln">20</span><span class="cl"> <span class="kr">end</span></span></span><span class="line"><span class="ln">21</span><span class="cl"> </span></span><span class="line"><span class="ln">22</span><span class="cl"> <span class="n">up</span> <span class="o">=</span> <span class="kr">function</span><span class="p">(</span><span class="n">event</span><span class="p">)</span></span></span><span class="line"><span class="ln">23</span><span class="cl">     <span class="kd">local</span> <span class="n">cmd</span> <span class="o">=</span> </span></span><span class="line"><span class="ln">24</span><span class="cl">     <span class="c1">-- go to src dir</span></span></span><span class="line"><span class="ln">25</span><span class="cl">         <span class="s1">'sh -c </span><span class="se">\'</span><span class="s1">cd "'</span></span></span><span class="line"><span class="ln">26</span><span class="cl">         <span class="o">..</span><span class="n">event.source</span><span class="o">..</span></span></span><span class="line"><span class="ln">27</span><span class="cl">     <span class="c1">-- read files</span></span></span><span class="line"><span class="ln">28</span><span class="cl">         <span class="s1">'"; tar cf - "./'</span></span></span><span class="line"><span class="ln">29</span><span class="cl">         <span class="o">..</span><span class="n">event.path</span><span class="o">..</span></span></span><span class="line"><span class="ln">30</span><span class="cl">     <span class="c1">-- connect to server and go to dst dir</span></span></span><span class="line"><span class="ln">31</span><span class="cl">         <span class="s1">'" | ssh -T -x -p '</span><span class="o">..</span><span class="n">port</span><span class="o">..</span><span class="s1">' '</span><span class="o">..</span><span class="n">remote</span><span class="o">..</span><span class="s1">' "sh -c </span><span class="se">\\\"</span><span class="s1">cd </span><span class="se">\\\\\\\"</span><span class="s1">'</span></span></span><span class="line"><span class="ln">32</span><span class="cl">         <span class="o">..</span><span class="n">event.target</span><span class="o">..</span></span></span><span class="line"><span class="ln">33</span><span class="cl">     <span class="c1">-- write files</span></span></span><span class="line"><span class="ln">34</span><span class="cl">         <span class="s1">'</span><span class="se">\\\\\\\"</span><span class="s1">; tar xvf -</span><span class="se">\\\"</span><span class="s1"> " </span><span class="se">\'</span><span class="s1">'</span></span></span><span class="line"><span class="ln">35</span><span class="cl"> </span></span><span class="line"><span class="ln">36</span><span class="cl">     <span class="n">spawnShell</span><span class="p">(</span><span class="n">event</span><span class="p">,</span> <span class="n">cmd</span><span class="p">)</span></span></span><span class="line"><span class="ln">37</span><span class="cl"> <span class="kr">end</span></span></span><span class="line"><span class="ln">38</span><span class="cl"> </span></span><span class="line"><span class="ln">39</span><span class="cl"> </span></span><span class="line"><span class="ln">40</span><span class="cl"> <span class="n">bash</span> <span class="o">=</span> <span class="p">{</span></span></span><span class="line"><span class="ln">41</span><span class="cl">     <span class="n">maxProcesses</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span></span></span><span class="line"><span class="ln">42</span><span class="cl">     <span class="n">onCreate</span> <span class="o">=</span> <span class="n">up</span><span class="p">,</span></span></span><span class="line"><span class="ln">43</span><span class="cl">     <span class="n">onModify</span> <span class="o">=</span> <span class="n">up</span><span class="p">,</span></span></span><span class="line"><span class="ln">44</span><span class="cl">     <span class="n">onDelete</span> <span class="o">=</span> <span class="s2">"echo 'deletion not supported: ^targetPathname'"</span><span class="p">,</span></span></span><span class="line"><span class="ln">45</span><span class="cl">     <span class="n">onMove</span>   <span class="o">=</span> <span class="s2">"mv ^o.targetPathname ^d.targetPathname"</span><span class="p">,</span></span></span><span class="line"><span class="ln">46</span><span class="cl">     <span class="n">onStartup</span> <span class="o">=</span> <span class="kr">function</span><span class="p">(</span><span class="n">event</span><span class="p">)</span></span></span><span class="line"><span class="ln">47</span><span class="cl">         <span class="n">ensure_target</span><span class="p">(</span><span class="n">event</span><span class="p">)</span></span></span><span class="line"><span class="ln">48</span><span class="cl">         <span class="n">up</span><span class="p">(</span><span class="n">event</span><span class="p">)</span></span></span><span class="line"><span class="ln">49</span><span class="cl">     <span class="kr">end</span><span class="p">,</span></span></span><span class="line"><span class="ln">50</span><span class="cl"> <span class="p">}</span></span></span><span class="line"><span class="ln">51</span><span class="cl"> </span></span><span class="line"><span class="ln">52</span><span class="cl"> <span class="n">sync</span><span class="p">{</span><span class="n">bash</span><span class="p">,</span> </span></span><span class="line"><span class="ln">53</span><span class="cl">     <span class="n">source</span><span class="o">=</span><span class="s2">"/Users/JohnDoe/Documents/KoboKOReader/"</span><span class="p">,</span> <span class="c1">-- Files in this directory  will be copied over to the target destination whenever lsycnd is running, ctrl+c to end process</span></span></span><span class="line"><span class="ln">54</span><span class="cl">     <span class="n">target</span><span class="o">=</span><span class="s2">"/mnt/onboard/destination_folder_name"</span><span class="p">,</span> <span class="c1">-- Files from source directory (above) will be copied over to this target destination</span></span></span><span class="line"><span class="ln">55</span><span class="cl"> <span class="p">}</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>Hope this helps! Happy reading with KOReader.</p>                             <a href="/blog/ssh-into-koreader-kobo-ereader-from-macos/kobo_night.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/ssh-into-koreader-kobo-ereader-from-macos/kobo_night.webp" alt="Photo of a Kobo Aura One in night light mode.">    </a>                              <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/fixing-toc-anchor-links-behind-top-navigation-header-in-hugo/</id><link rel="alternate" href="https://burgeonlab.com/blog/fixing-toc-anchor-links-behind-top-navigation-header-in-hugo/"/><title type="html">Fixing TOC Anchor Links Behind Top Navigation Header in Hugo</title><published>2024-04-05T11:51:56Z</published><updated>2024-04-05T11:51:56Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/images/default_post_cover.png"/><summary type="html">How to prevent anchor links from hiding behind the sticky top nav bar using only CSS scroll padding and media queries for responsive screen size changes.</summary><content type="html"><![CDATA[<h2 id="the-problem-at-hand">The Problem At Hand<a href="#the-problem-at-hand" class="h-anchor" title="Permalink to #The Problem At Hand"></a></h2>   <p>My current Hugo theme is <a href="https://github.com/lxndrblz/anatole" target="_blank" class="ext-link" rel="noopener noreferrer">Anatole</a> and I have only been using HTML and CSS to customize the site.</p><p>Today, I will go through a very simple way of adding some padding to the top of page while the top navigation header bar is present (i.e., when using a desktop browser or when the window is wider than 945px). It does not add any visible padding, only functionally, by adding a padding to the top of the page.</p><p>This is to fix the issue of the subheadings hiding behind the sticky header when it is clicked from the anchor links in the table of contents (TOC).</p><h2 id="easiest-fix-with-only-css">Easiest Fix With Only CSS<a href="#easiest-fix-with-only-css" class="h-anchor" title="Permalink to #Easiest Fix With Only CSS"></a></h2>   <p>By adding this css code to your custom.css stylesheet, you can account for the height of the top navigation bar when clicking on the header links from the table of contents.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css"><span class="line"><span class="ln">1</span><span class="cl">  <span class="nt">html</span> <span class="p">{</span></span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="n">scroll-padding-top</span><span class="p">:</span> <span class="mi">8</span><span class="kt">%</span><span class="p">;</span></span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="p">}</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h3 id="a-slightly-more-sophisticated-fix">A Slightly More Sophisticated Fix<a href="#a-slightly-more-sophisticated-fix" class="h-anchor" title="Permalink to #A Slightly More Sophisticated Fix"></a></h3>   <p>This could work if your site doesn’t have a mobile view. But if there is a separate mobile layout for the site, e.g., the top nav bar disappears into a burger side menu or the bar is thinner than the desktop version, we can use @media queries to add breakpoints to have it respond differently on different screen sizes. You can read more about <a href="https://www.w3schools.com/howto/howto_css_media_query_breakpoints.asp" target="_blank" class="ext-link" rel="noopener noreferrer">typical device breakpoints here</a>.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">@</span><span class="k">media</span> <span class="nt">only</span> <span class="nt">screen</span> <span class="nt">and</span> <span class="o">(</span><span class="nt">max-width</span><span class="o">:</span> <span class="nt">946px</span><span class="o">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="nt">html</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">scroll-padding-top</span><span class="p">:</span> <span class="mi">2</span><span class="kt">%</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="p">}</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="p">@</span><span class="k">media</span> <span class="nt">only</span> <span class="nt">screen</span> <span class="nt">and</span> <span class="o">(</span><span class="nt">min-width</span><span class="o">:</span> <span class="nt">946px</span><span class="o">)</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="nt">html</span> <span class="p">{</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">scroll-padding-top</span><span class="p">:</span> <span class="mi">8</span><span class="kt">%</span><span class="p">;</span></span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="p">}</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">}</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>On my current theme, when the window goes below 945px, the top navigation bar disappears. If I didn’t add these @media queries, whenever the site is on mobile view, the TOC links will jump lower than expected as the bar is not in the way. Hence it is set at 2% only when it is on mobile/smaller windows.</p><p>I hope this is easy enough to understand! I am glad I didn’t need to mess with anchor classes or id as some other blogs recommended.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup><sup id="fnref:2"><a href="#fn:2" class="footnote-ref">2</a></sup></p><div class="footnotes"><hr><ol><li id="fn:1"><p><a href="https://calvinke.com/seo/fixed-header-anchor-css/" target="_blank" class="ext-link" rel="noopener noreferrer">https://calvinke.com/seo/fixed-header-anchor-css/</a> <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li><li id="fn:2"><p><a href="https://getpublii.com/blog/one-line-css-solution-to-prevent-anchor-links-from-scrolling-behind-a-sticky-header.html" target="_blank" class="ext-link" rel="noopener noreferrer">https://getpublii.com/blog/one-line-css-solution-to-prevent-anchor-links-from-scrolling-behind-a-sticky-header.html</a> <a href="#fnref:2" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/customizing-neofetch-load-weather-data-faster/</id><link rel="alternate" href="https://burgeonlab.com/blog/customizing-neofetch-load-weather-data-faster/"/><title type="html">Customizing Neofetch: Load Weather Data Faster</title><published>2024-03-30T14:34:25Z</published><updated>2024-03-30T14:34:25Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/customizing-neofetch-load-weather-data-faster/terminal.webp"/><summary type="html">Customize Neofetch to display weather data faster. Using a BASH script to update weather hourly, significantly reduces delays when running Neofetch frequently.</summary><content type="html"><![CDATA[<h2 id="my-terminal-setup">My Terminal Setup<a href="#my-terminal-setup" class="h-anchor" title="Permalink to #My Terminal Setup"></a></h2>                                <a href="/blog/customizing-neofetch-load-weather-data-faster/terminal.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/customizing-neofetch-load-weather-data-faster/terminal.webp" alt="screenshot of a macos terminal window with neofetch loaded, running an omz theme.">    </a>       <p>I customized my MacOS terminal with <a href="https://ohmyz.sh/" target="_blank" class="ext-link" rel="noopener noreferrer">OMZ</a>. In the process, I learnt an awful lot about the ins and outs of config files, navigating a project’s <a href="https://github.com/ohmyzsh/ohmyzsh" target="_blank" class="ext-link" rel="noopener noreferrer">GitHub page</a> and how to use <a href="https://www.nano-editor.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Nano</a> (but eventually switching to using <a href="https://vscodium.com/" target="_blank" class="ext-link" rel="noopener noreferrer">VSCodium</a> as my main text editor).</p><p>For the colour theme, <a href="https://github.com/lysyi3m/macos-terminal-themes" target="_blank" class="ext-link" rel="noopener noreferrer">this GitHub page</a> has a lot to choose from for the default macOS Terminal app.</p><h2 id="neofetch">Neofetch<a href="#neofetch" class="h-anchor" title="Permalink to #Neofetch"></a></h2>   <p>While I was looking for cool terminal setups for inspiration, I came across this popular command-line tool called <a href="https://github.com/dylanaraps/neofetch" target="_blank" class="ext-link" rel="noopener noreferrer">Neofetch</a>. It shows a summary of the machine specs with a ASCII logo of the current OS (or any other custom ASCII).</p><p>I have made customizations using the config file, which is located in <code>${HOME}/.config/Neofetch/config.conf</code>, to display only a few of the defaults, e.g. OS version, RAM, CPU usage and uptime. <em>(My config.conf file is at the end of the post for reference.)</em></p><h3 id="displaying-the-current-weather">Displaying the Current Weather<a href="#displaying-the-current-weather" class="h-anchor" title="Permalink to #Displaying the Current Weather"></a></h3>   <p>The most interesting feature for me is to see the current weather. Originally, I have followed the official documentation to add a one-liner to load the weather of my current location via wttr.in.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">1</span><span class="cl">prin <span class="s2">"Weather"</span> <span class="s2">"</span><span class="k">$(</span>curl wttr.in/?0?q?T <span class="p">|</span> awk <span class="s1">'/°(C|F)/ {printf $(NF-1) $(NF) " ("a")"} /,/ {a=$0}'</span><span class="k">)</span><span class="s2">"</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h3 id="delay-in-loading">Delay in Loading<a href="#delay-in-loading" class="h-anchor" title="Permalink to #Delay in Loading"></a></h3>   <p>But because I have set Neofetch to run on every new terminal instance, I noticed how there’s a significant delay each time it tried fetching the current weather condition. I googled to see if there was anyone having similar issues and I found a guy called <a href="https://www.adamsdesk.com/posts/neofetch-weather-data-caching/" target="_blank" class="ext-link" rel="noopener noreferrer">Adam</a> with the same problem. He wrote a BASH script that would cache the weather forecast hourly in a .txt file, so Neofetch wouldn’t try to fetch the data constantly.</p>                             <a href="/blog/customizing-neofetch-load-weather-data-faster/adams-code.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/customizing-neofetch-load-weather-data-faster/adams-code.webp" alt="Screenshot of Adamsdesk's BASH script">    </a>       <p>Caption: <em>Screenshot of Adam’s script.</em></p><p>Unfortunately, the code he shared didn’t work for me. It only worked on the first run, i.e., when the cached .txt file didn’t exist. Once it was present with the current weather data, it wasn’t able to update after the chosen duration has passed (e.g.set to one hour or 3600s).</p><h3 id="modified-bash-script-to-cache-weather-data">Modified BASH Script to Cache Weather Data<a href="#modified-bash-script-to-cache-weather-data" class="h-anchor" title="Permalink to #Modified BASH Script to Cache Weather Data"></a></h3>   <p>I therefore modified his code to add a last retrieval time.txt in the process; using that to calculate the time difference instead of the last modified time of the cached weather.txt file. I also changed the refresh rate to 7200 (two hours).</p><p>The modified code is as follows:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="o">}</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl">getWeather<span class="o">()</span> <span class="o">{</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="nv">weather_params</span><span class="o">=</span><span class="s2">"tai+po?format="</span>%c+%C+%t+<span class="s2">"("</span>%f<span class="s2">")"</span>+%h<span class="s2">"RH"""</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nv">data_path</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.config/neofetch/data-weather.txt"</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="nv">last_retrieval_path</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.config/neofetch/last-retrieval.txt"</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="nv">duration</span><span class="o">=</span><span class="m">7200</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"></span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="c1"># Check if the last retrieval time file exists</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">if</span> <span class="o">[[</span> -e <span class="s2">"</span><span class="nv">$last_retrieval_path</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span></span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="nv">last_retrieval</span><span class="o">=</span><span class="k">$(</span>cat <span class="s2">"</span><span class="nv">$last_retrieval_path</span><span class="s2">"</span><span class="k">)</span></span></span><span class="line"><span class="ln">11</span><span class="cl"></span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="c1"># Calculate the time elapsed since the last retrieval</span></span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="nv">current_time</span><span class="o">=</span><span class="k">$(</span>date +%s<span class="k">)</span></span></span><span class="line"><span class="ln">14</span><span class="cl">        <span class="nv">time_elapsed</span><span class="o">=</span><span class="k">$((</span>current_time <span class="o">-</span> last_retrieval<span class="k">))</span></span></span><span class="line"><span class="ln">15</span><span class="cl"></span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="c1"># Compare the time elapsed with the duration</span></span></span><span class="line"><span class="ln">17</span><span class="cl">        <span class="k">if</span> <span class="o">((</span>time_elapsed ><span class="o">=</span> duration<span class="o">))</span><span class="p">;</span> <span class="k">then</span></span></span><span class="line"><span class="ln">18</span><span class="cl">            getWeatherData</span></span><span class="line"><span class="ln">19</span><span class="cl">            setWeatherData</span></span><span class="line"><span class="ln">20</span><span class="cl">            <span class="nb">echo</span> <span class="s2">"</span><span class="nv">$current_time</span><span class="s2">"</span> > <span class="s2">"</span><span class="nv">$last_retrieval_path</span><span class="s2">"</span></span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="k">else</span></span></span><span class="line"><span class="ln">22</span><span class="cl">            setWeatherData</span></span><span class="line"><span class="ln">23</span><span class="cl">        <span class="k">fi</span></span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="k">else</span></span></span><span class="line"><span class="ln">25</span><span class="cl">        getWeatherData</span></span><span class="line"><span class="ln">26</span><span class="cl">        setWeatherData</span></span><span class="line"><span class="ln">27</span><span class="cl">        <span class="nb">echo</span> <span class="s2">"</span><span class="k">$(</span>date +%s<span class="k">)</span><span class="s2">"</span> > <span class="s2">"</span><span class="nv">$last_retrieval_path</span><span class="s2">"</span></span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="k">fi</span></span></span><span class="line"><span class="ln">29</span><span class="cl"></span></span><span class="line"><span class="ln">30</span><span class="cl">    showWeatherData</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="o">}</span></span></span><span class="line"><span class="ln">32</span><span class="cl">setWeatherData<span class="o">()</span> <span class="o">{</span></span></span><span class="line"><span class="ln">33</span><span class="cl">    <span class="nv">weather_data</span><span class="o">=</span><span class="k">$(</span>cat <span class="s2">"</span><span class="nv">$data_path</span><span class="s2">"</span><span class="k">)</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="o">}</span></span></span><span class="line"><span class="ln">35</span><span class="cl">getWeatherData<span class="o">()</span> <span class="o">{</span></span></span><span class="line"><span class="ln">36</span><span class="cl">    curl -s <span class="s2">"https://wttr.in/</span><span class="nv">$weather_params</span><span class="s2">"</span> > <span class="s2">"</span><span class="nv">$data_path</span><span class="s2">"</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="o">}</span></span></span><span class="line"><span class="ln">38</span><span class="cl">showWeatherData<span class="o">()</span> <span class="o">{</span></span></span><span class="line"><span class="ln">39</span><span class="cl">    prin <span class="s2">"Weather: </span><span class="nv">$weather_data</span><span class="s2">"</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="o">}</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>Happy to say it’s all working fine now and loading much quicker than before. Hope this helps!</p><p>Shoutout to Adam Douglas at <a href="https://www.adamsdesk.com/" target="_blank" class="ext-link" rel="noopener noreferrer">adamsdesk</a>!</p><h2 id="addendum">Addendum<a href="#addendum" class="h-anchor" title="Permalink to #Addendum"></a></h2>   <h3 id="neofetchconfigconf">neofetch/config.conf<a href="#neofetchconfigconf" class="h-anchor" title="Permalink to #neofetch/config.conf"></a></h3>          <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">  1</span><span class="cl">print_info<span class="o">()</span> <span class="o">{</span></span></span><span class="line"><span class="ln">  2</span><span class="cl">    info <span class="s2">"OS"</span> distro</span></span><span class="line"><span class="ln">  3</span><span class="cl">    info <span class="s2">"Host"</span> model</span></span><span class="line"><span class="ln">  4</span><span class="cl">    info <span class="s2">"CPU"</span> cpu</span></span><span class="line"><span class="ln">  5</span><span class="cl">    info <span class="s2">"Shell"</span> shell</span></span><span class="line"><span class="ln">  6</span><span class="cl">    info <span class="s2">"Uptime"</span> uptime</span></span><span class="line"><span class="ln">  7</span><span class="cl">    info <span class="s2">"Packages"</span> packages</span></span><span class="line"><span class="ln">  8</span><span class="cl">    info <span class="s2">"Terminal"</span> term</span></span><span class="line"><span class="ln">  9</span><span class="cl">    info underline </span></span><span class="line"><span class="ln"> 10</span><span class="cl">    info <span class="s2">"CPU Usage"</span> cpu_usage </span></span><span class="line"><span class="ln"> 11</span><span class="cl">    info <span class="s2">"Memory"</span> memory </span></span><span class="line"><span class="ln"> 12</span><span class="cl">    info <span class="s2">"Font"</span> font</span></span><span class="line"><span class="ln"> 13</span><span class="cl">    info <span class="s2">"Local IP"</span> local_ip</span></span><span class="line"><span class="ln"> 14</span><span class="cl">    info underline</span></span><span class="line"><span class="ln"> 15</span><span class="cl">    prin <span class="s2">"Date: </span><span class="k">$(</span>date <span class="s1">'+%a %Y-%m-%d'</span><span class="k">)</span><span class="s2"> UTC+8"</span></span></span><span class="line"><span class="ln"> 16</span><span class="cl">    getWeather</span></span><span class="line"><span class="ln"> 17</span><span class="cl">    info cols</span></span><span class="line"><span class="ln"> 18</span><span class="cl"><span class="o">}</span></span></span><span class="line"><span class="ln"> 19</span><span class="cl"></span></span><span class="line"><span class="ln"> 20</span><span class="cl">getWeather<span class="o">()</span> <span class="o">{</span></span></span><span class="line"><span class="ln"> 21</span><span class="cl">    <span class="nv">weather_params</span><span class="o">=</span><span class="s2">"tai+po?format="</span>%c+%C+%t+<span class="s2">"("</span>%f<span class="s2">")"</span>+%h<span class="s2">"RH"""</span></span></span><span class="line"><span class="ln"> 22</span><span class="cl">    <span class="nv">data_path</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.config/neofetch/data-weather.txt"</span></span></span><span class="line"><span class="ln"> 23</span><span class="cl">    <span class="nv">last_retrieval_path</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.config/neofetch/last-retrieval.txt"</span></span></span><span class="line"><span class="ln"> 24</span><span class="cl">    <span class="nv">duration</span><span class="o">=</span><span class="m">7200</span></span></span><span class="line"><span class="ln"> 25</span><span class="cl"></span></span><span class="line"><span class="ln"> 26</span><span class="cl">    <span class="c1"># Check if the last retrieval time file exists</span></span></span><span class="line"><span class="ln"> 27</span><span class="cl">    <span class="k">if</span> <span class="o">[[</span> -e <span class="s2">"</span><span class="nv">$last_retrieval_path</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span></span></span><span class="line"><span class="ln"> 28</span><span class="cl">        <span class="nv">last_retrieval</span><span class="o">=</span><span class="k">$(</span>cat <span class="s2">"</span><span class="nv">$last_retrieval_path</span><span class="s2">"</span><span class="k">)</span></span></span><span class="line"><span class="ln"> 29</span><span class="cl"></span></span><span class="line"><span class="ln"> 30</span><span class="cl">        <span class="c1"># Calculate the time elapsed since the last retrieval</span></span></span><span class="line"><span class="ln"> 31</span><span class="cl">        <span class="nv">current_time</span><span class="o">=</span><span class="k">$(</span>date +%s<span class="k">)</span></span></span><span class="line"><span class="ln"> 32</span><span class="cl">        <span class="nv">time_elapsed</span><span class="o">=</span><span class="k">$((</span>current_time <span class="o">-</span> last_retrieval<span class="k">))</span></span></span><span class="line"><span class="ln"> 33</span><span class="cl"></span></span><span class="line"><span class="ln"> 34</span><span class="cl">        <span class="c1"># Compare the time elapsed with the duration</span></span></span><span class="line"><span class="ln"> 35</span><span class="cl">        <span class="k">if</span> <span class="o">((</span>time_elapsed ><span class="o">=</span> duration<span class="o">))</span><span class="p">;</span> <span class="k">then</span></span></span><span class="line"><span class="ln"> 36</span><span class="cl">            getWeatherData</span></span><span class="line"><span class="ln"> 37</span><span class="cl">            setWeatherData</span></span><span class="line"><span class="ln"> 38</span><span class="cl">            <span class="nb">echo</span> <span class="s2">"</span><span class="nv">$current_time</span><span class="s2">"</span> > <span class="s2">"</span><span class="nv">$last_retrieval_path</span><span class="s2">"</span></span></span><span class="line"><span class="ln"> 39</span><span class="cl">        <span class="k">else</span></span></span><span class="line"><span class="ln"> 40</span><span class="cl">            setWeatherData</span></span><span class="line"><span class="ln"> 41</span><span class="cl">        <span class="k">fi</span></span></span><span class="line"><span class="ln"> 42</span><span class="cl">    <span class="k">else</span></span></span><span class="line"><span class="ln"> 43</span><span class="cl">        getWeatherData</span></span><span class="line"><span class="ln"> 44</span><span class="cl">        setWeatherData</span></span><span class="line"><span class="ln"> 45</span><span class="cl">        <span class="nb">echo</span> <span class="s2">"</span><span class="k">$(</span>date +%s<span class="k">)</span><span class="s2">"</span> > <span class="s2">"</span><span class="nv">$last_retrieval_path</span><span class="s2">"</span></span></span><span class="line"><span class="ln"> 46</span><span class="cl">    <span class="k">fi</span></span></span><span class="line"><span class="ln"> 47</span><span class="cl"></span></span><span class="line"><span class="ln"> 48</span><span class="cl">    showWeatherData</span></span><span class="line"><span class="ln"> 49</span><span class="cl"><span class="o">}</span></span></span><span class="line"><span class="ln"> 50</span><span class="cl">setWeatherData<span class="o">()</span> <span class="o">{</span></span></span><span class="line"><span class="ln"> 51</span><span class="cl">    <span class="nv">weather_data</span><span class="o">=</span><span class="k">$(</span>cat <span class="s2">"</span><span class="nv">$data_path</span><span class="s2">"</span><span class="k">)</span></span></span><span class="line"><span class="ln"> 52</span><span class="cl"><span class="o">}</span></span></span><span class="line"><span class="ln"> 53</span><span class="cl">getWeatherData<span class="o">()</span> <span class="o">{</span></span></span><span class="line"><span class="ln"> 54</span><span class="cl">    curl -s <span class="s2">"https://wttr.in/</span><span class="nv">$weather_params</span><span class="s2">"</span> > <span class="s2">"</span><span class="nv">$data_path</span><span class="s2">"</span></span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="o">}</span></span></span><span class="line"><span class="ln"> 56</span><span class="cl">showWeatherData<span class="o">()</span> <span class="o">{</span></span></span><span class="line"><span class="ln"> 57</span><span class="cl">    prin <span class="s2">"Weather: </span><span class="nv">$weather_data</span><span class="s2">"</span></span></span><span class="line"><span class="ln"> 58</span><span class="cl"><span class="o">}</span></span></span><span class="line"><span class="ln"> 59</span><span class="cl"></span></span><span class="line"><span class="ln"> 60</span><span class="cl"><span class="c1"># Title</span></span></span><span class="line"><span class="ln"> 61</span><span class="cl"></span></span><span class="line"><span class="ln"> 62</span><span class="cl"><span class="c1"># Hide/Show Fully qualified domain name.</span></span></span><span class="line"><span class="ln"> 63</span><span class="cl"><span class="c1">#</span></span></span><span class="line"><span class="ln"> 64</span><span class="cl"><span class="c1"># Default:  'off'</span></span></span><span class="line"><span class="ln"> 65</span><span class="cl"><span class="c1"># Values:   'on', 'off'</span></span></span><span class="line"><span class="ln"> 66</span><span class="cl"><span class="c1"># Flag:     --title_fqdn</span></span></span><span class="line"><span class="ln"> 67</span><span class="cl"><span class="nv">title_fqdn</span><span class="o">=</span><span class="s2">"off"</span></span></span><span class="line"><span class="ln"> 68</span><span class="cl"></span></span><span class="line"><span class="ln"> 69</span><span class="cl"><span class="c1"># Kernel</span></span></span><span class="line"><span class="ln"> 70</span><span class="cl"></span></span><span class="line"><span class="ln"> 71</span><span class="cl"><span class="c1"># Shorten the output of the kernel function.</span></span></span><span class="line"><span class="ln"> 72</span><span class="cl"></span></span><span class="line"><span class="ln"> 73</span><span class="cl"><span class="c1"># Default:  'on'</span></span></span><span class="line"><span class="ln"> 74</span><span class="cl"><span class="c1"># Values:   'on', 'off'</span></span></span><span class="line"><span class="ln"> 75</span><span class="cl"><span class="c1"># Flag:     --kernel_shorthand</span></span></span><span class="line"><span class="ln"> 76</span><span class="cl"><span class="c1"># Supports: Everything except *BSDs (except PacBSD and PC-BSD)</span></span></span><span class="line"><span class="ln"> 77</span><span class="cl"></span></span><span class="line"><span class="ln"> 78</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln"> 79</span><span class="cl"><span class="c1"># on:  '4.8.9-1-ARCH'</span></span></span><span class="line"><span class="ln"> 80</span><span class="cl"><span class="c1"># off: 'Linux 4.8.9-1-ARCH'</span></span></span><span class="line"><span class="ln"> 81</span><span class="cl"><span class="nv">kernel_shorthand</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln"> 82</span><span class="cl"></span></span><span class="line"><span class="ln"> 83</span><span class="cl"><span class="c1"># Distro</span></span></span><span class="line"><span class="ln"> 84</span><span class="cl"></span></span><span class="line"><span class="ln"> 85</span><span class="cl"><span class="c1"># Shorten the output of the distro function</span></span></span><span class="line"><span class="ln"> 86</span><span class="cl"></span></span><span class="line"><span class="ln"> 87</span><span class="cl"><span class="c1"># Default:  'off'</span></span></span><span class="line"><span class="ln"> 88</span><span class="cl"><span class="c1"># Values:   'on', 'tiny', 'off'</span></span></span><span class="line"><span class="ln"> 89</span><span class="cl"><span class="c1"># Flag:     --distro_shorthand</span></span></span><span class="line"><span class="ln"> 90</span><span class="cl"><span class="c1"># Supports: Everything except Windows and Haiku</span></span></span><span class="line"><span class="ln"> 91</span><span class="cl"><span class="nv">distro_shorthand</span><span class="o">=</span><span class="s2">"tiny"</span></span></span><span class="line"><span class="ln"> 92</span><span class="cl"></span></span><span class="line"><span class="ln"> 93</span><span class="cl"><span class="c1"># Show/Hide OS Architecture.</span></span></span><span class="line"><span class="ln"> 94</span><span class="cl"><span class="c1"># Show 'x86_64', 'x86' and etc in 'Distro:' output.</span></span></span><span class="line"><span class="ln"> 95</span><span class="cl"></span></span><span class="line"><span class="ln"> 96</span><span class="cl"><span class="c1"># Default: 'on'</span></span></span><span class="line"><span class="ln"> 97</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln"> 98</span><span class="cl"><span class="c1"># Flag:    --os_arch</span></span></span><span class="line"><span class="ln"> 99</span><span class="cl"></span></span><span class="line"><span class="ln">100</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">101</span><span class="cl"><span class="c1"># on:  'Arch Linux x86_64'</span></span></span><span class="line"><span class="ln">102</span><span class="cl"><span class="c1"># off: 'Arch Linux'</span></span></span><span class="line"><span class="ln">103</span><span class="cl"><span class="nv">os_arch</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">104</span><span class="cl"></span></span><span class="line"><span class="ln">105</span><span class="cl"><span class="c1"># Uptime</span></span></span><span class="line"><span class="ln">106</span><span class="cl"></span></span><span class="line"><span class="ln">107</span><span class="cl"><span class="c1"># Shorten the output of the uptime function</span></span></span><span class="line"><span class="ln">108</span><span class="cl"></span></span><span class="line"><span class="ln">109</span><span class="cl"><span class="c1"># Default: 'on'</span></span></span><span class="line"><span class="ln">110</span><span class="cl"><span class="c1"># Values:  'on', 'tiny', 'off'</span></span></span><span class="line"><span class="ln">111</span><span class="cl"><span class="c1"># Flag:    --uptime_shorthand</span></span></span><span class="line"><span class="ln">112</span><span class="cl"></span></span><span class="line"><span class="ln">113</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">114</span><span class="cl"><span class="c1"># on:   '2 days, 10 hours, 3 mins'</span></span></span><span class="line"><span class="ln">115</span><span class="cl"><span class="c1"># tiny: '2d 10h 3m'</span></span></span><span class="line"><span class="ln">116</span><span class="cl"><span class="c1"># off:  '2 days, 10 hours, 3 minutes'</span></span></span><span class="line"><span class="ln">117</span><span class="cl"><span class="nv">uptime_shorthand</span><span class="o">=</span><span class="s2">"tiny"</span></span></span><span class="line"><span class="ln">118</span><span class="cl"></span></span><span class="line"><span class="ln">119</span><span class="cl"><span class="c1"># Memory</span></span></span><span class="line"><span class="ln">120</span><span class="cl"></span></span><span class="line"><span class="ln">121</span><span class="cl"><span class="c1"># Show memory pecentage in output.</span></span></span><span class="line"><span class="ln">122</span><span class="cl"></span></span><span class="line"><span class="ln">123</span><span class="cl"><span class="c1"># Default: 'off'</span></span></span><span class="line"><span class="ln">124</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">125</span><span class="cl"><span class="c1"># Flag:    --memory_percent</span></span></span><span class="line"><span class="ln">126</span><span class="cl"></span></span><span class="line"><span class="ln">127</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">128</span><span class="cl"><span class="c1"># on:   '1801MiB / 7881MiB (22%)'</span></span></span><span class="line"><span class="ln">129</span><span class="cl"><span class="c1"># off:  '1801MiB / 7881MiB'</span></span></span><span class="line"><span class="ln">130</span><span class="cl"><span class="nv">memory_percent</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">131</span><span class="cl"></span></span><span class="line"><span class="ln">132</span><span class="cl"><span class="c1"># Change memory output unit.</span></span></span><span class="line"><span class="ln">133</span><span class="cl"></span></span><span class="line"><span class="ln">134</span><span class="cl"><span class="c1"># Default: 'mib'</span></span></span><span class="line"><span class="ln">135</span><span class="cl"><span class="c1"># Values:  'kib', 'mib', 'gib'</span></span></span><span class="line"><span class="ln">136</span><span class="cl"><span class="c1"># Flag:    --memory_unit</span></span></span><span class="line"><span class="ln">137</span><span class="cl"></span></span><span class="line"><span class="ln">138</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">139</span><span class="cl"><span class="c1"># kib  '1020928KiB / 7117824KiB'</span></span></span><span class="line"><span class="ln">140</span><span class="cl"><span class="c1"># mib  '1042MiB / 6951MiB'</span></span></span><span class="line"><span class="ln">141</span><span class="cl"><span class="c1"># gib: ' 0.98GiB / 6.79GiB'</span></span></span><span class="line"><span class="ln">142</span><span class="cl"><span class="nv">memory_unit</span><span class="o">=</span><span class="s2">"gib"</span></span></span><span class="line"><span class="ln">143</span><span class="cl"></span></span><span class="line"><span class="ln">144</span><span class="cl"><span class="c1"># Packages</span></span></span><span class="line"><span class="ln">145</span><span class="cl"></span></span><span class="line"><span class="ln">146</span><span class="cl"><span class="c1"># Show/Hide Package Manager names.</span></span></span><span class="line"><span class="ln">147</span><span class="cl"></span></span><span class="line"><span class="ln">148</span><span class="cl"><span class="c1"># Default: 'tiny'</span></span></span><span class="line"><span class="ln">149</span><span class="cl"><span class="c1"># Values:  'on', 'tiny' 'off'</span></span></span><span class="line"><span class="ln">150</span><span class="cl"><span class="c1"># Flag:    --package_managers</span></span></span><span class="line"><span class="ln">151</span><span class="cl"></span></span><span class="line"><span class="ln">152</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">153</span><span class="cl"><span class="c1"># on:   '998 (pacman), 8 (flatpak), 4 (snap)'</span></span></span><span class="line"><span class="ln">154</span><span class="cl"><span class="c1"># tiny: '908 (pacman, flatpak, snap)'</span></span></span><span class="line"><span class="ln">155</span><span class="cl"><span class="c1"># off:  '908'</span></span></span><span class="line"><span class="ln">156</span><span class="cl"><span class="nv">package_managers</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">157</span><span class="cl"></span></span><span class="line"><span class="ln">158</span><span class="cl"><span class="c1"># Shell</span></span></span><span class="line"><span class="ln">159</span><span class="cl"></span></span><span class="line"><span class="ln">160</span><span class="cl"><span class="c1"># Show the path to $SHELL</span></span></span><span class="line"><span class="ln">161</span><span class="cl"></span></span><span class="line"><span class="ln">162</span><span class="cl"><span class="c1"># Default: 'off'</span></span></span><span class="line"><span class="ln">163</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">164</span><span class="cl"><span class="c1"># Flag:    --shell_path</span></span></span><span class="line"><span class="ln">165</span><span class="cl"></span></span><span class="line"><span class="ln">166</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">167</span><span class="cl"><span class="c1"># on:  '/bin/bash'</span></span></span><span class="line"><span class="ln">168</span><span class="cl"><span class="c1"># off: 'bash'</span></span></span><span class="line"><span class="ln">169</span><span class="cl"><span class="nv">shell_path</span><span class="o">=</span><span class="s2">"off"</span></span></span><span class="line"><span class="ln">170</span><span class="cl"></span></span><span class="line"><span class="ln">171</span><span class="cl"><span class="c1"># Show $SHELL version</span></span></span><span class="line"><span class="ln">172</span><span class="cl"></span></span><span class="line"><span class="ln">173</span><span class="cl"><span class="c1"># Default: 'on'</span></span></span><span class="line"><span class="ln">174</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">175</span><span class="cl"><span class="c1"># Flag:    --shell_version</span></span></span><span class="line"><span class="ln">176</span><span class="cl"></span></span><span class="line"><span class="ln">177</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">178</span><span class="cl"><span class="c1"># on:  'bash 4.4.5'</span></span></span><span class="line"><span class="ln">179</span><span class="cl"><span class="c1"># off: 'bash'</span></span></span><span class="line"><span class="ln">180</span><span class="cl"><span class="nv">shell_version</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">181</span><span class="cl"></span></span><span class="line"><span class="ln">182</span><span class="cl"><span class="c1"># CPU</span></span></span><span class="line"><span class="ln">183</span><span class="cl"></span></span><span class="line"><span class="ln">184</span><span class="cl"><span class="c1"># CPU speed type</span></span></span><span class="line"><span class="ln">185</span><span class="cl"></span></span><span class="line"><span class="ln">186</span><span class="cl"><span class="c1"># Default: 'bios_limit'</span></span></span><span class="line"><span class="ln">187</span><span class="cl"><span class="c1"># Values: 'scaling_cur_freq', 'scaling_min_freq', 'scaling_max_freq', 'bios_limit'.</span></span></span><span class="line"><span class="ln">188</span><span class="cl"><span class="c1"># Flag:    --speed_type</span></span></span><span class="line"><span class="ln">189</span><span class="cl"><span class="c1"># Supports: Linux with 'cpufreq'</span></span></span><span class="line"><span class="ln">190</span><span class="cl"><span class="c1"># NOTE: Any file in '/sys/devices/system/cpu/cpu0/cpufreq' can be used as a value.</span></span></span><span class="line"><span class="ln">191</span><span class="cl"><span class="nv">speed_type</span><span class="o">=</span><span class="s2">"bios_limit"</span></span></span><span class="line"><span class="ln">192</span><span class="cl"></span></span><span class="line"><span class="ln">193</span><span class="cl"><span class="c1"># CPU speed shorthand</span></span></span><span class="line"><span class="ln">194</span><span class="cl"></span></span><span class="line"><span class="ln">195</span><span class="cl"><span class="c1"># Default: 'off'</span></span></span><span class="line"><span class="ln">196</span><span class="cl"><span class="c1"># Values: 'on', 'off'.</span></span></span><span class="line"><span class="ln">197</span><span class="cl"><span class="c1"># Flag:    --speed_shorthand</span></span></span><span class="line"><span class="ln">198</span><span class="cl"><span class="c1"># NOTE: This flag is not supported in systems with CPU speed less than 1 GHz</span></span></span><span class="line"><span class="ln">199</span><span class="cl"></span></span><span class="line"><span class="ln">200</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">201</span><span class="cl"><span class="c1"># on:    'i7-6500U (4) @ 3.1GHz'</span></span></span><span class="line"><span class="ln">202</span><span class="cl"><span class="c1"># off:   'i7-6500U (4) @ 3.100GHz'</span></span></span><span class="line"><span class="ln">203</span><span class="cl"><span class="nv">speed_shorthand</span><span class="o">=</span><span class="s2">"off"</span></span></span><span class="line"><span class="ln">204</span><span class="cl"></span></span><span class="line"><span class="ln">205</span><span class="cl"><span class="c1"># Enable/Disable CPU brand in output.</span></span></span><span class="line"><span class="ln">206</span><span class="cl"></span></span><span class="line"><span class="ln">207</span><span class="cl"><span class="c1"># Default: 'on'</span></span></span><span class="line"><span class="ln">208</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">209</span><span class="cl"><span class="c1"># Flag:    --cpu_brand</span></span></span><span class="line"><span class="ln">210</span><span class="cl"></span></span><span class="line"><span class="ln">211</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">212</span><span class="cl"><span class="c1"># on:   'Intel i7-6500U'</span></span></span><span class="line"><span class="ln">213</span><span class="cl"><span class="c1"># off:  'i7-6500U (4)'</span></span></span><span class="line"><span class="ln">214</span><span class="cl"><span class="nv">cpu_brand</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">215</span><span class="cl"></span></span><span class="line"><span class="ln">216</span><span class="cl"><span class="c1"># CPU Speed</span></span></span><span class="line"><span class="ln">217</span><span class="cl"><span class="c1"># Hide/Show CPU speed.</span></span></span><span class="line"><span class="ln">218</span><span class="cl"></span></span><span class="line"><span class="ln">219</span><span class="cl"><span class="c1"># Default: 'on'</span></span></span><span class="line"><span class="ln">220</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">221</span><span class="cl"><span class="c1"># Flag:    --cpu_speed</span></span></span><span class="line"><span class="ln">222</span><span class="cl"></span></span><span class="line"><span class="ln">223</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">224</span><span class="cl"><span class="c1"># on:  'Intel i7-6500U (4) @ 3.1GHz'</span></span></span><span class="line"><span class="ln">225</span><span class="cl"><span class="c1"># off: 'Intel i7-6500U (4)'</span></span></span><span class="line"><span class="ln">226</span><span class="cl"><span class="nv">cpu_speed</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">227</span><span class="cl"></span></span><span class="line"><span class="ln">228</span><span class="cl"><span class="c1"># CPU Cores</span></span></span><span class="line"><span class="ln">229</span><span class="cl"><span class="c1"># Display CPU cores in output</span></span></span><span class="line"><span class="ln">230</span><span class="cl"></span></span><span class="line"><span class="ln">231</span><span class="cl"><span class="c1"># Default: 'logical'</span></span></span><span class="line"><span class="ln">232</span><span class="cl"><span class="c1"># Values:  'logical', 'physical', 'off'</span></span></span><span class="line"><span class="ln">233</span><span class="cl"><span class="c1"># Flag:    --cpu_cores</span></span></span><span class="line"><span class="ln">234</span><span class="cl"><span class="c1"># Support: 'physical' doesn't work on BSD.</span></span></span><span class="line"><span class="ln">235</span><span class="cl"></span></span><span class="line"><span class="ln">236</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">237</span><span class="cl"><span class="c1"># logical:  'Intel i7-6500U (4) @ 3.1GHz' (All virtual cores)</span></span></span><span class="line"><span class="ln">238</span><span class="cl"><span class="c1"># physical: 'Intel i7-6500U (2) @ 3.1GHz' (All physical cores)</span></span></span><span class="line"><span class="ln">239</span><span class="cl"><span class="c1"># off:      'Intel i7-6500U @ 3.1GHz'</span></span></span><span class="line"><span class="ln">240</span><span class="cl"><span class="nv">cpu_cores</span><span class="o">=</span><span class="s2">"logical"</span></span></span><span class="line"><span class="ln">241</span><span class="cl"></span></span><span class="line"><span class="ln">242</span><span class="cl"><span class="c1"># CPU Temperature</span></span></span><span class="line"><span class="ln">243</span><span class="cl"><span class="c1"># Hide/Show CPU temperature.</span></span></span><span class="line"><span class="ln">244</span><span class="cl"><span class="c1"># Note the temperature is added to the regular CPU function.</span></span></span><span class="line"><span class="ln">245</span><span class="cl"></span></span><span class="line"><span class="ln">246</span><span class="cl"><span class="c1"># Default: 'off'</span></span></span><span class="line"><span class="ln">247</span><span class="cl"><span class="c1"># Values:  'C', 'F', 'off'</span></span></span><span class="line"><span class="ln">248</span><span class="cl"><span class="c1"># Flag:    --cpu_temp</span></span></span><span class="line"><span class="ln">249</span><span class="cl"><span class="c1"># Supports: Linux, BSD</span></span></span><span class="line"><span class="ln">250</span><span class="cl"><span class="c1"># NOTE: For FreeBSD and NetBSD-based systems, you'll need to enable</span></span></span><span class="line"><span class="ln">251</span><span class="cl"><span class="c1">#       coretemp kernel module. This only supports newer Intel processors.</span></span></span><span class="line"><span class="ln">252</span><span class="cl"></span></span><span class="line"><span class="ln">253</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">254</span><span class="cl"><span class="c1"># C:   'Intel i7-6500U (4) @ 3.1GHz [27.2°C]'</span></span></span><span class="line"><span class="ln">255</span><span class="cl"><span class="c1"># F:   'Intel i7-6500U (4) @ 3.1GHz [82.0°F]'</span></span></span><span class="line"><span class="ln">256</span><span class="cl"><span class="c1"># off: 'Intel i7-6500U (4) @ 3.1GHz'</span></span></span><span class="line"><span class="ln">257</span><span class="cl"><span class="nv">cpu_temp</span><span class="o">=</span><span class="s2">"off"</span></span></span><span class="line"><span class="ln">258</span><span class="cl"></span></span><span class="line"><span class="ln">259</span><span class="cl"><span class="c1"># GPU</span></span></span><span class="line"><span class="ln">260</span><span class="cl"></span></span><span class="line"><span class="ln">261</span><span class="cl"><span class="c1"># Enable/Disable GPU Brand</span></span></span><span class="line"><span class="ln">262</span><span class="cl"></span></span><span class="line"><span class="ln">263</span><span class="cl"><span class="c1"># Default: 'on'</span></span></span><span class="line"><span class="ln">264</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">265</span><span class="cl"><span class="c1"># Flag:    --gpu_brand</span></span></span><span class="line"><span class="ln">266</span><span class="cl"></span></span><span class="line"><span class="ln">267</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">268</span><span class="cl"><span class="c1"># on:  'AMD HD 7950'</span></span></span><span class="line"><span class="ln">269</span><span class="cl"><span class="c1"># off: 'HD 7950'</span></span></span><span class="line"><span class="ln">270</span><span class="cl"><span class="nv">gpu_brand</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">271</span><span class="cl"></span></span><span class="line"><span class="ln">272</span><span class="cl"><span class="c1"># Which GPU to display</span></span></span><span class="line"><span class="ln">273</span><span class="cl"></span></span><span class="line"><span class="ln">274</span><span class="cl"><span class="c1"># Default: 'all'</span></span></span><span class="line"><span class="ln">275</span><span class="cl"><span class="c1"># Values:  'all', 'dedicated', 'integrated'</span></span></span><span class="line"><span class="ln">276</span><span class="cl"><span class="c1"># Flag:    --gpu_type</span></span></span><span class="line"><span class="ln">277</span><span class="cl"><span class="c1"># Supports: Linux</span></span></span><span class="line"><span class="ln">278</span><span class="cl"></span></span><span class="line"><span class="ln">279</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">280</span><span class="cl"><span class="c1"># all:</span></span></span><span class="line"><span class="ln">281</span><span class="cl"><span class="c1">#   GPU1: AMD HD 7950</span></span></span><span class="line"><span class="ln">282</span><span class="cl"><span class="c1">#   GPU2: Intel Integrated Graphics</span></span></span><span class="line"><span class="ln">283</span><span class="cl"></span></span><span class="line"><span class="ln">284</span><span class="cl"><span class="c1"># dedicated:</span></span></span><span class="line"><span class="ln">285</span><span class="cl"><span class="c1">#   GPU1: AMD HD 7950</span></span></span><span class="line"><span class="ln">286</span><span class="cl"></span></span><span class="line"><span class="ln">287</span><span class="cl"><span class="c1"># integrated:</span></span></span><span class="line"><span class="ln">288</span><span class="cl"><span class="c1">#   GPU1: Intel Integrated Graphics</span></span></span><span class="line"><span class="ln">289</span><span class="cl"><span class="nv">gpu_type</span><span class="o">=</span><span class="s2">"all"</span></span></span><span class="line"><span class="ln">290</span><span class="cl"></span></span><span class="line"><span class="ln">291</span><span class="cl"><span class="c1"># Resolution</span></span></span><span class="line"><span class="ln">292</span><span class="cl"></span></span><span class="line"><span class="ln">293</span><span class="cl"><span class="c1"># Display refresh rate next to each monitor</span></span></span><span class="line"><span class="ln">294</span><span class="cl"><span class="c1"># Default: 'off'</span></span></span><span class="line"><span class="ln">295</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">296</span><span class="cl"><span class="c1"># Flag:    --refresh_rate</span></span></span><span class="line"><span class="ln">297</span><span class="cl"><span class="c1"># Supports: Doesn't work on Windows.</span></span></span><span class="line"><span class="ln">298</span><span class="cl"></span></span><span class="line"><span class="ln">299</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">300</span><span class="cl"><span class="c1"># on:  '1920x1080 @ 60Hz'</span></span></span><span class="line"><span class="ln">301</span><span class="cl"><span class="c1"># off: '1920x1080'</span></span></span><span class="line"><span class="ln">302</span><span class="cl"><span class="nv">refresh_rate</span><span class="o">=</span><span class="s2">"off"</span></span></span><span class="line"><span class="ln">303</span><span class="cl"></span></span><span class="line"><span class="ln">304</span><span class="cl"><span class="c1"># Gtk Theme / Icons / Font</span></span></span><span class="line"><span class="ln">305</span><span class="cl"></span></span><span class="line"><span class="ln">306</span><span class="cl"><span class="c1"># Shorten output of GTK Theme / Icons / Font</span></span></span><span class="line"><span class="ln">307</span><span class="cl"></span></span><span class="line"><span class="ln">308</span><span class="cl"><span class="c1"># Default: 'off'</span></span></span><span class="line"><span class="ln">309</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">310</span><span class="cl"><span class="c1"># Flag:    --gtk_shorthand</span></span></span><span class="line"><span class="ln">311</span><span class="cl"></span></span><span class="line"><span class="ln">312</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">313</span><span class="cl"><span class="c1"># on:  'Numix, Adwaita'</span></span></span><span class="line"><span class="ln">314</span><span class="cl"><span class="c1"># off: 'Numix [GTK2], Adwaita [GTK3]'</span></span></span><span class="line"><span class="ln">315</span><span class="cl"><span class="nv">gtk_shorthand</span><span class="o">=</span><span class="s2">"off"</span></span></span><span class="line"><span class="ln">316</span><span class="cl"></span></span><span class="line"><span class="ln">317</span><span class="cl"><span class="c1"># Enable/Disable gtk2 Theme / Icons / Font</span></span></span><span class="line"><span class="ln">318</span><span class="cl"></span></span><span class="line"><span class="ln">319</span><span class="cl"><span class="c1"># Default: 'on'</span></span></span><span class="line"><span class="ln">320</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">321</span><span class="cl"><span class="c1"># Flag:    --gtk2</span></span></span><span class="line"><span class="ln">322</span><span class="cl"></span></span><span class="line"><span class="ln">323</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">324</span><span class="cl"><span class="c1"># on:  'Numix [GTK2], Adwaita [GTK3]'</span></span></span><span class="line"><span class="ln">325</span><span class="cl"><span class="c1"># off: 'Adwaita [GTK3]'</span></span></span><span class="line"><span class="ln">326</span><span class="cl"><span class="nv">gtk2</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">327</span><span class="cl"></span></span><span class="line"><span class="ln">328</span><span class="cl"><span class="c1"># Enable/Disable gtk3 Theme / Icons / Font</span></span></span><span class="line"><span class="ln">329</span><span class="cl"></span></span><span class="line"><span class="ln">330</span><span class="cl"><span class="c1"># Default: 'on'</span></span></span><span class="line"><span class="ln">331</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">332</span><span class="cl"><span class="c1"># Flag:    --gtk3</span></span></span><span class="line"><span class="ln">333</span><span class="cl"></span></span><span class="line"><span class="ln">334</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">335</span><span class="cl"><span class="c1"># on:  'Numix [GTK2], Adwaita [GTK3]'</span></span></span><span class="line"><span class="ln">336</span><span class="cl"><span class="c1"># off: 'Numix [GTK2]'</span></span></span><span class="line"><span class="ln">337</span><span class="cl"><span class="nv">gtk3</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">338</span><span class="cl"></span></span><span class="line"><span class="ln">339</span><span class="cl"><span class="c1"># IP Address</span></span></span><span class="line"><span class="ln">340</span><span class="cl"></span></span><span class="line"><span class="ln">341</span><span class="cl"><span class="c1"># Website to ping for the public IP</span></span></span><span class="line"><span class="ln">342</span><span class="cl"></span></span><span class="line"><span class="ln">343</span><span class="cl"><span class="c1"># Default: 'http://ident.me'</span></span></span><span class="line"><span class="ln">344</span><span class="cl"><span class="c1"># Values:  'url'</span></span></span><span class="line"><span class="ln">345</span><span class="cl"><span class="c1"># Flag:    --ip_host</span></span></span><span class="line"><span class="ln">346</span><span class="cl"><span class="nv">public_ip_host</span><span class="o">=</span><span class="s2">"http://ident.me"</span></span></span><span class="line"><span class="ln">347</span><span class="cl"></span></span><span class="line"><span class="ln">348</span><span class="cl"><span class="c1"># Public IP timeout.</span></span></span><span class="line"><span class="ln">349</span><span class="cl"></span></span><span class="line"><span class="ln">350</span><span class="cl"><span class="c1"># Default: '2'</span></span></span><span class="line"><span class="ln">351</span><span class="cl"><span class="c1"># Values:  'int'</span></span></span><span class="line"><span class="ln">352</span><span class="cl"><span class="c1"># Flag:    --ip_timeout</span></span></span><span class="line"><span class="ln">353</span><span class="cl"><span class="nv">public_ip_timeout</span><span class="o">=</span><span class="m">2</span></span></span><span class="line"><span class="ln">354</span><span class="cl"></span></span><span class="line"><span class="ln">355</span><span class="cl"><span class="c1"># Desktop Environment</span></span></span><span class="line"><span class="ln">356</span><span class="cl"></span></span><span class="line"><span class="ln">357</span><span class="cl"><span class="c1"># Show Desktop Environment version</span></span></span><span class="line"><span class="ln">358</span><span class="cl"></span></span><span class="line"><span class="ln">359</span><span class="cl"><span class="c1"># Default: 'on'</span></span></span><span class="line"><span class="ln">360</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">361</span><span class="cl"><span class="c1"># Flag:    --de_version</span></span></span><span class="line"><span class="ln">362</span><span class="cl"><span class="nv">de_version</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">363</span><span class="cl"></span></span><span class="line"><span class="ln">364</span><span class="cl"><span class="c1"># Disk</span></span></span><span class="line"><span class="ln">365</span><span class="cl"></span></span><span class="line"><span class="ln">366</span><span class="cl"><span class="c1"># Which disks to display.</span></span></span><span class="line"><span class="ln">367</span><span class="cl"><span class="c1"># The values can be any /dev/sdXX, mount point or directory.</span></span></span><span class="line"><span class="ln">368</span><span class="cl"><span class="c1"># NOTE: By default we only show the disk info for '/'.</span></span></span><span class="line"><span class="ln">369</span><span class="cl"></span></span><span class="line"><span class="ln">370</span><span class="cl"><span class="c1"># Default: '/'</span></span></span><span class="line"><span class="ln">371</span><span class="cl"><span class="c1"># Values:  '/', '/dev/sdXX', '/path/to/drive'.</span></span></span><span class="line"><span class="ln">372</span><span class="cl"><span class="c1"># Flag:    --disk_show</span></span></span><span class="line"><span class="ln">373</span><span class="cl"></span></span><span class="line"><span class="ln">374</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">375</span><span class="cl"><span class="c1"># disk_show=('/' '/dev/sdb1'):</span></span></span><span class="line"><span class="ln">376</span><span class="cl"><span class="c1">#      'Disk (/): 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">377</span><span class="cl"><span class="c1">#      'Disk (/mnt/Videos): 823G / 893G (93%)'</span></span></span><span class="line"><span class="ln">378</span><span class="cl"></span></span><span class="line"><span class="ln">379</span><span class="cl"><span class="c1"># disk_show=('/'):</span></span></span><span class="line"><span class="ln">380</span><span class="cl"><span class="c1">#      'Disk (/): 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">381</span><span class="cl"></span></span><span class="line"><span class="ln">382</span><span class="cl"><span class="nv">disk_show</span><span class="o">=(</span><span class="s1">'/'</span><span class="o">)</span></span></span><span class="line"><span class="ln">383</span><span class="cl"></span></span><span class="line"><span class="ln">384</span><span class="cl"><span class="c1"># Disk subtitle.</span></span></span><span class="line"><span class="ln">385</span><span class="cl"><span class="c1"># What to append to the Disk subtitle.</span></span></span><span class="line"><span class="ln">386</span><span class="cl"></span></span><span class="line"><span class="ln">387</span><span class="cl"><span class="c1"># Default: 'mount'</span></span></span><span class="line"><span class="ln">388</span><span class="cl"><span class="c1"># Values:  'mount', 'name', 'dir', 'none'</span></span></span><span class="line"><span class="ln">389</span><span class="cl"><span class="c1"># Flag:    --disk_subtitle</span></span></span><span class="line"><span class="ln">390</span><span class="cl"></span></span><span class="line"><span class="ln">391</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">392</span><span class="cl"><span class="c1"># name:   'Disk (/dev/sda1): 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">393</span><span class="cl"><span class="c1">#         'Disk (/dev/sdb2): 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">394</span><span class="cl"></span></span><span class="line"><span class="ln">395</span><span class="cl"><span class="c1"># mount:  'Disk (/): 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">396</span><span class="cl"><span class="c1">#         'Disk (/mnt/Local Disk): 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">397</span><span class="cl"><span class="c1">#         'Disk (/mnt/Videos): 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">398</span><span class="cl"></span></span><span class="line"><span class="ln">399</span><span class="cl"><span class="c1"># dir:    'Disk (/): 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">400</span><span class="cl"><span class="c1">#         'Disk (Local Disk): 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">401</span><span class="cl"><span class="c1">#         'Disk (Videos): 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">402</span><span class="cl"></span></span><span class="line"><span class="ln">403</span><span class="cl"><span class="c1"># none:   'Disk: 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">404</span><span class="cl"><span class="c1">#         'Disk: 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">405</span><span class="cl"><span class="c1">#         'Disk: 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">406</span><span class="cl"><span class="nv">disk_subtitle</span><span class="o">=</span><span class="s2">"dir"</span></span></span><span class="line"><span class="ln">407</span><span class="cl"></span></span><span class="line"><span class="ln">408</span><span class="cl"><span class="c1"># Disk percent.</span></span></span><span class="line"><span class="ln">409</span><span class="cl"><span class="c1"># Show/Hide disk percent.</span></span></span><span class="line"><span class="ln">410</span><span class="cl"></span></span><span class="line"><span class="ln">411</span><span class="cl"><span class="c1"># Default: 'on'</span></span></span><span class="line"><span class="ln">412</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">413</span><span class="cl"><span class="c1"># Flag:    --disk_percent</span></span></span><span class="line"><span class="ln">414</span><span class="cl"></span></span><span class="line"><span class="ln">415</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">416</span><span class="cl"><span class="c1"># on:  'Disk (/): 74G / 118G (66%)'</span></span></span><span class="line"><span class="ln">417</span><span class="cl"><span class="c1"># off: 'Disk (/): 74G / 118G'</span></span></span><span class="line"><span class="ln">418</span><span class="cl"><span class="nv">disk_percent</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">419</span><span class="cl"></span></span><span class="line"><span class="ln">420</span><span class="cl"><span class="c1"># Song</span></span></span><span class="line"><span class="ln">421</span><span class="cl"></span></span><span class="line"><span class="ln">422</span><span class="cl"><span class="c1"># Manually specify a music player.</span></span></span><span class="line"><span class="ln">423</span><span class="cl"></span></span><span class="line"><span class="ln">424</span><span class="cl"><span class="c1"># Default: 'auto'</span></span></span><span class="line"><span class="ln">425</span><span class="cl"><span class="c1"># Values:  'auto', 'player-name'</span></span></span><span class="line"><span class="ln">426</span><span class="cl"><span class="c1"># Flag:    --music_player</span></span></span><span class="line"><span class="ln">427</span><span class="cl"></span></span><span class="line"><span class="ln">428</span><span class="cl"><span class="c1"># Available values for 'player-name':</span></span></span><span class="line"><span class="ln">429</span><span class="cl"></span></span><span class="line"><span class="ln">430</span><span class="cl"><span class="c1"># amarok</span></span></span><span class="line"><span class="ln">431</span><span class="cl"><span class="c1"># audacious</span></span></span><span class="line"><span class="ln">432</span><span class="cl"><span class="c1"># banshee</span></span></span><span class="line"><span class="ln">433</span><span class="cl"><span class="c1"># bluemindo</span></span></span><span class="line"><span class="ln">434</span><span class="cl"><span class="c1"># clementine</span></span></span><span class="line"><span class="ln">435</span><span class="cl"><span class="c1"># cmus</span></span></span><span class="line"><span class="ln">436</span><span class="cl"><span class="c1"># deadbeef</span></span></span><span class="line"><span class="ln">437</span><span class="cl"><span class="c1"># deepin-music</span></span></span><span class="line"><span class="ln">438</span><span class="cl"><span class="c1"># dragon</span></span></span><span class="line"><span class="ln">439</span><span class="cl"><span class="c1"># elisa</span></span></span><span class="line"><span class="ln">440</span><span class="cl"><span class="c1"># exaile</span></span></span><span class="line"><span class="ln">441</span><span class="cl"><span class="c1"># gnome-music</span></span></span><span class="line"><span class="ln">442</span><span class="cl"><span class="c1"># gmusicbrowser</span></span></span><span class="line"><span class="ln">443</span><span class="cl"><span class="c1"># gogglesmm</span></span></span><span class="line"><span class="ln">444</span><span class="cl"><span class="c1"># guayadeque</span></span></span><span class="line"><span class="ln">445</span><span class="cl"><span class="c1"># io.elementary.music</span></span></span><span class="line"><span class="ln">446</span><span class="cl"><span class="c1"># iTunes</span></span></span><span class="line"><span class="ln">447</span><span class="cl"><span class="c1"># juk</span></span></span><span class="line"><span class="ln">448</span><span class="cl"><span class="c1"># lollypop</span></span></span><span class="line"><span class="ln">449</span><span class="cl"><span class="c1"># mocp</span></span></span><span class="line"><span class="ln">450</span><span class="cl"><span class="c1"># mopidy</span></span></span><span class="line"><span class="ln">451</span><span class="cl"><span class="c1"># mpd</span></span></span><span class="line"><span class="ln">452</span><span class="cl"><span class="c1"># muine</span></span></span><span class="line"><span class="ln">453</span><span class="cl"><span class="c1"># netease-cloud-music</span></span></span><span class="line"><span class="ln">454</span><span class="cl"><span class="c1"># olivia</span></span></span><span class="line"><span class="ln">455</span><span class="cl"><span class="c1"># playerctl</span></span></span><span class="line"><span class="ln">456</span><span class="cl"><span class="c1"># pogo</span></span></span><span class="line"><span class="ln">457</span><span class="cl"><span class="c1"># pragha</span></span></span><span class="line"><span class="ln">458</span><span class="cl"><span class="c1"># qmmp</span></span></span><span class="line"><span class="ln">459</span><span class="cl"><span class="c1"># quodlibet</span></span></span><span class="line"><span class="ln">460</span><span class="cl"><span class="c1"># rhythmbox</span></span></span><span class="line"><span class="ln">461</span><span class="cl"><span class="c1"># sayonara</span></span></span><span class="line"><span class="ln">462</span><span class="cl"><span class="c1"># smplayer</span></span></span><span class="line"><span class="ln">463</span><span class="cl"><span class="c1"># spotify</span></span></span><span class="line"><span class="ln">464</span><span class="cl"><span class="c1"># strawberry</span></span></span><span class="line"><span class="ln">465</span><span class="cl"><span class="c1"># tauonmb</span></span></span><span class="line"><span class="ln">466</span><span class="cl"><span class="c1"># tomahawk</span></span></span><span class="line"><span class="ln">467</span><span class="cl"><span class="c1"># vlc</span></span></span><span class="line"><span class="ln">468</span><span class="cl"><span class="c1"># xmms2d</span></span></span><span class="line"><span class="ln">469</span><span class="cl"><span class="c1"># xnoise</span></span></span><span class="line"><span class="ln">470</span><span class="cl"><span class="c1"># yarock</span></span></span><span class="line"><span class="ln">471</span><span class="cl"><span class="nv">music_player</span><span class="o">=</span><span class="s2">"auto"</span></span></span><span class="line"><span class="ln">472</span><span class="cl"></span></span><span class="line"><span class="ln">473</span><span class="cl"><span class="c1"># Format to display song information.</span></span></span><span class="line"><span class="ln">474</span><span class="cl"></span></span><span class="line"><span class="ln">475</span><span class="cl"><span class="c1"># Default: '%artist% - %album% - %title%'</span></span></span><span class="line"><span class="ln">476</span><span class="cl"><span class="c1"># Values:  '%artist%', '%album%', '%title%'</span></span></span><span class="line"><span class="ln">477</span><span class="cl"><span class="c1"># Flag:    --song_format</span></span></span><span class="line"><span class="ln">478</span><span class="cl"></span></span><span class="line"><span class="ln">479</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">480</span><span class="cl"><span class="c1"># default: 'Song: Jet - Get Born - Sgt Major'</span></span></span><span class="line"><span class="ln">481</span><span class="cl"><span class="nv">song_format</span><span class="o">=</span><span class="s2">"%artist% - %album% - %title%"</span></span></span><span class="line"><span class="ln">482</span><span class="cl"></span></span><span class="line"><span class="ln">483</span><span class="cl"><span class="c1"># Print the Artist, Album and Title on separate lines</span></span></span><span class="line"><span class="ln">484</span><span class="cl"></span></span><span class="line"><span class="ln">485</span><span class="cl"><span class="c1"># Default: 'off'</span></span></span><span class="line"><span class="ln">486</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">487</span><span class="cl"><span class="c1"># Flag:    --song_shorthand</span></span></span><span class="line"><span class="ln">488</span><span class="cl"></span></span><span class="line"><span class="ln">489</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">490</span><span class="cl"><span class="c1"># on:  'Artist: The Fratellis'</span></span></span><span class="line"><span class="ln">491</span><span class="cl"><span class="c1">#      'Album: Costello Music'</span></span></span><span class="line"><span class="ln">492</span><span class="cl"><span class="c1">#      'Song: Chelsea Dagger'</span></span></span><span class="line"><span class="ln">493</span><span class="cl"></span></span><span class="line"><span class="ln">494</span><span class="cl"><span class="c1"># off: 'Song: The Fratellis - Costello Music - Chelsea Dagger'</span></span></span><span class="line"><span class="ln">495</span><span class="cl"><span class="nv">song_shorthand</span><span class="o">=</span><span class="s2">"off"</span></span></span><span class="line"><span class="ln">496</span><span class="cl"></span></span><span class="line"><span class="ln">497</span><span class="cl"><span class="c1"># 'mpc' arguments (specify a host, password etc).</span></span></span><span class="line"><span class="ln">498</span><span class="cl"></span></span><span class="line"><span class="ln">499</span><span class="cl"><span class="c1"># Default:  ''</span></span></span><span class="line"><span class="ln">500</span><span class="cl"><span class="c1"># Example: mpc_args=(-h HOST -P PASSWORD)</span></span></span><span class="line"><span class="ln">501</span><span class="cl"><span class="nv">mpc_args</span><span class="o">=()</span></span></span><span class="line"><span class="ln">502</span><span class="cl"></span></span><span class="line"><span class="ln">503</span><span class="cl"><span class="c1"># Text Colors</span></span></span><span class="line"><span class="ln">504</span><span class="cl"></span></span><span class="line"><span class="ln">505</span><span class="cl"><span class="c1"># Text Colors</span></span></span><span class="line"><span class="ln">506</span><span class="cl"></span></span><span class="line"><span class="ln">507</span><span class="cl"><span class="c1"># Default:  'distro'</span></span></span><span class="line"><span class="ln">508</span><span class="cl"><span class="c1"># Values:   'distro', 'num' 'num' 'num' 'num' 'num' 'num'</span></span></span><span class="line"><span class="ln">509</span><span class="cl"><span class="c1"># Flag:     --colors</span></span></span><span class="line"><span class="ln">510</span><span class="cl"></span></span><span class="line"><span class="ln">511</span><span class="cl"><span class="c1"># Each number represents a different part of the text in</span></span></span><span class="line"><span class="ln">512</span><span class="cl"><span class="c1"># this order: 'title', '@', 'underline', 'subtitle', 'colon', 'info'</span></span></span><span class="line"><span class="ln">513</span><span class="cl"></span></span><span class="line"><span class="ln">514</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">515</span><span class="cl"><span class="c1"># colors=(distro)      - Text is colored based on Distro colors.</span></span></span><span class="line"><span class="ln">516</span><span class="cl"><span class="c1"># colors=(4 6 1 8 8 6) - Text is colored in the order above.</span></span></span><span class="line"><span class="ln">517</span><span class="cl"><span class="nv">colors</span><span class="o">=(</span>distro<span class="o">)</span></span></span><span class="line"><span class="ln">518</span><span class="cl"></span></span><span class="line"><span class="ln">519</span><span class="cl"><span class="c1"># Text Options</span></span></span><span class="line"><span class="ln">520</span><span class="cl"></span></span><span class="line"><span class="ln">521</span><span class="cl"><span class="c1"># Toggle bold text</span></span></span><span class="line"><span class="ln">522</span><span class="cl"></span></span><span class="line"><span class="ln">523</span><span class="cl"><span class="c1"># Default:  'on'</span></span></span><span class="line"><span class="ln">524</span><span class="cl"><span class="c1"># Values:   'on', 'off'</span></span></span><span class="line"><span class="ln">525</span><span class="cl"><span class="c1"># Flag:     --bold</span></span></span><span class="line"><span class="ln">526</span><span class="cl"><span class="nv">bold</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">527</span><span class="cl"></span></span><span class="line"><span class="ln">528</span><span class="cl"><span class="c1"># Enable/Disable Underline</span></span></span><span class="line"><span class="ln">529</span><span class="cl"></span></span><span class="line"><span class="ln">530</span><span class="cl"><span class="c1"># Default:  'on'</span></span></span><span class="line"><span class="ln">531</span><span class="cl"><span class="c1"># Values:   'on', 'off'</span></span></span><span class="line"><span class="ln">532</span><span class="cl"><span class="c1"># Flag:     --underline</span></span></span><span class="line"><span class="ln">533</span><span class="cl"><span class="nv">underline_enabled</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">534</span><span class="cl"></span></span><span class="line"><span class="ln">535</span><span class="cl"><span class="c1"># Underline character</span></span></span><span class="line"><span class="ln">536</span><span class="cl"></span></span><span class="line"><span class="ln">537</span><span class="cl"><span class="c1"># Default:  '-'</span></span></span><span class="line"><span class="ln">538</span><span class="cl"><span class="c1"># Values:   'string'</span></span></span><span class="line"><span class="ln">539</span><span class="cl"><span class="c1"># Flag:     --underline_char</span></span></span><span class="line"><span class="ln">540</span><span class="cl"><span class="nv">underline_char</span><span class="o">=</span><span class="s2">"-"</span></span></span><span class="line"><span class="ln">541</span><span class="cl"></span></span><span class="line"><span class="ln">542</span><span class="cl"><span class="c1"># Info Separator</span></span></span><span class="line"><span class="ln">543</span><span class="cl"><span class="c1"># Replace the default separator with the specified string.</span></span></span><span class="line"><span class="ln">544</span><span class="cl"></span></span><span class="line"><span class="ln">545</span><span class="cl"><span class="c1"># Default:  ':'</span></span></span><span class="line"><span class="ln">546</span><span class="cl"><span class="c1"># Flag:     --separator</span></span></span><span class="line"><span class="ln">547</span><span class="cl"></span></span><span class="line"><span class="ln">548</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">549</span><span class="cl"><span class="c1"># separator="->":   'Shell-> bash'</span></span></span><span class="line"><span class="ln">550</span><span class="cl"><span class="c1"># separator=" =":   'WM = dwm'</span></span></span><span class="line"><span class="ln">551</span><span class="cl"><span class="nv">separator</span><span class="o">=</span><span class="s2">" |"</span></span></span><span class="line"><span class="ln">552</span><span class="cl"></span></span><span class="line"><span class="ln">553</span><span class="cl"><span class="c1"># Color Blocks</span></span></span><span class="line"><span class="ln">554</span><span class="cl"></span></span><span class="line"><span class="ln">555</span><span class="cl"><span class="c1"># Color block range</span></span></span><span class="line"><span class="ln">556</span><span class="cl"><span class="c1"># The range of colors to print.</span></span></span><span class="line"><span class="ln">557</span><span class="cl"></span></span><span class="line"><span class="ln">558</span><span class="cl"><span class="c1"># Default:  '0', '15'</span></span></span><span class="line"><span class="ln">559</span><span class="cl"><span class="c1"># Values:   'num'</span></span></span><span class="line"><span class="ln">560</span><span class="cl"><span class="c1"># Flag:     --block_range</span></span></span><span class="line"><span class="ln">561</span><span class="cl"></span></span><span class="line"><span class="ln">562</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">563</span><span class="cl"></span></span><span class="line"><span class="ln">564</span><span class="cl"><span class="c1"># Display colors 0-7 in the blocks.  (8 colors)</span></span></span><span class="line"><span class="ln">565</span><span class="cl"><span class="c1"># neofetch --block_range 0 7</span></span></span><span class="line"><span class="ln">566</span><span class="cl"></span></span><span class="line"><span class="ln">567</span><span class="cl"><span class="c1"># Display colors 0-15 in the blocks. (16 colors)</span></span></span><span class="line"><span class="ln">568</span><span class="cl"><span class="c1"># neofetch --block_range 0 15</span></span></span><span class="line"><span class="ln">569</span><span class="cl"><span class="nv">block_range</span><span class="o">=(</span><span class="m">0</span> 15<span class="o">)</span></span></span><span class="line"><span class="ln">570</span><span class="cl"></span></span><span class="line"><span class="ln">571</span><span class="cl"><span class="c1"># Toggle color blocks</span></span></span><span class="line"><span class="ln">572</span><span class="cl"></span></span><span class="line"><span class="ln">573</span><span class="cl"><span class="c1"># Default:  'on'</span></span></span><span class="line"><span class="ln">574</span><span class="cl"><span class="c1"># Values:   'on', 'off'</span></span></span><span class="line"><span class="ln">575</span><span class="cl"><span class="c1"># Flag:     --color_blocks</span></span></span><span class="line"><span class="ln">576</span><span class="cl"><span class="nv">color_blocks</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">577</span><span class="cl"></span></span><span class="line"><span class="ln">578</span><span class="cl"><span class="c1"># Color block width in spaces</span></span></span><span class="line"><span class="ln">579</span><span class="cl"></span></span><span class="line"><span class="ln">580</span><span class="cl"><span class="c1"># Default:  '3'</span></span></span><span class="line"><span class="ln">581</span><span class="cl"><span class="c1"># Values:   'num'</span></span></span><span class="line"><span class="ln">582</span><span class="cl"><span class="c1"># Flag:     --block_width</span></span></span><span class="line"><span class="ln">583</span><span class="cl"><span class="nv">block_width</span><span class="o">=</span><span class="m">4</span></span></span><span class="line"><span class="ln">584</span><span class="cl"></span></span><span class="line"><span class="ln">585</span><span class="cl"><span class="c1"># Color block height in lines</span></span></span><span class="line"><span class="ln">586</span><span class="cl"></span></span><span class="line"><span class="ln">587</span><span class="cl"><span class="c1"># Default:  '1'</span></span></span><span class="line"><span class="ln">588</span><span class="cl"><span class="c1"># Values:   'num'</span></span></span><span class="line"><span class="ln">589</span><span class="cl"><span class="c1"># Flag:     --block_height</span></span></span><span class="line"><span class="ln">590</span><span class="cl"><span class="nv">block_height</span><span class="o">=</span><span class="m">1</span></span></span><span class="line"><span class="ln">591</span><span class="cl"></span></span><span class="line"><span class="ln">592</span><span class="cl"><span class="c1"># Color Alignment</span></span></span><span class="line"><span class="ln">593</span><span class="cl"></span></span><span class="line"><span class="ln">594</span><span class="cl"><span class="c1"># Default: 'auto'</span></span></span><span class="line"><span class="ln">595</span><span class="cl"><span class="c1"># Values: 'auto', 'num'</span></span></span><span class="line"><span class="ln">596</span><span class="cl"><span class="c1"># Flag: --col_offset</span></span></span><span class="line"><span class="ln">597</span><span class="cl"></span></span><span class="line"><span class="ln">598</span><span class="cl"><span class="c1"># Number specifies how far from the left side of the terminal (in spaces) to</span></span></span><span class="line"><span class="ln">599</span><span class="cl"><span class="c1"># begin printing the columns, in case you want to e.g. center them under your</span></span></span><span class="line"><span class="ln">600</span><span class="cl"><span class="c1"># text.</span></span></span><span class="line"><span class="ln">601</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">602</span><span class="cl"><span class="c1"># col_offset="auto" - Default behavior of neofetch</span></span></span><span class="line"><span class="ln">603</span><span class="cl"><span class="c1"># col_offset=7      - Leave 7 spaces then print the colors</span></span></span><span class="line"><span class="ln">604</span><span class="cl"><span class="nv">col_offset</span><span class="o">=</span><span class="s2">"auto"</span></span></span><span class="line"><span class="ln">605</span><span class="cl"></span></span><span class="line"><span class="ln">606</span><span class="cl"><span class="c1"># Progress Bars</span></span></span><span class="line"><span class="ln">607</span><span class="cl"></span></span><span class="line"><span class="ln">608</span><span class="cl"><span class="c1"># Bar characters</span></span></span><span class="line"><span class="ln">609</span><span class="cl"></span></span><span class="line"><span class="ln">610</span><span class="cl"><span class="c1"># Default:  '-', '='</span></span></span><span class="line"><span class="ln">611</span><span class="cl"><span class="c1"># Values:   'string', 'string'</span></span></span><span class="line"><span class="ln">612</span><span class="cl"><span class="c1"># Flag:     --bar_char</span></span></span><span class="line"><span class="ln">613</span><span class="cl"></span></span><span class="line"><span class="ln">614</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">615</span><span class="cl"><span class="c1"># neofetch --bar_char 'elapsed' 'total'</span></span></span><span class="line"><span class="ln">616</span><span class="cl"><span class="c1"># neofetch --bar_char '-' '='</span></span></span><span class="line"><span class="ln">617</span><span class="cl"><span class="nv">bar_char_elapsed</span><span class="o">=</span><span class="s2">"~"</span></span></span><span class="line"><span class="ln">618</span><span class="cl"><span class="nv">bar_char_total</span><span class="o">=</span><span class="s2">"="</span></span></span><span class="line"><span class="ln">619</span><span class="cl"></span></span><span class="line"><span class="ln">620</span><span class="cl"><span class="c1"># Toggle Bar border</span></span></span><span class="line"><span class="ln">621</span><span class="cl"></span></span><span class="line"><span class="ln">622</span><span class="cl"><span class="c1"># Default:  'on'</span></span></span><span class="line"><span class="ln">623</span><span class="cl"><span class="c1"># Values:   'on', 'off'</span></span></span><span class="line"><span class="ln">624</span><span class="cl"><span class="c1"># Flag:     --bar_border</span></span></span><span class="line"><span class="ln">625</span><span class="cl"><span class="nv">bar_border</span><span class="o">=</span><span class="s2">"on"</span></span></span><span class="line"><span class="ln">626</span><span class="cl"></span></span><span class="line"><span class="ln">627</span><span class="cl"><span class="c1"># Progress bar length in spaces</span></span></span><span class="line"><span class="ln">628</span><span class="cl"><span class="c1"># Number of chars long to make the progress bars.</span></span></span><span class="line"><span class="ln">629</span><span class="cl"></span></span><span class="line"><span class="ln">630</span><span class="cl"><span class="c1"># Default:  '15'</span></span></span><span class="line"><span class="ln">631</span><span class="cl"><span class="c1"># Values:   'num'</span></span></span><span class="line"><span class="ln">632</span><span class="cl"><span class="c1"># Flag:     --bar_length</span></span></span><span class="line"><span class="ln">633</span><span class="cl"><span class="nv">bar_length</span><span class="o">=</span><span class="m">15</span></span></span><span class="line"><span class="ln">634</span><span class="cl"></span></span><span class="line"><span class="ln">635</span><span class="cl"><span class="c1"># Progress bar colors</span></span></span><span class="line"><span class="ln">636</span><span class="cl"><span class="c1"># When set to distro, uses your distro's logo colors.</span></span></span><span class="line"><span class="ln">637</span><span class="cl"></span></span><span class="line"><span class="ln">638</span><span class="cl"><span class="c1"># Default:  'distro', 'distro'</span></span></span><span class="line"><span class="ln">639</span><span class="cl"><span class="c1"># Values:   'distro', 'num'</span></span></span><span class="line"><span class="ln">640</span><span class="cl"><span class="c1"># Flag:     --bar_colors</span></span></span><span class="line"><span class="ln">641</span><span class="cl"></span></span><span class="line"><span class="ln">642</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">643</span><span class="cl"><span class="c1"># neofetch --bar_colors 3 4</span></span></span><span class="line"><span class="ln">644</span><span class="cl"><span class="c1"># neofetch --bar_colors distro 5</span></span></span><span class="line"><span class="ln">645</span><span class="cl"><span class="nv">bar_color_elapsed</span><span class="o">=</span><span class="s2">"distro"</span></span></span><span class="line"><span class="ln">646</span><span class="cl"><span class="nv">bar_color_total</span><span class="o">=</span><span class="s2">"distro"</span></span></span><span class="line"><span class="ln">647</span><span class="cl"></span></span><span class="line"><span class="ln">648</span><span class="cl"><span class="c1"># Info display</span></span></span><span class="line"><span class="ln">649</span><span class="cl"><span class="c1"># Display a bar with the info.</span></span></span><span class="line"><span class="ln">650</span><span class="cl"></span></span><span class="line"><span class="ln">651</span><span class="cl"><span class="c1"># Default: 'off'</span></span></span><span class="line"><span class="ln">652</span><span class="cl"><span class="c1"># Values:  'bar', 'infobar', 'barinfo', 'off'</span></span></span><span class="line"><span class="ln">653</span><span class="cl"><span class="c1"># Flags:   --cpu_display</span></span></span><span class="line"><span class="ln">654</span><span class="cl"><span class="c1">#          --memory_display</span></span></span><span class="line"><span class="ln">655</span><span class="cl"><span class="c1">#          --battery_display</span></span></span><span class="line"><span class="ln">656</span><span class="cl"><span class="c1">#          --disk_display</span></span></span><span class="line"><span class="ln">657</span><span class="cl"></span></span><span class="line"><span class="ln">658</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">659</span><span class="cl"><span class="c1"># bar:     '[---=======]'</span></span></span><span class="line"><span class="ln">660</span><span class="cl"><span class="c1"># infobar: 'info [---=======]'</span></span></span><span class="line"><span class="ln">661</span><span class="cl"><span class="c1"># barinfo: '[---=======] info'</span></span></span><span class="line"><span class="ln">662</span><span class="cl"><span class="c1"># off:     'info'</span></span></span><span class="line"><span class="ln">663</span><span class="cl"><span class="nv">cpu_display</span><span class="o">=</span><span class="s2">"barinfo"</span></span></span><span class="line"><span class="ln">664</span><span class="cl"><span class="nv">memory_display</span><span class="o">=</span><span class="s2">"barinfo"</span></span></span><span class="line"><span class="ln">665</span><span class="cl"><span class="nv">battery_display</span><span class="o">=</span><span class="s2">"off"</span></span></span><span class="line"><span class="ln">666</span><span class="cl"><span class="nv">disk_display</span><span class="o">=</span><span class="s2">"barinfo"</span></span></span><span class="line"><span class="ln">667</span><span class="cl"></span></span><span class="line"><span class="ln">668</span><span class="cl"><span class="c1"># Backend Settings</span></span></span><span class="line"><span class="ln">669</span><span class="cl"></span></span><span class="line"><span class="ln">670</span><span class="cl"><span class="c1"># Image backend.</span></span></span><span class="line"><span class="ln">671</span><span class="cl"></span></span><span class="line"><span class="ln">672</span><span class="cl"><span class="c1"># Default:  'ascii'</span></span></span><span class="line"><span class="ln">673</span><span class="cl"><span class="c1"># Values:   'ascii', 'caca', 'chafa', 'jp2a', 'iterm2', 'off',</span></span></span><span class="line"><span class="ln">674</span><span class="cl"><span class="c1">#           'pot', 'termpix', 'pixterm', 'tycat', 'w3m', 'kitty'</span></span></span><span class="line"><span class="ln">675</span><span class="cl"><span class="c1"># Flag:     --backend</span></span></span><span class="line"><span class="ln">676</span><span class="cl"><span class="nv">image_backend</span><span class="o">=</span><span class="s2">"ascii"</span></span></span><span class="line"><span class="ln">677</span><span class="cl"></span></span><span class="line"><span class="ln">678</span><span class="cl"><span class="c1"># Image Source</span></span></span><span class="line"><span class="ln">679</span><span class="cl"></span></span><span class="line"><span class="ln">680</span><span class="cl"><span class="c1"># Which image or ascii file to display.</span></span></span><span class="line"><span class="ln">681</span><span class="cl"></span></span><span class="line"><span class="ln">682</span><span class="cl"><span class="c1"># Default:  'auto'</span></span></span><span class="line"><span class="ln">683</span><span class="cl"><span class="c1"># Values:   'auto', 'ascii', 'wallpaper', '/path/to/img', '/path/to/ascii', '/path/to/dir/'</span></span></span><span class="line"><span class="ln">684</span><span class="cl"><span class="c1">#           'command output (neofetch --ascii "$(fortune | cowsay -W 30)")'</span></span></span><span class="line"><span class="ln">685</span><span class="cl"><span class="c1"># Flag:     --source</span></span></span><span class="line"><span class="ln">686</span><span class="cl"></span></span><span class="line"><span class="ln">687</span><span class="cl"><span class="c1"># NOTE: 'auto' will pick the best image source for whatever image backend is used.</span></span></span><span class="line"><span class="ln">688</span><span class="cl"><span class="c1">#       In ascii mode, distro ascii art will be used and in an image mode, your</span></span></span><span class="line"><span class="ln">689</span><span class="cl"><span class="c1">#       wallpaper will be used.</span></span></span><span class="line"><span class="ln">690</span><span class="cl"><span class="nv">image_source</span><span class="o">=</span><span class="s2">"auto"</span></span></span><span class="line"><span class="ln">691</span><span class="cl"></span></span><span class="line"><span class="ln">692</span><span class="cl"><span class="c1"># Ascii Options</span></span></span><span class="line"><span class="ln">693</span><span class="cl"></span></span><span class="line"><span class="ln">694</span><span class="cl"><span class="c1"># Ascii distro</span></span></span><span class="line"><span class="ln">695</span><span class="cl"><span class="c1"># Which distro's ascii art to display.</span></span></span><span class="line"><span class="ln">696</span><span class="cl"></span></span><span class="line"><span class="ln">697</span><span class="cl"><span class="c1"># Default: 'auto'</span></span></span><span class="line"><span class="ln">698</span><span class="cl"><span class="c1"># Values:  'auto', 'distro_name'</span></span></span><span class="line"><span class="ln">699</span><span class="cl"><span class="c1"># Flag:    --ascii_distro</span></span></span><span class="line"><span class="ln">700</span><span class="cl"><span class="c1"># NOTE: AIX, Alpine, Anarchy, Android, Antergos, antiX, "AOSC OS",</span></span></span><span class="line"><span class="ln">701</span><span class="cl"><span class="c1">#       "AOSC OS/Retro", Apricity, ArcoLinux, ArchBox, ARCHlabs,</span></span></span><span class="line"><span class="ln">702</span><span class="cl"><span class="c1">#       ArchStrike, XFerience, ArchMerge, Arch, Artix, Arya, Bedrock,</span></span></span><span class="line"><span class="ln">703</span><span class="cl"><span class="c1">#       Bitrig, BlackArch, BLAG, BlankOn, BlueLight, bonsai, BSD,</span></span></span><span class="line"><span class="ln">704</span><span class="cl"><span class="c1">#       BunsenLabs, Calculate, Carbs, CentOS, Chakra, ChaletOS,</span></span></span><span class="line"><span class="ln">705</span><span class="cl"><span class="c1">#       Chapeau, Chrom*, Cleanjaro, ClearOS, Clear_Linux, Clover,</span></span></span><span class="line"><span class="ln">706</span><span class="cl"><span class="c1">#       Condres, Container_Linux, CRUX, Cucumber, Debian, Deepin,</span></span></span><span class="line"><span class="ln">707</span><span class="cl"><span class="c1">#       DesaOS, Devuan, DracOS, DarkOs, DragonFly, Drauger, Elementary,</span></span></span><span class="line"><span class="ln">708</span><span class="cl"><span class="c1">#       EndeavourOS, Endless, EuroLinux, Exherbo, Fedora, Feren, FreeBSD,</span></span></span><span class="line"><span class="ln">709</span><span class="cl"><span class="c1">#       FreeMiNT, Frugalware, Funtoo, GalliumOS, Garuda, Gentoo, Pentoo,</span></span></span><span class="line"><span class="ln">710</span><span class="cl"><span class="c1">#       gNewSense, GNOME, GNU, GoboLinux, Grombyang, Guix, Haiku, Huayra,</span></span></span><span class="line"><span class="ln">711</span><span class="cl"><span class="c1">#       Hyperbola, janus, Kali, KaOS, KDE_neon, Kibojoe, Kogaion,</span></span></span><span class="line"><span class="ln">712</span><span class="cl"><span class="c1">#       Korora, KSLinux, Kubuntu, LEDE, LFS, Linux_Lite,</span></span></span><span class="line"><span class="ln">713</span><span class="cl"><span class="c1">#       LMDE, Lubuntu, Lunar, macos, Mageia, MagpieOS, Mandriva,</span></span></span><span class="line"><span class="ln">714</span><span class="cl"><span class="c1">#       Manjaro, Maui, Mer, Minix, LinuxMint, MX_Linux, Namib,</span></span></span><span class="line"><span class="ln">715</span><span class="cl"><span class="c1">#       Neptune, NetBSD, Netrunner, Nitrux, NixOS, Nurunner,</span></span></span><span class="line"><span class="ln">716</span><span class="cl"><span class="c1">#       NuTyX, OBRevenge, OpenBSD, openEuler, OpenIndiana, openmamba,</span></span></span><span class="line"><span class="ln">717</span><span class="cl"><span class="c1">#       OpenMandriva, OpenStage, OpenWrt, osmc, Oracle, OS Elbrus, PacBSD,</span></span></span><span class="line"><span class="ln">718</span><span class="cl"><span class="c1">#       Parabola, Pardus, Parrot, Parsix, TrueOS, PCLinuxOS, Peppermint,</span></span></span><span class="line"><span class="ln">719</span><span class="cl"><span class="c1">#       popos, Porteus, PostMarketOS, Proxmox, Puppy, PureOS, Qubes, Radix,</span></span></span><span class="line"><span class="ln">720</span><span class="cl"><span class="c1">#       Raspbian, Reborn_OS, Redstar, Redcore, Redhat, Refracted_Devuan,</span></span></span><span class="line"><span class="ln">721</span><span class="cl"><span class="c1">#       Regata, Rosa, sabotage, Sabayon, Sailfish, SalentOS, Scientific,</span></span></span><span class="line"><span class="ln">722</span><span class="cl"><span class="c1">#       Septor, SereneLinux, SharkLinux, Siduction, Slackware, SliTaz,</span></span></span><span class="line"><span class="ln">723</span><span class="cl"><span class="c1">#       SmartOS, Solus, Source_Mage, Sparky, Star, SteamOS, SunOS,</span></span></span><span class="line"><span class="ln">724</span><span class="cl"><span class="c1">#       openSUSE_Leap, openSUSE_Tumbleweed, openSUSE, SwagArch, Tails,</span></span></span><span class="line"><span class="ln">725</span><span class="cl"><span class="c1">#       Trisquel, Ubuntu-Budgie, Ubuntu-GNOME, Ubuntu-MATE, Ubuntu-Studio,</span></span></span><span class="line"><span class="ln">726</span><span class="cl"><span class="c1">#       Ubuntu, Venom, Void, Obarun, windows10, Windows7, Xubuntu, Zorin,</span></span></span><span class="line"><span class="ln">727</span><span class="cl"><span class="c1">#       and IRIX have ascii logos</span></span></span><span class="line"><span class="ln">728</span><span class="cl"><span class="c1"># NOTE: Arch, Ubuntu, Redhat, and Dragonfly have 'old' logo variants.</span></span></span><span class="line"><span class="ln">729</span><span class="cl"><span class="c1">#       Use '{distro name}_old' to use the old logos.</span></span></span><span class="line"><span class="ln">730</span><span class="cl"><span class="c1"># NOTE: Ubuntu has flavor variants.</span></span></span><span class="line"><span class="ln">731</span><span class="cl"><span class="c1">#       Change this to Lubuntu, Kubuntu, Xubuntu, Ubuntu-GNOME,</span></span></span><span class="line"><span class="ln">732</span><span class="cl"><span class="c1">#       Ubuntu-Studio, Ubuntu-Mate  or Ubuntu-Budgie to use the flavors.</span></span></span><span class="line"><span class="ln">733</span><span class="cl"><span class="c1"># NOTE: Arcolinux, Dragonfly, Fedora, Alpine, Arch, Ubuntu,</span></span></span><span class="line"><span class="ln">734</span><span class="cl"><span class="c1">#       CRUX, Debian, Gentoo, FreeBSD, Mac, NixOS, OpenBSD, android,</span></span></span><span class="line"><span class="ln">735</span><span class="cl"><span class="c1">#       Antrix, CentOS, Cleanjaro, ElementaryOS, GUIX, Hyperbola,</span></span></span><span class="line"><span class="ln">736</span><span class="cl"><span class="c1">#       Manjaro, MXLinux, NetBSD, Parabola, POP_OS, PureOS,</span></span></span><span class="line"><span class="ln">737</span><span class="cl"><span class="c1">#       Slackware, SunOS, LinuxLite, OpenSUSE, Raspbian,</span></span></span><span class="line"><span class="ln">738</span><span class="cl"><span class="c1">#       postmarketOS, and Void have a smaller logo variant.</span></span></span><span class="line"><span class="ln">739</span><span class="cl"><span class="c1">#       Use '{distro name}_small' to use the small variants.</span></span></span><span class="line"><span class="ln">740</span><span class="cl"><span class="nv">ascii_distro</span><span class="o">=</span><span class="s2">"auto"</span></span></span><span class="line"><span class="ln">741</span><span class="cl"></span></span><span class="line"><span class="ln">742</span><span class="cl"><span class="c1"># Ascii Colors</span></span></span><span class="line"><span class="ln">743</span><span class="cl"></span></span><span class="line"><span class="ln">744</span><span class="cl"><span class="c1"># Default:  'distro'</span></span></span><span class="line"><span class="ln">745</span><span class="cl"><span class="c1"># Values:   'distro', 'num' 'num' 'num' 'num' 'num' 'num'</span></span></span><span class="line"><span class="ln">746</span><span class="cl"><span class="c1"># Flag:     --ascii_colors</span></span></span><span class="line"><span class="ln">747</span><span class="cl"></span></span><span class="line"><span class="ln">748</span><span class="cl"><span class="c1"># Example:</span></span></span><span class="line"><span class="ln">749</span><span class="cl"><span class="c1"># ascii_colors=(distro)      - Ascii is colored based on Distro colors.</span></span></span><span class="line"><span class="ln">750</span><span class="cl"><span class="c1"># ascii_colors=(4 6 1 8 8 6) - Ascii is colored using these colors.</span></span></span><span class="line"><span class="ln">751</span><span class="cl"><span class="nv">ascii_colors</span><span class="o">=(</span>distro<span class="o">)</span></span></span><span class="line"><span class="ln">752</span><span class="cl"></span></span><span class="line"><span class="ln">753</span><span class="cl"><span class="c1"># Bold ascii logo</span></span></span><span class="line"><span class="ln">754</span><span class="cl"><span class="c1"># Whether or not to bold the ascii logo.</span></span></span><span class="line"><span class="ln">755</span><span class="cl"></span></span><span class="line"><span class="ln">756</span><span class="cl"><span class="c1"># Default: 'on'</span></span></span><span class="line"><span class="ln">757</span><span class="cl"><span class="c1"># Values:  'on', 'off'</span></span></span><span class="line"><span class="ln">758</span><span class="cl"><span class="c1"># Flag:    --ascii_bold</span></span></span><span class="line"><span class="ln">759</span><span class="cl"><span class="nv">ascii_bold</span><span class="o">=</span><span class="s2">"off"</span></span></span><span class="line"><span class="ln">760</span><span class="cl"></span></span><span class="line"><span class="ln">761</span><span class="cl"><span class="c1"># Image Options</span></span></span><span class="line"><span class="ln">762</span><span class="cl"></span></span><span class="line"><span class="ln">763</span><span class="cl"><span class="c1"># Image loop</span></span></span><span class="line"><span class="ln">764</span><span class="cl"><span class="c1"># Setting this to on will make neofetch redraw the image constantly until</span></span></span><span class="line"><span class="ln">765</span><span class="cl"><span class="c1"># Ctrl+C is pressed. This fixes display issues in some terminal emulators.</span></span></span><span class="line"><span class="ln">766</span><span class="cl"></span></span><span class="line"><span class="ln">767</span><span class="cl"><span class="c1"># Default:  'off'</span></span></span><span class="line"><span class="ln">768</span><span class="cl"><span class="c1"># Values:   'on', 'off'</span></span></span><span class="line"><span class="ln">769</span><span class="cl"><span class="c1"># Flag:     --loop</span></span></span><span class="line"><span class="ln">770</span><span class="cl"><span class="nv">image_loop</span><span class="o">=</span><span class="s2">"off"</span></span></span><span class="line"><span class="ln">771</span><span class="cl"></span></span><span class="line"><span class="ln">772</span><span class="cl"><span class="c1"># Thumbnail directory</span></span></span><span class="line"><span class="ln">773</span><span class="cl"></span></span><span class="line"><span class="ln">774</span><span class="cl"><span class="c1"># Default: '~/.cache/thumbnails/neofetch'</span></span></span><span class="line"><span class="ln">775</span><span class="cl"><span class="c1"># Values:  'dir'</span></span></span><span class="line"><span class="ln">776</span><span class="cl"><span class="nv">thumbnail_dir</span><span class="o">=</span><span class="s2">"</span><span class="si">${</span><span class="nv">XDG_CACHE_HOME</span><span class="k">:-</span><span class="si">${</span><span class="nv">HOME</span><span class="si">}</span><span class="p">/.cache</span><span class="si">}</span><span class="s2">/thumbnails/neofetch"</span></span></span><span class="line"><span class="ln">777</span><span class="cl"></span></span><span class="line"><span class="ln">778</span><span class="cl"><span class="c1"># Crop mode</span></span></span><span class="line"><span class="ln">779</span><span class="cl"></span></span><span class="line"><span class="ln">780</span><span class="cl"><span class="c1"># Default:  'normal'</span></span></span><span class="line"><span class="ln">781</span><span class="cl"><span class="c1"># Values:   'normal', 'fit', 'fill'</span></span></span><span class="line"><span class="ln">782</span><span class="cl"><span class="c1"># Flag:     --crop_mode</span></span></span><span class="line"><span class="ln">783</span><span class="cl"></span></span><span class="line"><span class="ln">784</span><span class="cl"><span class="c1"># See this wiki page to learn about the fit and fill options.</span></span></span><span class="line"><span class="ln">785</span><span class="cl"><span class="c1"># https://github.com/dylanaraps/neofetch/wiki/What-is-Waifu-Crop%3F</span></span></span><span class="line"><span class="ln">786</span><span class="cl"><span class="nv">crop_mode</span><span class="o">=</span><span class="s2">"normal"</span></span></span><span class="line"><span class="ln">787</span><span class="cl"></span></span><span class="line"><span class="ln">788</span><span class="cl"><span class="c1"># Crop offset</span></span></span><span class="line"><span class="ln">789</span><span class="cl"><span class="c1"># Note: Only affects 'normal' crop mode.</span></span></span><span class="line"><span class="ln">790</span><span class="cl"></span></span><span class="line"><span class="ln">791</span><span class="cl"><span class="c1"># Default:  'center'</span></span></span><span class="line"><span class="ln">792</span><span class="cl"><span class="c1"># Values:   'northwest', 'north', 'northeast', 'west', 'center'</span></span></span><span class="line"><span class="ln">793</span><span class="cl"><span class="c1">#           'east', 'southwest', 'south', 'southeast'</span></span></span><span class="line"><span class="ln">794</span><span class="cl"><span class="c1"># Flag:     --crop_offset</span></span></span><span class="line"><span class="ln">795</span><span class="cl"><span class="nv">crop_offset</span><span class="o">=</span><span class="s2">"center"</span></span></span><span class="line"><span class="ln">796</span><span class="cl"></span></span><span class="line"><span class="ln">797</span><span class="cl"><span class="c1"># Image size</span></span></span><span class="line"><span class="ln">798</span><span class="cl"><span class="c1"># The image is half the terminal width by default.</span></span></span><span class="line"><span class="ln">799</span><span class="cl"></span></span><span class="line"><span class="ln">800</span><span class="cl"><span class="c1"># Default: 'auto'</span></span></span><span class="line"><span class="ln">801</span><span class="cl"><span class="c1"># Values:  'auto', '00px', '00%', 'none'</span></span></span><span class="line"><span class="ln">802</span><span class="cl"><span class="c1"># Flags:   --image_size</span></span></span><span class="line"><span class="ln">803</span><span class="cl"><span class="c1">#          --size</span></span></span><span class="line"><span class="ln">804</span><span class="cl"><span class="nv">image_size</span><span class="o">=</span><span class="s2">"auto"</span></span></span><span class="line"><span class="ln">805</span><span class="cl"></span></span><span class="line"><span class="ln">806</span><span class="cl"><span class="c1"># Gap between image and text</span></span></span><span class="line"><span class="ln">807</span><span class="cl"></span></span><span class="line"><span class="ln">808</span><span class="cl"><span class="c1"># Default: '3'</span></span></span><span class="line"><span class="ln">809</span><span class="cl"><span class="c1"># Values:  'num', '-num'</span></span></span><span class="line"><span class="ln">810</span><span class="cl"><span class="c1"># Flag:    --gap</span></span></span><span class="line"><span class="ln">811</span><span class="cl"><span class="nv">gap</span><span class="o">=</span><span class="m">3</span></span></span><span class="line"><span class="ln">812</span><span class="cl"></span></span><span class="line"><span class="ln">813</span><span class="cl"><span class="c1"># Image offsets</span></span></span><span class="line"><span class="ln">814</span><span class="cl"><span class="c1"># Only works with the w3m backend.</span></span></span><span class="line"><span class="ln">815</span><span class="cl"></span></span><span class="line"><span class="ln">816</span><span class="cl"><span class="c1"># Default: '0'</span></span></span><span class="line"><span class="ln">817</span><span class="cl"><span class="c1"># Values:  'px'</span></span></span><span class="line"><span class="ln">818</span><span class="cl"><span class="c1"># Flags:   --xoffset</span></span></span><span class="line"><span class="ln">819</span><span class="cl"><span class="c1">#          --yoffset</span></span></span><span class="line"><span class="ln">820</span><span class="cl"><span class="nv">yoffset</span><span class="o">=</span><span class="m">0</span></span></span><span class="line"><span class="ln">821</span><span class="cl"><span class="nv">xoffset</span><span class="o">=</span><span class="m">0</span></span></span><span class="line"><span class="ln">822</span><span class="cl"></span></span><span class="line"><span class="ln">823</span><span class="cl"><span class="c1"># Image background color</span></span></span><span class="line"><span class="ln">824</span><span class="cl"><span class="c1"># Only works with the w3m backend.</span></span></span><span class="line"><span class="ln">825</span><span class="cl"></span></span><span class="line"><span class="ln">826</span><span class="cl"><span class="c1"># Default: ''</span></span></span><span class="line"><span class="ln">827</span><span class="cl"><span class="c1"># Values:  'color', 'blue'</span></span></span><span class="line"><span class="ln">828</span><span class="cl"><span class="c1"># Flag:    --bg_color</span></span></span><span class="line"><span class="ln">829</span><span class="cl"><span class="nv">background_color</span><span class="o">=</span></span></span><span class="line"><span class="ln">830</span><span class="cl"></span></span><span class="line"><span class="ln">831</span><span class="cl"><span class="c1"># Misc Options</span></span></span><span class="line"><span class="ln">832</span><span class="cl"></span></span><span class="line"><span class="ln">833</span><span class="cl"><span class="c1"># Stdout mode</span></span></span><span class="line"><span class="ln">834</span><span class="cl"><span class="c1"># Turn off all colors and disables image backend (ASCII/Image).</span></span></span><span class="line"><span class="ln">835</span><span class="cl"><span class="c1"># Useful for piping into another command.</span></span></span><span class="line"><span class="ln">836</span><span class="cl"><span class="c1"># Default: 'off'</span></span></span><span class="line"><span class="ln">837</span><span class="cl"><span class="c1"># Values: 'on', 'off'</span></span></span><span class="line"><span class="ln">838</span><span class="cl"><span class="nv">stdout</span><span class="o">=</span><span class="s2">"off"</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/mac-mini-m1-problems/</id><link rel="alternate" href="https://burgeonlab.com/blog/mac-mini-m1-problems/"/><title type="html">Mac Mini M1 Problems</title><published>2023-12-13T12:09:15Z</published><updated>2023-12-13T12:09:15Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/images/default_post_cover.png"/><summary type="html">Problems encountered on a new Mac Mini M1, e.g. Universal Control issues, apps not opening, and drive ejection problems. Workarounds provided.</summary><content type="html"><![CDATA[<h2 id="headache-after-upgrading-from-catalina-to-sonoma">Headache After Upgrading From Catalina To Sonoma<a href="#headache-after-upgrading-from-catalina-to-sonoma" class="h-anchor" title="Permalink to #Headache After Upgrading From Catalina To Sonoma"></a></h2>   <p>Having come from a 10+ year old MacBook Air that’s still running smoothly on macOS Catalina; I find it a bit disconcerting how I need to troubleshoot through weird bugs and errors on a modern M1 Apple Silicon Mac Mini running macOS Sonoma 14. I’ve used Mojave, El Capitan and Catalina on the Air and don’t remember having this many bugs/errors…</p><p>Anyhow, this post will mainly be bullet points on how I troubleshoot the issues encountered. I will be updating this post as I find solutions to further problems. Hope it helps you!</p><h2 id="problems-on-my-mac-mini-m1-2020-so-far">Problems On My Mac Mini M1 2020 (So Far)<a href="#problems-on-my-mac-mini-m1-2020-so-far" class="h-anchor" title="Permalink to #Problems On My Mac Mini M1 2020 (So Far)"></a></h2>   <ol><li>Unable to eject connected disks</li><li>Universal Control not connecting with iPad Mini 6</li><li>Apps unable to open when downloaded outside of the Mac App Store</li></ol><h3 id="error-cannot-eject-connected-disks">Error: Cannot Eject Connected Disks<a href="#error-cannot-eject-connected-disks" class="h-anchor" title="Permalink to #Error: Cannot Eject Connected Disks"></a></h3>   <ul><li>Close all active applications that could be using the disk.</li><li>Force restart Finder.</li><li>Log out and log back in. Try ejecting the disk again.</li><li>Turn off the computer and unplug the drive after the drive stops spinning/light turns off.</li></ul><h4 id="note-about-thunderbolt-external-enclosuresunsafe-shutdowns">Note About Thunderbolt External Enclosures—Unsafe Shutdowns<a href="#note-about-thunderbolt-external-enclosuresunsafe-shutdowns" class="h-anchor" title="Permalink to #Note About Thunderbolt External Enclosures—Unsafe Shutdowns"></a></h4>   <ul><li>Despite ejecting Thunderbolt<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup> drive volumes, the drive remains connected within Disk Utility. If the drive is unplugged while the computer is running, it will result in +1 in the “unsafe shutdown” counter.</li><li>After researching for other users facing <a href="https://forums.macrumors.com/threads/thunderbolt-3-m-2-nvme-ssd-enclosures.2027925/page-30" target="_blank" class="ext-link" rel="noopener noreferrer">similar issues</a> with their SSD enclosures, it seems to be a problem with the Mac NVME driver not fully ejecting the Thunderbolt device.</li><li>To prevent unsafe shutdowns, I make sure to only physically disconnect when the Mac is off.</li></ul><h3 id="error-universal-control-not-working">Error: Universal Control Not Working<a href="#error-universal-control-not-working" class="h-anchor" title="Permalink to #Error: Universal Control Not Working"></a></h3>   <p>Here are the troubleshooting steps.</p><ul><li>Toggle off “Block all incoming connections” and “Enable stealth mode” in System Settings > Network > Firewall > Options.</li><li>In the same window, add “Control Center.app” (<code>/System/Library/CoreServices/ControlCenter.app</code>) and “Universal Control.app” (<code>/System/Library/CoreServices/UniversalControl.app</code>) to “Allow incoming connections”.</li><li>Remember to turn on bluetooth on both devices and connected to the same WiFi network.</li><li>In System Settings > Displays > Advance, ensure all three toggles are on under “Link to Mac or iPad”.</li><li>Confirm any third-party firewall/network filter is not blocking Universal Control, e.g. Little Snitch, LuLu, NetBarrier.</li><li>Both devices should be signed in under the same Apple ID with 2FA turned on.</li><li>If you’ve done all the above but it’s still not connecting/device is not being recognized; sleep your Mac and try again upon wake.</li><li>To confirm it has worked, you should see a second device graphic next to your main monitor, in System Settings > Displays. Clicking the + button will show “Link keyboard and mouse to” and your device name e.g. ipadmini6. You can arrange the display so that you can slide the mouse easily to the remote device.</li></ul><h3 id="error-the-app-is-damaged-and-can-not-be-opened-on-mac">Error: “The App is Damaged and Can not be Opened on Mac”<a href="#error-the-app-is-damaged-and-can-not-be-opened-on-mac" class="h-anchor" title="Permalink to #Error: &ldquo;The App is Damaged and Can not be Opened on Mac&rdquo;"></a></h3>   <ul><li>This is caused by com.apple.quarantine, a safety feature. It prevents opening apps from unidentified developers or apps downloaded outside of the Mac App Store.</li><li>As I often use <a href="https://formulae.brew.sh/" target="_blank" class="ext-link" rel="noopener noreferrer">Homebrew</a> or download apps directly from GitHub repos, I get this error quite often.</li><li>To remove the quarantine attribute from the app, open terminal and type the following. This is an example for the app <a href="https://peazip.github.io/" target="_blank" class="ext-link" rel="noopener noreferrer">PeaZip</a>.</li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">1</span><span class="cl">xattr -d com.apple.quarantine /Applications/peazip.app</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><ul><li>If you want to remove the attribute recursively (including all its files and subfolders), add the <code>-r flag</code>. For example:</li></ul>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash"><span class="line"><span class="ln">1</span><span class="cl">xattr -r -d com.apple.quarantine /Applications/Feishin.app</span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><div class="footnotes"><hr><ol><li id="fn:1"><p>I have the ACASIS TBU405PROM1 with Samsung 980 Pro 2TB partitioned into two volumes; one for Time Machine, the other for general purpose. <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/adding-a-simple-scroll-to-the-top-button-to-your-hugo-site/</id><link rel="alternate" href="https://burgeonlab.com/blog/adding-a-simple-scroll-to-the-top-button-to-your-hugo-site/"/><title type="html">Adding a Simple “Scroll to the Top” Button to Your Hugo Site</title><published>2023-10-21T10:58:31Z</published><updated>2025-10-29T00:00:00Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/adding-a-simple-scroll-to-the-top-button-to-your-hugo-site/button.webp"/><summary type="html">Learn how to easily add a basic scroll to top button to your Hugo site with just HTML and CSS, without the need of JavaScript.</summary><content type="html"><![CDATA[<h2 id="update-2025">Update (2025)<a href="#update-2025" class="h-anchor" title="Permalink to #Update (2025)"></a></h2>   <p>I recently realised my jump-to-top button is being hidden by uBlock Origin as it is being categorized as “Other Annoyances” under “Cosmetic Filtering”. To prevent the scroll to top button from being blocked, I changed a few things to exclude it from the list (check it by turning on uBlock > Open the logger). Try changing it to phrases not in the list but still makes sense.</p><ol><li>Change <code>id</code> from <code>top-anchor</code> to <code>uptop-anchor</code></li><li>Change <code>class</code> from <code>back-to-top</code> to <code>skip-to-top</code></li><li>Change <code>aria-label</code> from <code>Back to top</code> to <code>Skip back up</code></li></ol><p>This is the new HTML button snippet, with the arrow now updated to use an icon partial— an upwards <code>triangle.svg</code>, instead of an up arrow symbol like before.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html"><span class="line"><span class="ln">1</span><span class="cl"><span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"#uptop-anchor"</span> <span class="na">class</span><span class="o">=</span><span class="s">"skip-to-top"</span> <span class="na">aria-label</span><span class="o">=</span><span class="s">"Skip back up"</span><span class="p">></span>{{ partial "icon.html" "triangle" }}<span class="p"></</span><span class="nt">a</span><span class="p">></span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>And for the anchor link, since I’ve switch theme’s since then, I’ve added the <code>id</code> to the <code>container</code> class which is the main wrapper for the whole page.</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html"><span class="line"><span class="ln">1</span><span class="cl"><span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"container"</span> <span class="na">id</span><span class="o">=</span><span class="s">"uptop-anchor"</span><span class="p">></span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>Lastly, update the CSS to use the new names.</p><hr><h2 id="just-html-and-css">Just HTML and CSS<a href="#just-html-and-css" class="h-anchor" title="Permalink to #Just HTML and CSS"></a></h2>   <p>With only basic HTML and CSS knowledge, it was great news when I found out that a scroll-to-top button can be easily created without Javascript<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup>. I went ahead to add it to my current Hugo theme; <a href="https://github.com/lxndrblz/anatole" target="_blank" class="ext-link" rel="noopener noreferrer">Anatole</a>.</p><p>The main gist is to create an anchor at the top of the page, and have the link/button go to the anchor when pressed so it “scrolls to the top”.</p><h2 id="where-to-put-the-anchor">Where to Put the Anchor<a href="#where-to-put-the-anchor" class="h-anchor" title="Permalink to #Where to Put the Anchor"></a></h2>   <p>Since the navigation bar/header bar persists while scrolling in the Anatole theme; instead of adding the anchor <code>id="top-anchor"</code> to <code>header class="header"</code>, add it to <code>class="wrapper__main" id="top-anchor"</code>.</p><p>Placing the anchor in <code>class="wrapper__main"</code> and also <code>class="wrapper__main wrapper__main--fullscreen"</code> will make the link jump up to the top correctly - otherwise it will not move when the button is pressed. Add this <code>id</code> to <code>baseof.html</code> under <code>layouts/_default</code>.</p><p>If you don’t have a <code>baseof.html</code> file already in your Hugo directory, try to find and copy the default from your theme’s <a href="https://github.com/lxndrblz/anatole/tree/master/layouts/_default" target="_blank" class="ext-link" rel="noopener noreferrer">repo</a>.</p><h3 id="link-for-button">Link For Button<a href="#link-for-button" class="h-anchor" title="Permalink to #Link For Button"></a></h3>          <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html"><span class="line"><span class="ln">1</span><span class="cl"> <span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"#top-anchor"</span><span class="p">><</span><span class="nt">button</span> <span class="na">class</span><span class="o">=</span><span class="s">"top-anchor"</span><span class="p">></span>⇧<span class="p"></</span><span class="nt">button</span><span class="p">></</span><span class="nt">a</span><span class="p">></span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><p>Place this link right above <code></body></code> in <code>baseof.html</code>. My link text is an up arrow symbol, but you could use an emoji, text or image instead.</p><h2 id="create-button-using-css">Create Button Using CSS<a href="#create-button-using-css" class="h-anchor" title="Permalink to #Create Button Using CSS"></a></h2>   <p>Create a css file if it doesn’t already exist.</p><p><code>assets/css/syntax.css</code></p><p>Then create a button with your own customizations with the class created earlier (<code>top-anchor</code>). I have used these options for mine:</p>       <div class="highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">.</span><span class="nc">top-anchor</span> <span class="p">{</span> <span class="c">/*jump to top button, link is in baseof.html */</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="k">position</span><span class="p">:</span> <span class="kc">fixed</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="k">bottom</span><span class="p">:</span> <span class="mi">9</span><span class="kt">px</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="k">right</span><span class="p">:</span> <span class="mi">9</span><span class="kt">px</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="k">max-width</span><span class="p">:</span> <span class="mi">39</span><span class="kt">px</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="k">max-height</span><span class="p">:</span> <span class="mi">39</span><span class="kt">px</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="k">height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="k">border</span><span class="p">:</span> <span class="mi">3</span><span class="kt">px</span> <span class="kc">ridge</span> <span class="mh">#2a3031</span><span class="p">;</span></span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="k">border-radius</span><span class="p">:</span> <span class="mf">0.5</span><span class="kt">rem</span><span class="p">;</span></span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="k">justify-content</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span></span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="k">background</span><span class="p">:</span> <span class="mh">#353b3c</span><span class="p">;</span></span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="k">font-size</span><span class="p">:</span> <span class="mi">2</span><span class="kt">rem</span><span class="p">;</span></span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="k">color</span><span class="p">:</span> <span class="mh">#c6c7c4</span><span class="p">;</span></span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="k">font-weight</span><span class="p">:</span> <span class="mi">500</span><span class="p">;</span></span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="k">cursor</span><span class="p">:</span> <span class="kc">pointer</span><span class="p">;</span></span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="k">opacity</span><span class="p">:</span> <span class="mi">50</span><span class="kt">%</span><span class="p">;</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="ln">19</span><span class="cl"></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="p">.</span><span class="nc">top-anchor</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span></span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="k">opacity</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span></span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="k">color</span><span class="p">:</span> <span class="mh">#B06969</span><span class="p">;</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="p">}</span></span></span></code></pre></div>        <button class="highlight-copy-btn">            </button>  </div><h2 id="result">Result<a href="#result" class="h-anchor" title="Permalink to #Result"></a></h2>   <p>You should get a button on the bottom right corner like this:</p>                             <a href="/blog/adding-a-simple-scroll-to-the-top-button-to-your-hugo-site/button.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/adding-a-simple-scroll-to-the-top-button-to-your-hugo-site/button.webp" alt="Screenshot of scroll to top button on Hugo blog">    </a>       <p>I’ve made mine 50% opacity to reduce distraction, but upon hovering, it will light up to 100% opacity and change to red. You can see the link points to the anchor.</p><p>Hope this helps!</p><div class="footnotes"><hr><ol><li id="fn:1"><p><a href="https://secluded.site/adding-a-better-scroll-to-top-button-without-javascript/" target="_blank" class="ext-link" rel="noopener noreferrer">https://secluded.site/adding-a-better-scroll-to-top-button-without-javascript/</a> <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/booting-dietpi-from-ssd-on-a-raspberry-pi-4b-8gb/</id><link rel="alternate" href="https://burgeonlab.com/blog/booting-dietpi-from-ssd-on-a-raspberry-pi-4b-8gb/"/><title type="html">Booting DietPi from SSD on a Raspberry Pi 4B 8GB</title><published>2023-10-01T20:35:10Z</published><updated>2023-10-01T20:35:10Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/booting-dietpi-from-ssd-on-a-raspberry-pi-4b-8gb/title.webp"/><summary type="html">Booting DietPi Linux from an external SSD on a Raspberry Pi 4B for faster performance. Install using backup images to preserve an existing /home partition.</summary><content type="html"><![CDATA[<h2 id="setting-up-dietpi-on-a-headless-raspberry-pi-4b-8gb">Setting up Dietpi on a Headless Raspberry Pi 4B 8GB<a href="#setting-up-dietpi-on-a-headless-raspberry-pi-4b-8gb" class="h-anchor" title="Permalink to #Setting up Dietpi on a Headless Raspberry Pi 4B 8GB"></a></h2>   <p>Using <a href="https://etcher.balena.io/" target="_blank" class="ext-link" rel="noopener noreferrer">Balena Etcher</a> on macOS, I flashed a copy of the <a href="https://dietpi.com/#downloadinfo" target="_blank" class="ext-link" rel="noopener noreferrer">Raspberry Pi 2/3/4 DietPi</a> image onto a Kingston 128GB Canvas Go Plus microSD card. Went through the default configuration via SSH in a Wi-Fi, headless setup.</p>                             <a href="/blog/booting-dietpi-from-ssd-on-a-raspberry-pi-4b-8gb/title.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/booting-dietpi-from-ssd-on-a-raspberry-pi-4b-8gb/title.webp" alt="Screenshot of terminal window showing DietPi OS.">    </a>       <h3 id="good-ol-ethernet-saves-the-day">Good Ol’ Ethernet Saves the Day<a href="#good-ol-ethernet-saves-the-day" class="h-anchor" title="Permalink to #Good Ol’ Ethernet Saves the Day"></a></h3>   <p>Following the installation <a href="https://dietpi.com/docs/install/" target="_blank" class="ext-link" rel="noopener noreferrer">guide</a>, despite enabling WiFi in the the initial one-time-use <code>dietpi.txt</code> and entering my WiFi credentials in <code>dietpi-wifi.txt</code>, I was unable to get the RPi to connect to my network (which meant  I couldn’t scan for its IP address). I resorted to plugging it into the router with an Ethernet cable; got the IP address of the RPi and turned on WiFi manually in <code>dietpi-config</code> after the first boot. My router is not conveniently accessible, so it was a relief when I could get it to work after the initial setup.</p><h3 id="not-all-wifi-channels-are-showing">Not All WiFi Channels Are Showing<a href="#not-all-wifi-channels-are-showing" class="h-anchor" title="Permalink to #Not All WiFi Channels Are Showing"></a></h3>   <p>If you are having trouble finding the 5Ghz channel of your WiFi connection, ensure the country code is correct. For example, I had HK (Hong Kong) set in the DietPi network locale settings, but it could only detect my 2.4Ghz network. After changing it to CN (China), I was able to see the 5G SSID.</p><h2 id="booting-dietpi-from-external-ssd">Booting Dietpi From External SSD<a href="#booting-dietpi-from-external-ssd" class="h-anchor" title="Permalink to #Booting Dietpi From External SSD"></a></h2>   <p>I bought a UGREEN USB 3.1 enclosure<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup> for my Samsung 870 EVO SATA SSD.</p>                             <a href="/blog/booting-dietpi-from-ssd-on-a-raspberry-pi-4b-8gb/enclosure.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/booting-dietpi-from-ssd-on-a-raspberry-pi-4b-8gb/enclosure.webp" alt="UGREEN 2.5 inch SATA drive USB Enclosure">    </a>                                    <a href="/blog/booting-dietpi-from-ssd-on-a-raspberry-pi-4b-8gb/enclosure_spec.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/booting-dietpi-from-ssd-on-a-raspberry-pi-4b-8gb/enclosure_spec.webp" alt="Specification of aluminium usb SSD enclosure">    </a>       <p>The SSD was previously formatted as ext4 from when I was messing around with antiX and Debian on two old PCs. I had my music library in one of the /home partitions (the other two partitions being a /swap and /boot).</p><p>And here is why it became a bit tricky for me; I couldn’t find an easy way to clone the current OS image from the microSD onto the existing /boot partition, delete the /swap and keep the /home so my music files remain untouched.</p><p>I can’t read or write ext4 on macOS (ext4fuse didn’t work and I didn’t want to pay for the Paragon software), so I had to find a way to clone on the Raspberry Pi CLI via SSH. It took me some searching before I found a way to make a backup image of the currently running DietPi OS from the microSD.</p><h3 id="use-image-backup-script-to-make-an-image">Use Image-Backup Script to Make an Image<a href="#use-image-backup-script-to-make-an-image" class="h-anchor" title="Permalink to #Use Image-Backup Script to Make an Image"></a></h3>   <p>Originally I was going to use <code>dd</code> to make a copy but I read that it is not good practice to copy a live, working system this way. I eventually used a tool called <code>image-utils</code> found at the <a href="https://forums.raspberrypi.com/viewtopic.php?t=332000" target="_blank" class="ext-link" rel="noopener noreferrer">Raspberry Pi forum</a>. Using this <code>image-backup</code> script, a backup of the current system can be made</p><p>I tried flashing the modified DietPi backup image to the /boot partition, preserving the /home partition with my music files, but in the end, all I could do was flash the .img file using Etcher again on my Mac to the SSD, formatting the drive in the process.</p><p>With that out of the way, I shut down the Pi, removed the microSD and plugged in the UGREEN enclosure into one the the USB 3.0 (blue) ports and turned on the Pi. It booted up without any trouble and I called it a day.</p><h2 id="installing-software">Installing Software<a href="#installing-software" class="h-anchor" title="Permalink to #Installing Software"></a></h2>   <p>Filled with anticipation, I went ahead and installed some services via the optimized dietpi-software list.</p>                             <a href="/blog/booting-dietpi-from-ssd-on-a-raspberry-pi-4b-8gb/dietpi_software.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/booting-dietpi-from-ssd-on-a-raspberry-pi-4b-8gb/dietpi_software.webp" alt="Software to be installed on the DietPi distro for Raspberry Pi 4B">    </a>       <p>I only have experience with <a href="https://www.navidrome.org/" target="_blank" class="ext-link" rel="noopener noreferrer">Navidrome</a> and <a href="https://pi-hole.net/" target="_blank" class="ext-link" rel="noopener noreferrer">Pi-hole</a>. Some other self-hosted applications I am planning to run on my Raspberry Pi 4B homelab are:</p><ul><li><a href="https://miniflux.app/docs/installation.html" target="_blank" class="ext-link" rel="noopener noreferrer">Miniflux</a> - RSS</li><li><a href="https://gitlab.com/py_crash/docker-libregrammar" target="_blank" class="ext-link" rel="noopener noreferrer">Libregrammar</a> - LT fork</li><li><a href="https://github.com/wallabag/wallabag" target="_blank" class="ext-link" rel="noopener noreferrer">Wallabag</a> - bookmark manager</li><li><a href="https://calibre-ebook.com/" target="_blank" class="ext-link" rel="noopener noreferrer">calibre</a> - ebook manager</li><li><a href="https://tailscale.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Tailscale</a> - VPN</li></ul><p>A great place to discover programs to self host is <a href="https://github.com/awesome-selfhosted/awesome-selfhosted" target="_blank" class="ext-link" rel="noopener noreferrer">awesome-selfhosted/awesome-selfhosted</a>. I will be sharing how I set up each of these as I learn how to do it!</p><div class="footnotes"><hr><ol><li id="fn:1"><p>I bought the metal case variant UGREEN 2.5" SATA Enclosure, with the phrase “Protect What You Love!” laser etched on the bottom right corner of the top plate. The USB cable is a Micro B to USB-A 3.0. There’s more info from <a href="https://jamesachambers.com/best-ssd-storage-adapters-for-raspberry-pi-4-400/" target="_blank" class="ext-link" rel="noopener noreferrer">James Chamber’s page</a> on what external drives works well with the Raspberry Pi. <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/sudden-influx-of-new-gear/</id><link rel="alternate" href="https://burgeonlab.com/blog/sudden-influx-of-new-gear/"/><title type="html">Sudden Influx of New Gear</title><published>2023-09-28T19:05:34Z</published><updated>2023-09-28T19:05:34Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/blog/sudden-influx-of-new-gear/macmini_geniusbar.webp"/><summary type="html">Post about upgrading my over a decade old MacBook Air, problems with the new Mac Mini M1 and a new Raspberry Pi purchase.</summary><content type="html"><![CDATA[<h2 id="introduction">Introduction<a href="#introduction" class="h-anchor" title="Permalink to #Introduction"></a></h2>   <p>Having gone on a deep dive in the mid of September learning about Hugo and Git for documenting my Raspberry Pi 4B homelab journey; I found myself suddenly distracted by a great deal on a Mac Mini M1 in the local second-hand market.</p>                             <a href="/blog/sudden-influx-of-new-gear/macmini_geniusbar.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/sudden-influx-of-new-gear/macmini_geniusbar.webp" alt="At the Apple Genius Bar">    </a>       <h2 id="retiring-my-trusty-daily-driver">Retiring My Trusty Daily Driver<a href="#retiring-my-trusty-daily-driver" class="h-anchor" title="Permalink to #Retiring My Trusty Daily Driver"></a></h2>   <p>My digital life, since 2012, has been lived through the Apple MacBook Air mid-2012 13", maxed out spec (Dual-Core Intel Core i7 2Ghz, 8GB DDR3 RAM, 512GB SATA SSD). It has served me very well over the years, but increasingly more apps are dropping their software support for Catalina, like Adobe Lightroom Classic - an essential app for me.</p><p>I also had the internal SSD fail on me last year. Luckily a full format, fsck and clean OS install to Catalina saved it; albeit temporarily. Since then, I’ve been on the lookout for a replacement - to which I chose the Mac Mini M1, acting as a stopgap upgrade before investing in a MacBook Pro 14" or 16" with the rumoured impressive 3nm Apple Silicon chip.</p>                             <a href="/blog/sudden-influx-of-new-gear/ssd_health.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/sudden-influx-of-new-gear/ssd_health.webp" alt="Smartctl results of the 2012 MacBook Air">    </a>       <p>Fast forward to last week, I found an almost like new (SSD lifespan only about 1TB data written, less than 30 hours power on hours) Mac Mini M1 in the configuration I want, 16GB RAM + 1TB SSD, for ~$570USD/$540EUR. I pounced at the deal - it even had Apple Care till February of next year!</p><h2 id="apple-m1-misery">Apple M1 Misery?<a href="#apple-m1-misery" class="h-anchor" title="Permalink to #Apple M1 Misery?"></a></h2>   <p>I always had a smooth experience with macOS El Capitan and Mojave on my Air. Even during the bigger jump to Catalina, most software ran well with no crashes or major issues. All the apps I want to run works well. Unfortunately, I cannot say the same for the M1 Mac Mini running Ventura 13.6.</p><p>Ventura OS itself was fairly buggy. Even when moving around Finder and System Settings, I found little bugs here and there. These were some of the things that didn’t work for me:</p><ul><li>Pointer often changes to an “adjust width” icon even when I’m no where near the edge of a window</li><li>The window asking for my password doesn’t pop to the front of the Settings window when I change a setting, making it appear as if Settings is not responding</li><li>Universal Control is not straight forward to setup</li><li>USB-A ports on the back loses power/connectivity after sleeping occasionally (<a href="https://discussions.apple.com/thread/253514345?answerId=256577222022&sortBy=rank" target="_blank" class="ext-link" rel="noopener noreferrer">Apple discussions</a> and <a href="https://forums.macrumors.com/threads/mac-mini-m1-usb-ports-not-working-after-wake-from-sleep.2326616/?post=32549313#post-32549313" target="_blank" class="ext-link" rel="noopener noreferrer">also reports here</a>)</li><li>Often unable to eject external drives and have to resort to logging out or restarting, even with spotlight indexing turned of for said drive</li><li>Not entering sleep mode normally, constantly has wakeup power events scheduled automatically and repeatedly, which I am guessing is related to the loss of power in the USB-A ports. (<a href="https://discussions.apple.com/thread/254333766?page=4" target="_blank" class="ext-link" rel="noopener noreferrer">Reports of it here</a> and on <a href="https://forums.macrumors.com/threads/macos-ventura-13-1-power-events-cause-wakeup-from-sleep.2373692/" target="_blank" class="ext-link" rel="noopener noreferrer">MacRumors forums</a>)</li></ul><h3 id="hardware-fail-there-may-be-an-issue-with-a-memory-module">Hardware Fail: “There may be an issue with a memory module.”<a href="#hardware-fail-there-may-be-an-issue-with-a-memory-module" class="h-anchor" title="Permalink to #Hardware Fail: &ldquo;There may be an issue with a memory module.&rdquo;"></a></h3>   <p>After about two days of setting up and manually migrating files over, the Mac Mini started acting really weird with around 50+ logged crash reports in Console, especially with Electron apps. Then Firefox started crashing repeatedly and on came reboot loops with the “pink screen of death”.</p><p>This was it I thought, I bought a second-hand dud. Something must be wrong hardware-wise. I went ahead with an Apple Diagnostic scan <em>(long press power button to enter recovery mode and then press and hold cmd+D)</em> and lo and behold; there was a memory failure (reference code: PPM001).</p>                             <a href="/blog/sudden-influx-of-new-gear/memfail_m1.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/sudden-influx-of-new-gear/memfail_m1.webp" alt="Mac Mini M1 Diagnostic Scan">    </a>       <p>I felt silly for not doing this hardware test earlier, before I started setting up all my apps and preferences, troubleshooting through all the weird quirks of Apple Silicon. I brought the Mini straight to the Genius Bar where they said the logic board along with the I/O wall will need to be replaced and will take at least five to seven days.</p><h2 id="cant-retire-yet">Can’t Retire Yet<a href="#cant-retire-yet" class="h-anchor" title="Permalink to #Can&rsquo;t Retire Yet"></a></h2>   <p>So today, I am back writing this post on my trusty MacBook Air. My original plan, after getting the Mini all setup, was to replace the battery and SSD on the Air so it could get a new lease of life. I could test out some linux distros on it, maybe try running Docker, or dedicate it for some side project/experimentation… I think these might have to wait a bit until I get the Mini running smoothly as my daily driver.</p><p>I have already bought a nice set of screwdriver bits containing the the Pentalobe 0.8 and 1.2, thermal paste (Arctic MX-4) and a specific SATA M.2 SSD adapter to do the upgrades soon. I will purchase a replacement battery and a SSD nearer the date of upgrade. I’m currently considering these SATA M.2 drives:</p><ul><li>Micron 1300 1TB</li><li>Micron M600 512GB</li><li>Crucial MX500 1TB</li><li>Samsung PM871 512GB</li></ul><p>I will share which SSD I end up choosing and how to the upgrades go. But to check out the new screwdriver, I took off the bottom cover of the MacBook Air and cleaned out <em>11-year-old dust</em>! It runs around 5 to 10 °C lower now compared to before cleaning it. I’ll replace the thermal paste when I do the memory/battery upgrade. I can’t risk losing my daily driver!</p><p>                            <a href="/blog/sudden-influx-of-new-gear/dust_before.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/sudden-influx-of-new-gear/dust_before.webp" alt="11 year old crud">    </a>                                    <a href="/blog/sudden-influx-of-new-gear/dust_after.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/sudden-influx-of-new-gear/dust_after.webp" alt="After cleaning my 2012 MacBook Air">    </a>      </p><h2 id="new-raspberry-pi-5">New Raspberry Pi 5<a href="#new-raspberry-pi-5" class="h-anchor" title="Permalink to #New Raspberry Pi 5"></a></h2>   <p>Just as I was going to upload this post, I found out the new Pi 5 is coming next month. It is such a bummer that I got my 4B <strong>literally</strong> two weeks ago. It is even at the same price but the speed of almost everything is many times faster, not to mention, it has a PCIe slot. Oh well… I already bought a dual fan attachment, which has arrived earlier this week.</p>                             <a href="/blog/sudden-influx-of-new-gear/rpi_dual_fan.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/sudden-influx-of-new-gear/rpi_dual_fan.webp" alt="Yurobot dual fan attachment with LED for the Raspberry Pi 4B">    </a>       <p>I decided the distro to install is <a href="https://dietpi.com/" target="_blank" class="ext-link" rel="noopener noreferrer">DietPi</a>. Will be seeing if I can boot it from an external SSD (Samsung 870 EVO 512GB SATA SSD) in a USB UGREEN enclosure and start my homelabbing journey!</p>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/blog/hello-hugo/</id><link rel="alternate" href="https://burgeonlab.com/blog/hello-hugo/"/><title type="html">Hello Hugo: Starting My Static Site Generator Journey</title><published>2023-09-18T20:00:15Z</published><updated>2023-09-18T20:00:15Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/images/default_post_cover.png"/><summary type="html">Introduction to using Hugo static site generator for blogging, choosing Hugo over WordPress and how to deploy your first site to GitHub Pages.</summary><content type="html"><![CDATA[<h2 id="learning-new-things">Learning new things<a href="#learning-new-things" class="h-anchor" title="Permalink to #Learning new things"></a></h2>   <p>Having grown up with Xanga, LiveJournal, Wordpress.com and Tumblr, then graduating to self-hosting Wordpress; using Hugo is a breath of fresh air in my blogging journey.</p><p>Fast forward to late 2023, I have been trying to wrap my head around GitHub and being more competent with CLI. At the same time, I learnt to customize the macOS terminal with <a href="https://ohmyz.sh/" target="_blank" class="ext-link" rel="noopener noreferrer">OMZ</a> and <a href="https://github.com/romkatv/powerlevel10k" target="_blank" class="ext-link" rel="noopener noreferrer">PowerLevel10k</a>, using Nano to edit files, using <a href="https://www.ssh.com/academy/ssh" target="_blank" class="ext-link" rel="noopener noreferrer">SSH</a> with keys and <a href="https://www.digitalocean.com/community/tutorials/how-to-use-rsync-to-sync-local-and-remote-directories" target="_blank" class="ext-link" rel="noopener noreferrer">rsync</a>.</p><h3 id="my-current-setup">My current setup<a href="#my-current-setup" class="h-anchor" title="Permalink to #My current setup"></a></h3>   <ul><li>MacBook Air 13" (mid-2012) 2GHz Dual-Core i7, 8GB DDR3, 512GB running Catalina (10.15) in clamshell mode<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup></li><li>OWLab Spring keyboard (my first Alice layout) with KTT Wine Red Linear Switches (44g actuation, 50g bottom out)</li><li>BenQ Zowie EC1-B mouse</li><li>Dell 24" U2415 60Hz display</li></ul><p>Coming from someone without any coding background, I have to admit some of what I am learning are hard to grasp. But being a lifetime learner I am determined to get my projects executed!</p><h3 id="experimentation-with-linux">Experimentation with Linux<a href="#experimentation-with-linux" class="h-anchor" title="Permalink to #Experimentation with Linux"></a></h3>   <p>My motivation to learn these new skills comes from the fact I want to setup a <strong>homelab</strong> with a Raspberry Pi 4B 8GB rev 9 (which I have <em>finally</em> acquired at retail price after the restock surge in Q3/Q4 2023).</p><p>During the time I was waiting for the restock, I have been messing around with Linux on two old PCs from the early 2010s; a HP Slimline desktop with stock Debian 12 bookworm and a HP Mini 210-1100 netbook with the super lightweight Debian-based distro — <a href="https://antixlinux.com/" target="_blank" class="ext-link" rel="noopener noreferrer">antiX</a>.</p><p>I learnt how to use Gparted, configure basic network configs and how window managers worked. In the end, I was able to successfully self-host Navidrome and it was exhilarating to say the least!</p><h3 id="raspberry-pi">Raspberry Pi<a href="#raspberry-pi" class="h-anchor" title="Permalink to #Raspberry Pi"></a></h3>                                <a href="/blog/hello-hugo/hello-hugo-cover.webp" target="_blank" rel="noopener noreferrer" class="clickable-image">      <img src="/blog/hello-hugo/hello-hugo-cover.webp" alt="Raspberry Pi 1 Model B in acrylic case">    </a>       <p>During this new-found fascination with Linux, I unboxed a brand new <strong><a href="https://www.raspberrypi.com/news/model-b-now-ships-with-512mb-of-ram/" target="_blank" class="ext-link" rel="noopener noreferrer">Raspberry Pi Model B Rev 2 (512MB ram)</a></strong> I bought 11 years ago in 2012! I found it earlier this month while unpacking old moving boxes I have in storage. It is running headless with the latest Raspberry Pi Lite in 32-bit. I installed Pi-Hole, feeling quite pleased with how it turned out! It is indeed a great first project for those starting out with the Raspberry Pi. The guides online were plentiful and relatively straightforward. SSH connection is via my MacBook Air or iPad Mini 6 with <a href="https://secureshellfish.app/" target="_blank" class="ext-link" rel="noopener noreferrer">Secure ShellFish</a>.</p><h2 id="creating-a-place-to-log-my-technical-trek">Creating a place to log my “technical trek”<a href="#creating-a-place-to-log-my-technical-trek" class="h-anchor" title="Permalink to #Creating a place to log my &ldquo;technical trek&rdquo;"></a></h2>   <h2 id="choosing-a-static-site-generator-as-my-content-management-system-cms">Choosing a Static Site Generator as my Content Management System (CMS)<a href="#choosing-a-static-site-generator-as-my-content-management-system-cms" class="h-anchor" title="Permalink to #Choosing a Static Site Generator as my Content Management System (CMS)"></a></h2>   <p>Having some experience with <a href="https://www.markdownguide.org/cheat-sheet/" target="_blank" class="ext-link" rel="noopener noreferrer">Markdown</a> while using <a href="https://obsidian.md/" target="_blank" class="ext-link" rel="noopener noreferrer">Obsidian</a> (my choice of PKM which I highly recommend) and back in my college days with <a href="https://apps.apple.com/al/app/mou-markdown-editor/id1308265497" target="_blank" class="ext-link" rel="noopener noreferrer">Mou</a> and <a href="https://brettterpstra.com/projects/nvalt/" target="_blank" class="ext-link" rel="noopener noreferrer">Nvalt</a> for note taking — I think it is only wise to write in .md for this second blog I’m starting. (You can find me under my main handle <a href="https://www.eclecticpassions.net" target="_blank" class="ext-link" rel="noopener noreferrer">@eclecticpassions</a>).</p><p>Writing in the Wordpress web interface is quite finicky… I am not a fan. Trying to optimize the webpage load times is also a hassle. Recently, I discovered webrings dedicated to lightweight webpages, e.g. <a href="https://250kb.club/" target="_blank" class="ext-link" rel="noopener noreferrer">250kb.club</a>, <a href="https://512kb.club/" target="_blank" class="ext-link" rel="noopener noreferrer">512kb.club</a> and <a href="https://1mb.club/" target="_blank" class="ext-link" rel="noopener noreferrer">1mb.club</a>.</p><p>These fast, content-focused blogs piqued my interests greatly. I realized most of the sites listed are static websites, using SSGs like Jekyll or Hugo. I also recall some of my Mastodon friends have mentioned it before. Hence, I bought a new domain name on <a href="https://porkbun.com/" target="_blank" class="ext-link" rel="noopener noreferrer">Porkbun</a> (first time customer, liking it so far), installed <a href="https://gohugo.io/" target="_blank" class="ext-link" rel="noopener noreferrer">Hugo</a> (seems to be quite popular and one of the fastest SSGs around), chose a theme (<a href="https://github.com/lxndrblz/anatole" target="_blank" class="ext-link" rel="noopener noreferrer">Anatole</a>) and deployed it with <a href="https://pages.github.com/" target="_blank" class="ext-link" rel="noopener noreferrer">GitHub Pages</a>.</p><h3 id="choice-of-domain-name">Choice of domain name<a href="#choice-of-domain-name" class="h-anchor" title="Permalink to #Choice of domain name"></a></h3>   <p>It took me a day to think of a suitable name but I am quite happy with it. I think “Burgeon Lab” suits my use case quite well.</p> <blockquote>  <p>Burgeon (<em>verb</em>) — Begin to grow or increase rapidly; flourish.</p></blockquote> <p>I will treat it as a place where I can document my side projects and share my progress.</p><h3 id="setting-up-github-actions">Setting up GitHub Actions<a href="#setting-up-github-actions" class="h-anchor" title="Permalink to #Setting up GitHub Actions"></a></h3>   <p>Being able to run a local instance of my Hugo site was relatively straightforward, but I had quite a bit of troubleshooting headaches before I got the Hugo directory working on my GitHub Pages repo. Will post a tutorial on it soon.</p><h2 id="first-two-projects">First two projects<a href="#first-two-projects" class="h-anchor" title="Permalink to #First two projects"></a></h2>   <p>To start off, I will be documenting these two topics:</p><ol><li><p>Setting up and using Hugo as a beginner coming from Wordpress</p></li><li><p>Setting up self-hosted services on my Raspberry Pi 4B with <a href="https://www.docker.com/get-started/" target="_blank" class="ext-link" rel="noopener noreferrer">Docker</a> and <a href="https://www.portainer.io/" target="_blank" class="ext-link" rel="noopener noreferrer">Portainer</a>, acting as my first ever homelab server</p></li></ol><p>Thanks for reading. If you intend to follow along, please follow my <a href="https://burgeonlab.com/subscribe/">blog’s feed</a> with your favourite RSS aggregator/reader app;. or follow me on <a href="https://fosstodon.org/@eclecticpassions/" target="_blank" class="ext-link" rel="noopener noreferrer">Mastodon</a> where I will be post updates.</p><div class="footnotes"><hr><ol><li id="fn:1"><p>My beloved MBA from 2012 is still running strong! I am just waiting for the 3nm Apple silicon chips to be released, planning to get either the 14" or 16" MacBook Pro. But with possible rumours of it being delayed, I am considering getting a M1 or M2 Mac Mini with 16GB ram in the meantime (if I can find one in the second-hand market). <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry><entry><id>https://burgeonlab.com/about/</id><link rel="alternate" href="https://burgeonlab.com/about/"/><title type="html">About: Naty the Tech Dabbler &amp; Tinkerer</title><published>2023-09-16T23:48:18Z</published><updated>2025-06-15T00:00:00Z</updated><author><name>Naty S</name></author><link rel="enclosure" type="image/webp" href="https://burgeonlab.com/about/og_img_about.webp"/><summary type="html">Some background about Naty S, author of BurgeonLab.com.</summary><content type="html"><![CDATA[<h2 id="about-this-site">About This Site<a href="#about-this-site" class="h-anchor" title="Permalink to #About This Site"></a></h2>   <p>I created a dedicated <a href="https://burgeonlab.com/colophon/">/colophon</a> detailing the technicalities on how this site is run, tools used, etc.</p><h2 id="content-expectations">Content Expectations<a href="#content-expectations" class="h-anchor" title="Permalink to #Content Expectations"></a></h2>   <p>This blog is a little outlet for me to document my tech explorations and topics I find interesting. Posts are grouped into <a href="https://burgeonlab.com/series/">/series</a> for easier access and have <a href="https://burgeonlab.com/tags/">/tags</a> for suggesting related content.</p> <blockquote>  <p><strong>DISCLAIMER</strong>: All content you read on <em>BurgeonLab.com</em> is created by a human and will never be generated by AI. Learn what <a href="https://burgeonlab.com/colophon/#genai-usage">AI tools</a> I <em>do use</em> on this blog.</p></blockquote> <p>Soon, I will be taking up the <a href="https://100daystooffload.com/" target="_blank" class="ext-link" rel="noopener noreferrer">#100DaysToOffload</a> challenge to help incentivize me to become a sustainable blogger. Topics might become more diverse, so consider <a href="https://burgeonlab.com/subscribe/">subscribing</a> to my RSS<sup id="fnref:1"><a href="#fn:1" class="footnote-ref">1</a></sup> or Atom feed to stay up to date on future content (I appreciate your readership!). For now, <em>BurgeonLab</em> will mainly be a tech-focused blog.</p><h2 id="contact">Contact<a href="#contact" class="h-anchor" title="Permalink to #Contact"></a></h2>   <p>Say hi via any of the platforms on my <a href="https://burgeonlab.com/contact/">/contact</a> page. I’m always up for a chat about shared hobbies or topics of interest, and would be more than happy if you want to collaborate on something together.</p><p>If you have queries about something specific on this site, pease leave a message in the comments section at the bottom of the relevant page using a Mastodon or <a href="https://jointhefediverse.net/" target="_blank" class="ext-link" rel="noopener noreferrer">Fediverse</a> account. Alternatively, you can directly send me an email or shoot me a message on Matrix or Signal.</p><h2 id="links">Links<a href="#links" class="h-anchor" title="Permalink to #Links"></a></h2>   <p>Here is my <a href="https://eclecticpassions.net/links/" target="_blank" class="ext-link" rel="noopener noreferrer">linkinbio</a> and <a href="https://keyoxide.org/aspe:keyoxide.org:ECRWVAY2NPHAVIXTQEBZCU5HRQ/" target="_blank" class="ext-link" rel="noopener noreferrer">Keyoxide</a> profile. There’s also a dedicated <a href="https://burgeonlab.com/contact/">/contact</a> page if you want to connect.</p><p>Or you can visit my other two blogs!</p><p><strong>General Blog:</strong> <a href="https://eclecticpassions.net/" target="_blank" class="ext-link" rel="noopener noreferrer">eclecticpassions.net</a></p><p><a href="https://eclecticpassions.net/feed" target="_blank" class="ext-link" rel="noopener noreferrer"><img src="https://img.shields.io/badge/Subscribe_to_eclecticpassions.net-RSS-orange?style=for-the-badge&logo=RSS&logoColor=%23FE7D37" alt="Static Badge"></a></p><p><strong>Photography Blog:</strong> <a href="https://aperture2iris.com/" target="_blank" class="ext-link" rel="noopener noreferrer">aperture2iris.com</a></p><p><a href="https://aperture2iris.com/index.xml" target="_blank" class="ext-link" rel="noopener noreferrer"><img src="https://img.shields.io/badge/Subscribe_to_aperture2iris.com-RSS-orange?style=for-the-badge&logo=RSS&logoColor=%23FE7D37" alt="Static Badge"></a></p><h2 id="about-me">About Me<a href="#about-me" class="h-anchor" title="Permalink to #About Me"></a></h2>   <p>Okay, a little about me. I’m Naty, a trained healthcare professional currently looking for opportunities outside primary care. I am a third-culture kid who grew up and studied abroad in Canada and Ireland, currently based in Hong Kong, SAR.</p><p>Some words I’d use to describe myself, in no particular order:</p><table class="traits-table"><tr><td>quick-thinking</td><td>serious</td></tr><tr><td>observant</td><td>perfectionist</td></tr><tr><td>good memory</td><td>old-fashioned</td></tr><tr><td>skeptical</td><td>punctual</td></tr><tr><td>helpful</td><td>pessimistic</td></tr><tr><td>detail-oriented</td><td>dexterous</td></tr><tr><td>introvert</td><td>geeky</td></tr><tr><td>disciplined</td><td>eccentric</td></tr><tr><td>competitive</td><td>responsible</td></tr><tr><td>studious</td><td>a bit OCD</td></tr><tr><td>kind</td><td>adaptable</td></tr></table><h3 id="origin-of-my-passion-for-technology">Origin of My Passion for Technology<a href="#origin-of-my-passion-for-technology" class="h-anchor" title="Permalink to #Origin of My Passion for Technology"></a></h3>   <p>Ever since I was a kid, I loved computers and all things technology. I still remember my mom getting me my first computer—a HP Pavilion tower PC with <a href="https://en.wikipedia.org/wiki/Windows_95" target="_blank" class="ext-link" rel="noopener noreferrer">Windows 95</a>. I was lucky to get early exposure to a second OS at school too, with the <a href="https://en.wikipedia.org/wiki/IMac_G3" target="_blank" class="ext-link" rel="noopener noreferrer">iMac G3</a>. <em>Oh, days were much simpler back then…</em></p><h3 id="technology-stack">Technology Stack<a href="#technology-stack" class="h-anchor" title="Permalink to #Technology Stack"></a></h3>   <p>The only “official” IT studies I’ve completed is IGCSE ICT; but within my social groups, I am pleased to be the go-to techie and IT support person! I enjoy a good challenge and learning new skills meets that criteria. As a non-IT professional, I’m proud of my self-taught technical skills through experimentation and online resources (if I may say so myself)!</p><p>The table below is the tech stack I’ve been practicing. By no means am I an expert in any of these—but I am able to do the things I want and are familiar with the basics at least. I’m most comfortable using Zsh, Markdown, TOML, Hugo, WordPress, Obsidian, and the three operating systems.</p> <input type="checkbox" id="toggleRows" hidden><label for="toggleRows" id="showBtn">Show table »</label><label for="toggleRows" id="hideBtn">Hide table «</label><table class="techstack">    <tbody>  <tr>      <td>CLI Tools</td>      <td class="icon-row">        <a href="https://zsh.sourceforge.io/" target="_blank" rel="noopener noreferrer">                  </a>        <a href="https://brew.sh/" target="_blank" rel="noopener noreferrer">                  </a>      </td>  </tr>  <tr>    <td>Operating Systems</td>    <td class="icon-row">      <a href="https://en.wikipedia.org/wiki/Android_(operating_system)" target="_blank" rel="noopener noreferrer">                      </a>      <a href="https://en.wikipedia.org/wiki/MacOS" target="_blank" rel="noopener noreferrer">                </a>        <a href="https://www.linuxmint.com/" target="_blank" rel="noopener noreferrer"></a>         <a href="https://kernel.org/" target="_blank" rel="noopener noreferrer"></a></td></tr>  <tr>    <td>Frontend</td>    <td class="icon-row">      <a href="https://vscodium.com/" target="_blank" rel="noopener noreferrer">                </a>        <a href="https://html.spec.whatwg.org/multipage/" target="_blank" rel="noopener noreferrer">          </a>          <a href="https://www.w3.org/TR/css/#css" target="_blank" rel="noopener noreferrer"></a>          <a href="https://tailwindcss.com/" target="_blank" rel="noopener noreferrer"></a>        </td></tr><tr>  <td>Programming Languages</td>  <td class="icon-row">    <a href="https://www.python.org/" target="_blank" rel="noopener noreferrer"></a>    <a href="https://go.dev/" target="_blank" rel="noopener noreferrer"></a>  </td></tr><tr>  <td>Markup Languages</td>  <td class="icon-row">    <a href="https://en.wikipedia.org/wiki/Markdown" target="_blank" rel="noopener noreferrer"></a>    <a href="https://toml.io/en/" target="_blank" rel="noopener noreferrer">       </a>      </td></tr>  <tr>      <td>CMS</td>      <td class="icon-row">        <a href="https://gohugo.io/" target="_blank" rel="noopener noreferrer">        </a>        <a href="https://wordpress.org/" target="_blank" rel="noopener noreferrer">          </a>        </td>  </tr>  <tr>      <td>Version Control</td>      <td class="icon-row">        <a href="https://git-scm.com/" target="_blank" rel="noopener noreferrer"></a>        <a href="https://github.com/" target="_blank" rel="noopener noreferrer"></a>      </td>  </tr>  <tr>    <td>Home Lab</td>    <td class="icon-row">      <a href="https://www.raspberrypi.com/" target="_blank" rel="noopener noreferrer"></a>      <a href="https://docs.docker.com/get-started/" target="_blank" rel="noopener noreferrer"></a>      <a href="https://portal.portainer.io/knowledge/" target="_blank" rel="noopener noreferrer">        </a>      </td></tr><tr>    <td>Data Science</td>    <td class="icon-row">      <a href="https://numpy.org/" target="_blank" rel="noopener noreferrer">        </a>        <a href="https://pandas.pydata.org/" target="_blank" rel="noopener noreferrer"></a>        <a href="https://matplotlib.org/" target="_blank" rel="noopener noreferrer"></a></td></tr><tr>    <td>Database</td>    <td class="icon-row">      <a href="https://sqlite.org/index.html" target="_blank" rel="noopener noreferrer"></a>    </td></tr><tr>    <td>Generative AI</td>    <td class="icon-row">      <a href="https://ollama.com/" target="_blank" rel="noopener noreferrer"></a>    </td></tr><tr>  <td>PKM (Personal Knowledge Management)</td>  <td class="icon-row">    <a href="https://obsidian.md/" target="_blank" rel="noopener noreferrer"></a>      </td>    </tr>  </tbody></table><h2 id="other-hobbies">Other Hobbies<a href="#other-hobbies" class="h-anchor" title="Permalink to #Other Hobbies"></a></h2>   <p>I’m a sucker for hobbies, <em>lots of them</em>. (Last updated: 2025-06-18)</p><ul><li>Photography (both 35mm film and digital)</li><li>Speciality coffee/tea</li><li>Stationery<ul><li>Japanese paper goods (diaries, etc)</li><li>Fountain pens</li></ul></li><li>Car detailing</li><li>Watches</li><li>House plants/germinating seeds</li><li>Synths (Volcas, Circuit, Pocket Operators)</li><li>Head-fi</li><li>Art/crafts:<ul><li>Leathercraft/shoeshining</li><li>Hand embroidery (Kogin-zashi, Brazilian/dimensional)</li><li>Watercolours</li></ul></li><li>Cleaning (yes, the chore!)</li><li>EDC (everyday carry)</li><li>Hiking</li><li>Poetry</li><li>Sim racing</li><li>Formula 1/F1 Fantasy</li><li>Video games<ul><li>Favourite genres<ul><li>Racing</li><li>Music/rhythm</li><li>First-person shooters</li></ul></li></ul></li><li>Some of my favourite games<ul><li>CS:GO (ex-Gold Nova 3)</li><li>Euro Truck Simulator</li><li>F1</li><li>Gran Turismo</li><li>Taiko no Tatsujin 太鼓の達人 (handheld & arcade vers.)</li><li>Katamari Damacy</li><li>The Sims</li></ul></li></ul><p>I hope to write posts about some of these in the future… I’ll will update this page when it’s available!</p><div class="footnotes"><hr><ol><li id="fn:1"><p>RSS is one of the best ways of consuming content on the web efficiently and conveniently. I have written <a href="https://burgeonlab.com/blog/about-rss-feeds/">setting up RSS</a>  for your blog and will write another piece in the future on my setup when consuming RSS content. <a href="#fnref:1" class="footnote-backref">↩︎</a></p></li></ol></div>             <hr>            <p><i>[Thanks for reading my blog using a RSS reader! For a lightweight summary feed instead, <a href='https://burgeonlab.com/subscribe'>subscribe here</a>.]</i></p>            ]]></content></entry></feed>