<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:cf="http://www.microsoft.com/schemas/rss/core/2005" xml:lang="en-us" xmlns:webfeeds="http://webfeeds.org/rss/1.0"><title>~hedy</title><subtitle>~hedy's home: hedy's canonical web presence including contact information and a blog.</subtitle><link rel="self" type="application/atom+xml" href="https://home.hedy.dev/posts/atom.xml"/><updated>2026-02-23T01:32:27Z</updated><id>https://home.hedy.dev/posts/</id><webfeeds:icon>https://home.hedy.dev/favicon192.d7df36da6ff39312004c5892625a8176.png</webfeeds:icon><webfeeds:cover>https://home.hedy.dev/favicon.90d06356c5e08f398149619025955e54.svg</webfeeds:cover><logo>https://home.hedy.dev/favicon.90d06356c5e08f398149619025955e54.svg</logo><webfeeds:accentColor>232326</webfeeds:accentColor><cf:treatAs>list</cf:treatAs><cf:listinfo><cf:sort ns="http://www.w3.org/2005/Atom" element="published" data-type="date" label="Date published" default="true"/></cf:listinfo><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><entry><title>Separating dark mode and night shift on a Mac</title><link rel="alternate" href="https://home.hedy.dev/posts/separating-dark-mode-and-night-shift-on-a-mac/"/><id>https://home.hedy.dev/posts/separating-dark-mode-and-night-shift-on-a-mac/</id><published>2025-01-21T13:02:05Z</published><updated>2025-01-21T13:04:37Z</updated><summary type="text">How I finally figured out a workaround to scheduling dark mode and night shift separately on a Mac.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>On iOS, it’s possible to use a custom schedule to turn on the night shift (slightly tinted yellow-orange) mode separately from a custom schedule for dark mode. For some reason, this isn’t possible on a Mac. Without resorting to 3rd-party apps, you can only either:</p>
<ol>
<li>Turn off night shift, then use automatic dark mode based on sunrise/sunset, or</li>
<li>Schedule night shift, and have dark mode follow its schedule.</li>
</ol>
<p>Unfortunately, I turn on night shift all the time (a warmer screen environment, or so I tell myself — it’s mostly placebo) but prefer to only turn on dark mode after sunset, and so option 2 is out.</p>
<p>Here’s how I used to achieve this: for night shift, I would apply a custom schedule (such as 6:00 to 5:59) that will make sure it’s always turned on. It was then possible to manually toggle dark mode via the menu bar (by enabling the display settings to be shown, alternatively, use the control center) when using my Mac at night.</p>
<p>However, I recently got tired of having to do this manually. While looking through configuration options for the control center, I found the Color Filters accessibility feature and found a workaround.</p>
<p>First, I turn off night shift and set dark mode to be “automatic”. Hopefully — according to the docs — this will have it follow the local sunrise and sunset times (if it doesn’t work, I’ll find out soon enough).</p>
<p>Now for the night shift. I use the Color Tint option for the Color Filters accessibility feature (under “Display”). Then I set the tint to <code>#FF8900</code> and adjust the intensity as needed.</p>
<p><em>Voila</em>, an ad-hoc night shift that’s likely not suitable as a scientifically sound replacement, but certainly good enough for achieve the placebo effect.</p>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Separating%20dark%20mode%20and%20night%20shift%20on%20a%20Mac">Reply via email</a></p>]]></content></entry><entry><title>📚 The Anthropocene Reviewed</title><link rel="alternate" href="https://home.hedy.dev/posts/the-anthropocene-reviewed/"/><id>https://home.hedy.dev/posts/the-anthropocene-reviewed/</id><published>2025-01-11T01:34:50Z</published><updated>2025-01-11T02:09:17Z</updated><summary type="text">Reviewing a book full of reviews by John Green.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>This would have been a great opportunity to use “The Anthropocene Reviewed, reviewed” if not for the sake of my conforming to consistent title format for book review posts. Alas, the pun is already used in one of the episodes of the author’s podcast of the same name.</p>
<br />
<figure><img src="https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1616514130i/55145261.jpg"
    alt="Book cover for The Anthropocene Reviewed" width="400"><figcaption>
      <p><a href="https://www.goodreads.com/book/show/55145261-the-anthropocene-reviewed">View on Goodreads</a></p>
    </figcaption>
</figure>

<br />
<p>I discovered this book through Goodreads’ annual choice awards. I’ve read and appreciated John Green’s fiction before, so I decided to give this non-fiction work of his made during the pandemic a try.</p>
<p>Reviewing anything and everything that humans experience as a collective isn’t a new idea. I can think of one recent example on top of my head. Neal Agawal who made the famous <a href="https://neal.fun/password-game/">Password Puzzle</a> has a game named “<a href="https://neal.fun/earth-reviews/">Earth Reviews</a>” where topics like math gets less than three stars and cats get more than four out of five stars. Anyone can also leave a review that goes along with their rating.</p>
<p>Unlike Earth Reviews, The Anthropocene Reviewed includes much less general topics and explores each with much more depth, based on John Green’s personal experience. Many topics are US-specific, so it reads a lot like an autobiography that covers personal growth and development at times. Chapters are not chronologically ordered either; it can be considered a compilation of personal essays with ratings out of five stars included at the end of each chapter.</p>
<p>I started reading in 2023 and removed the book from my “currently reading” to my “DNF” shelf several times in the past two years. I’ve only picked it up recently for the lack of better books to read (that is, books that could fit my frighteningly precise mood at a given point in time, last year).</p>
<p>The reviews started off amazing. It began with reviews of topics such as humans’ temporal range, our capacity for wonder, and sunsets. It was filled with great insights and set a relaxing yet worldly scene for the stage. It gave me high hopes for what was to come.</p>
<p>Alas, the middle one third of the book wasn’t so interesting. It seemed as though the author was only describing personal accounts of little significance or lessons to be learned. It felt a little disconnected from the great start.</p>
<p>One might argue that the disconnect in the middle chapters should be blamed on my discontinuing the book after the first few chapters. But surely there should be a reason for why I felt compelled to put it off in the first place, shouldn’t there?</p>
<p>Luckily, I churned through the middle and finally it transitioned to a satisfying, rather existential end.</p>
<p>The ending was good enough that I was not only left satiated with an existential crisis, I was also reminded of how well the book began.</p>
<p>Overall, the reviews were insightful. I really enjoyed reading about John Green’s life in America, his travels and his perspective in things we all give little thought to each day. I’ll give it 4.5 out of 5 stars.</p>

<h2 id="quotes">
  <a class="anchor" href="#quotes" rel="nofollow">#</a>
  Quotes</h2>

<blockquote>
<p>Making conclusions about a book’s quality from a 175-word review is hard work for artificial intelligences, whereas star ratings are ideal for them.</p>
</blockquote>
<blockquote>
<p>I’m reminded of something my religion professor Donald Rogan told me once: “Never predict the end of the world. You’re almost certain to be wrong, and if you’re right, no one will be around to congratulate you.”</p>
</blockquote>
<blockquote>
<p>Marveling at the perfection of that leaf, I was reminded that aesthetic beauty is as much about how and whether you look as what you see. From the quark to the supernova, the wonders do not cease. It is our attentiveness that is in short supply, our ability and willingness to do the work that awe requires.</p>
</blockquote>
<blockquote>
<p>For many species of large animals in the twenty-first century, the single most important determinant of survival is whether their existence is useful to humans.</p>
</blockquote>
<p>On air-conditioning:</p>
<blockquote>
<p>Like most other energy-intensive innovations, AC primarily benefits people in rich communities, while the consequences of climate change are borne disproportionately by people in impoverished communities.</p>
</blockquote>
<blockquote>
<p>A blog in the Atlantic wrote, “To think the temperature in a building is sexist is absurd.” But it’s not absurd. What’s absurd is reducing workplace productivity by using precious fossil fuels to excessively cool an office building so that men wearing ornamental jackets will feel more comfortable.</p>
</blockquote>
<p>In the chapter about <a href="https://en.wikipedia.org/wiki/Staphylococcus_aureus"><em>Staphylococcus aureus</em></a>, John Green quotes an exchange between <a href="https://en.wikipedia.org/wiki/Alexander_Ogston">Alexander Ogston</a> who discovered <em>Staphylococcus</em> and <a href="https://en.wikipedia.org/wiki/Joseph_Lister">Joseph Lister</a> who revolutionized the craft of surgery through his research into sterilization techniques:</p>
<blockquote>
<p>“You have changed surgery… from being a hazardous lottery into a safe and soundly based science,” which was only a bit of an exaggeration.</p>
</blockquote>
<blockquote>
<p>After visiting Lister and observing complex knee surgeries healing without infection, Ogston returned to the hospital in Aberdeen and tore down the sign above the operating room that read, “Prepare to meet thy God.” No longer would surgery be a last-ditch, desperate effort.</p>
</blockquote>
<p>On sunsets, and Indianapolis:</p>
<blockquote>
<p>I don’t buy the romantic notion that scientific understanding somehow robs the universe of its beauty, but I still can’t find language to describe how breathtakingly beautiful sunsets are—not breathtakingly, actually, but breath-givingly beautiful.</p>
</blockquote>
<blockquote>
<p>Indianapolis’s favorite literary son, Kurt Vonnegut, wrote that one of the flaws in the human character “is that everybody wants to build and nobody wants to do maintenance.”</p>
</blockquote>
<p>And finally, a few final quotes from the postscript.</p>
<blockquote>
<p><a href="https://en.wikipedia.org/wiki/Hank_Green">Hank Green</a> likes to remind me that I am made out of the materials of the universe, that I contain nothing but those materials. “Really,” he told me once, “you’re just a hunk of Earth trying to sustain a departure from chemical equilibrium.”</p>
</blockquote>
<blockquote>
<p>In a song he wrote years ago called “The Universe Is Weird,” Hank sings that the weirdest thing is that, in us, “the universe created a tool with which to know itself.”</p>
</blockquote>
<blockquote>
<p>When I think of how I have enjoyed the Anthropocene so far, I think of Robert Frost, who wrote, “Like a piece of ice on a hot stove, the poem must ride on its own melting.” So it is with poems, and so it is with us. Like ice on a hot stove, we must ride on a melting Earth, all the while knowing who is melting it. A species that has only ever found its way to more must now find its way to less.</p>
</blockquote>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20%f0%9f%93%9a%20The%20Anthropocene%20Reviewed">Reply via email</a></p>]]></content></entry><entry><title>Blog questions challenge</title><link rel="alternate" href="https://home.hedy.dev/posts/blog-questions-challenge/"/><id>https://home.hedy.dev/posts/blog-questions-challenge/</id><published>2025-01-06T05:12:31Z</published><updated>2025-01-06T07:40:31Z</updated><summary type="text">My response to the blog questions challenge started by Kev Quirk.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p><a href="https://kevquirk.com/">Kev Quirk</a> started a personal blog challenge where you answer a few questions and “tag” a few people of your choosing to do the same. The questions are all meta in nature and I already have a dedicated <a href="/meta">meta page</a> that talks all about it; but I thought it’d fun to join in although I wasn’t tagged directly and inspire more participants because <a href="https://meta-ring.hedy.dev/">I absolutely love reading meta content</a> on people’s blogs and <a href="/posts/meta-pages/">would love to see more of them</a>.</p>
<p>So, let’s get right into it.</p>

<h2 id="why-did-you-start-blogging-in-the-first-place">
  <a class="anchor" href="#why-did-you-start-blogging-in-the-first-place" rel="nofollow">#</a>
  Why did you start blogging in the first place?</h2>

<p>I published my first blog post using Blogger about a decade ago as a form of public long-form note-taking for cool tech “tips and tricks” I’ve learnt every day.</p>
<p>Back then, I was only just getting into tinkering with technology as a hobby rather than a necessity. I realized that there were many little things I had previously ignored when it comes to the user interfaces we interact with everyday; so many opportunities for maximizing my productivity and technical knowledge that I could unlock by just paying more attention to the software and hardware I was using, and researching even just a little about them: finding out how it works under the hood, customizing and optimizing it.</p>
<p>I didn’t care much about how the blog looked or how posts were written. Only that I was recording my notes and publishing it for anyone who shared similar interests.</p>

<h2 id="what-platform-are-you-using-to-manage-your-blog-and-why-did-you-choose-it">
  <a class="anchor" href="#what-platform-are-you-using-to-manage-your-blog-and-why-did-you-choose-it" rel="nofollow">#</a>
  What platform are you using to manage your blog and why did you choose it?</h2>

<p>This blog is currently powered by <a href="https://gohugo.io/">Hugo</a>, a static-site generator. I open up my terminal, make the edits, run a single command, and Hugo rebuilds my entire website from scratch. When I’m satisfied with the changes, I run another command which will deploy my website to Netlify.</p>
<p>I chose Hugo when starting my current blog because there were many similar blogs I liked I could reference. However, as my blog evolved I’ve started to reach the limits of what Hugo could do for me and have been experimenting with alternatives since. As of writing, I have not yet found a suitable alternative worth the switch, but I suspect this will change very soon.</p>

<h2 id="have-you-blogged-on-other-platforms-before">
  <a class="anchor" href="#have-you-blogged-on-other-platforms-before" rel="nofollow">#</a>
  Have you blogged on other platforms before?</h2>

<p>I’ve tried Blogger, which lets you write and publish everything within the web interface and provided you a free subdomain. Before this blog, I have also worked with Gatsby back when it was new and shiny, Django with its admin interface, Wordpress, and a couple of CMSes.</p>
<p>Since switching to Hugo, I have tried WriteFreely (which publishes to the Fediverse), bashblog (simple, automatically installed on <a href="https://tildeverse.org/">tildes</a>), <a href="https://smol.pub/">smol.pub</a> (which I still keep — it syndicates to <a href="https://geminiquickst.art/">Gemini</a> and Gopher), and <a href="https://bearblog.dev/">Bear Blog</a> (which I’ve recently started trying out, and plan to keep as a secondary blog). Not to mention a whole lot of static-site generators such as <a href="https://lume.land/">Lume</a> (which I still use for side projects), <a href="https://11ty.dev/">Eleventy</a>, and most recently <a href="https://astro.build/">Astro</a>.</p>

<h2 id="how-do-you-write-your-posts">
  <a class="anchor" href="#how-do-you-write-your-posts" rel="nofollow">#</a>
  How do you write your posts?</h2>

<p>I write drafts in <a href="https://obsidian.md/">Obsidian</a> with a very minimal set up, in a vault that double-serves as my digital journal. This is useful because it allows me to write freely for myself and select from existing entries those I want to publish.</p>
<p>After the initial draft in Obsidian, I copy the markdown into my editor in the terminal, use Hugo’s live-reloading server to make minor edits before building and deploying to the internet.</p>

<h2 id="when-do-you-feel-most-inspired-to-write">
  <a class="anchor" href="#when-do-you-feel-most-inspired-to-write" rel="nofollow">#</a>
  When do you feel most inspired to write?</h2>

<p>Inspiration strikes when I read books, read other people’s blogs, have a long-form conversation with someone, and really any activity that makes me feel as though the ideas shared are worth recording and preserving.</p>
<p>There are unfortunately very few opportunities in my daily schedule to properly sit down undisturbed and write long posts in one ago. When I have a half-fledged idea, I would jot it down either in my physical notebook or on my self-hosted <a href="https://usememos.com/">Memos</a> instance to revisit later. I often begin drafting posts by writing an outline of things I want to include. This makes it easier to make context switches and jump back in when I’m able to continue the draft.</p>

<h2 id="do-you-publish-immediately-after-writing-or-do-you-let-it-simmer-a-bit-as-a-draft">
  <a class="anchor" href="#do-you-publish-immediately-after-writing-or-do-you-let-it-simmer-a-bit-as-a-draft" rel="nofollow">#</a>
  Do you publish immediately after writing, or do you let it simmer a bit as a draft?</h2>

<p>I usually publish immediately — bar minor edits and technical adjustments — after I’ve deemed a draft complete. However, it’s usually hard to get to this stage because I tend to edit too much while I am writing the first draft. I discovered that this is only the case when I’m <a href="/posts/typing-vs-writing/">typing rather than writing with a pen on paper</a> so I’ve started to explore solutions to accommodate, such as using an unfamiliar editor or one where cognitive load is required to go back and make edits while writing.</p>

<h2 id="whats-your-favorite-post-on-your-blog">
  <a class="anchor" href="#whats-your-favorite-post-on-your-blog" rel="nofollow">#</a>
  What’s your favorite post on your blog?</h2>

<p>My favorite post will likely always be one that is sitting in my drafts folder — I’m passionate about the topic and I want to share it with the world, but for whatever reason, I’m unable to complete or publish it.</p>
<p>As for existing posts I’ve written, for now it would be <a href="https://home.hedy.dev/posts/the-joy-of-feed-readers/">The joy of feed readers and alternative ways to consume content</a>.</p>

<h2 id="any-future-plans-for-your-blog">
  <a class="anchor" href="#any-future-plans-for-your-blog" rel="nofollow">#</a>
  Any future plans for your blog?</h2>

<blockquote>
<p>Maybe a redesign, a move to another platform, or adding a new feature?</p>
</blockquote>
<p>I’m constantly redesigning my blog. Moving away from Hugo is one of my priorities in my long list of website ideas. Some of which include:</p>
<ul>
<li>A glossary for common terms that might cause confusion throughout my blog, with links to the definitions when mentioned in blog posts. Inspired by <a href="https://takeonrules.com/">Jeremy Friesen’s blog</a>;</li>
<li>A sitemap page,</li>
<li>A page that lists books I recently read or my all-time recommendations,</li>
<li>A <a href="https://slashpages.net/#podroll">/podroll</a> for podcasts,</li>
<li>A link garden for more permanent <a href="/bookmarks">bookmarks</a> within categories.</li>
</ul>
<hr>
<p>Lastly, I’ll add another question of my own.</p>

<h2 id="why-do-you-write-other-than-your-blog-do-you-write-long-form-content-elsewhere">
  <a class="anchor" href="#why-do-you-write-other-than-your-blog-do-you-write-long-form-content-elsewhere" rel="nofollow">#</a>
  Why do you write? Other than your blog, do you write long-form content elsewhere?</h2>

<p>I write to think, to relax, to make sense of my thoughts, and to communicate ideas. I think and plan a lot better when I can type away on an empty document or with a pen in my hand. I also write because I love reading books. I like to wonder about how the way words are placed makes me feel what the writer wants me to feel, see what the writer sees. Eloquence and articulacy are invigorating, and that power is contagious.</p>
<p>Digital journals are a great way to live more mindfully. It can also help with <a href="https://www.joanwestenberg.com/the-art-of-not-sharing/">oversharing</a>. I keep several digital journals around. They are accessible on all of my devices which means I can jot down ideas and reflections any time as long as I have a device with me.</p>
<p>I also keep several physical notebooks with different journalling purposes. I prefer notebook pages without any lines or dotted grids and a 0.7mm black ballpoint pen to write with. This way I can write free-form, include doodles alongside my writing, write sideways or in different handwriting styles of different sizes without being limited by lines and grids. Fortunately, I don’t usually have an issue with alignment and indentation or messy handwriting. Sometimes I would write with my non-dominant hand or use a different keyboard-layout when typing just so I could <a href="/posts/typing-vs-writing/">write slower</a>.</p>
<hr>

<h2 id="your-turn">
  <a class="anchor" href="#your-turn" rel="nofollow">#</a>
  Your turn!</h2>

<p>That’s all of the answers I will be including today. I never knew how much I could talk about my blog itself until I actually sat down and wrote it out. There are many tangents and side stories which I will save for another day, perhaps.</p>
<p>Now, I’d love to read your answers. I’ll be tagging <a href="https://netigen.com/">Courtney</a>, <a href="https://jamesg.blog/">James</a>, <a href="https://joelchrono.xyz/">Joel</a>, and <a href="https://leilukin.com/">Leilukin</a>. Feel free to participate even if you aren’t tagged!</p>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Blog%20questions%20challenge">Reply via email</a></p>]]></content></entry><entry><title>📚 The Night Circus</title><link rel="alternate" href="https://home.hedy.dev/posts/the-night-circus/"/><id>https://home.hedy.dev/posts/the-night-circus/</id><published>2025-01-03T07:14:02Z</published><updated>2025-01-04T03:45:00Z</updated><summary type="text">A much delayed book review for The Night Circus by Erin Morgenstern.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>The Night Circus is a book I had wanted to read for a long time, as an enjoyer of other “magical circus” stories inspired by it. It ended up giving me mixed feelings, and so it was difficult to write a satisfactory review that captured all that ought to be said. Despite this, I thought it’d be awesome to start a book-review series on my blog with this book, and so I’ll try my best to reproduce the short review I wrote for it over a year ago.</p>
<br />
<figure><img src="https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1387124618i/9361589.jpg"
    alt="Book cover for The Night Circus"><figcaption>
      <p><a href="https://www.goodreads.com/book/show/9361589-the-night-circus">View on Goodreads</a></p>
    </figcaption>
</figure>

<br />
<blockquote>
<p><em>The circus arrives without warning. No announcements precede it. It is simply there, when yesterday it was not.</em></p>
</blockquote>
<br />
<p>This book is one of the must-reads when it comes to magical circuses and historical fantasy. It’s rated over four stars on Goodreads with plenty of mixed reviews — the result of good marketing.</p>
<p>The story is set in a world with a soft magic system: “anyone can be taught” / “with a bit of studying…”</p>
<p>It describes a long-standing battle between two masters with different educational approaches who choose their own students and set them into duel that span over decades. Two students thrown into competition, but this time, they fell in love.</p>
<p>The book is full of descriptive prose that enchanted me with attractions and entertainment in the circus. There were many opportunities for sensory imagery, even when it comes to the refreshments at the small shops within the circus.</p>
<p>I tend to enjoy a book with long passages of beautiful prose for the magic. However, what was described just felt too intangible and inexplicable to be fully appreciated. The magical system was barely ever explained. You could argue that that was the whole point, and was what made this fantasy. But really, what does it mean for Bailey taking over the ‘weight’ of the circus in the end? What magical energy must be contained or protected and why? What happened to Celia and Marco?</p>
<p>In the end, the pieces of imagery that stuck with me was only the grandfather clock placed at the entrance of the circus, and the dark, sweet, swirling cups of liquid desserts sold as circus delights.</p>
<p>The narration follows a non-linear timeline. It explored many different characters with multiple perspectives and side plots. Many of which seemed to only provide the atmosphere with little to no contribution to the plot at all. It jumps around, lost me at times, pulled me back with flowery prose then finally threw me off again with a seemingly beautiful yet confusing ending.</p>
<p>Overall, I really enjoyed reading the book but the world-building just isn’t my cup of tea (pun unintentional). I give The Night Circus 3.9/5 stars; worth a re-read.</p>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20%f0%9f%93%9a%20The%20Night%20Circus">Reply via email</a></p>]]></content></entry><entry><title>Default Apps 2024</title><link rel="alternate" href="https://home.hedy.dev/posts/default-apps/"/><id>https://home.hedy.dev/posts/default-apps/</id><published>2024-12-31T12:48:06Z</published><updated>2025-01-01T01:10:07Z</updated><summary type="text">Software I appreciate. Or, long lists of apps I would include if I had a /uses page.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>I don’t have a dedicated <code>/uses</code> page; only a little table on my <a href="/about/">about</a> page that lists the development-related essentials. Since today is the last day of 2024, I decided to jump on the “default apps” trend and listing the primary software I use as of today for each common task. I’ll also include notes where there was a recent update to the list of significance.</p>

<h2 id="general">
  <a class="anchor" href="#general" rel="nofollow">#</a>
  General</h2>

<ul>
<li><strong>Launcher</strong>: Alfred <br />
I use Alfred only for launching applications; it’s much faster than the catch-all MacOS Spotlight Search.</li>
<li><strong>Books</strong>: Kindle Paperwhite and Goodreads (I do have a <a href="https://tildebooks.org/user/hedy">bookwyrm account</a> if you’d like to follow my updates from the Fediverse :)</li>
<li><strong>Podcasts</strong>: Pocket Casts</li>
<li><strong>Music</strong>: InnerTune</li>
</ul>

<h2 id="productivity">
  <a class="anchor" href="#productivity" rel="nofollow">#</a>
  Productivity</h2>

<ul>
<li><strong>Notes</strong>: <a href="https://logseq.com/">Logseq</a> for research, <a href="https://usememos.com/">Memos</a> (self-hosted) for personal micro-blogging, and Notion for others</li>
<li><strong>Writing</strong>: <a href="https://obsidian.md">Obsidian</a></li>
<li><strong>Todos</strong>: Todoist</li>
</ul>
<p>Notion gives me sync with zero overhead in the free tier and its mobile apps have slightly better UX than Logseq (which is still reletively young). Logseq is an outliner, and I personally find it especially suited for certain kinds of note-taking for some of my research. I also use it for long-term personal project task management that integrates well with the journal feature.</p>
<p>Unfortunately, Logseq isn’t quite fit for long-form writing and drafting; so I’m currently exploring syncing options — possibly using Syncthing or Git repositories — with Obsidian.</p>
<p>I’ve considered using Org-mode with Emacs on the desktop and Orgzly on mobile, however Org is a little to complex for my needs (it’s true, perhaps I’m just not patient enough to overcome the learning curve) and much prefer something I can more easily manage and wrap my head around. Markdown has a wider range of support and can be converted to blog posts under my currently setup easier. If I ever decide to switch to Org for <abbr title="Personal Knowledge Management">PKM</abbr>, I might opt for <a href="https://protesilaos.com/emacs/denote">denote</a> or <a href="https://kaorahi.github.io/howm/">HOWM</a> as companions.</p>

<h2 id="internet">
  <a class="anchor" href="#internet" rel="nofollow">#</a>
  Internet</h2>

<ul>
<li>
<p><strong>Web browser</strong>: Vivaldi (graphical), <a href="https://home.hedy.dev/posts/the-joy-of-feed-readers/#alternative-browsers">w3m (textual)</a></p>
<p>I’ve tried many graphical web browsers in the past, enough to write a long “browser reviews” post (it’s planned!) <br /></p>
<p>I like Vivaldi for its vertical tabs, extension customization support, built-in indicator for detected RSS/Atom feeds, and the reader mode. I disable its other features such the feed reader, calendar, notes, and mail client and would personally prefer the Firefox backend, but Vivaldi is currently the best option for personal use.</p>
</li>
<li>
<p><strong><a href="https://geminiquickst.art">Gemini</a> (and Gopher) client</strong>: <a href="https://github.com/hedyhli/gelim">gelim</a> and Lagrange</p>
</li>
<li>
<p><strong>Bookmarks</strong>: Raindrop <br />
I save web bookmarks in raindrop using the browser extension and mobile apps, then I export it with a script that display my public bookmarks <a href="https://home.hedy.dev/bookmarks/">here on my website</a>.</p>
</li>
<li>
<p><strong>Read-it-later</strong>: Instapaper</p>
</li>
<li>
<p><strong>RSS/Atom Feed reader</strong>: <a href="https://github.com/nkanaev/yarr">yarr!</a> (self-hosted, PWA)</p>
</li>
<li>
<p><strong>Search</strong>: DuckDuckGo for its <a href="https://duckduckgo.com/bang">bangs</a></p>
</li>
<li>
<p><strong>Email service</strong>: Proton Mail</p>
</li>
<li>
<p><strong>Email client</strong>: Proton Mail and <a href="https://aerc-mail.org">aerc</a> (for other emails)</p>
</li>
</ul>

<h2 id="social-media">
  <a class="anchor" href="#social-media" rel="nofollow">#</a>
  Social Media</h2>

<ul>
<li><strong>Reddit client</strong>: <a href="https://github.com/QuantumBadger/RedReader">Red Reader</a> (android)</li>
<li><strong>Mastodon client</strong>: <a href="https://phanpy.social/">Phanpy</a> (PWA<sup><a href="#fn:1" id="fnref:1" role="doc-noteref">[1]</a></sup>)</li>
<li><strong>Pleroma/Akkoma client</strong><sup><a href="#fn:2" id="fnref:2" role="doc-noteref">[2]</a></sup>: Husky (android)</li>
<li><strong>Bluesky client</strong>: the official web client as a PWA <br />
I used to use <a href="https://tokimeki.blue/">TOKIMEKI</a>, but some Bluesky features such as starter-packs prefers to open links in its official client instead, so I’ve switched away from TOKIMEKI until the situation improves.</li>
<li><strong>IRC client</strong>: <a href="https://sr.ht/~delthas/senpai/">senpai</a> and <a href="https://codeberg.org/emersion/gamja">gamja</a></li>
<li><strong>IRC bouncer</strong>: soju on <a href="https://man.sr.ht/chat.sr.ht/">SourceHut</a></li>
<li><strong>Matrix client</strong>: <a href="https://cinny.in/">Cinny</a></li>
<li><strong>Lemmy client</strong><sup><a href="#fn:3" id="fnref:3" role="doc-noteref">[3]</a></sup>: <a href="https://vger.app/">Voyager</a> (mobile)</li>
</ul>

<h2 id="development">
  <a class="anchor" href="#development" rel="nofollow">#</a>
  Development</h2>

<p>Configuration and scripts of interest for everything I use in the terminal can be found in my <a href="https://github.com/hedyhli/dotfiles">dotfiles</a>.</p>
<ul>
<li><strong>Editor/IDE</strong>: NeoVim, Emacs (for Lisps and <a href="https://smol.hedy.dev/re-baty-emacs-from-scratch">occasionally</a> Org-mode)</li>
<li><strong>Terminal</strong>: Kitty</li>
<li><strong>Shell</strong>: Fish</li>
<li><strong>File manager</strong>: ranger</li>
<li><strong>Multiplexer</strong>: tmux <br />
I’ve tried Zellij, but I did not have a good experience with WASM plugins; perhaps I should write about it sometime.</li>
<li><strong>Dotfiles manager</strong>: yadm</li>
</ul>
<br />
<p>That’s all of the apps I’ll include this year. I could potentially reproduce
something similar on a dedicated page, only it will get out of date fairly
easily. I care a lot about choosing the right tools and tailoring them to best
fit my needs. Do you have similar setups? What do you like/dislike about it?</p>
<hr hidden="" /><div class="footnotes" role="doc-endnotes">
<p><strong>Footnotes</strong></p>
<ol>
<li id="fn:1">
<p>Progressive Web App. In this context, it means I have the web app “installed” on mobile using — what is usually called — the “add to home screen” feature.&#160;<a class="back" href="#fnref:1" aria-labelledby="bl1-1 bl2-1" role="doc-backlink"><span id="bl1-1">[↩]</span></a></p>
</li>
<li id="fn:2">
<p><a href="https://pleroma.social/">Pleroma</a> and <a href="https://akkoma.social/">Akkoma</a> can be thought of as alternatives to Mastodon.&#160;<a class="back" href="#fnref:2" aria-labelledby="bl1-2 bl2-2" role="doc-backlink"><span id="bl1-2">[↩]</span></a></p>
</li>
<li id="fn:3">
<p>A reddit alternative part of the fediverse. See <a href="https://en.wikipedia.org/wiki/Lemmy_(social_network)">Lemmy on Wikipedia</a>.&#160;<a class="back" href="#fnref:3" aria-labelledby="bl1-3 bl2-3" role="doc-backlink"><span id="bl1-3">[↩]</span></a></p>
</li>
</ol>
</div>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Default%20Apps%202024">Reply via email</a></p>]]></content></entry><entry><title>The joy of feed readers and alternative ways to consume content</title><link rel="alternate" href="https://home.hedy.dev/posts/the-joy-of-feed-readers/"/><id>https://home.hedy.dev/posts/the-joy-of-feed-readers/</id><published>2024-12-14T06:35:31Z</published><updated>2024-12-14T07:31:27Z</updated><summary type="text">Experiencing the internet through different lenses: feed readers, textual browsers, the Gemini protocol, and more.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>Let’s talk about feed readers: software that lets you subscribe to <a href="https://aboutfeeds.com/">feeds</a> to get updates when new content is published.</p>
<p>There are many kinds of feeds and feed readers. We’ll focus on the ones that provide the entire article from the feed, and lets you read the content directly in feed readers.</p>
<p>If you’ve never used a feed reader before, I highly recommend trying one out after reading <a href="https://aboutfeeds.com/">this getting started guide</a> by <a href="https://interconnected.org/">Matt Webb</a>.</p>
<br />
<figure><img src="/posts/the-joy-of-feed-readers/yarr-prot.png"
    alt="A screenshot of yarr, a feed reader I use"><figcaption>
      <p>An example post by Protesilaos Stavrou in my feed reader, <a href="https://github.com/nkanaev/yarr">yarr</a>.</p>
    </figcaption>
</figure>

<br />
<p>There’s something magical in consuming content on the internet using feed readers.</p>
<p>Each of us have a list of RSS/Atom feeds we follow. Feeds from friends, from internet strangers whose blog posts you serendipitously stumbled upon and decided you want more, feeds from popular publishers and newsletters and organizations and projects you want to subscribe to for updates.</p>
<p>Every once in a while these feeds are refreshed and you are shown a list of new entries waiting for us to read. You select one of them and the content is displayed directly within your feed reader, in a familiar and comfortable interface. Perhaps you choose to listen to them with a screen reader or perhaps you read from them directly. Either way, the content is saved in your feed reader. It’s there, together with all the other new posts waiting for you to read and all the posts from the past you have saved.</p>
<p>Browsing the internet is like visiting a public library without taking the books home. It’s easy to stumble upon new books and get engrossed after flipping through a few pages. As you read them, you share the physical space of all the other readers in the library. When you’re done with the book you put it back onto the shelf for the next person to pick up. The key here, is that you’re looking at something that’s provided for you to read.</p>
<p>Using feed readers then, is like buying physical books and having a personal library. You pick a book from the shelf. It’s yours, you can open it, hold it in your hands and read it anytime, and put it back to your shelf together with all your other collections.</p>
<p>It’s like subscribing to newspapers and magazines. Each day a new edition gets delivered to you. A copy for you to read on your own as you grab a cup of coffee and make yourself comfortable on your favorite couch.</p>
<p>With feed readers, you get your own copy. All articles look the same when you read it from your feed reader’s reading panel. Familiar and welcoming. As you read from your feed reader, you are alone with your thoughts and the author of the content in front of you. No ads, no pop ups asking you enter your email address, no floating navigation bar covering half of the screen when you choose to zoom in. Just the content<sup><a href="#fn:1" id="fnref:1" role="doc-noteref">[1]</a></sup>.</p>
<p>It almost feels like browsing something different from the internet entirely…</p>

<h2 id="does-this-sound-familiar">
  <a class="anchor" href="#does-this-sound-familiar" rel="nofollow">#</a>
  Does this sound familiar?</h2>

<p>It turns out, feed readers aren’t the only place you can consume content on the internet this way. Here are the few I could think of.</p>

<h3 id="alternative-browsers">
  <a class="anchor" href="#alternative-browsers" rel="nofollow">##</a>
  Alternative browsers</h3>

<p>How do you browse pages on the web? If you’re like most people, you might use a graphical browser such as Chrome, Firefox, or Safari.</p>
<p>Vivaldi, Arc, or maybe even Qutebrowser, you say? Well, all them are Chrome-based. This means websites look the same in Chrome as it does in all of those browsers<sup><a href="#fn:2" id="fnref:2" role="doc-noteref">[2]</a></sup>. There are also other graphical browsers based on Firefox or Safari.</p>
<p>Apart from reading web pages rendered by browsers like these, there are other ways to browse the web. And some of them lets you consume content in a similar way to reading from feed readers.</p>
<p>For instance, consider text-mode browsers.</p>
<p>As the name suggests, web pages displayed in these kinds of browsers show only text. Images are replaced with their alt text. Only some styling rules from CSS are followed, if at all. JavaScript is usually<sup><a href="#fn:3" id="fnref:3" role="doc-noteref">[3]</a></sup> not executed.</p>
<p>For the technically-inclined: I’m talking about TUI (<a href="https://en.wikipedia.org/wiki/Text-based_user_interface">Text User Interface</a>) browsers such as <a href="https://w3m.sourceforge.net">w3m</a> and <a href="https://en.wikipedia.org/wiki/Lynx_(web_browser)">lynx</a>, accessed from a terminal.</p>
<p>Here’s how an <a href="https://www.rollingstone.com/culture/culture-commentary/internet-future-about-to-get-weird-1234938403">example article</a> looks like in such a browser. Text in blue are links, and text in green are alt text in place of images.</p>
<figure><img src="/posts/the-joy-of-feed-readers/w3m-window.png"
    alt="Screenshot of an article in w3m"><figcaption>
      <p>An article in <a href="https://w3m.sourceforge.net">w3m</a> with mostly textual content, a few links in blue, and an image alt text in green in a <a href="https://en.wikipedia.org/wiki/Terminal_emulator">terminal window</a>.</p>
    </figcaption>
</figure>

<br />
<p>Like feed readers, it shows just the content. Like feed readers, the reading experience is determined by the website owner. Though unlike feed readers, you can use it to browse anything regardless of whether a website wants you to.</p>
<p>Textual browsers are especially fit for browsing indie blogs similar to feed readers — content-focused, and less likely to contain clutter such as graphical user interface features and popups.</p>
<p>There are also browsers that display mostly text, but have support for images. One example is <a href="https://www.gnu.org/software/emacs/manual/html_mono/eww.html">EWW</a>:</p>
<figure><img src="/posts/the-joy-of-feed-readers/eww-window.png"
    alt="Screenshot of an article with an image in EWW"><figcaption>
      <p>An article in EWW with some text and an image.</p>
    </figcaption>
</figure>

<br />
<p>Images and variable-width fonts are both supported. There’s a scrollbar if you want one. Hovering on links with your mouse shows the link destination. In the screenshot, the bottom half of the screen shows a cut-off image from the article. It shows what web pages might look like with no CSS and JavaScript.</p>
<p>Some styles can also be rendered if there are defined inline. For example:</p>
<figure><img src="/posts/the-joy-of-feed-readers/eww-cognate.png"
    alt="Screenshot of a page in EWW with code syntax highlighting"><figcaption>
      <p>A page with inline styles in EWW.</p>
    </figcaption>
</figure>

<br />
<p>In this case, text within <code>&lt;pre&gt;</code> tags displayed in a monospace font. There are <code>&lt;span&gt;</code>’s within these code blocks that have set their text color using the HTML <code>style</code> attribute, these colors are applied accordingly.</p>

<h3 id="read-it-later-and-archiving-services">
  <a class="anchor" href="#read-it-later-and-archiving-services" rel="nofollow">##</a>
  Read-it-later and archiving services</h3>

<p>Services like <a href="https://wallabag.org">Wallabag</a> lets you save links to be read later. Some makes an archive of that page so that you can read them directly within the interface in case the original resource is no longer available.</p>
<br />
<figure><img src="/posts/the-joy-of-feed-readers/wallabag.png"
    alt="Screenshot of an article in Wallabag"><figcaption>
      <p>An article in Wallabag.</p>
    </figcaption>
</figure>

<br />
<p>This is similar to feed readers in that a local copy of an article is saved and you can freely customize how it is displayed.</p>

<h3 id="the-gemini-protocol">
  <a class="anchor" href="#the-gemini-protocol" rel="nofollow">##</a>
  The Gemini Protocol</h3>

<p>What’s part of the internet, but outside the World Wide Web? The web we are familiar with use the HTTP and HTTPS protocols for transferring data from a web server to your browser. Content on the web is typically served with HTML.</p>
<p>Gemini is a different protocol. Content on <a href="https://geminiprotocol.net/">Geminispace</a> use Gemtext, a different content format from HTML. Gemtext is similar to markdown. It is extremely simple: there are only paragraphs, lists, headings, links, quotes, and pre-formatted text. Links have to be on a line of their own. This makes parsing Gemtext easy: you only have to look at the first few characters of a line to know how to render it:</p>
<pre tabindex="0"><code class="language-gmi" data-lang="gmi"># Heading 1
## Heading 2
### Heading 3

A new line introduces a separate paragraph.

=&gt; gemini://example.org/ This text is shown for the link
=&gt; gemini://example.org/ Here is another link

* List item 1
* List itme 2

&gt; Here&#39;s a quote.
</code></pre><p>There is no equivalent to CSS or JavaScript. In Gemtext, there is only content. This means it is up to Gemini “browsers” to decide how a page is shown to the user, rather than the website author — everything from the the colors and font to the margins and spacing.</p>
<p>Here is how a page in Geminispace looks like in Lagrange, a graphical Gemini browser.</p>
<figure><img src="/posts/the-joy-of-feed-readers/lagrange.png"
    alt="Screenshot of a page from Geminispace in Lagrange."><figcaption>
      <p>A page from Geminispace displayed in Lagrange.</p>
    </figcaption>
</figure>

<br />
<p>Like feed readers, the user can customize how pages are displayed, and an author must opt-in (i.e., host their content on Gemini) for you to see it.</p>

<h2 id="closing-thoughts">
  <a class="anchor" href="#closing-thoughts" rel="nofollow">#</a>
  Closing thoughts</h2>

<p>Feed readers are wonderful. You can keep up with the latest updates from multiple sources without hassle, and catch up only when you want to, in a single, unified interface.</p>
<p>There are many ways to consume content on the internet apart from using a graphical browser with full CSS and JavaScript support. Sometimes, especially for non-textual content, it is best to use the browser that the website author wants you to use. Giving users the full control of a page’s style isn’t always a good idea. Other times, using different kinds of software to browse the web and the internet beyond the web can be a much better experience.</p>
<p>Do you use any of the method of browsing content on the internet I have
mentioned? Do you know of any others?</p>
<hr hidden="" /><div class="footnotes" role="doc-endnotes">
<p><strong>Footnotes</strong></p>
<ol>
<li id="fn:1">
<p>Assuming, of course, that the author has made the content provided from the RSS or Atom feed complete and readable within most feed readers.&#160;<a class="back" href="#fnref:1" aria-labelledby="bl1-1 bl2-1" role="doc-backlink"><span id="bl1-1">[↩]</span></a></p>
</li>
<li id="fn:2">
<p>Well, not always. But do bare with me for second.&#160;<a class="back" href="#fnref:2" aria-labelledby="bl1-2 bl2-2" role="doc-backlink"><span id="bl1-2">[↩]</span></a></p>
</li>
<li id="fn:3">
<p>The only TUI browser I am aware of that supports JavaScript is <a href="https://github.com/rkd77/elinks">elinks</a> with a compile flag. If you know any others, I’d love to know.&#160;<a class="back" href="#fnref:3" aria-labelledby="bl1-3 bl2-3" role="doc-backlink"><span id="bl1-3">[↩]</span></a></p>
</li>
</ol>
</div>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20The%20joy%20of%20feed%20readers%20and%20alternative%20ways%20to%20consume%20content">Reply via email</a></p>]]></content></entry><entry><title>Removing comments on my posts</title><link rel="alternate" href="https://home.hedy.dev/posts/removing-comments/"/><id>https://home.hedy.dev/posts/removing-comments/</id><published>2024-11-29T07:40:07Z</published><updated>2024-11-30T07:19:49Z</updated><summary type="text">How the comment system on my blog and gemlog worked, and why I am now removing it.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>A few years ago I had a wonderful idea. Arguably the best system I could think of for comments for my blog: a mailing list.</p>
<p>Here’s how it worked.</p>
<p>I had set up a mailing list on <a href="https://lists.sr.ht/">lists.sr.ht</a>, which allowed people to subscribe and unsubscribe using only email (no sr.ht account required).</p>
<p>When I published a new post, I would produce a plaintext version<sup><a href="#fn:1" id="fnref:1" role="doc-noteref">[1]</a></sup> and post it on the mailing list as a new thread. I would then put a <code>mailto:</code> link with a <code>in-reply-to</code> field at the end of the post so that people who wish to comment can click on the link, send an email, and the content will be displayed right under the thread as a public “comment”.</p>
<p>The mailing list would also serve as a newsletter. Someone who was subscribed would receive new posts at their inbox. They could hit the “reply-all” button in their email client to post a public “comment”, or just “reply” to email me privately.</p>
<p>To display these comments at the end of each post, I could either include a link to the lists.sr.ht thread which displays all the emails in that thread nicely in a single minimalist web page, or use sr.ht’s API to fetch all replies and display them myself.</p>
<p>This system had many advantages:</p>
<ul>
<li>No JavaScript needed to fetch and display comments</li>
<li>No external server required, except choosing a mailing list service.</li>
<li>The authenticity of commentors were guaranteed (even more so with email encryption) since a email address to send the comment from is required.</li>
<li>It double serves as a newsletter. When someone reading the post in their inbox wishes to comment, they can do so directly from their email client.</li>
<li>It was simple and intuitive. We all know how to send emails.</li>
<li>There was no need to sign up on my blog, or require an account on a 3rd-party service to post a comment.</li>
</ul>
<p>Perhaps this kind of system is long tried and tested with the pitfalls discussed and explored, perhaps not. Nonetheless, I implemented it exactly as I planned, except for displaying those comments by fetching from an API — I only included a link to the corresponding lists.sr.ht thread.</p>
<p>Today, I am shutting down the mailing list and — following my <a href="/posts/webmentions/">removal of Webmentions</a> — there will be no more comments on my blog at all. Here are are some of the reasons why I made this decision.</p>

<h2 id="1-posting-needed-more-overhead">
  <a class="anchor" href="#1-posting-needed-more-overhead" rel="nofollow">#</a>
  1. Posting needed more overhead</h2>

<p>To commit to the mailing list system, I had to make sure all new posts were emailed every single time.</p>
<p>This means I would:</p>
<ol>
<li>convert the markdown (the primary format I use for content) into plaintext by hand</li>
<li>copy the result, paste it into my email client, and write the subject line</li>
<li>send the email to the mailing list address</li>
<li>go back to my browser, navigate to the link to the thread of the email I just posted and copy it</li>
<li>go back to my editor and update the metadata of the post to include the link I copied</li>
</ol>
<p>…before deploying my updated website.</p>
<p>All of these steps are non-trivial to automate, and naturally, I decided the time to figure out converting markdown into plaintext sensibly, sending emails from a script, and constructing the link of the thread, to automate the task would most definitely not be worth it over doing it manually each time.</p>

<h2 id="2-it-wasnt-really-a-newsletter">
  <a class="anchor" href="#2-it-wasnt-really-a-newsletter" rel="nofollow">#</a>
  2. It wasn’t really a newsletter</h2>

<p>Sometimes I write new posts daily, sometimes once in a year. I wouldn’t subscribe to a newsletter that starts to send me emails every day following a long period of inactivity — I would use a feed reader for blogs. To support comments on all my posts I needed to send an email for every post I publish.</p>
<p>If I were to actually start a newsletter that mirrored content on my blog, I would do a weekly digest instead. Unfortunately, combining different posts into a single email thread doesn’t quite work for the mailing-list-as-comments system I’m aiming for here.</p>
<p>I didn’t consider my mailing list an actual newsletter though it could function as one, neither did I really consider it a proper mailing list — I was only using it for the sake of a platform for public comments.</p>

<h2 id="3-it-wasnt-used-much">
  <a class="anchor" href="#3-it-wasnt-used-much" rel="nofollow">#</a>
  3. It wasn’t used much</h2>

<p>Alongside the <code>mailto:</code> links that lets people reply publicly to posts, I included a “reply via email privately” link. This was simply a <code>mailto:</code> to my personal email address with the subject line set to <code>Re:</code> + the title of the post. I did this for both my web and <a href="https://geminiquickst.art/">Gemini</a> posts.</p>
<p>For some reason, all replies via emails I received were sent to me privately.</p>
<p>If I were a reader of someone else’s blog and they had a public and a private option for “reply via email”, I would also go for the private option; it’s a nice opportunity to start a personal conversation with someone. Even if I just wanted to let them know about a typo in their post, I would choose “reply via email privately” and tell them about what I liked about the post, their site, and other things that might not even be related to the post or their blog, such as a project of theirs I found useful and have questions about.</p>
<p>I never thought to ask those who have replied via email to my posts privately — I already appreciate the fact that they’ve resonated with ideas presented in my post and have taken the time to share their thoughts — perhaps the reasons are similar to mine, but either way, the mailing list did not see much usage.</p>

<h2 id="4-3rd-party-content-didnt-quite-fit-on-my-personal-blog">
  <a class="anchor" href="#4-3rd-party-content-didnt-quite-fit-on-my-personal-blog" rel="nofollow">#</a>
  4. 3rd-party content didn’t quite fit on my personal blog</h2>

<p>I explored ways I could get replies to display at the end of a post, and I didn’t end up feeling satisfied with any way I came up with, so I ended up linking to the thread on lists.sr.ht instead.</p>
<p>My posts are hosted on my personal website. Everything here is authored by myself. Contents of comments, if I were to display them on posts, would be considered 3rd-party content. It would be totally fine had it been content on guestbooks, but comments at the end of my posts are not the same as guestbooks.</p>
<p>Blog articles also get syndicated to a number of places. To social media, to link-aggregators. My mailing list would be another place posts are syndicated to. All of these places are communities in itself, and all of these let members comment and interact with my post. Displaying comments from my mailing list on my blog would essentially declare my mailing list as the “canonical” source of comments. People from those other places might even end up commenting on the comments on my mailing list, because they are shown alongside the primary blog post content!</p>
<p>This just doesn’t sit right with me.</p>

<h2 id="conclusion">
  <a class="anchor" href="#conclusion" rel="nofollow">#</a>
  Conclusion</h2>

<p>I don’t regret experimenting with mailing lists for a commenting platform. I know of a few people that do something similar (but not quite identical), and I’m glad it has worked for them.</p>
<p>By the time you see this post, I will have removed all links to the mailing list on my blog.</p>
<p>You can still (and are encouraged to) use the “reply via email” link at the end of each post to share your thoughts on each topic. There is also a form that lets you send <a href="/posts/webmentions">Webmentions</a> — sites that links to my post. These will get manually included in the “syndications” section at the end of each post when there are any. You also have the option to interact from those places.</p>
<p>Sourcehut lists does not allow mailing list owners to view subscribers. If you had subscribed to the list, please use the <a href="/feeds/">RSS or Atom feeds</a> instead, thank you for supporting me.</p>
<hr hidden="" /><div class="footnotes" role="doc-endnotes">
<p><strong>Footnotes</strong></p>
<ol>
<li id="fn:1">
<p>lists.sr.ht requires all emails to <a href="https://useplaintext.email/">use plaintext</a>. The setup can easily support HTML rendered posts by swapping lists.sr.ht with another mailing list provider.&#160;<a class="back" href="#fnref:1" aria-labelledby="bl1-1 bl2-1" role="doc-backlink"><span id="bl1-1">[↩]</span></a></p>
</li>
</ol>
</div>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Removing%20comments%20on%20my%20posts">Reply via email</a></p>]]></content></entry><entry><title>Webmentions</title><link rel="alternate" href="https://home.hedy.dev/posts/webmentions/"/><id>https://home.hedy.dev/posts/webmentions/</id><published>2024-11-28T01:09:55Z</published><updated>2024-11-28T07:08:42Z</updated><summary type="text">How I've added Webmentions to my site over the years, and my general thoughts on the protocol.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>Webmention is a system in which other websites can notify you that when their site has mentioned a page on your site. It originated from the <a href="https://indieweb.org/Webmention">IndieWeb</a> and is now a <a href="https://www.w3.org/TR/webmention/">W3C Recommendation</a>.</p>
<p>To receive Webmentions, your site needs to expose an endpoint that will collect these Webmentions in a server, and link to it in a <code>&lt;link rel=&quot;webmention&quot; href=&quot;[endpoint]&quot;&gt;</code> tag in your <code>&lt;head&gt;</code>. This endpoint can be self-hosted (such as <a href="https://webmentiond.org/">webmentiond</a>), or with a service such as <a href="https://webmention.io">webmention.io</a>.</p>
<p>Someone that mentioned your site submits a POST request with a <code>source</code> parameter as the URL of their site, and the <code>target</code> parameter being the URL on your site which they linked.</p>
<p>You can then choose to display a list of all Webmentions of a particular page at the bottom of each page on your site, or simply get a notification and move on.</p>
<p>This seems simple. And it probably is if your blogging platform supports it without any overhead. But for hand-crafted personal sites with your own suite of tools — possibly statically-generated — implementing Webmentions is a bit more involved.</p>
<p>I set out to support Webmentions on my blog a few years ago with these goals:</p>
<ul>
<li>Webmentions I receive should go through manual approval before they can be displayed;</li>
<li>Webmentions I receive also should have been automatically verified before being listed for manual approval (such as checking the <code>source</code> and <code>target</code> or legitimate, and that the <code>source</code> actually does link to the <code>target</code>);</li>
<li>When I send Webmentions to pages I mention, the process should be automatic (such as on each deploy);</li>
<li>A website should not be pinged twice for the same page on my site that mentions it;</li>
<li>Display approved Webmentions so readers can continue reading further discussion (if there is any) after finishing a blog post.</li>
</ul>
<p>In this post, I’ll walk you through the several stages of my Webmention support, revisit the list above as a checklist on each stage, and share with you my thoughts on Webmention and the eventual direction I went with Webmention support.</p>

<h2 id="1-receiver">
  <a class="anchor" href="#1-receiver" rel="nofollow">#</a>
  1. Receiver</h2>

<p>I initially supported Webmentions by simply signing up on <a href="https://webmention.io">webmention.io</a>, a free service that lets you receive Webmentions from other people. It’s made by <a href="http://aaronparecki.com/">Aaron Perecki</a>, the author of the Webmentions specification.</p>
<p>In my site’s <code>&lt;head&gt;</code>, I included a link to the endpoint I was provided with from webmention.io:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;webmention&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://webmention.io/home.hedy.dev/webmention&#34;</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>This was enough for my website to receive Webmentions, should someone choose to send them.</p>
<p>I could use the API at <code>https://webmention.io/api/mentions.html?target=&lt;page-URL&gt;</code> to view Webmentions for a given page on my website, and I can also automatically show a link to it at the end of each blog post on my website to save the hassle of parsing and display the entries myself.</p>
<p>At this stage, I did not consider sending Webmentions for other people.</p>
<ul>
<li>✅ Manual approval: I could delete entries from the &lt;webmention.io&gt; dashboard and only include a public link that displays the list of Webmentions after all remaining entries are approved.</li>
<li>✅ Automatic verification: This was handled by &lt;webmention.io&gt;.</li>
<li>🟨 Automatic sending of Webmentions for changes on my site.</li>
<li>✅ A website should not be pinged twice for the same page on my site that mentions it;</li>
<li>🟨 Display approved Webmentions: well, somewhat, but not so much as I hoped for at the time.</li>
</ul>

<h2 id="2-sending-webmentions">
  <a class="anchor" href="#2-sending-webmentions" rel="nofollow">#</a>
  2. Sending Webmentions</h2>

<p>I decided that I should probably send some Webmentions to people, surely it takes less effort than writing a personalized email?</p>
<p>My site was (and still is) statically generated with Hugo. I found out about <a href="https://github.com/nekr0z/static-webmentions">static-webmentions</a>, the right tool for the job, and decided to give it a go.</p>
<p>After installing and adding the relevant configuration to my <code>config.toml</code>, I ran <code>static-webmentions find</code>.</p>
<p>It did not exit for several minutes, without producing any output. I noticed there was a new <code>mentions.json</code> file. Perhaps that was enough? I ran <code>static-webmentions send</code>, it crashed with a trace-back log of Goroutines.</p>
<p>I looked through the default configuration, added <code>concurrentFiles = 1</code> and <code>concurrentRequests = 1</code>, and <code>static-webmentions send</code> worked… for some reason.</p>
<p>It produced a lot of output, showing me the sites it tried (and failed) to find a Webmention endpoint for. Sites that included my own and sites that aren’t even using the HTTP(S) protocol. I updated my list of excludes in <code>config.toml</code> and tried again.</p>
<p>More output.</p>
<p>Hang on… wouldn’t that send repeated Webmentions to sites it has previously successfully sent to?</p>
<p>I checked the docs again. Looks like it did support watching for new changes using an <code>oldDir</code> and <code>newDir</code> directories. Luckily, I kept a previous deployment locally. I updated <code>config.toml</code>, but since I hadn’t updated anything between the two runs, it would not detect any new Webmentions this (third) time around.</p>
<p>Well, guess I’d test it out the next time I updated my website.</p>

<h2 id="3-display-webmentions">
  <a class="anchor" href="#3-display-webmentions" rel="nofollow">#</a>
  3. Display Webmentions</h2>

<p>As I started to share links from my site elsewhere on the internet, I decided that I should try display Webmentions.</p>
<p>I found out about the <a href="https://brid.gy">brid.gy</a> service which could connect your website to social media and send you Webmentions when someone likes or comments on a post that links to your website. I had it set up, and started receiving more Webmentions.</p>
<p>To display Webmentions, I was initially fetching from the API provided by webmention.io. The Webmentions that brid.gy sends provided more data than simply a <code>source</code> and <code>target</code> field because they were really interactions such as likes and comments rather than simply mentioning links.</p>
<p>This was fine, I didn’t need to handle likes and comments from sources other than brid.gy as I was yet to receive any. I wrote HTML templates that will display all those Webmentions nicely like a comment section on a forum site.</p>
<p>Oops, Fediverse custom emotes were showing as full width images! I fixed those with CSS. But they didn’t have any semantic information that could differentiate them from actual images, and so all images would show as emotes…</p>
<p>The only solution I could think of was to edit the comment data to add a custom <code>class=&quot;emoji&quot;</code>. Which meant I had to save the JSON data from the webmention.io API locally. I made a script with some <code>jq</code>-fu to extract the relevant data, then manually merged new data with existing (edited) data manually when new Webmentions arrive every time.</p>
<p>This was’t too bad. An ad-hoc approval process that checks one of the receiver requirement boxes!</p>
<p>But even if I could get the entire process streamlined with a script, I’d still have to store the approved Webmentions somewhere if it wasn’t on webmention.io’s servers, in case I lose those files locally. It doesn’t fit to track them in Git along with my site repo, so I decided to deploy it to a temporary static site (such as <a href="https://pgs.sh">pgs.sh</a>) before each deploy.</p>
<p>Again, that wasn’t <em>too</em> bad.</p>
<p>A review of the third pass:</p>
<ul>
<li>✅ Manual approval: I could delete entries from the webmention.io dashboard like before, but this time, the JSON data has to go through a script, from which I copy the result and manually merge it with existing Webmentions. This means I was incentived to wait for a batch of Webmentions before merging it and rebuilding my site to display the new entries.</li>
<li>✅ Automatic verification: Again, this was handled by webmention.io.</li>
<li>🟨 Automatic sending of Webmentions for changes on my site.</li>
<li>✅ A website should not be pinged twice for the same page on my site that mentions it;</li>
<li>✅ Display approved Webmentions: not so timely though, due to what I’ve described in the first checklist item.</li>
</ul>

<h2 id="4-sender-v2">
  <a class="anchor" href="#4-sender-v2" rel="nofollow">#</a>
  4. Sender, v2</h2>

<p>Some time later, I changed my deployment set up and a fully representative <code>oldDir</code> for <code>static-webmentions</code> was no longer possible.</p>
<p>Without an <code>oldDir</code>, it would send <em>all Webmentions</em> for <em>all pages</em>  on each invocation. Not only would this be a waste of resources, it would also drastically increase the time needed for each deployment, even if I just needed to quickly update a typo.</p>
<p>To make matters worse, <code>static-webmentions</code> kept crashing even with the <code>concurrentFiles = 1</code> workaround that previously worked. I had no choice but to look for alternatives.</p>
<p>From the <a href="https://indieweb.org/Webmention">IndieWeb Wiki page for Webmentions</a>, I found a number of tools that were said to make sending Webmentions easy. Unfortunately, a good portion of them were unmaintained, and about half do not work with my setup.</p>
<p>I found one tool that lets you submit a link to a web page, and it handles attempting to send Webmentions for all linked sites on the page, made by none other than Aaron Parecki of webmention.io, called <a href="https://telegraph.p3k.io/">Telegraph</a>.</p>
<p>This service made it easy to send new Webmentions when I publish new posts or create new pages — I could just submit the link of the new page. When I edit existing pages I could also submit a manual <code>source</code> and <code>target</code> and it would handle the discovery of Webmention endpoints for me.</p>
<p>As I started to receive Webmentions from brid.gy more frequently, I found myself using my previous setup for the receiver a lot less. I could check Webmentions easily, but fetching and processing new Webmentions just so they could be displayed at the end of each blog post was quite involved.</p>
<p>The reason I displayed Webmentions in the first place was for readers to have easy access to discussion and responses surrounding a blog post for further reading after finishing a post on my blog. This usually meant the Fediverse or other social media links, or link aggregators.</p>
<p>I decided that it wasn’t worth all the work making all the Webmentions display the way I want on my site. I could link to the specific Mastodon or link-aggregator post instead, and readers can view the interactions and comments displayed at the original source. It only added a single extra click, and it was a little weird to show 3rd party content right under my own post on a personal site.</p>
<p>Furthermore, despite all the controversy regarding different servers seeing a different set up of replies and different number of likes/reposts, Fediverse web clients will always be more up to date than my blog. Trying to parse brid.gy’s Webmentions and displaying it sensibly was re-inventing the wheel.</p>
<p>I ditched displaying Webmentions altogether and resorted to updating my site when I get notified of a new Webmention to link to that site at the end of a post.</p>
<p>A review of this pass:</p>
<ul>
<li>✅ Manual approval: nothing is approved until I manually link to mentions on my site;</li>
<li>✅ Automatic verification;</li>
<li>✅ Automatic sending of Webmentions for changes on my site: not completely automatic, but I just need to submit the link of the new page;</li>
<li>✅ A website should not be pinged twice: Telegraph shows a list of previously sent Webmentions;</li>
<li>✅ Display approved Webmentions.</li>
</ul>

<h2 id="conclusion">
  <a class="anchor" href="#conclusion" rel="nofollow">#</a>
  Conclusion</h2>

<p>If Webmentions remained what was originally envisioned — a simple <code>source</code> mentioning a <code>target</code> — things would be a lot better. This change doesn’t improve the sender situation, but it certainly makes it easier to receive and display Webmentions.</p>
<p>I have never tried the Webmention support on hosted blogging platforms such as Wordpress, nor have I used <code>webmentiond</code> (it self-hosting, as well as a mail server, which isn’t possible for many blogging hobbyists), but from my experience with implementing Webmentions on my statically generated site, receiving Webmentions is easy, sending and displaying them, not so much.</p>
<p>For now, I will be keeping my implementation the way I described in the fourth pass. It’s unlikely this setup will change in the foreseeable future.</p>
<p>My preferred method of communication is email, and I would encourage you to use the “Reply via Email” link at the end of the each post to send me your thoughts and suggestions (though there is a form to send Webmentions manually too, if you prefer). Even if you’re only notifying me of a response to my post on your site. It’s personal, it’s customized, and it’s less likely to get loss.</p>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Webmentions">Reply via email</a></p>]]></content></entry><entry><title>Hugo shortcodes in Lume</title><link rel="alternate" href="https://home.hedy.dev/posts/hugo-shortcodes-in-lume/"/><id>https://home.hedy.dev/posts/hugo-shortcodes-in-lume/</id><published>2024-11-25T07:12:24Z</published><updated>2024-11-29T08:37:57Z</updated><summary type="text">I needed HTML components in Lume similar to Hugo shortcodes and tried MDX. Here's how I implemented Hugo shortcodes in Lume after facing issues with MDX.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>Recently, I’ve been looking at <a href="https://lume.land/">Lume</a>, a new-ish static-site generator that runs on <a href="https://deno.land/">Deno</a> as a possible replacement for Hugo on my site.</p>
<p>I decided to give it a try when starting a new project, the static site for <a href="https://meta-ring.hedy.dev/">Meta Ring</a>. The many things Lume does well that made me consider the switch is beyond the scope of the article. Instead, let’s talk about one thing I miss from Hugo: shortcodes.</p>
<blockquote class="callout note">
  <p class="title"><strong>Note</strong></p>
<p>If you’re looking for the TL;DR solution, skip to the <a href="#conclusion">conclusion</a>.</p>
</blockquote>
<p>The homepage for my webring required a list of webring members, which is defined in a data file as JSON. It’s trivial to use a template engine to parse the JSON and display the HTML, but I needed a way to specify where I want to insert that member list in my <code>index.md</code>. This would be easy had I used Hugo, because Hugo shortcodes lets me do exactly that.</p>
<p>For my Lume project, I explored some alternatives such as MDX, but finally arrived at a custom solution. First, I’ll explain how Hugo shortcodes work.</p>

<h2 id="hugo-shortcodes">
  <a class="anchor" href="#hugo-shortcodes" rel="nofollow">#</a>
  Hugo shortcodes</h2>

<p><a href="https://gohugo.io/content-management/shortcodes/">Shortcodes</a> in Hugo are fairly similar to components in <a href="https://mdxjs.com/">MDX</a>. MDX lets you write your usual JSX/TSX components and embed them directly within markdown for content pages and blog posts. This is extremely useful in CMSes and static-site generators when you need to insert a custom component of any kind directly within markdown content<sup><a href="#fn:1" id="fnref:1" role="doc-noteref">[1]</a></sup>.</p>
<p>In Hugo, shortcodes are defined using Go HTML templates. When executed, they receive certain metadata such as the page being rendered and the content included within the tags.</p>

<h3 id="example">
  <a class="anchor" href="#example" rel="nofollow">##</a>
  Example</h3>

<p>Here’s how I would implement the webring member listing in Hugo.</p>
<p>Create a shortcode template at <code>layouts/shortcodes/members.html</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{{/* ...code to fetch the JSON */}}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ range $members }}
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ .url }}&#34;</span><span class="p">&gt;</span>{{ .name }}<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ .colophon }}&#34;</span><span class="p">&gt;</span>{{ .colophon }}<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ end }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Use the shortcode in markdown:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">Some introductory material
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gh"># Members
</span></span></span><span class="line"><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="cl">Check out our awesome members!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{{% members %}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Some content that follows.
</span></span></code></pre></div>
<h2 id="mdx">
  <a class="anchor" href="#mdx" rel="nofollow">#</a>
  MDX</h2>

<p>To implement components similar to Hugo shortcodes in my Lume site, I first looked at MDX since it was included as an official plugin.</p>
<p>The limitation became apparent pretty quickly. You have to define your components with JSX or TSX. If you wish to use HTML templates to define components, using them will require an ugly workaround:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">dangerouslySetInnerHTML</span><span class="o">=</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">__html</span><span class="o">:</span> <span class="nx">comp</span><span class="p">.</span><span class="nx">members</span><span class="p">()</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="p">/&gt;</span>
</span></span></code></pre></div><p>Furthermore, the MDX content format also comes with <a href="https://github.com/micromark/mdx-state-machine#72-deviations-from-markdown">its own limitations</a> on top of both markdown and JSX, such as no support for autolinks (<code>&lt;url&gt;</code>) and no HTML/JSX comments.</p>
<p>And what if I decided to just roll with it and define my components in JSX/TSX? That’ll mean adding <code>npm:react</code> (or preact) on top of my stack of dependencies!</p>
<p>Which means, I’ll need something else. Preferably, something that lets me define components with HTML templates…<sup><a href="#fn:2" id="fnref:2" role="doc-noteref">[2]</a></sup></p>

<h2 id="without-mdx">
  <a class="anchor" href="#without-mdx" rel="nofollow">#</a>
  Without MDX</h2>

<p>Here’s what we currently have:</p>
<ul>
<li>
<p>Lume uses <a href="https://github.com/markdown-it/markdown-it">markdown-it</a> by default for creating pages in markdown. It supports extension with custom plugins that change how markdown is rendered. It also supports including HTML directly in the markdown, but text within HTML tags will still be parsed as markdown, similar to GitHub Flavored Markdown.</p>
</li>
<li>
<p>Lume pages have a <code>templateEngine</code> field. This is a list that decides how your raw <code>.md</code> files will get transformed into <code>.html</code><sup><a href="#fn:3" id="fnref:3" role="doc-noteref">[3]</a></sup>. By default, this will only be markdown-it. I can add <code>vento</code> (or another HTML templating engine) to the list, so that markdown pages can be preprocessed using a templating engine before being fed into markdown-it. This will let us use <code>{{ templating tags }}</code> within markdown to include our components.</p>
</li>
<li>
<p>Lume has its own “<a href="https://lume.land/docs/core/components/">generic Lume components</a>”. You can define HTML templates, or even JSX/TSX components in the <code>_components/</code> directory. Then, in page templates, use components with <code>{{ comp.MyComponent({ ...options }) }}</code>. The relevant component is executed and the tag replaced with the output HTML as part of the static site build process.</p>
</li>
</ul>
<p>All this is enough to have a bare-bones implementation of Hugo shortcodes within Lume — but there’s a catch. Let me explain how to make it mostly works and we’ll get to the catch later.</p>

<h2 id="a-half-baked-solution">
  <a class="anchor" href="#a-half-baked-solution" rel="nofollow">#</a>
  A half-baked solution</h2>

<p>We’ll define the member list component in <code>_components/members.html</code> as a Lume component with <a href="https://vento.js.org/">Vento</a> templating:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ for item of members }}
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">strong</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ item.url }}&#34;</span><span class="p">&gt;</span>{{ item.name }}<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">strong</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ item.colophon }}&#34;</span><span class="p">&gt;</span>{{ item.colophon }}<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ /for }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span></code></pre></div><blockquote class="callout note">
  <p class="title"><strong>Note</strong></p>
<p>The <code>members</code> variable contains the unmarshalled JSON list. This works by putting the member list in <code>_data/members.json</code> as Lume’s <a href="https://lume.land/docs/creating-pages/shared-data/">shared data</a>.</p>
</blockquote>
<p>Next, in the frontmatter of <code>_index.md</code>, specify that we want to use Vento (our templating engine) to preprocess the markdown before passing it to markdown-it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Meta Ring&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">templateEngine</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">vento, md]</span><span class="w">
</span></span></span></code></pre></div><p>And finally, in the relevant location in <code>index.md</code>, use the component:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl"><span class="gu">## Members
</span></span></span><span class="line"><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="cl">{{ comp.members() }}
</span></span></code></pre></div><p>This works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h2</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;members&#34;</span> <span class="na">tabindex</span><span class="o">=</span><span class="s">&#34;-1&#34;</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#members&#34;</span> <span class="na">aria-hidden</span><span class="o">=</span><span class="s">&#34;true&#34;</span><span class="p">&gt;</span>##<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span> Members<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">strong</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://home.hedy.dev/&#34;</span><span class="p">&gt;</span>~hedy<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">strong</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://home.hedy.dev/meta/&#34;</span><span class="p">&gt;</span>https://home.hedy.dev/meta/<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- ... --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>So where’s the catch?</p>
<p>Review the entire process:</p>
<ol>
<li><code>index.md</code> is rendered with Vento,</li>
<li>Vento sees a <code>comp.members()</code> call,</li>
<li>Vento renders <code>_components/members.html</code>,</li>
<li>The call is replaced with the resulting HTML,</li>
<li>Contents of <code>index.md</code> with <code>comp.members()</code> replaced with HTML <code>&lt;ul&gt; ... &lt;/ul&gt;</code> is passed to markdown-it.</li>
</ol>
<p>The issue here is that markdown-it continues to attempt to parse markdown even within HTML tags. But in Hugo, HTML shortcodes are strictly preserved as HTML, as-is.</p>
<p>If the members component were defined much less succinctly, such as including an extra newline somewhere, or if any of the <code>item.*</code> data we’re inserting contains newlines with markdown, these will get rendered by markdown-it.</p>
<p>For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ for item of members }}
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span><span class="c">&lt;!-- look, a newline! --&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">strong</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ item.url }}&#34;</span><span class="p">&gt;</span>{{ item.name }}<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">strong</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ item.colophon }}&#34;</span><span class="p">&gt;</span>{{ item.colophon }}<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ /for }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>This produces:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span><span class="c">&lt;!-- look, a newline! --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">strong</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://home.hedy.dev/&#34;</span><span class="p">&gt;</span>~hedy<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">strong</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://home.hedy.dev/meta/&#34;</span><span class="p">&gt;</span>https://home.hedy.dev/meta/<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- ... --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Which means we need a way to make markdown-it ignore whatever the output of the components we use, similar to “<a href="https://gohugo.io/render-hooks/passthrough/">passthrough hooks</a>” in Goldmark for Hugo. For instance, have whatever that is between delimiters <code>:::</code> be rendered as-is, like fenced code blocks<sup><a href="#fn:4" id="fnref:4" role="doc-noteref">[4]</a></sup>.</p>

<h2 id="the-final-solution">
  <a class="anchor" href="#the-final-solution" rel="nofollow">#</a>
  The final solution</h2>

<p>Unfortunately, I couldn’t find any existing passthrough plugins for markdown-it, so I rolled my own, based on the <a href="https://github.com/markdown-it/markdown-it-container">markdown-it-container</a> plugin and the built-in fenced code block rule.</p>
<p>I’ve included it along with the code for the Meta Ring site in a <a href="https://github.com/hedyhli/meta-ring/blob/main/passthrough.mjs"><code>passthrough.mjs</code></a> file, and used it as a plugin in my Lume config:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">passthrough</span> <span class="kr">from</span> <span class="s2">&#34;./passthrough.mjs&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">site</span><span class="p">.</span><span class="nx">hooks</span><span class="p">.</span><span class="nx">addMarkdownItPlugin</span><span class="p">(</span><span class="nx">passthrough</span><span class="p">,</span> <span class="p">{});</span>
</span></span></code></pre></div><p>Update the component template to use these delimiters:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">:::
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ for item of members }}
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span><span class="c">&lt;!-- look, a newline! --&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">strong</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ item.url }}&#34;</span><span class="p">&gt;</span>{{ item.name }}<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">strong</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ item.colophon }}&#34;</span><span class="p">&gt;</span>{{ item.colophon }}<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ /for }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">:::
</span></span></code></pre></div><p>And that’s it! Regardless of the format of HTML we have in the component, it will be rendered as-is by markdown-it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h2</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;members&#34;</span> <span class="na">tabindex</span><span class="o">=</span><span class="s">&#34;-1&#34;</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#members&#34;</span> <span class="na">aria-hidden</span><span class="o">=</span><span class="s">&#34;true&#34;</span><span class="p">&gt;</span>##<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span> Members<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span><span class="c">&lt;!-- look, a newline! --&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">strong</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://home.hedy.dev/&#34;</span><span class="p">&gt;</span>~hedy<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">strong</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://home.hedy.dev/meta/&#34;</span><span class="p">&gt;</span>https://home.hedy.dev/meta/<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- ... --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span></code></pre></div>
<h2 id="conclusion">
  <a class="anchor" href="#conclusion" rel="nofollow">#</a>
  Conclusion</h2>

<p>There isn’t yet an existing plugin for Lume that provides something similar to Hugo shortcodes, but it wasn’t hard for me to implement it myself.</p>
<p>Here’s how it works, in review:</p>
<ul>
<li>Define components in any templating engine format, in <code>_components/</code>. Include delimiters, or a <code>markdown=&quot;no&quot;</code> attribute (specific to your markdown processor) to ensure the HTML for components are not processed as markdown. For markdown-it, I’ve implemented this functionality using <a href="#the-final-solution">a custom plugin</a>.</li>
<li>Use your templating engine to pre-process markdown pages by specifying the list <code>templateEngine</code> in the frontmatter. For instance, <code>templateEngine: [vento, md]</code>.</li>
<li>Include the component using the correct tags for your templating engine in markdown pages, such as <code>{{ comp.MyComponent() }}</code> in Vento, or <code>&lt;%= comp.MyComponent() %&gt;</code> in Eta.</li>
</ul>
<p>And this will get you as close to Hugo shortcodes as I am aware of.</p>
<p>To escape tag delimiters for your templating engine in markdown, you’ll have to either avoiding setting <code>templateEngine</code> on pages that do not need components, or use an escape tag. For example, <code>{{ echo }}{{ comp.example() }}{{/ echo }}</code> in Vento produces <code>{{ comp.example() }}</code>.</p>
<p>The source code for <a href="https://meta-ring.hedy.dev/">Meta Ring</a> is available on
<a href="https://github.com/hedyhli/meta-ring">GitHub</a>.</p>
<hr hidden="" /><div class="footnotes" role="doc-endnotes">
<p><strong>Footnotes</strong></p>
<ol>
<li id="fn:1">
<p>Usually, this just means anything you might want to use twice that needs to be HTML inserted within markdown.&#160;<a class="back" href="#fnref:1" aria-labelledby="bl1-1 bl2-1" role="doc-backlink"><span id="bl1-1">[↩]</span></a></p>
</li>
<li id="fn:2">
<p>I did not use Web Components, because I needed the components to be resolved during the build process by the static site generator, and have support for older browsers, TUI browsers, and screen readers.&#160;<a class="back" href="#fnref:2" aria-labelledby="bl1-2 bl2-2" role="doc-backlink"><span id="bl1-2">[↩]</span></a></p>
</li>
<li id="fn:3">
<p>Note that the name <code>templateEngine</code> is misleading. In Lume, you can create page with Markdown, with HTML templates, with TypeScript, with JSX, or even anything else supported by plugins. Creating HTML pages with Markdown, in Lume, is essentially running a markdown renderer (in this case markdown-it), on your <code>.md</code> files as a “template engine”.&#160;<a class="back" href="#fnref:3" aria-labelledby="bl1-3 bl2-3" role="doc-backlink"><span id="bl1-3">[↩]</span></a></p>
</li>
<li id="fn:4">
<p>It may seem like this is a rather involved workaround, but it so happened that I needed a passthrough plugin like this in another part of the site.&#160;<a class="back" href="#fnref:4" aria-labelledby="bl1-4 bl2-4" role="doc-backlink"><span id="bl1-4">[↩]</span></a></p>
</li>
</ol>
</div>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Hugo%20shortcodes%20in%20Lume">Reply via email</a></p>]]></content></entry><entry><title>Starting afresh</title><link rel="alternate" href="https://home.hedy.dev/posts/starting-afresh/"/><id>https://home.hedy.dev/posts/starting-afresh/</id><published>2024-11-15T06:15:09Z</published><updated>2024-11-24T08:50:04Z</updated><summary type="text">Restart, from scratch.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>It’s nice to set up your digital workspace again sometimes, from scratch. One that isn’t your main computer so as to not interfere with ongoing work. You are freed from the shackles of backward compatibility and limitations of your previous setups. Pick a new shell, use another file manager, another editor.</p>
<p>Pubnixes<sup><a href="#fn:1" id="fnref:1" role="doc-noteref">[1]</a></sup> provide you a shell account and web, sometimes gemini and gopher hosting. When you log in with SSH, you are presented with a new home. A shell that may be different to your primary workstation, none of your familiar aliases and shell scripts, only some CLI programs you know and love are installed and there are some tools you’ve never heard of.</p>
<p>You feel a responsibility to tend to your new digital garden that is your public home page. You explore the local services, such as decentralized chat, a local forum, a wiki, and you start to discover foreign workflows you consider adopting.</p>
<p>Getting a package installed typically involves contacting a sysadmin. But since you are not the only user on the system, it’s likely others have similar problems and have solved it using a different tool. You get to learn new ways to do things, such as editing files and browsing the internet from the command line and the local file system. It’s like moving in to a friend’s place temporarily, or settling in a dorm room of a different country — you shop in unfamiliar places and buy things from unfamiliar brands.</p>
<p>It might seem daunting, especially when it’s put this way, and even more so when you look at it from the perspective of needing to set up everything <em>again</em>, the way you’re used to. However, similar to moving to a different country permanently, you would sometimes discover things you otherwise would not have if not for having to go out looking for alternatives.</p>
<p><a href="https://rawtext.club">rawtext.club</a> (“RTC”) is a pubnix I was a member of for several years. It was recently reinstated on a different server, and I had to experience exactly what I described. Note this though: I wasn’t setting up a new laptop, it was a digital home like any other, but with different purposes. <code>ranger</code>, a terminal file manager I use everywhere else was not installed, and so I decided to try out another TUI file manager I’ve been looking at, <a href="https://github.com/Canop/broot">broot</a>. The fish shell isn’t installed like it used to either, so I decided to roll with bash and get familiar with <code>ctrl-r</code>. There were no static-site generators; I decided to try something new, such as <a href="https://kiln.adnano.co/">kiln</a> or <a href="https://github.com/jamestomasino/burrow">burrow</a>, or simply do with a hacky shell script for now until my site or gopherhole grows out of it, if ever.</p>
<p>On my computer, I would normally refrain from trying a different tool when I’m perfectly happy with what I have. Tools that I don’t have issues with that solve the same problem are most likely equally good when we valuate it based on how well it solves <em>all</em> of my problems, and usually making the migration (muscle memory and learning curve included) to make one thing a little easier isn’t worth it in the long run. On RTC, I was more than happy to try different tools because there was practically zero risk (most of my work does not occur there), and that there was no status quo to challenge in the first place.</p>
<p>Through this process I might discover something interesting or another way of doing things that can benefit other aspects of my digital workspace because as times goes on I would not do it the same way had I started from scratch. It’s like making different choices when you play a choose-your-adventure game a second time, or living an alternate version of myself. I see my existing decisions in new light, explore possible solutions for tools I have issues with, and most importantly, I’ve made myself comfortable in a second home quite unalike the one I’m used to.</p>
<p>It’s nice to start afresh sometimes.</p>
<hr hidden="" /><div class="footnotes" role="doc-endnotes">
<p><strong>Footnotes</strong></p>
<ol>
<li id="fn:1">
<p>A pubnix, or a public access unix system, such as SDF: <a href="https://en.wikipedia.org/wiki/SDF_Public_Access_Unix_System">https://en.wikipedia.org/wiki/SDF_Public_Access_Unix_System</a>, or the tildes of the <a href="https://tildeverse.org/">Tildeverse</a>.&#160;<a class="back" href="#fnref:1" aria-labelledby="bl1-1 bl2-1" role="doc-backlink"><span id="bl1-1">[↩]</span></a></p>
</li>
</ol>
</div>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Starting%20afresh">Reply via email</a></p>]]></content></entry><entry><title>A new webring for the weird and wonderful personal web</title><link rel="alternate" href="https://home.hedy.dev/posts/meta-ring/"/><id>https://home.hedy.dev/posts/meta-ring/</id><published>2024-11-11T07:45:11Z</published><updated>2024-11-29T08:37:57Z</updated><summary type="text">I made a webring!</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>Following up on my <a href="https://home.hedy.dev/posts/meta-pages/">recent post about colophons (“meta” pages)</a>, I’ve decided to create a webring to gather more personal websites through opt-ins.</p>
<p>I chewed over the initial idea for the former blog post (what, really, was I advocating for?) and wrote a bit more about it in detail in the webring home page — <a href="https://meta-ring.hedy.dev/">meta-ring.hedy.dev</a>. Here are a few snippets.</p>
<blockquote>
<p>Your personal home page is unique. It is the result of careful craft and deliberation and it represents a piece of you. You make intentional design choices to reflect the values you believe in and how you see yourself, how you wish to be perceived. You choose to use certain technologies that gets you excited, or makes you productive, or simply ones that just “works” for your use-case. You have built workflows and systems for and around producing content on your personal pages, systems that works just for your own little corner of the internet; and you write about all of this in a dedicated page on your very own website and share it with others like you, who are also fascinated by the cool and quirky personal web.</p>
</blockquote>
<blockquote>
<p>What should it be about?</p>
<p>This could be anything you want to write about which you see fit — what do you care a lot about on your personal site? Is it accessibility? Is it the fonts, the color scheme, or other aspects of design? Is it your blogging scripts, or did you hack together your own static-site generator to build your site from your own custom content format?</p>
</blockquote>
<blockquote>
<p>This webring focuses on personal websites. This means organization and project home pages will not be accepted.</p>
<p>It’s great to know about the technology behind your project sites, but the purpose of this webring is to encourage unique personal sites, the joy of exploration and tinkering with technology, reflecting it in your personal site, and sharing it with the world.</p>
</blockquote>
<p>If you’re feeling hesitant or have questions, you’re always welcome to click on the “Reply via Email” link at the bottom of each post.</p>
<p>A webring is only of as much significance as its members, and this one has only just started — if you think this is something you’d like to advocate for, I’d love to see what you’ve made. <a href="https://meta-ring.hedy.dev/#how-do-i-join%3F">Join</a> the webring by submitting a GitHub PR, or if you prefer, <a href="https://lists.sr.ht/~hedy/inbox">patches to this mailing list</a> for <a href="https://sr.ht/~hedy/meta-ring">this SourceHut repo</a> also works.</p>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20A%20new%20webring%20for%20the%20weird%20and%20wonderful%20personal%20web">Reply via email</a></p>]]></content></entry><entry><title>Writing Fonts</title><link rel="alternate" href="https://home.hedy.dev/posts/writing-fonts/"/><id>https://home.hedy.dev/posts/writing-fonts/</id><published>2024-11-09T12:18:55Z</published><updated>2024-11-09T14:30:48Z</updated><summary type="text">A little story on my favorite fonts for writing and reading.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>In search of the perfect font for drafting and digital journalling.</p>

<h2 id="inter">
  <a class="anchor" href="#inter" rel="nofollow">#</a>
  Inter</h2>

<p>The story began when I was looking for Logseq themes. I chose <a href="https://github.com/playerofgames/logseq-mia-theme">miA</a>, a theme inspired by iA Writer and MacOS UI. It recommends IBM Plex Sans and iA Writer Quattro<sup><a href="#fn:1" id="fnref:1" role="doc-noteref">[1]</a></sup> as one of the fonts for text. For some reason, neither called out to me at the time. By serendipitous discovery I found and have come to adore the <a href="https://rsms.me/inter/">Inter</a> font. I switched to that for Logseq as well as Emacs for the Emacs variable pitch font (such as in Org Mode).</p>
<p>Through unrelated rabbit-holes I rediscovered the beauty of Inter in Omnivore, a bookmarking service that offers an interface for reading saved articles. I chose the Inter font yet again, and thought that this was the most beautiful thing in the world. (Perhaps the pixel-perfect display of my reading device played a bigger role to this sentiment, but Inter sure looks really good.)</p>

<h2 id="ia-writer-quattro">
  <a class="anchor" href="#ia-writer-quattro" rel="nofollow">#</a>
  iA Writer Quattro</h2>

<p>A year or two later, I picked up Logseq again after a period of inactivity. Logseq had gone through many rounds of updates and the miA theme was sadly no longer fully compatible. I then went back and forth between rolling my own theme (minor customizations to the default with CSS) and making the fixes on top of miA. The primary font used for text caught my attention yet again. But this time, iA Writer Quattro<sup><a href="#fn:2" id="fnref:2" role="doc-noteref">[2]</a></sup> emerged as a new contender. Unfortunately, I quickly realized after switching to Quattro in a rush to perfect my Logseq experience, that ligatures (such as turning <code>-</code> followed by <code>&gt;</code> into an arrow) was specific to Inter!</p>

<h2 id="ibm-plex-sans-and-mono">
  <a class="anchor" href="#ibm-plex-sans-and-mono" rel="nofollow">#</a>
  IBM Plex Sans and Mono</h2>

<p>Looking more into the iA Writer fonts I discovered that they were based on top of IBM Plex fonts. I decided to switch back to Inter for the text font, but use IBM Plex Mono for code, hoping to retain the fuzzy feeling I get when I behold the Quattro by leveraging my frequent use of code blocks, but keep those pretty ligatures<sup><a href="#fn:3" id="fnref:3" role="doc-noteref">[3]</a></sup>.</p>
<p>It worked out well. In fact, I was so happy with IBM Plex Mono that I jumped ship by switching to IBM Plex Mono in my terminal (where I previously always used Fira Code)!</p>

<h2 id="so-the-best-writing-font">
  <a class="anchor" href="#so-the-best-writing-font" rel="nofollow">#</a>
  So… the best writing font?</h2>

<p>I have a blog which, admittedly, receives unbearably infrequent updates until only recently. Carrying my new-found passion of admiring amazing fonts for reading and writing, I decided to try crafting a writing experience that makes the process enjoyable. In other words, incentivizing myself to write more through the lure of seeing text in a beautiful font appear before my caret. I started off with IBM Plex Sans<sup><a href="#fn:4" id="fnref:4" role="doc-noteref">[4]</a></sup>.</p>
<p>It worked quite well, until I started to write more frequently. Something just felt off. I missed writing prose in the terminal where my caret was block-shaped and it felt as though I was whispering into the void. Instead I was making an important speech in front of the entire world ad-hoc. What I see as I wrote was what my reader will see. It was as though I have to get everything perfect the first time or any blunders will look stupid under the beautiful variable-pitch font.</p>
<p>So I decided to get a free trial of iA Writer. Turns out, it uses a mostly monospace-looking font by default. How can it be? We’re not writing code! Oh, right, this came from typewriters which essentially <em>had</em> to produce fixed-width text. But surely there are other reasons apart from… legacy or even nostalgia? I decided to give <a href="http://ia.net/topics/in-search-of-the-perfect-writing-font/">iA Writer’s article on Duospace</a> a proper read. A little shocked to find that “in search of the perfect writing font” — a decent subtitle for the post I am currently writing right there in the title — articulated my experience quite precisely:</p>
<blockquote>
<p>In contrast to proportional fonts that communicate “this is almost done” monospace fonts suggest “this text is work in progress.” It is the more honest typographic choice for a text that is not ready to publish.</p>
<p>[…]</p>
<p>Proportional fonts save space. They suggest that you “hurry up and fill the page.” Monospaced fonts, on the other hand, feel more productive. Every typed letter translates into a homogenous visual progress in writing. It is both more relaxing to write at a slower pace and more satisfying as the progress is more tangible.</p>
</blockquote>
<p>I’ve been using iA Writer Duo for drafting and digital journal ever since.</p>
<p>On my blog, as of writing, I use Inter for the post body, IBM Plex Sans for headings and IBM Plex Mono for code. On posts such as this one which I deem to require a more accurate representation of my writing environment, I use iA Writer Duo.</p>

<h2 id="addendum-reading-fonts">
  <a class="anchor" href="#addendum-reading-fonts" rel="nofollow">#</a>
  Addendum: Reading fonts</h2>

<p>Here are some of my favorite fonts suited for reading:</p>
<ul>
<li><a href="https://rsms.me/inter/">Inter</a> &amp; <a href="https://www.ibm.com/plex/">IBM Plex Sans</a>, as mentioned in the post</li>
<li><a href="https://github.com/iaolo/iA-Fonts">iA Writer Duo &amp; Quattro</a> (the Duo might be a good choice for use in websites that goes for the “monospace”/retro vibe while remaining fairly easy to read)</li>
<li><a href="https://en.wikipedia.org/wiki/Fira_(typeface)">Fira Sans</a></li>
<li><a href="https://en.wikipedia.org/wiki/Atkinson_Hyperlegible">Atkinson Hyperlegible</a></li>
<li>Basier</li>
<li><a href="https://vercel.com/font">Geist</a></li>
<li>Epilogue (found on <a href="https://lume.land/">https://lume.land/</a>)</li>
<li>Aptos, the font Microsoft recently started using — to me if feels to be a blend between Inter and IBM Plex Sans, but that isn’t to say that it is superior to either.</li>
</ul>
<hr hidden="" /><div class="footnotes" role="doc-endnotes">
<p><strong>Footnotes</strong></p>
<ol>
<li id="fn:1">
<p>It may have been Duo instead. Though it’s one of them, for the miA sans variant.&#160;<a class="back" href="#fnref:1" aria-labelledby="bl1-1 bl2-1" role="doc-backlink"><span id="bl1-1">[↩]</span></a></p>
</li>
<li id="fn:2">
<p>If it interests you, it may be worth looking into other fonts iA Writer includes — Mono, Duo, and Quattro in <a href="https://ia.net/topics/in-search-of-the-perfect-writing-font">this article about Duospace</a> as well as <a href="https://ia.net/topics/a-typographic-christmas">this other one</a> on the fonts.&#160;<a class="back" href="#fnref:2" aria-labelledby="bl1-2 bl2-2" role="doc-backlink"><span id="bl1-2">[↩]</span></a></p>
</li>
<li id="fn:3">
<p>I might have some unpopular opinion on ligatures. Although I had been using Fira Code in my terminal since forever, which is a font known for its ligatures, I turned them off completely as it didn’t feel right to me. In code, I want the font to reflect what I write and what the parser of the compiler eventually sees. Especially so when learning new languages as I need to acclimatize to new operators. Ligatures in code divorce the semantics of the operator from the symbols itself and they rarely make a piece of code easier to read for me. Ligatures in prose, though, they are cute and sensible because I would only ever want a <code>-&gt;</code> to actually represent an arrow.&#160;<a class="back" href="#fnref:3" aria-labelledby="bl1-3 bl2-3" role="doc-backlink"><span id="bl1-3">[↩]</span></a></p>
</li>
<li id="fn:4">
<p>Apart from the font, there were some other design choices made such as a clear and minimal interface, a pleasant yet good contrast color scheme, and a system that simply “works” without having to distract me with wanting fixing all the little broken things.&#160;<a class="back" href="#fnref:4" aria-labelledby="bl1-4 bl2-4" role="doc-backlink"><span id="bl1-4">[↩]</span></a></p>
</li>
</ol>
</div>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Writing%20Fonts">Reply via email</a></p>]]></content></entry><entry><title>Custom syntax highlighting with Hugo and Tree-sitter</title><link rel="alternate" href="https://home.hedy.dev/posts/hugo-custom-highlight-with-tree-sitter/"/><id>https://home.hedy.dev/posts/hugo-custom-highlight-with-tree-sitter/</id><published>2024-11-08T10:14:45Z</published><updated>2024-11-09T00:17:07Z</updated><summary type="text">How I made a Hugo site work with Tree-sitter for custom syntax highlighting.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>I recently helped migrate the homepage of <a href="https://cognate-lang.github.io">Cognate</a>, a stack-based, lisp- and ML-inspired programming language, to Hugo. Here’s how I added Cognate language syntax highlighting for our Hugo site.</p>

<h2 id="overview">
  <a class="anchor" href="#overview" rel="nofollow">#</a>
  Overview</h2>

<p>We needed syntax highlighting for code blocks in Cognate, but Hugo comes with Chroma (its syntax highlighter) baked in, and does not allow any form of customization through Hugo when it comes to supporting additional languages:</p>
<ul>
<li><a href="https://github.com/gohugoio/hugo/issues/11496">An open GitHub issue on this topic</a> with “Unscheduled” milestone as of writing</li>
<li><a href="https://github.com/gohugoio/hugo/issues/796">No arbitrary code execution in Hugo templates</a></li>
<li><a href="https://github.com/gohugoio/hugo/pull/11085">No support for custom functions as Go plugins yet</a>, though WASM is <a href="https://github.com/gohugoio/hugo/issues/12737">planned</a>, it seems a little overkill for our purposes</li>
</ul>
<p>Which means our only choice was to either pre-process markdown code blocks and let Hugo’s Goldmark<sup><a href="#fn:1" id="fnref:1" role="doc-noteref">[1]</a></sup> render <code>&lt;pre&gt;</code> blocks already highlighted in markdown content files, or post-process the HTML after Goldmark is done with them.</p>
<p>My initial instinct was to go with the former, since finding markdown code-blocks was simply a matter of <code>/^```cognate/ { ... }</code> in awk. In hindsight, this was not the simpler solution, because we’ll have to keep a copy of the entire <code>content/</code> directory structure for Hugo to consume, whereas processing HTML can be done per-file as Hugo is already done with it.</p>
<p>In reality, Goldmark ended up attempting to parse the HTML within <code>&lt;pre&gt;</code> tags as markdown (despite setting <code>unsafe = true</code> in the config to allow HTML in markdown). Hugo’s <a href="https://gohugo.io/render-hooks/passthrough/">passthrough hooks</a> is supposed to help with this by allowing you to configure delimiters, between which Goldmark will preserve the markdown (in our case, HTML), raw. Unfortunately this did not work for some reason.</p>
<p>So we opted for post-processing the HTML, and the process we ended up with looks like this:</p>
<ol>
<li>Use a <a href="https://gohugo.io/render-hooks/code-blocks/">code block render hook</a> to mark Cognate code blocks (e.g., <code>language-cognate</code> class, or a <code>&lt;!--cognate--&gt;</code>), and run Hugo as usual</li>
<li>Run a script on each output HTML file, detect Cognate code blocks (marked by Step 1)</li>
<li>Run our custom syntax highlighter, and update the HTML file</li>
</ol>

<h2 id="step-1-render-hook">
  <a class="anchor" href="#step-1-render-hook" rel="nofollow">#</a>
  Step 1: Render hook</h2>

<p>The purpose of this step is for our script later on to determine which code blocks are Cognate. If you are using Chroma for other languages, you can simply look for <code>&lt;code class=&quot;language-[LANGUAGE]&gt;&quot;</code> which is added by Chroma.</p>
<p>In our case, we’ll use a <a href="https://gohugo.io/render-hooks/code-blocks/">Hugo render hook</a> to mark the language ourselves.</p>
<p>The template at <code>layouts/_default/_markup/render-codeblock-cognate.html</code> will executed for each Cognate code block when Hugo renders each markdown file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{{ printf &#34;<span class="c">&lt;!--cognate--&gt;</span>&#34; | safeHTML }}<span class="p">&lt;</span><span class="nt">pre</span><span class="p">&gt;&lt;</span><span class="nt">code</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ .Inner | safeHTML }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">code</span><span class="p">&gt;&lt;/</span><span class="nt">pre</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>This makes the HTML output for code blocks deterministic, so we can easily look for the code we want to highlight later on.</p>

<h2 id="step-2-post-process-html">
  <a class="anchor" href="#step-2-post-process-html" rel="nofollow">#</a>
  Step 2: Post-process HTML</h2>

<p>In this step we use a script to update each output HTML file produced by Hugo.</p>
<p>Any programming language will do. Here I have opted for a simple Makefile with awk:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-make" data-lang="make"><span class="line"><span class="cl"><span class="nv">AWK</span><span class="o">=</span>awk
</span></span><span class="line"><span class="cl"><span class="nv">HUGO</span><span class="o">=</span>hugo
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">.PHONY</span><span class="o">:</span> <span class="n">build</span>
</span></span><span class="line"><span class="cl"><span class="nf">build</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">	<span class="c1"># ... rest of your build process</span>
</span></span><span class="line"><span class="cl">	<span class="k">$(</span>HUGO<span class="k">)</span>
</span></span><span class="line"><span class="cl">	<span class="c1"># ... rest of your build process</span>
</span></span><span class="line"><span class="cl">	make <span class="k">$(</span>shell find public -type f -name <span class="s2">&#34;*.html&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">public/%.html</span><span class="o">:</span> .<span class="n">FORCE</span>
</span></span><span class="line"><span class="cl">	@mv <span class="nv">$@</span> tmp.html
</span></span><span class="line"><span class="cl">	<span class="k">$(</span>AWK<span class="k">)</span> -v <span class="nv">outfile</span><span class="o">=</span><span class="nv">$@</span> -f scripts/process-code-blocks.awk tmp.html
</span></span><span class="line"><span class="cl">	@rm tmp.html
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">.PHONY</span><span class="o">:</span> .<span class="n">FORCE</span>
</span></span><span class="line"><span class="cl"><span class="nf">.FORCE</span><span class="o">:</span>
</span></span></code></pre></div><p>In a single <code>make</code>/<code>make build</code>, it builds the site with Hugo, then runs the awk script at <code>scripts/process-code-blocks.awk</code> which goes through each HTML file and runs our custom syntax highlighter:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-awk" data-lang="awk"><span class="line"><span class="cl"><span class="nb">BEGIN</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">inCode</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">code</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>  <span class="c1"># The raw cognate code to highlight</span>
</span></span><span class="line"><span class="cl">  <span class="nx">highlightCmd</span> <span class="o">=</span> <span class="s2">&#34;$PWD&#34;</span> <span class="s2">&#34;/scripts/highlight&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">inCode</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="o">$</span><span class="mi">0</span> <span class="o">!~</span> <span class="sr">/^&lt;!--cognate/</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Print HTML other than &lt;pre&gt;&lt;/pre&gt; lines as-is</span>
</span></span><span class="line"><span class="cl">  <span class="kr">print</span> <span class="o">$</span><span class="mi">0</span> <span class="o">&gt;&gt;</span> <span class="nx">outfile</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">$</span><span class="mi">0</span> <span class="o">==</span> <span class="s2">&#34;&lt;/code&gt;&lt;/pre&gt;&#34;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># End of a code block</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">inCode</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">printf</span> <span class="s2">&#34;%s&#34;</span><span class="p">,</span> <span class="s2">&#34;&lt;pre&gt;&lt;code&gt;&#34;</span> <span class="o">&gt;&gt;</span> <span class="nx">outfile</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">print</span> <span class="nx">code</span> <span class="o">|</span> <span class="p">(</span><span class="nx">highlightCmd</span> <span class="s2">&#34; &gt;&gt; &#34;</span> <span class="nx">outfile</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">close</span><span class="p">(</span><span class="nx">highlightCmd</span> <span class="s2">&#34; &gt;&gt; &#34;</span> <span class="nx">outfile</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">printf</span> <span class="s2">&#34;%s&#34;</span><span class="p">,</span> <span class="s2">&#34;&lt;/code&gt;&lt;/pre&gt;&#34;</span> <span class="o">&gt;&gt;</span> <span class="nx">outfile</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">inCode</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">code</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">inCode</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Within code block</span>
</span></span><span class="line"><span class="cl">  <span class="nx">code</span> <span class="o">=</span> <span class="nx">code</span> <span class="o">$</span><span class="mi">0</span> <span class="s2">&#34;\n&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="sr">/^&lt;!--cognate/</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Start of code block</span>
</span></span><span class="line"><span class="cl">  <span class="nx">inCode</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In the script, we look for <code>&lt;!--cognate--&gt;</code>, extract the code block, run it through <code>highlightCmd</code>, and save it in the output file. All other lines of HTML are copied as-is.</p>

<h2 id="step-3-the-highlight-script">
  <a class="anchor" href="#step-3-the-highlight-script" rel="nofollow">#</a>
  Step 3: The <code>highlight</code> script</h2>

<p>If the language you want to use is not supported by Chroma, you might have some luck with <a href="https://highlightjs.org">Highlight.js</a> or <a href="https://pygments.org">Pygments</a>. Both allow you to implement lexers in additional languages with regex<sup><a href="#fn:2" id="fnref:2" role="doc-noteref">[2]</a></sup>.</p>
<p>For Cognate, we decided to go with <a href="https://tree-sitter.github.io">Tree-sitter</a> since we already have a <a href="https://github.com/hedyhli/tree-sitter-cognate">Tree-sitter grammar</a> for implementing editor support. Luckily, Tree-sitter CLI supports outputting highlights in HTML. So a single command will do for running syntax highlighting.</p>
<p>In <code>script/highlight</code> (<code>highlightCmd</code>), we’ll call <code>tree-sitter highlight</code> with <code>--html</code>/<code>-H</code> flag and specify the path to <code>highlights.scm</code> for highlight captures<sup><a href="#fn:3" id="fnref:3" role="doc-noteref">[3]</a></sup>. It outputs a full HTML page together with line numbers implemented using a HTML table, but we only need the <code>&lt;pre&gt;</code>; <code>sed(1)</code> comes in handy (courtesy of <a href="https://github.com/StavromulaBeta">@StavromulaBeta</a>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sed" data-lang="sed"><span class="line"><span class="cl"><span class="k">s</span><span class="p">/</span><span class="sr">.*&lt;table&gt;\n\(.*\)\n&lt;\/table&gt;.*</span><span class="p">/</span><span class="s1">\1</span><span class="p">/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">s</span><span class="p">/</span><span class="sr">&lt;tr&gt;&lt;td class=line-number&gt;[0-9]\+&lt;\/td&gt;&lt;td class=line&gt;\([^\n]*\)\n&lt;\/td&gt;&lt;\/tr&gt;</span><span class="p">/</span><span class="s1">\1</span><span class="p">/</span><span class="k">g</span><span class="w">
</span></span></span></code></pre></div><p>This strips out everything we don’t want from Tree-sitter’s HTML output.</p>
<p>Putting it together, <code>scripts/highlight</code> looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">tree-sitter highlight <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --query-paths <span class="nv">$PWD</span>/highlights.scm <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --config-path tree-sitter.json <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --scope source.cog -H /dev/stdin <span class="p">|</span> sed -z <span class="s1">&#39;
</span></span></span><span class="line"><span class="cl"><span class="s1">s/.*&lt;table&gt;\n\(.*\)\n&lt;\/table&gt;.*/\1/
</span></span></span><span class="line"><span class="cl"><span class="s1">s/&lt;tr&gt;&lt;td class=line-number&gt;[0-9]\+&lt;\/td&gt;&lt;td class=line&gt;\([^\n]*\)\n&lt;\/td&gt;&lt;\/tr&gt;/\1/g&#39;</span>
</span></span></code></pre></div><p>Two points of note:</p>
<ol>
<li><code>--scope</code> is needed here to have stdin be recognized as Cognate code — “source.cog” is configured in the <code>package.json</code> of Tree-sitter grammar repo.</li>
<li><code>--config-path tree-sitter.json</code> is a config file for Tree-sitter. Conveniently, we can use this file to configure the color scheme for syntax highlighting.</li>
</ol>
<p>Pipe in some Cognate code from stdin, and out comes rendered HTML with syntax highlighting. This script is run on each Cognate code block found in each HTML file produced by Hugo.</p>

<h2 id="conclusion">
  <a class="anchor" href="#conclusion" rel="nofollow">#</a>
  Conclusion</h2>

<p>That’s it! This is the entire build process for <a href="https://cognate-lang.github.io/">Cognate’s project website</a> from start to finish with custom syntax highlighting using Tree-sitter.</p>
<p>Perhaps parsing Tree-sitter CLI’s output was unnecessary and using a simple regex-based syntax highlighting, or even a simple script with a Tree-sitter library in a supported programming language will make less overhead. It might also have been easier if Hugo’s passthrough render hooks worked they way I thought it would.</p>
<p>But well, the process I’ve illustrated above works, and it will continue to work to serve our purposes unless Tree-sitter or Hugo make drastic changes to its output format in future releases.</p>
<p>The source code for Cognate’s website is available on <a href="https://github.com/cognate-lang/cognate-lang.github.io/">GitHub</a>.</p>

<h2 id="see-also">
  <a class="anchor" href="#see-also" rel="nofollow">#</a>
  See also</h2>

<ul>
<li><a href="https://jo3-l.dev/posts/shiki-hugo/">Highlighting Code with Shiki in Hugo</a> — An arguably more reliable approach to post-processing HTML by using a proper HTML parser, and Shiki for highlighting. Uses JavaScript rather than Makefiles, Awk, and Sed.</li>
<li><a href="https://gohugo.io/categories/render-hooks/">Hugo render hooks</a></li>
<li><a href="https://gohugo.io/render-hooks/passthrough/">Hugo passthrough hooks</a></li>
</ul>
<hr hidden="" /><div class="footnotes" role="doc-endnotes">
<p><strong>Footnotes</strong></p>
<ol>
<li id="fn:1">
<p>Hugo uses Goldmark for markdown processing.&#160;<a class="back" href="#fnref:1" aria-labelledby="bl1-1 bl2-1" role="doc-backlink"><span id="bl1-1">[↩]</span></a></p>
</li>
<li id="fn:2">
<p>See <a href="https://highlightjs.readthedocs.io/en/stable/language-guide.html">Language Definition Guide for Highlight.js</a> and <a href="https://pygments.org/docs/lexerdevelopment/">“Write your own lexer from Pygments’ docs</a>.&#160;<a class="back" href="#fnref:2" aria-labelledby="bl1-2 bl2-2" role="doc-backlink"><span id="bl1-2">[↩]</span></a></p>
</li>
<li id="fn:3">
<p>This file tells Tree-sitter which parts of the code to highlight, and what highlight group should apply. Captures are done by pattern matching on the syntax tree with lisp syntax.&#160;<a class="back" href="#fnref:3" aria-labelledby="bl1-3 bl2-3" role="doc-backlink"><span id="bl1-3">[↩]</span></a></p>
</li>
</ol>
</div>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Custom%20syntax%20highlighting%20with%20Hugo%20and%20Tree-sitter">Reply via email</a></p>]]></content></entry><entry><title>Add a Meta page to your personal website</title><link rel="alternate" href="https://home.hedy.dev/posts/meta-pages/"/><id>https://home.hedy.dev/posts/meta-pages/</id><published>2024-11-05T11:24:29Z</published><updated>2024-12-18T07:47:09Z</updated><summary type="text">Why we should have more /meta (or similar) pages on our personal sites, and examples in the wild.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<details open=""><summary>Update (2024-11-11)</summary>
<p>I’ve made a webring! Read more about this topic at
<a href="https://meta-ring.hedy.dev/">meta-ring.hedy.dev</a></p>
</details>
<p>If you have a personal website, chances are, you take great care into making your site <em>yours</em>. You update it frequently, you might have a workflow for posting on your blog. You might have made sure to properly implement features you care a lot about.</p>
<p>What tools do you use to draft your blog posts? Where do you <a href="https://indieweb.org/POSSE">syndicate</a> your content? What are some inspirations or standards you follow for your site’s design?</p>
<p>I love reading personal blogs, and I also love it when people talk about their site and blog themselves. Similar to movements for <code>/uses</code> and <code>/now</code>, include a <code>/meta</code> page on your website and write about your website itself!</p>
<p>Some people include information about their site in a dedicated section of their <code>/about</code>, or <code>/uses</code> page. That’s great too — the point is to share with others how your blog is made, how it came to be, and all other meta information you want your readers to know — if you haven’t written about the topics elsewhere, a <code>/meta</code> page is a good place to put it. Do you link to your site’s source code in the footer? This is a good opportunity to link it in a dedicated page too.</p>

<h2 id="examples">
  <a class="anchor" href="#examples" rel="nofollow">#</a>
  Examples</h2>

<p><a href="https://seirdy.one/">Seirdy</a> has a <a href="https://seirdy.one/meta/">/meta page</a> where they talk about:</p>
<ul>
<li>Tools and services used to build and deploy their site</li>
<li>Other versions of their site — on Tor, on Gemini</li>
<li>Badges and website directories they’re part of</li>
<li>Links to other pages under <code>/meta/</code>, such as all the things they’ve yet to implement, accessibility and design choices, etc.</li>
</ul>
<p><a href="https://kevquirk.com/">Kev Quirk</a> has <a href="https://kevquirk.com/about#2">a section titled “Colophon” in his about page</a> that includes:</p>
<ul>
<li>His site design and inspiration</li>
<li>What his site is built on and what it runs on</li>
</ul>
<p><a href="https://gregoryhammond.ca/">Gregory Hammond</a> mentions why he writes on his blog on his <a href="https://gregoryhammond.ca/about/">about page</a>, he also has a list of <a href="https://gregoryhammond.ca/policies/">policies</a> he follows including:</p>
<ul>
<li>An accessibility statement</li>
<li>An AI statement</li>
<li>A Privacy Policy and Terms &amp; Conditions</li>
</ul>
<p><a href="https://anirudh.fi/">icyphox</a> has a section named “colophon” in their <a href="https://anirudh.fi/about/">about page</a> that mentions:</p>
<ul>
<li>The site is self-hosted</li>
<li>The site is built with a custom static site generator</li>
<li>Fonts used throughout their site</li>
</ul>
<p><a href="https://takeonrules.com/">Jeremy Friesen</a> has a <a href="https://takeonrules.com/about/colophon/">Colophon page</a> that details their journey in blogging with links to blog posts for each major change in the website. He even has a dedicated <a href="https://takeonrules.com/site-map/changelog/">changelog</a> page.</p>
<p><a href="https://home.hedy.dev/">My personal website</a> contains a <a href="https://home.hedy.dev/meta/">/meta page</a> that includes:</p>
<ul>
<li>How my site is built and where it is hosted</li>
<li>Features and standards my site adopts and ones I’m yet to implement</li>
<li>Where and how I write blog posts</li>
<li>Website directories, webrings, and badges</li>
</ul>
<br />
<p>Now it’s your turn! If you’ve been blogging for a while you may have come across common questions such as people asking about the tools you use to build you site, or how you host it. You can now simply link them to that page :)</p>

<h2 id="see-also">
  <a class="anchor" href="#see-also" rel="nofollow">#</a>
  See also</h2>

<ul>
<li><a href="https://indieweb.org/colophon">Colophons on the IndieWeb</a></li>
</ul>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Add%20a%20Meta%20page%20to%20your%20personal%20website">Reply via email</a></p>]]></content></entry><entry><title>Typing vs Writing</title><link rel="alternate" href="https://home.hedy.dev/posts/typing-vs-writing/"/><id>https://home.hedy.dev/posts/typing-vs-writing/</id><published>2024-11-04T01:58:05Z</published><updated>2024-12-30T23:47:04Z</updated><summary type="text">Exploring the effects of a difference in speed of typing vs writing with respect to the speed of thought.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>I type faster than I can write. But regardless of how I put my thoughts down, the speed at which my thoughts can keep up with me putting them down is the same<sup><a href="#fn:1" id="fnref:1" role="doc-noteref">[1]</a></sup>. I believe that my speed of thought is right in-between the speed of my handwriting and typing; and this affects how I perceive the general act of writing<sup><a href="#fn:2" id="fnref:2" role="doc-noteref">[2]</a></sup> on my abilities to articulate.</p>
<p>Let’s explore an analogy.</p>
<p>Imagine a “producer” of data which inputs data in packets of size <code>I</code> into the system, the packets are produced unpredictably, but it is guaranteed that they come in packets of fixed size <code>I</code> when a new packet is produced. Then, there’s a pipe in which the input must past through to be used as output. This pipe has capacity <code>P</code>. (It may be helpful to visualize a physical pipe<sup><a href="#fn:3" id="fnref:3" role="doc-noteref">[3]</a></sup> with a decently wide diameter, and incoming “packets” as blobs of liquid, or some other material of your choice.)</p>
<p>When <code>P &lt; I</code>, we have a “bottle-neck”. Each following packet must wait for the one before to be processed in the pipe, because an entire packet cannot fit into the pipe at one time.</p>
<p>When <code>P &gt;= I</code>, the pipe must wait for new packets to come much more often than when <code>P &lt; I</code> because all incoming packets can be processed instantly as they come.</p>
<p>The producer here would henceforth be whichever part of my brain that produces thought, where the packets of data would be thoughts themselves — a single, standalone strand of thought that can be articulated by a word (an idea to swap an existing word with better vocabulary), a phrase, a sentence (which expresses the thought), or even paragraphs or an essay (which explains a point). You can then imagine the “pipe” or whatever the pipe is connected to on the opposite side, be the medium in which I express my thoughts — through writing on paper, through typing, through speech.</p>
<p>You might then draw the connection here where I theorize that <code>P</code> would be less than <code>I</code> in the case of writing (since my writing speed is relatively slower), and <code>P</code> is greater than or equal to <code>I</code> in the case of typing.</p>
<p>Allow me to now match my experiences with those effects I have listed for each of the conditions above about waiting/processing packets.</p>
<p>When writing, I often feel as though my mind is brimming with ideas on how to continue a sentence, what I can write next. All the while my hand is still focused on how the current letter should be drawn.</p>
<p>When typing, I can complete the process of putting down the thought currently being processed pretty quickly. Often, I’m at a loss of what to type next and have to pause.</p>
<p>What this means is that, writing by hand feels like many “packets” of thoughts queueing up behind a pipe too narrow in diameter just waiting to be let out. But there’s no rush. While these thoughts wait in the background, they get to “mature”. I might consider amendments to them when I finally come around to write them down.</p>
<p>Typing, on the other hand, makes it so all the initial “draft” versions of the thoughts are the ones materialized and I don’t get as much time mentally to chew over certain ideas before they get put on the screen. Furthermore, I would often finish typing a word or sentence then suddenly finding myself stuck, unable to determine what to type next — I lose my “train of thoughts”. My thinking can’t catch up with how quickly I’m able to type it all out. I’m often left feeling empty, no longer confident I’m able to complete the piece at all<sup><a href="#fn:4" id="fnref:4" role="doc-noteref">[4]</a></sup>.</p>
<p>But is being able to make adjustments to thoughts after letting them “mature” in the waiting room actually a good thing? Is it better to lower the friction between the process in my mind (thinking), and the process of writing it down? Does the increased length of breaks between the sentences typed lead to (or incentivize) longer, more focused, periods of reviewing my work? If I work on eliminating self-doubt and lack of confidence, perhaps the (increased) psychological effects caused by typing faster than I think would simply be of little significance — my hands resting on my keyboard are simply waiting for my thinking to catch up, and the analysis ends there?</p>
<p>I feel slow and unproductive when writing, thinking I could easily complete the task on my computer. But often I find that the written work I produce is of better quality than when I type it out. There are of course many factors that should be considered when figuring out which method of putting thoughts down works best for you; here I have only explored one aspect of the differences between typing and writing: imagining them as methods that process thoughts as they come with and the effects of the discrepancy in its “processing speed”.</p>
<hr hidden="" /><div class="footnotes" role="doc-endnotes">
<p><strong>Footnotes</strong></p>
<ol>
<li id="fn:1">
<p>Or so I shall stipulate for the purposes of our discussion.&#160;<a class="back" href="#fnref:1" aria-labelledby="bl1-1 bl2-1" role="doc-backlink"><span id="bl1-1">[↩]</span></a></p>
</li>
<li id="fn:2">
<p>I will  be sticking to “writing” as “writing on paper, by hand, with a pen” hereafter and so I clarify here with “general writing” to mean either writing on paper or typing on a keyboard.&#160;<a class="back" href="#fnref:2" aria-labelledby="bl1-2 bl2-2" role="doc-backlink"><span id="bl1-2">[↩]</span></a></p>
</li>
<li id="fn:3">
<p>A funnel might work too.&#160;<a class="back" href="#fnref:3" aria-labelledby="bl1-3 bl2-3" role="doc-backlink"><span id="bl1-3">[↩]</span></a></p>
</li>
<li id="fn:4">
<p>This by no means suggests that I never get stuck or lose my train of thought when writing by hand. I am simply exploring the differences between the two modes of putting thoughts “down on paper” based on personal experience, through a (rather crude) analogy.&#160;<a class="back" href="#fnref:4" aria-labelledby="bl1-4 bl2-4" role="doc-backlink"><span id="bl1-4">[↩]</span></a></p>
</li>
</ol>
</div>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Typing%20vs%20Writing">Reply via email</a></p>]]></content></entry><entry><title>Vim visual block mode for column editing</title><link rel="alternate" href="https://home.hedy.dev/posts/vim-column-editing/"/><id>https://home.hedy.dev/posts/vim-column-editing/</id><published>2024-04-10T05:22:46Z</published><updated>2024-11-29T06:30:05Z</updated><summary type="text">The visual block mode in Vim/Neovim is quite powerful. You can use it for "column" editing and vertically pasting blocks of text similar to paste(1).</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>The visual block mode in Vim lets you edit text simultaneously across adjacent
lines, similar to the “Alt-drag” feature in modern editors, but there’s more you
can do with it.</p>
<p>One of the basic formatting style that can be done to plain text content would
be putting blocks of lines into columns, i.e., from this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1
</span></span><span class="line"><span class="cl">- Item 1
</span></span><span class="line"><span class="cl">- Item 2
</span></span><span class="line"><span class="cl">- Item 3
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">List title 2
</span></span><span class="line"><span class="cl">- Item 1
</span></span><span class="line"><span class="cl">- Item 2
</span></span><span class="line"><span class="cl">- Item 3
</span></span></code></pre></div><p>To this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1  List title 2
</span></span><span class="line"><span class="cl">- Item 1      - Item 1
</span></span><span class="line"><span class="cl">- Item 2      - Item 2
</span></span><span class="line"><span class="cl">- Item 3      - Item 3
</span></span></code></pre></div><p>You can easily convert the text in between those two formats just by using
visual block mode to do something similar to paste(1) (see the
<a href="#appendix">appendix</a>).</p>
<p>Here’s a video demonstration.</p>
<video controls="" name="media">
    <source src="/posts/vim-column-editing/demo.webm
" alt="demo video" type="video/webm"/>
</video>
<h2 id="creating-columns">Creating columns</h2>
<p>Let’s start with the vertical form, and put your cursor on the position marked
by the block (<code>▋</code>)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1
</span></span><span class="line"><span class="cl">- Item 1
</span></span><span class="line"><span class="cl">- Item 2
</span></span><span class="line"><span class="cl">- Item 3
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">List title 2
</span></span><span class="line"><span class="cl">- Item 1
</span></span><span class="line"><span class="cl">- Item 2
</span></span><span class="line"><span class="cl">▋ Item 3
</span></span></code></pre></div><p>Begin visual block selection mode with <code>&lt;C-v&gt;</code>. Select the first character at
each line with <code>3k</code>, then extend selection to the entire block using <code>$h</code>.</p>
<p>Your cursor should now be on the “2” with the entire “List title 2” and the
three list items below it selected:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1
</span></span><span class="line"><span class="cl">- Item 1
</span></span><span class="line"><span class="cl">- Item 2
</span></span><span class="line"><span class="cl">- Item 3
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">List title ▋
</span></span><span class="line"><span class="cl">- Item 1
</span></span><span class="line"><span class="cl">- Item 2
</span></span><span class="line"><span class="cl">- Item 3
</span></span></code></pre></div><p>Now hit <code>x</code> (or <code>d</code>) to remove the block and save it in one of the default
registers temporarily.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1
</span></span><span class="line"><span class="cl">- Item 1
</span></span><span class="line"><span class="cl">- Item 2
</span></span><span class="line"><span class="cl">- Item 3
</span></span></code></pre></div><p>Now, we prepare the first list to be able to paste the second list that was just
deleted. Pad spaces at the end of each line of the first list<sup><a href="#fn:1" id="fnref:1" role="doc-noteref">[1]</a></sup>. Spaces will be shown
as <code>_</code> throughout the example.</p>
<p>If you don’t already have Vim/Neovim setup to highlight trailing spaces, you can
do so temporarily now by running <code>:match Underlined '\s\+$'</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1_
</span></span><span class="line"><span class="cl">- Item 1_____
</span></span><span class="line"><span class="cl">- Item 2_____
</span></span><span class="line"><span class="cl">- Item 3_____
</span></span></code></pre></div><p>Now we can paste the block we’ve deleted! Place your cursor on the trailing
space after “List title 1”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1▋
</span></span><span class="line"><span class="cl">- Item 1_____
</span></span><span class="line"><span class="cl">- Item 2_____
</span></span><span class="line"><span class="cl">- Item 3_____
</span></span></code></pre></div><p>And hit <code>p</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1 List title 2
</span></span><span class="line"><span class="cl">- Item 1     - Item 1
</span></span><span class="line"><span class="cl">- Item 2     - Item 2
</span></span><span class="line"><span class="cl">- Item 3     - Item 3
</span></span></code></pre></div><p><em>Voila!</em> The two lists are now merged into two columns side by side.</p>
<p>You can easily adjust the spacing between the columns using visual block mode.
Place your cursor on the space in-between the two list titles:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1▋List title 2
</span></span><span class="line"><span class="cl">- Item 1     - Item 1
</span></span><span class="line"><span class="cl">- Item 2     - Item 2
</span></span><span class="line"><span class="cl">- Item 3     - Item 3
</span></span></code></pre></div><p>Enter visual block mode and select the entire vertical column of spaces:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1▋List title 2
</span></span><span class="line"><span class="cl">- Item 1    ▋- Item 1
</span></span><span class="line"><span class="cl">- Item 2    ▋- Item 2
</span></span><span class="line"><span class="cl">- Item 3    ▋- Item 3
</span></span></code></pre></div><p>Hit <code>A</code> to append text after each selection. Add a few spaces as you see fit,
and exit visual block mode (<code>ESC</code>).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1    List title 2
</span></span><span class="line"><span class="cl">- Item 1        - Item 1
</span></span><span class="line"><span class="cl">- Item 2        - Item 2
</span></span><span class="line"><span class="cl">- Item 3        - Item 3
</span></span></code></pre></div><h2 id="removing-columns">Removing columns</h2>
<p>Let’s transform the text in reverse<sup><a href="#fn:2" id="fnref:2" role="doc-noteref">[2]</a></sup>. We’ll break these two columns up and have
them show up one following another vertically like what we’ve started with. This
might be needed when the first or second list becomes too long and you decide to
stop having them show up side by side.</p>
<p>First, place your cursor at the beginning of the third item on the second list.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1    List title 2
</span></span><span class="line"><span class="cl">- Item 1        - Item 1
</span></span><span class="line"><span class="cl">- Item 2        - Item 2
</span></span><span class="line"><span class="cl">- Item 3        ▋ Item 3
</span></span></code></pre></div><p>We’ll use a similar process as before to select the second list with visual
block mode. Enter visual block mode with <code>&lt;C-v&gt;</code>, hit <code>3k</code>, then <code>$h</code>.</p>
<p>Now delete the text like before.</p>
<p>Next we’ll have to create enough space under the first list for the second list
to be pasted. (Note the trailing spaces here are only shown for completeness.)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1____
</span></span><span class="line"><span class="cl">- Item 1________
</span></span><span class="line"><span class="cl">- Item 2________
</span></span><span class="line"><span class="cl">- Item 3________
</span></span><span class="line"><span class="cl">⏎
</span></span><span class="line"><span class="cl">⏎
</span></span><span class="line"><span class="cl">⏎
</span></span><span class="line"><span class="cl">⏎
</span></span><span class="line"><span class="cl">⏎
</span></span></code></pre></div><p>There should be four empty lines after a blank line following the first list.</p>
<p>Place your cursor on the start of the second blank line:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1____
</span></span><span class="line"><span class="cl">- Item 1________
</span></span><span class="line"><span class="cl">- Item 2________
</span></span><span class="line"><span class="cl">- Item 3________
</span></span><span class="line"><span class="cl">⏎
</span></span><span class="line"><span class="cl">▋
</span></span><span class="line"><span class="cl">⏎
</span></span><span class="line"><span class="cl">⏎
</span></span><span class="line"><span class="cl">⏎
</span></span></code></pre></div><p>And hit <code>p</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1____
</span></span><span class="line"><span class="cl">- Item 1________
</span></span><span class="line"><span class="cl">- Item 2________
</span></span><span class="line"><span class="cl">- Item 3________
</span></span><span class="line"><span class="cl">⏎
</span></span><span class="line"><span class="cl">List title 2
</span></span><span class="line"><span class="cl">- Item 1
</span></span><span class="line"><span class="cl">- Item 2
</span></span><span class="line"><span class="cl">- Item 3
</span></span></code></pre></div><p>And we’re back to the original format.</p>
<p>You can easily remove the trailing spaces on the first list by running the
substitution command on a visual selection of the first list.</p>
<p>Use either visual mode or visual line mode to select the first paragraph, then
use <code>:'&lt;,'&gt;s/\s\+$//</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List title 1
</span></span><span class="line"><span class="cl">- Item 1
</span></span><span class="line"><span class="cl">- Item 2
</span></span><span class="line"><span class="cl">- Item 3
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">List title 2
</span></span><span class="line"><span class="cl">- Item 1
</span></span><span class="line"><span class="cl">- Item 2
</span></span><span class="line"><span class="cl">- Item 3
</span></span></code></pre></div><p>That’s it!</p>
<p>Visual block mode is just one of the useful tools when formatting plain text and
making ascii art. I frequently use the replace mode (“write through”) with <code>R</code>
to overwrite content without breaking up columns.</p>
<p>You can learn more about the visual block mode in Vim in the docs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vim" data-lang="vim"><span class="line"><span class="cl"><span class="p">:</span><span class="nx">h</span> <span class="nx">visual</span><span class="p">-</span><span class="nx">block</span>
</span></span></code></pre></div><h2 id="appendix">Appendix</h2>
<p>Here’s an example shell session to demonstrate how you can do the same thing
described in the article using the shell built-in <code>paste(1)</code>. Again, spaces are
represented as <code>_</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ cat &gt; l1.txt
</span></span><span class="line"><span class="cl">List title 1_
</span></span><span class="line"><span class="cl">- Item 1_____
</span></span><span class="line"><span class="cl">- Item 2_____
</span></span><span class="line"><span class="cl">- Item 3_____
</span></span><span class="line"><span class="cl">^D
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ cat &gt; l2.txt
</span></span><span class="line"><span class="cl">List title 2
</span></span><span class="line"><span class="cl">- Item 1
</span></span><span class="line"><span class="cl">- Item 2
</span></span><span class="line"><span class="cl">- Item 3
</span></span><span class="line"><span class="cl">^D
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ paste -d &#39; &#39; l1.txt l2.txt
</span></span><span class="line"><span class="cl">List title 1  List title 2
</span></span><span class="line"><span class="cl">- Item 1      - Item 1
</span></span><span class="line"><span class="cl">- Item 2      - Item 2
</span></span><span class="line"><span class="cl">- Item 3      - Item 3
</span></span></code></pre></div><p>As for converting it back, I might opt for awk(1), another scripting language,
or just use Vim’s visual block mode.</p>
<hr hidden="" /><div class="footnotes" role="doc-endnotes">
<p><strong>Footnotes</strong></p>
<ol>
<li id="fn:1">
<p>Alternatively, you can skip this step by using <code>:set virtualedit=all</code>.
Thanks to Martin Bays for pointing it out.&#160;<a class="back" href="#fnref:1" aria-labelledby="bl1-1 bl2-1" role="doc-backlink"><span id="bl1-1">[↩]</span></a></p>
</li>
<li id="fn:2">
<p>If you actually just want to reverse the process without any editing in
between, you should of course use a few undo commands ;-)&#160;<a class="back" href="#fnref:2" aria-labelledby="bl1-2 bl2-2" role="doc-backlink"><span id="bl1-2">[↩]</span></a></p>
</li>
</ol>
</div>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Vim%20visual%20block%20mode%20for%20column%20editing">Reply via email</a></p>]]></content></entry><entry><title>Setting up syntax highlighting for Hugo</title><link rel="alternate" href="https://home.hedy.dev/posts/hugo-syntax-highlighting/"/><id>https://home.hedy.dev/posts/hugo-syntax-highlighting/</id><published>2021-08-22T00:00:00Z</published><updated>2024-11-29T06:24:28Z</updated><summary type="text">How I've set up syntax highlighting for my website with dark mode support.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>Hugo uses <a href="https://github.com/alecthomas/chroma">chroma</a> as its syntax
highlighter. All you need for having your code highlighted is to let chroma put
the syntax classes in the generated HTML, for the correct language, and then
make sure you have corresponding CSS for those classes.</p>
<p>Let’s start by enabling syntax highlighting in your configuration file.</p>
<p>In your configuration file, <code>config.toml</code> for example, add these settings so
you can have your code highlighted and have it recognize Fenced Code Blocks for
markdown:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="nx">pygmentsUseClasses</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="nx">pygmentsCodeFences</span> <span class="p">=</span> <span class="kc">true</span>
</span></span></code></pre></div><p>Next you have to include the styles. There are two ways to go about this, one
is to choose a style from the list of available styles (more on that below),
and the second method is to use your own syntax theme.</p>
<p>You can have your own CSS styles, but there are <em>a lot</em> of classes, so if
you’re just starting out and want to have it working quickly, you should choose
an existing style.</p>
<p>Browse the <a href="https://xyproto.github.io/splash/docs/">gallery</a> of available
styles and use that style name to save the CSS file into your <code>assets</code>
directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">mkdir -p assets/syntax
</span></span><span class="line"><span class="cl">hugo gen chromastyles --style<span class="o">=</span>friendly &gt; assets/syntax/main.css
</span></span></code></pre></div><p>If you’d like to use different styles for dark mode and light mode (like me),
then you can change “main.css” to “light.css”, and save the dark mode style to
“dark.css”.  It doesn’t matter where you put the file, but just remember to use
that file name with referencing it later.</p>
<p>You don’t have to put it under the directory “syntax” too, if you don’t want
to.</p>

<h2 id="apply-the-styles">
  <a class="anchor" href="#apply-the-styles" rel="nofollow">#</a>
  Apply the styles</h2>

<p>You have to link to your CSS file that you’ve just created in your <code>&lt;head&gt;</code>.
There are several ways you can do so.</p>
<ol>
<li>Use a <code>&lt;link&gt;</code> tag and link to the URL of your CSS file, or</li>
<li>directly put the content of your file into a <code>&lt;style&gt;</code> tag.</li>
</ol>
<p>Here’s how you can do the second method.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-tmpl" data-lang="tmpl"><span class="line"><span class="cl"><span class="x">{{- </span><span class="cp">$</span><span class="n">syntaxCSS</span><span class="x"> := resources.Get &#34;syntax/main.css&#34; | minify -}}
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;style&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">	{{ </span><span class="cp">$</span><span class="n">syntaxCSS</span><span class="o">.</span><span class="n">Content</span><span class="x"> | safeCSS }}
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;/style&gt;
</span></span></span></code></pre></div><p>Assuming your CSS file is at <code>assets/syntax/main.css</code>.</p>
<p>All set for the styling part! Now you can write some content and try it out.</p>
<p>Remember to include the file type when putting code in markdown.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">This is not code
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="s">```sh
</span></span></span><span class="line"><span class="cl"><span class="s"></span><span class="c1"># and this is some sh code</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;hi!&#39;</span>
</span></span><span class="line"><span class="cl"><span class="s">```</span>
</span></span></code></pre></div>
<h2 id="dark-mode-and-light-mode">
  <a class="anchor" href="#dark-mode-and-light-mode" rel="nofollow">#</a>
  Dark mode and light mode</h2>

<p>You can have different styles of syntax highlighting for dark mode and light
mode.</p>
<p>First you have to export or save your CSS for dark mode and light mode in
separate files. Then, change the part in your <code>&lt;head&gt;</code> where you’ve included
the styles:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-tmpl" data-lang="tmpl"><span class="line"><span class="cl"><span class="x">{{- </span><span class="cp">$</span><span class="n">syntaxlight</span><span class="x"> := resources.Get &#34;syntax/light.css&#34; | minify -}}
</span></span></span><span class="line"><span class="cl"><span class="x">{{- </span><span class="cp">$</span><span class="n">syntaxdark</span><span class="x"> := resources.Get &#34;syntax/dark.css&#34; | minify  -}}
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;style&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">	{{ </span><span class="cp">$</span><span class="n">syntaxlight</span><span class="o">.</span><span class="n">Content</span><span class="x"> | safeCSS }}
</span></span></span><span class="line"><span class="cl"><span class="x">	@media (prefers-color-scheme: dark) {
</span></span></span><span class="line"><span class="cl"><span class="x">		{{ </span><span class="cp">$</span><span class="n">syntaxdark</span><span class="o">.</span><span class="n">Content</span><span class="x"> | safeCSS }}
</span></span></span><span class="line"><span class="cl"><span class="x">	}
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;/style&gt;
</span></span></span></code></pre></div><p>This assumes you have your light styles and dark mode styles stored in
<code>assets/syntax/light.css</code> and <code>assets/syntax/dark.css</code> respectively.</p>
<p>Instead of doing the above, you could have just put both in a single file and
include the <code>@media (prefers-color-scheme: dark)</code> line in there directly, and
this won’t require you to change your <code>&lt;head&gt;</code> at all. And of course you could
have just used CSS variables and store the theme’s colors into different
variables, use <code>var(--variable-name)</code> for each class in the styles, and finally
have a different set up variable values for <code>@media (prefers-color-scheme: dark) {}</code>.  But using the method of separating dark and light styles has the
advantage of changing the themes for light or dark mode in the future with the
<code>chroma</code> command and pipe it to the file directly, without having to edit the
CSS file by hand.</p>

<h2 id="applying-syntax-highlight-css-file-only-when-its-needed">
  <a class="anchor" href="#applying-syntax-highlight-css-file-only-when-its-needed" rel="nofollow">#</a>
  Applying syntax highlight CSS file only when it’s needed</h2>

<p>Fetching syntax highlighting theme and putting it in your <code>&lt;head&gt;</code> means that
every single page would have the syntax highlighting CSS code in the page,
regardless of whether the page actually uses it. If you’d like to only include
the syntax CSS files for pages that needs syntax highlighting, you can use a
page parameter named something like <code>highlight</code>.</p>
<p>Here’s how it would work.</p>
<ul>
<li>Set a <code>highlight</code> param in your global config file that is set to false by default</li>
<li>For each post or page in your <code>content/</code> that needs syntax highlighting, include <code>highlight: true</code>
in the frontmatter (assuming you’re using YAML).</li>
<li>In <code>&lt;head&gt;</code>, check whether the page’s <code>highlight</code> param is set to true, and only if it’s true, the
CSS resource is loaded.</li>
</ul>
<p>In your <code>&lt;head&gt;</code>, include this before fetching the CSS resource, and put
<code>{{ end }}</code> after <code>&lt;/style&gt;</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-tmpl" data-lang="tmpl"><span class="line"><span class="cl"><span class="x">{{- if .Params.highlight -}}
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;!--Put the code that fetches your syntax highlight CSS here--&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">{{- end -}}
</span></span></span></code></pre></div><p>This tells hugo to only load the resource and put the CSS in <code>&lt;style&gt;</code> if the page parameter
<code>highlight</code> is set to true.</p>

<h2 id="read-more">
  <a class="anchor" href="#read-more" rel="nofollow">#</a>
  Read more</h2>

<ul>
<li><a href="https://gohugo.io/getting-started/configuration-markup#highlight">Configuration options for syntax highlighting</a></li>
<li><a href="https://gohugo.io/content-management/syntax-highlighting/#example-highlight-shortcode">Highlighting specific lines in code snippets</a></li>
<li><a href="https://gohugo.io/content-management/shortcodes/#highlight">The built-in <code>highlight</code> shortcode for manual highlighting</a></li>
</ul>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Setting%20up%20syntax%20highlighting%20for%20Hugo">Reply via email</a></p>]]></content></entry><entry><title>Multiple emails in git for different computers</title><link rel="alternate" href="https://home.hedy.dev/posts/multiple-emails-git/"/><id>https://home.hedy.dev/posts/multiple-emails-git/</id><published>2021-06-16T23:50:00Z</published><updated>2024-04-13T06:41:37Z</updated><summary type="text">How to have different git author configurations depending on the computer and have it tracked with dotfiles.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>As someone who code on multiple machines to work on different projects, I like to commit with different emails.</p>
<p>I don’t know how everyone else handle different emails in .gitconfig <em>and</em> track dotfiles in a git repo at the same time, but here’s the solution I’ve come up with.</p>
<p>First, I have a global ~/.gitconfig with the default user email and some other global settings (by global I mean same cross different computers I work on).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1"># ~/.gitconfig</span>
</span></span><span class="line"><span class="cl"><span class="k">[user]</span>
</span></span><span class="line"><span class="cl"><span class="na">name</span> <span class="o">=</span> <span class="s">Default Name</span>
</span></span><span class="line"><span class="cl"><span class="na">email</span> <span class="o">=</span> <span class="s">me@default.email</span>
</span></span></code></pre></div><p>Then for each machine I have a ~/.gitconfig-local file which can override some settings just for that machine, such
as email, signing key, editor, etc.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1"># ~/.gitconfig-local</span>
</span></span><span class="line"><span class="cl"><span class="k">[user]</span>
</span></span><span class="line"><span class="cl"><span class="na">email</span> <span class="o">=</span> <span class="s">me@special.email</span>
</span></span></code></pre></div><p>Back in the global ~/.gitconfig, I have this snippet that tells git to also look for configuration in my ~/.gitconfig-local:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1"># ~/.gitconfig</span>
</span></span><span class="line"><span class="cl"><span class="k">[include]</span>
</span></span><span class="line"><span class="cl"><span class="na">path</span> <span class="o">=</span> <span class="s">~/.gitconfig-local</span>
</span></span></code></pre></div><p>Here, I can have ~/.gitconfig tracked in my dotfiles repo, but I do not have ~/.gitconfig-local tracked. This way, I can put anything I like specific to a particular machine in the ~/.gitconfig-local, as well as SMTP settings such as password, which you wouldn’t want to end up in a public git repo.</p>
<p>As to setting it up on a new machine, I have a setup script in my dotfiles repo that creates an empty ~/.gitconfig-local. I use this same method with fish configs — global config and a local config, you can have a look at them in my <a href="https://git.sr.ht/~hedy/dotfiles/">dotfiles repo</a> on sourcehut.</p>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Multiple%20emails%20in%20git%20for%20different%20computers">Reply via email</a></p>]]></content></entry><entry><title>On rants about gemini</title><link rel="alternate" href="https://home.hedy.dev/posts/on-rants-gemini/"/><id>https://home.hedy.dev/posts/on-rants-gemini/</id><published>2021-06-15T00:00:00Z</published><updated>2024-04-13T06:41:37Z</updated><summary type="text">A discussion about Gemini and addressing some of the complaints of the Gemini Protocol seen on the internet recently.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>Recently there’ve been some discussions about how Gemini “doesn’t fix anything” on Lobste.rs:</p>
<ul>
<li><a href="https://lobste.rs/s/vhlagb/why_gemini_is_not_my_favorite_internet">Why Gemini is Not My Favorite Internet Protocol</a></li>
<li><a href="https://lobste.rs/s/3nsvkk/gemini_is_useless">Gemini is Useless</a></li>
</ul>
<p>As well as a few posts related to that on smol.pub:</p>
<ul>
<li><a href="https://bentsai.smol.pub/embracing-constraints">bentsai.smol.pub/embracing-constraints</a></li>
<li><a href="https://mieum.smol.pub/for-the-love-of-gemini">mieum.smol.pub/for-the-love-of-gemini</a></li>
</ul>
<p>I think that those comments on Lobsters can be summarized into these main points:</p>
<ul>
<li>We don’t need a whole new protocol (and the corresponding new software) just because most of the web is gross</li>
<li>Gemtext doesn’t have tables, text styling, images, and forms. It’s too much of a significant price having to pay just to have a simpler document-focused version of the web</li>
<li>You simply can’t replace the web like this</li>
</ul>
<p>Let’s take a look at each in detail.</p>

<h2 id="replacing-the-web">
  <a class="anchor" href="#replacing-the-web" rel="nofollow">#</a>
  Replacing the web</h2>

<blockquote>
<p>[The web is] an organically grown mess of 25 years – but it will never be
replaced by a dead-simple solution such as Gemini.
(<a href="https://lobste.rs/s/ivryqt/what_is_this_gemini_thing_anyway_why_am_i">source</a>)</p>
</blockquote>
<p>And in response to that as mieum writes:</p>
<blockquote>
<p>Almost all of these posts are obsessed with the same strawman: some supposed audacity of Gemini to attempt replacing the web. It’s hilarious that these authors (and commentors) have such poor reading comprehension.</p>
</blockquote>
<p>As clearly as is written at the top on the Gemini homepage, people seem to forget that it neither wants nor tries to replace the web.</p>
<blockquote>
<p>Gemini is not “the fix” for the web’s problems (or for gopher’s for that matter). It is just a habitat, really; a way to inhabit a virtual space with other people. What <em>that</em> is and means will always be overlooked from the point of view that assumes Gemini <em>must</em> be all or nothing at all; that it <em>must</em> be a challenger to the throne.</p>
</blockquote>
<p>In my opinion, Gemini is just a virtual space to put your content and read other people’s work on. It’s not supposed to be “oh, the web sucks, let’s all just ditch the web switch over to this instead”. There are still many wonderful and peaceful things on the web. And if you’d prefer scrolling through feeds with personalized ads, or if you prefer super-responsive, sleek, modern web pages that have cute animations, then maybe Gemini might seem dull and that Gemini is not for you. Ultimately, (in the words of mieum) it’s a thriving ecosystem, and it doesn’t need to be anything more than that.</p>

<h2 id="lack-of-functionality-in-comparison-to-html">
  <a class="anchor" href="#lack-of-functionality-in-comparison-to-html" rel="nofollow">#</a>
  Lack of functionality in comparison to HTML</h2>

<p>In regards to gemtext limitations, we have to keep in mind that Gemini is a protocol. Gemtext is a markup language. You can serve whatever you like on Gemini — HTML, markdown, etc. And you can also view your HTML content over Gemini with a client that supports HTML. As far as I can tell, most features that gemtext lacks had been discussed in the mailing list before, all of which, I’m pretty sure, alternatives or workarounds were suggested. I think this comment by Alex summarises it well:</p>
<blockquote>
<p>Everyone wants something added to Gemini but disagrees what that something is. Personally, I think it should be in-line images and footnotes, but if Gemini became more complex, it would lose many of the traits that make it interesting. Gemini is a technology that invites us not to try and improve or optimize it, but to accept it as it is and work around its limitations – it is intentionally austere, and this is a feature, not a bug.
(<a href="https://lobste.rs/s/3nsvkk/gemini_is_useless#c_1zpxad">source</a>)</p>
</blockquote>
<p>For me, I’m fine with not having inline images, footnotes, or maybe even syntax highlighting in preformatted text. But as to tables, yes, that could make it look better, and easier to copy contents, but it would lose its simplicity and ease of developing new software around it. If gemtext was to support a similar type of table syntax as markdown, for example, the table would look extremely broken for clients that do not support it. Unless it is put in a preformatted block, though in which case that wouldn’t solve the problem of horizontal scrolling in small screens.</p>
<p>In regards to inline links, sure, that could clear some clutter by removing the “[1]”, but support for this could lead to escaping characters in the syntax, and then escaping the characters that escape the characters, which adds layer upon layer of complexities, again, losing its simplicity.</p>
<p>For me personally, if gemtext supported tables, inline links, and maybe “—” dividers, it <em>would</em> make my life a tiny bit easier when creating content, (though not necessarily when developing my own client). But it’s not like you can’t do <em>anything</em> with gemtext. You’d just have to get used to it. Gemini strictly separates the roles of content and presentation, the author provides the content and the client can control the presentation in whatever style it likes.</p>

<h2 id="do-we-need-a-whole-new-protocol">
  <a class="anchor" href="#do-we-need-a-whole-new-protocol" rel="nofollow">#</a>
  Do we need a whole new protocol?</h2>

<p>For me personally, I like fresh starts. So sure, a new protocol, new ways to think about the internet, and some getting used to is a great idea. It might be a pain for some to set up mirrors of their web content, but it’s the experience and room to explore endless possibilities on a new medium that matters. Since you need new software, it’s a perfect opportunity to learn a new language and get your own stuff out there. It’s just the beginning, so there aren’t many “rulers” in this space, as opposed to the web with popular browsers like chrome.</p>
<p>At the end of the day, the fact that I create and browse content in Geminispace every now and then does not mean I’ve decided to not use the web at all, nor does it mean that all of the good content is in Geminispace instead of the web. Like mozz said on Station:</p>
<blockquote>
<p>I’m not here because I hate the web, I’m here because I like Gemini.
(<a href="gemini://station.martinrue.com/mozz/d29f6cf900b04aef9e7a2332c2098f13">source</a>)</p>
</blockquote>
<p>You don’t have to ditch the web to use Gemini. You get to choose what you write about, but you don’t get to choose the color of your headers, or the font size of your body paragraphs. You are free to use the web if you want that.</p>

<h2 id="full-list-of-references">
  <a class="anchor" href="#full-list-of-references" rel="nofollow">#</a>
  Full list of references</h2>

<ul>
<li><a href="https://lobste.rs/s/vhlagb/why_gemini_is_not_my_favorite_internet">Why Gemini is Not My Favorite Internet Protocol (Lobste.rs)</a></li>
<li><a href="https://lobste.rs/s/3nsvkk/gemini_is_useless">Gemini is Useless (Lobste.rs)</a></li>
<li><a href="https://news.ycombinator.com/item?id=27490769">Gemini is Useless (HN)</a></li>
<li><a href="https://bentsai.smol.pub/embracing-constraints">bentsai.smol.pub/embracing-constraints</a></li>
<li><a href="https://mieum.smol.pub/for-the-love-of-gemini">mieum.smol.pub/for-the-love-of-gemini</a></li>
<li><a href="https://lobste.rs/s/ivryqt/what_is_this_gemini_thing_anyway_why_am_i">What is this Gemini thing anyway, and why am I excited about it?  (Lobste.rs)</a></li>
<li><a href="https://portal.mozz.us/gemini/station.martinrue.com/mozz/d29f6cf900b04aef9e7a2332c2098f13">mozz’s message posted on Station (via Gemini to HTTP Proxy)</a></li>
<li><a href="https://thomask.sdf.org/blog/2021/06/12/beyond-gemini.html">Beyond Gemini? (In response to “Why Gemini is not my favourite internet protocol”)</a></li>
</ul>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20On%20rants%20about%20gemini">Reply via email</a></p>]]></content></entry><entry><title>How this site works</title><link rel="alternate" href="https://home.hedy.dev/posts/site-meta/"/><id>https://home.hedy.dev/posts/site-meta/</id><published>2021-02-23T00:00:00Z</published><updated>2024-10-31T08:49:01Z</updated><summary type="text">A walkthrough of my workflows for the site and a gentle introduction to tilde.cafe and the Tildeverse.</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<details open="">
<summary>2023 Update</summary>
<p>I started writing this post on 2021, February 23 but I never completed it. So in
2023 I decided to finish up this outstanding post and get it done with.
Surprisingly, the content did not change much other than moving from ~team to
~cafe (more on that later). I still use Hugo and write in Markdown. And I still
publish to both WWW and Gemini.</p>
<p>Excuse the odd placing of this post, but otherwise, enjoy the read~</p>
</details>
<details open="">
<summary>2024 March Update</summary>
<p>I no longer host my website, gemini capsule and spartan content on tilde.cafe.
As of writing, I’ve switched to srht.site for static HTTPS/gemini hosting with
my own domain at home.hedy.dev.</p>
<p>I’m keeping this post (which as it turns out, I still never got to publish) as
a historical record, whose information, other than the aforementioned
differences, remains mostly accurate.</p>
</details>
<br />
<p>This is where my gemlog/blog lives. It is statically generated
with <a href="https://gohugo.io/">Hugo</a> and rsync-ed to my <code>public_html</code> and <code>public_gemini</code>
directories for HTML and gemini on <a href="https://tilde.cafe">tilde.cafe</a> respectively
on build.</p>
<p>The site source is always linked at the footer, pointing to the <a href="https://sr.ht/~hedy/site">sourcehut
repo</a> though the repo is also mirrored on
<a href="https://github.com/hedyhli/site">github</a>.</p>

<h2 id="tildecafe-and-the-tildeverse">
  <a class="anchor" href="#tildecafe-and-the-tildeverse" rel="nofollow">#</a>
  tilde.cafe and the Tildeverse</h2>

<blockquote>
<p>[The <a href="https://tildeverse.org">Tildeverse</a> is] a loose association of
like-minded tilde communities. <em>Tildes</em> are pubnixes in the spirit of
<a href="https://tilde.club/">tilde.club</a>, which was <a href="https://medium.com/message/tilde-club-i-had-a-couple-drinks-and-woke-up-with-1-000-nerds-a8904f0a2ebf">created in 2014 by Paul
Ford</a></p>
</blockquote>
<p>A <strong>Pubnix</strong> is a Public Access Unix System — in other words, a unix machine on
the internet that provides access to other people on the internet — which
started before WWW and even the public internet. Pubnixes you may have heard of
<a href="https://sdf.org">SDF</a>,
<a href="https://portal.mozz.us/gopher/circumlunar.space/">Circumlunar.Space</a>, and
<a href="https://tilde.club">tilde.club</a>.</p>
<p><a href="https://tilde.team/~cmccabe/">~cmccabe</a> has a <a href="https://gopher.mills.io/rawtext.club/1/~cmccabe/pubnixhist/">Pubnix History
Project</a> for you to
explore if you’re interested in learning more about pubnixes.</p>
<p>There are a lot of pubnixes that have joined the Tildeverse since the the ~club
and <a href="https://tilde.team">~team</a> (which hosts the tildeverse website together
with many services).</p>
<p>Most tildes have open registerations. Users are provided around 1 GB of disk
space along with SSH access and are able to host their own static site with CGI
under the main tilde domain, together with services like email, gemini/gopher
hosting, pastbin, and more.</p>
<p>However, the true value of tildes lie not in the services they provide but the
people and community. The tildeverse has an official IRC network, tilde.chat,
which most tildes use for their main channel of communication. They also have a
<a href="https://tilderadio.org">radio</a>, a <a href="https://tilde.zone">mastodon instance</a>,
<a href="https://tildegit.org">gitea instance</a>, among other services. The latter two
from the above are both hosted by <a href="https://ben.tilde.team">~ben</a> the owner of
~team.</p>
<p>I chose to host my personal website on tilde.cafe, a (relatively) newer tilde
that runs on Debian with a smaller user base, and for the past two years most
coding projects I had done were developed on ~Cafe’s server. It’s grown to
become my second digital home on the internet ever since I started to use hedy
at tilde.cafe for my primary email.</p>
<p>It was fun to work with ~cafe admins <a href="https://tilde.cafe/~spider/">~spider</a> and
<a href="https://tilde.cafe/~jan6">~jan6</a> in the early days, helping to set up and write
the <a href="https://tilde.cafe/wiki/">wiki</a> and automated website builds. We used the
#cafe channel on tilde.chat for our main chat.</p>
<p>Some time in 2021 or 2022, jan6 is no longer able to chat with us on #cafe in
tilde.chat, and since spider became busy with work and other areas of life, we
needed one more admin to take care of user support on tilde.cafe. I was
promoted to join spider and jan6 as sudoers and have been helping with signups
and sysadmin’ing since then.</p>
<p>I don’t plan to move off ~cafe to host my personal website, nor email in the
foreseable future. Though when I have side-projects that required dedicated
servers with subdomains, I will consider using my own server and domain. Until
then, I remain eternally grateful to ~cafe and other tilde-owners for
maintaining awesome communities and providing welcoming spaces online for
strangers to come together and build cool stuff, with money from the admin’s own
pockets.</p>
<p>If you’re someone who is recently discovering the tildeverse I highly recommend
<a href="https://rawtext.club">rawtext.club</a> (though not a “tilde” but a very cool
community of extremely interesting people), <a href="https://tilde.team">~team</a> (they
have a welcoming chat — #team — easily second most active channel on
tilde.chat), <a href="https://envs.net">~envs</a> (they have an <a href="https://pleroma.envs.net">Akkoma
instance</a>, used by Rohan Kumar from
<a href="https://seirdy.one">seirdy.one</a> whose articles you may have come across on the
internet), and of course <a href="https://tilde.cafe">~cafe</a> (drop me an
email if you registered after reading this and I’ll make sure to personally
approve your sign-up ;P). Others may not agree on my choices so this is not at
all to downplay the worth of <a href="https://tildeverse.org/members/">other tildes</a>:
there’s almost no harm in having an account on every single one, though you can
definitely meet some cool people!</p>

<h2 id="software-stack--workflows">
  <a class="anchor" href="#software-stack--workflows" rel="nofollow">#</a>
  Software stack &amp; workflows</h2>

<p>No javascript is used. All content is written in Markdown and Gemtext, with the
static HTMl output generated by Hugo. The styling is done with vanilla CSS,
<a href="/posts/hugo-syntax-highlighting/">syntax highlighting</a> provided by chroma.</p>
<p>When a new blog post is written, I would ensure both markdown and gemtext
sources are done, then I would run <code>make</code> which uses Hugo and does some
miscellaneous cleaning-up to produce HTMl files at <code>~/public_html</code> and gemtext
at <code>~/public_gemini</code>. I then use a browser to access the folder locally to check
that everything is working. When something needs updating, a single <code>make</code> is
enough to rebuild the entire site.</p>
<p>Finally, I commit and push. If I wasn’t working on ~cafe directly I would next
log into ~cafe, pull, and <code>make</code> from there. It’s guaranteed to perform the same
from my home computer and on ~cafe given the same Hugo versions.</p>
<p>Since I ran <code>make</code> on ~cafe, the actual <code>~/public_*</code> directories will be updated
on the changes would be live! 🎉</p>
<p>~cafe web server handles the rest. (FTR: on WWW we use nginx and on Gemini we
use Gemserv.)</p>

<h2 id="hosting">
  <a class="anchor" href="#hosting" rel="nofollow">#</a>
  Hosting</h2>

<p>The site and gemini capsule is to be accessed at <code>hedy.tilde.cafe</code>; it works on
both protocols. You may be curious how we could have per-user vhosts on gemini —
no, there is no built-in configuration option as of writing. This is done with a
shell script that appends to config file a separate server configuration when a
new user sign-up is approved, written by jan6.</p>
<p>The <code>&lt;user&gt;.tilde.cafe</code> vhost is also supported on
<a href="https://portal.mozz.us/gemini/spartan.mozz.us">spartan</a>. I wrote the spartan
server that cafe uses (<a href="https://github.com/hedyhli/spsrv">spsrv</a>) and it
provides a config option to use per-user vhosts.</p>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20How%20this%20site%20works">Reply via email</a></p>]]></content></entry><entry><title>Hello world</title><link rel="alternate" href="https://home.hedy.dev/posts/hello/"/><id>https://home.hedy.dev/posts/hello/</id><published>2021-02-05T01:44:15Z</published><updated>2024-04-11T10:25:50Z</updated><summary type="text">I'm alive!</summary><author><name>~hedy</name><email>hedy.dev@protonmail.com</email><uri>https://home.hedy.dev/</uri></author><content type="html"><![CDATA[

<p>Hello hugo</p>
<p>hope it works!</p>

<hr />
                <p><a href="mailto:hedy.dev@protonmail.com?subject=Re%3a%20Hello%20world">Reply via email</a></p>]]></content></entry></feed>