<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://chrismcleod.dev/">
  <title>Chris McLeod&#39;s blog</title>
  <subtitle>Blog posts by Chris McLeod, posted to https://chrismcleod.dev</subtitle>
  <link href="https://chrismcleod.dev/feed.xml" rel="self" />
  <link href="https://chrismcleod.dev/" />
  
  <updated>2024-02-18T21:06:00Z</updated>
  <id>https://chrismcleod.dev/</id>
  <author>
    <name>Chris McLeod</name>
    <email>feedback@chrismcleod.dev</email>
  </author>
  <entry>
    <title>Generate a Markdown List of Open Safari Tabs with an iOS Shortcut</title>
    <link href="https://chrismcleod.dev/blog/generate-a-markdown-list-of-open-safari-tabs-with-an-ios-shortcut/" />
    <updated>2024-02-18T21:06:00Z</updated>
    <id>https://chrismcleod.dev/blog/generate-a-markdown-list-of-open-safari-tabs-with-an-ios-shortcut/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;I mentioned in &lt;a href=&quot;https://chrismcleod.dev/blog/open-tab-dump-mobile-edition-2024-02-18/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;my last post&lt;/a&gt; that I created an iOS Shortcut to automate turning the list of tabs I have open in Safari into a Markdown list. Ryan asked if I could share that shortcut. I was always intending to, so here it is for everyone to make use of:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.icloud.com/shortcuts/8eccd3f15ac34c67b0a98ca6dd38c1c7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Get Open Tabs as Markdown&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;By way of explanation of what the shortcut does:&lt;/p&gt;
&lt;ul class=&quot;list&quot;&gt;
&lt;li&gt;Get a dictionary of all open tabs from Safari (this doesn’t include Private Browsing or tabs in a “collection”)&lt;/li&gt;
&lt;li&gt;Loop through the dictionary.
&lt;ul class=&quot;list&quot;&gt;
&lt;li&gt;For each tab, format the page title and URL into the Markdown format for a link, prefixed with &amp;quot;- &amp;quot; to make it a list item.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Combine the items in the returned dictionary into one blob of plain text, separated by new lines.&lt;/li&gt;
&lt;li&gt;Copy the resulting text to the clipboard.&lt;/li&gt;
&lt;li&gt;Open the Share Sheet, so you can “share” it to any app you choose.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You don’t have to be in any particular app to run this Shortcut. You can run it standalone from the Home Screen, Shortcuts app, or add it as an action in the Share Sheet.&lt;/p&gt;
</content
    >
  </entry>
  <entry>
    <title>Open Tab Dump: Mobile Edition (2024-02-18)</title>
    <link href="https://chrismcleod.dev/blog/open-tab-dump-mobile-edition-2024-02-18/" />
    <updated>2024-02-18T17:13:00Z</updated>
    <id>https://chrismcleod.dev/blog/open-tab-dump-mobile-edition-2024-02-18/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;I had a &lt;em&gt;lot&lt;/em&gt; of tabs open in Mobile Safari. It was kinda getting a bit overwhelming to have to scroll through them all to find a particular one, or if I already had something I was about to search for open.&lt;/p&gt;
&lt;p&gt;So I figured I’d save a list of them all somewhere, just in case, and wipe the slate clean on my phone. The list below are the ones worth sharing. I even created an iOS Shortcut to help automate creating the list - so who knows, it might become a regular feature!&lt;/p&gt;
&lt;h2 id=&quot;cool-blogs-blog-posts-and-where-to-find-them&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/open-tab-dump-mobile-edition-2024-02-18/#cool-blogs-blog-posts-and-where-to-find-them&quot;&gt;Cool Blogs, Blog Posts, and Where to Find Them&lt;/a&gt;&lt;/h2&gt;
&lt;ul class=&quot;list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://sizeof.cat/post/website-discovery/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Discover personal websites - sizeof(cat)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kevquirk.com/blogroll/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Cool People With Blogs | Kev Quirk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blogroll.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Ye Olde Blogroll - Blogroll.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://peopleandblogs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;People and Blogs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://grahammacphee.com/writing/mastodon-blog-comments&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Using Mastodon to power my blog comments | Graham Macphee&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://library.xandra.cc/everyone-should-blog/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;EveryoneShouldBlog.txt | the library of alexandra&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kevquirk.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Blog | Kev Quirk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://underlap.org/this-site-one-month-in&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;This site, one month in — underlap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zoeaubert.me/blog/first-games-of-40k/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;My first games of Warhammer 40k | Zoe Aubert&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anildash.com/2024/01/03/human-web-renaissance/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;The Web Renaissance Takes Off - Anil Dash&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://danmcquillan.org/ai_thatcherism.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AI as Algorithmic Thatcherism&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gilest.org/indie-easy.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;gilest.org: Make the indie web easier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.aaronsw.com/weblog/000404&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Bake, Don’t Fry (Aaron Swartz: The Weblog)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cassey.dev/ssg-frontmatter-template/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Frontmatter Templates for New Blog Posts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://citationneeded.news/substack-to-self-hosted-ghost/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Migrating from Substack to self-hosted Ghost: the details&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://indieseek.xyz/2024/01/05/the-state-of-the-independent-web-2022-updated-2024/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;The State of the Independent Web 2022: Updated 2024&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gkeenan.co/blogroll&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Blogroll&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zachholman.com/posts/only-90s-developers/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Only 90s Web Developers Remember This&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://amerpie.lol/my-bookmarks/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;📘 My Bookmarks | Lou Plummer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://personalsit.es/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PersonalSit.es | Yes we got hot and fresh sites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://11tybundle.dev/categories-test/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;38 Categories&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://opml-to-blogroll.lmika.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OPML to Blogroll&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sarajoy.dev/wips/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Works in Progress&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gregmorris.co.uk/2024/01/29/visit-more-blogs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Visit More Blogs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;web-and/or-tech-related&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/open-tab-dump-mobile-edition-2024-02-18/#web-and/or-tech-related&quot;&gt;Web and/or Tech-related&lt;/a&gt;&lt;/h2&gt;
&lt;ul class=&quot;list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.wired.com/story/first-gen-social-media-users-have-nowhere-to-go/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;First-Gen Social Media Users Have Nowhere to Go | WIRED&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloudcannon.com/blog/top-11-free-eleventy-themes-for-2024/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Top 11 free Eleventy themes for 2024 | CloudCannon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/editor/artificial-intelligence&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Use GitHub Copilot to enhance your coding with AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/developer/mobile-apps/azure-mobile-apps/quickstarts/maui/authentication?pivots=vs2022-windows&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Add authentication to your .NET MAUI app | Microsoft Learn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-gb/training/challenges?id=d6c6d844-1da1-470f-9157-ee9471c6b55d&amp;amp;ocid=ignite23_CSC_CSC-email_cnl&amp;amp;wt.mc_id=cloudskillschallenge_d6c6d844-1da1-470f-9157-ee9471c6b55d_30dtli_web_wwl&amp;amp;WT.mc_id=cloudskillschallenge_d6c6d844-1da1-470f-9157-ee9471c6b55d&amp;amp;source=learn&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Challenge | Microsoft Learn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=39086244&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Why isn’t Bluesky a peer-to-peer network? | Hacker News&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://1feed.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;1Feed | Your quiet place on the internet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eleventy-excellent.netlify.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Eleventy Excellent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;miniatures/warhammer-related&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/open-tab-dump-mobile-edition-2024-02-18/#miniatures/warhammer-related&quot;&gt;Miniatures/Warhammer related&lt;/a&gt;&lt;/h2&gt;
&lt;ul class=&quot;list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/Warhammer/comments/11174ag/psa_kr_kaiserrushforth_multicase_bags_are_the/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PSA: KR (KaiserRushforth) Multicase bags are the perfect fit for Really Useful Boxes for those who use the “magnetised bases and magnetic sheet-glued-to-box” method for miniature storage (more details in comment section). : r/Warhammer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.waylandgames.co.uk/kaiser-case-4-krm-k4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Kaiser Case 4 KR Multicase K4-B | Wayland Games&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.myminifactory.com/object/3d-print-gothic-ruins-set-1-print-in-place-338149&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;3D Printable Gothic Ruins Set #1 - Print-in-place by Fabricator’s Lair&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kickstarter.com/projects/tiredworldstudio/hexengarde-city-walls/pledge/new?clicked_reward=false&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Hexengarde: City Walls » Back this project — Kickstarter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://shop.army-case.com/gadgets/mpgmegapack/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;A-Case Magnetic Painting Grip Mega Pack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.warhammer-community.com/2023/04/12/painting-the-lion-how-eavy-metal-tackled-one-of-the-all-time-greats/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Painting the Lion – How ‘Eavy Metal Tackled One of the All-time Greats - Warhammer Community&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;miscellaneous&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/open-tab-dump-mobile-edition-2024-02-18/#miscellaneous&quot;&gt;Miscellaneous&lt;/a&gt;&lt;/h2&gt;
&lt;ul class=&quot;list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.wheresyourwifi.com/collections/photo-frames/products/photo-frame?variant=32953634095139&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Home Definition Photo Frame | WYWF – Where’s Your WiFi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.overclockers.co.uk/ducky-one-3-mist-tkl-80-usb-rgb-mechanical-gaming-keyboard-cherry-mx-brown-kb-2e8-dk.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Product Ducky One3 Mist TKL 80% USB RGB Mechanical Gaming Keyboard Cherry MX Brown Switch - UK Layout _ Edit | OcUK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kickstarter.com/projects/iconfactory/project-tapestry&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Project Tapestry by The Iconfactory — Kickstarter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/SquaredCircle/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;r/SquaredCircle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://newsletters.audible.com/p/rp/203a05b762bc0d6e.png?mi_u=amzn1.account.AGZROEUQU3SKT64CUIPQ36DJED7Q&amp;amp;mi_ecmp=900645741&amp;amp;Name=Chris&amp;amp;total%20plays=146&amp;amp;Total%20Minutes=5823&amp;amp;Longest%20Date=04/22/2023&amp;amp;Longest%20Minutes=430&amp;amp;Language=en_UK&amp;amp;country=GB&amp;amp;Bookwall=https://m.media-amazon.com/images/I/510CWCDwlcL._SL500_.jpg%2Chttps://m.media-amazon.com/images/I/51GFHsmJI5L._SL500_.jpg%2Chttps://m.media-amazon.com/images/I/51x44nOi5-L._SL500_.jpg%2Chttps://m.media-amazon.com/images/I/510CWCDwlcL._SL500_.jpg%2Chttps://m.media-amazon.com/images/I/51-KvEh3GfL._SL500_.jpg%2Chttps://m.media-amazon.com/images/I/51-jH9HAMOL._SL500_.jpg%2Chttps://m.media-amazon.com/images/I/51ad-KnCDGL._SL500_.jpg%2Chttps://m.media-amazon.com/images/I/51WAKqJAYvL._SL500_.jpg%2Chttps://m.media-amazon.com/images/I/51x44nOi5-L._SL500_.jpg%2Chttps://m.media-amazon.com/images/I/41UX9z-%2bVnL._SL500_.jpg%2Chttps://m.media-amazon.com/images/I/51rlO4NRM6L._SL500_.jpg%2C%2C%2C&amp;amp;Exclude=https://m.media-amazon.com/images/I/51rlO4NRM6L._SL500_.jpg%2Chttps://m.media-amazon.com/images/I/51WAKqJAYvL._SL500_.jpg%2C%2C%2C%2C%2C&amp;amp;Genre%201=18580606011&amp;amp;Top%20Author=Dan%2bAbnett&amp;amp;Pressed%20Play=146&amp;amp;ref_=pe_67106401_900645741&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;My Audible Listening Stats for 2023&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content
    >
  </entry>
  <entry>
    <title>Blogkeeping</title>
    <link href="https://chrismcleod.dev/blog/blogkeeping/" />
    <updated>2024-02-16T13:20:00Z</updated>
    <id>https://chrismcleod.dev/blog/blogkeeping/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;A list of the little online jobs I’ve been knocking off my to-do list this week:&lt;/p&gt;
&lt;h2 id=&quot;i-moved-mastodon-instance&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/blogkeeping/#i-moved-mastodon-instance&quot;&gt;I moved Mastodon instance&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’d been on Mastodon.online for a number of years, but always intended to move off of it at some point - get onto a smaller, less generic server. I took advantage of &lt;a href=&quot;https://home.omg.lol/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;omg.lol’s&lt;/a&gt; Valentine’s Day sale to &lt;a href=&quot;https://social.lol/@chrisplusplus&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;jump over to Social.lol&lt;/a&gt;, as I noticed that’s where a lot of the accounts I was recently interacting with were based and it had a good vibe. Plus - support a cool Open Web service!&lt;/p&gt;
&lt;p&gt;The switching process was “interesting”; the Mastodon.online side of it wasn’t working initially, so I had half the steps done at first and had to complete things the following morning. I also did not expect the “follower migration” step to bombard me with “Account followed you” notifications, as it was a migration, but it sure did! Otherwise it was pretty simple. The hardest part was probably re-configuring my cross-posting.&lt;/p&gt;
&lt;p&gt;But it’s all done now. I’m using it as an opportunity to reset what and who I follow, so if I was following you before, and haven’t immediately followed you back, it’s because I’m manually adding people instead of importing the export file.&lt;/p&gt;
&lt;h2 id=&quot;my-micro-blogs-now-have-a-consistent-look-and-feel&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/blogkeeping/#my-micro-blogs-now-have-a-consistent-look-and-feel&quot;&gt;My micro blogs now have a consistent look and feel&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It had started to bug me how wildly inconsistent my micro blogs were looking. In part this was due to me picking a default theme on a whim when setting up the sites in Micro.blog, but I never went back to fix it. Well, I finally found the oomph to do it.&lt;/p&gt;
&lt;p&gt;I used &lt;a href=&quot;https://mattlangford.com/tiny-theme/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Matt Langford’s Tiny Theme for Micro.blog&lt;/a&gt; because it has a really neat “microhook” system to make customising the templates easier without making upgrades harder. Then I took the styles from my main blog and mashed everything together until it looked like it was somewhat coherent to look at. The typography and “look and feel” should at least be familiar enough, even if it’s not 100% the same across platforms. I can make further tweaks some time in the future.&lt;/p&gt;
&lt;ul class=&quot;list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://micro.chrismcleod.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Books and Micro Things&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://worldsinminiature.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Worlds in Miniature&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;buy-me-a-coffee&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/blogkeeping/#buy-me-a-coffee&quot;&gt;Buy Me A Coffee?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In 20+ years of blogging I’ve never put up a paywall or asked for subscriptions. I’m not sure I’ve ever even put up ads. I’m not about to start now. But I &lt;em&gt;have&lt;/em&gt; setup a &lt;a href=&quot;https://www.buymeacoffee.com/mrkapowski&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Buy Me A Coffee page&lt;/a&gt; out of curiosity, as I’d seen many of the people I follow link to similar platforms from their sites. To be clear, I don’t expect anyone to donate anything, ever - but if they do, I’ll be grateful, and I’ll put it towards the next bag of ground coffee from the local roasters.&lt;/p&gt;
&lt;p&gt;Links to the Buy Me A Coffee page will be displayed at the bottom of blog posts on the web, across all my sites, and the site footer of this site, but won’t appear in feeds.&lt;/p&gt;
</content
    >
  </entry>
  <entry>
    <title>Things I&#39;m Enjoying Lately</title>
    <link href="https://chrismcleod.dev/blog/things-im-enjoying-lately/" />
    <updated>2024-02-14T16:54:00Z</updated>
    <id>https://chrismcleod.dev/blog/things-im-enjoying-lately/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;I’ve been planning to write a general “stuff I’m enjoying recently” post for a while, and my birthday seemed as good a time as any to do it 🥳🎈&lt;/p&gt;
&lt;p&gt;This post is largely media-centric, so there are a lot of YouTube embeds below. I might do some more posts like this on focussed on things I’m enjoying in other areas.&lt;/p&gt;
&lt;h2 id=&quot;gladiators-2024&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/things-im-enjoying-lately/#gladiators-2024&quot;&gt;Gladiators (2024)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The new “reboot” of Gladiators, that’s showing on the BBC Saturday evenings is so much better than it had any right to be It’s been so much fun to watch. It’s got many of the same contests, similar-but-updated presentation, and - most importantly - the same music. I’d say the main difference between the reboot and the original is that in the reboot they seem to realise how silly the premise is and lean into it slightly.&lt;/p&gt;
  &lt;div class=&quot;youtube-embed&quot;&gt; &lt;lite-youtube videoid=&quot;cXVj74Ay-IY&quot; style=&quot;background-image: url(&#39;https://i.ytimg.com/vi/cXVj74Ay-IY/hqdefault.jpg&#39;);&quot;&gt;
  &lt;button type=&quot;button&quot; class=&quot;lty-playbtn&quot;&gt;
    &lt;span class=&quot;lyt-visually-hidden&quot;&gt;Gladiators 2024 Official BBC Trailer&lt;/span&gt;
  &lt;/button&gt;
&lt;/lite-youtube&gt;&lt;/div&gt;
&lt;h2 id=&quot;gladiators-1992&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/things-im-enjoying-lately/#gladiators-1992&quot;&gt;Gladiators (1992)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Watching the reboot triggered some nostalgia for the original show and naturally pretty much all of it is available on YouTube, in all it’s low-res 90’s big-haired glory. We watched an episode with some 13 year olds who enjoy the modern version, and they were shocked to see just how little had changed (other than the haircuts and outfits)&lt;/p&gt;
  &lt;div class=&quot;youtube-embed&quot;&gt; &lt;lite-youtube videoid=&quot;Sv4VcUfGq74&quot; style=&quot;background-image: url(&#39;https://i.ytimg.com/vi/Sv4VcUfGq74/hqdefault.jpg&#39;);&quot;&gt;
  &lt;button type=&quot;button&quot; class=&quot;lty-playbtn&quot;&gt;
    &lt;span class=&quot;lyt-visually-hidden&quot;&gt;Gladiators Series 1 (1992)&lt;/span&gt;
  &lt;/button&gt;
&lt;/lite-youtube&gt;&lt;/div&gt;
&lt;h2 id=&quot;internet-friends-knife-party&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/things-im-enjoying-lately/#internet-friends-knife-party&quot;&gt;Internet Friends - Knife Party&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every so often the algorithm in Apple Music reminds me this song exists and it becomes an earworm for several days/weeks. Warning for the embedded version below - it’s sweary.&lt;/p&gt;
  &lt;div class=&quot;youtube-embed&quot;&gt; &lt;lite-youtube videoid=&quot;luJJBeCFeM0&quot; style=&quot;background-image: url(&#39;https://i.ytimg.com/vi/luJJBeCFeM0/hqdefault.jpg&#39;);&quot;&gt;
  &lt;button type=&quot;button&quot; class=&quot;lty-playbtn&quot;&gt;
    &lt;span class=&quot;lyt-visually-hidden&quot;&gt;Internet Friends - Knife Party&lt;/span&gt;
  &lt;/button&gt;
&lt;/lite-youtube&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-build-to-wrestlemania-40&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/things-im-enjoying-lately/#the-build-to-wrestlemania-40&quot;&gt;The Build to WrestleMania 40&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I dip in and out of Professional Wrestling every so often, just to see what’s happening. The best time of year to do so is January through to March/April, as you have the Royal Rumble kicking off the “road to WrestleMania”.&lt;br /&gt;
This year has been chaotic and messy, but &lt;em&gt;oh so good&lt;/em&gt;. It would take a great many more paragraphs to recap all of the threads and story beats in the mix (there are many years of layers to peel back), but the headline right now is Dwayne “The Rock” Johnson going full-on entitled Hollywood villain because he wasn’t able to swoop in and just take the marquee main event championship match from the rightful challenger and current fan-favourite Cody Rhodes.&lt;/p&gt;
  &lt;div class=&quot;youtube-embed&quot;&gt; &lt;lite-youtube videoid=&quot;BIZHsGjNfNg&quot; style=&quot;background-image: url(&#39;https://i.ytimg.com/vi/BIZHsGjNfNg/hqdefault.jpg&#39;);&quot;&gt;
  &lt;button type=&quot;button&quot; class=&quot;lty-playbtn&quot;&gt;
    &lt;span class=&quot;lyt-visually-hidden&quot;&gt;WrestleMania 40 Press Conference&lt;/span&gt;
  &lt;/button&gt;
&lt;/lite-youtube&gt;&lt;/div&gt;
&lt;h2 id=&quot;haylo-haley&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/things-im-enjoying-lately/#haylo-haley&quot;&gt;Haylo Haley&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One discovered through my partner’s YouTube recommendations. A Gen-Z Youtuber putting the world to rights over the really ridiculous “trends” coming out of TikTok. I’m old, I’m not on TikTok, so having someone break it down for me with the righteous fury of youth has been super enjoyable.&lt;/p&gt;
  &lt;div class=&quot;youtube-embed&quot;&gt; &lt;lite-youtube videoid=&quot;12ugu93LFIw&quot; style=&quot;background-image: url(&#39;https://i.ytimg.com/vi/12ugu93LFIw/hqdefault.jpg&#39;);&quot;&gt;
  &lt;button type=&quot;button&quot; class=&quot;lty-playbtn&quot;&gt;
    &lt;span class=&quot;lyt-visually-hidden&quot;&gt;We Need to Talk About the Sephora Mom&#39;s&lt;/span&gt;
  &lt;/button&gt;
&lt;/lite-youtube&gt;&lt;/div&gt;
&lt;h2 id=&quot;namis-life&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/things-im-enjoying-lately/#namis-life&quot;&gt;Nami’s Life&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another random recommendation on my partner’s YouTube account. It’s the vlog of a woman living in Japan, but there’s no talking, and you never see their face. Unless you turn on captions there is absolutely nothing to tell you what’s happening. Largely focusses on cooking and cleaning. Oddly relaxing. I’m not really sure what else to say about this, as it’s like nothing else I can think of.&lt;/p&gt;
  &lt;div class=&quot;youtube-embed&quot;&gt; &lt;lite-youtube videoid=&quot;_zER4Z9sCjk&quot; style=&quot;background-image: url(&#39;https://i.ytimg.com/vi/_zER4Z9sCjk/hqdefault.jpg&#39;);&quot;&gt;
  &lt;button type=&quot;button&quot; class=&quot;lty-playbtn&quot;&gt;
    &lt;span class=&quot;lyt-visually-hidden&quot;&gt;Small Habits for Life Organisation&lt;/span&gt;
  &lt;/button&gt;
&lt;/lite-youtube&gt;&lt;/div&gt;
</content
    >
  </entry>
  <entry>
    <title>More Words on Webmentions (and Backfeed)</title>
    <link href="https://chrismcleod.dev/blog/more-words-on-webmentions-and-backfeed/" />
    <updated>2024-02-11T13:42:00Z</updated>
    <id>https://chrismcleod.dev/blog/more-words-on-webmentions-and-backfeed/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;h2 id=&quot;webmentions-arent-actually-the-problem&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/more-words-on-webmentions-and-backfeed/#webmentions-arent-actually-the-problem&quot;&gt;Webmentions aren’t actually the problem&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After my earlier&lt;a href=&quot;https://chrismcleod.dev/blog/some-words-on-webmentions/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; post on webmentions&lt;/a&gt;, &lt;a href=&quot;https://campegg.com/2024/02/11/over-the-last.html&quot; class=&quot;u-in-reply-to&quot;&gt;Cam kindly reminded me that the bit I felt uneasy about is actually &lt;em&gt;backfeed&lt;/em&gt;&lt;/a&gt;. To quote the IndieWeb wiki:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Backfeed&lt;/strong&gt; is the process of syndicating &lt;a href=&quot;https://indieweb.org/interactions&quot; title=&quot;interactions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;interactions&lt;/a&gt; on your &lt;a href=&quot;https://indieweb.org/POSSE&quot; title=&quot;POSSE&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;POSSE&lt;/a&gt; copies &lt;em&gt;back&lt;/em&gt; (AKA reverse syndicating) to your original posts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It’s interesting reading the &lt;a href=&quot;https://indieweb.org/backfeed#Discussion&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;discussion section&lt;/a&gt; on the wiki page, as it acknowledges some of the concerns that have come up of late, but ultimately, skims over them.&lt;/p&gt;
&lt;p&gt;The list of “pro” reasons for backfeed mentions making it easier for people to reply, particularly those who are less technical. However true this might be, I believe it’s probably equally true that the less technical someone is the then less likely they will understand &lt;em&gt;why&lt;/em&gt; their words are appearing on a different site. Their expectations&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/more-words-on-webmentions-and-backfeed/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; are almost certainly “I replied/liked/whatever on Site A (more likely &lt;em&gt;App&lt;/em&gt; A), so it’s not going to appear on Site B”. Is it right we’re&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/more-words-on-webmentions-and-backfeed/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; subverting these expectations in an effort to make blogs and personal websites “more social”?&lt;/p&gt;
&lt;p&gt;Aside from the privacy and implementation concerns noted in the last post, Cam noted in his own post that another problem backfeed can lead to is &lt;a href=&quot;https://indieweb.org/context_collapse&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;context collapse&lt;/a&gt;. This isn’t a problem I have much experience with (so can’t really talk to it) but I thought it worth noting here for completeness.&lt;/p&gt;
&lt;h2 id=&quot;this-is-not-a-new-discussion&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/more-words-on-webmentions-and-backfeed/#this-is-not-a-new-discussion&quot;&gt;This is not a new discussion&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Terrence&lt;a href=&quot;https://shkspr.mobi/blog/2022/12/the-ethics-of-syndicating-comments-using-webmentions/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; wrote about the ethics of this topic&lt;/a&gt; in 2022. His concerns were/are pretty much identical to my own. Bix wrote similarly &lt;a href=&quot;https://bix.blog/2024/01/11/activitypub-is-to-the-indieweb-as-a-i-is-to-silicon-valley/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;on the integration of ActivityPub into everything&lt;/a&gt; just last month. Sebastian wrote &lt;a href=&quot;https://sebastiangreger.net/2018/05/indieweb-privacy-challenge-webmentions-backfeeds-gdpr/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;an in-depth look at the problem&lt;/a&gt; - particularly through the lens of GDPR - back in 2018! There will be many more posts out there about the issues - and no doubt, potential solutions - to the problems with backfeed as a concept. If you know of any good ones, please send it my way &lt;a href=&quot;https://social.lol/@chrisplusplus&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;on Mastodon&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;its-on-us-as-webmasters&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/more-words-on-webmentions-and-backfeed/#its-on-us-as-webmasters&quot;&gt;It’s on us as webmasters&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;(Yeah, I cited &lt;a href=&quot;https://thehistoryoftheweb.com/postscript/what-happened-to-the-webmaster/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;the deep magic&lt;/a&gt;. Substitute for “site admin” or “site owner” if you prefer.)&lt;/p&gt;
&lt;p&gt;How we handle the issues that have been raised is ultimately on us: the people who build and run the websites who integrate these features. We have to be more thoughtful about these things rather than rushing to “oh, cool new technology”. I didn’t really pay the issues much heed when I first implemented backfeed in 2019, but I should have.&lt;/p&gt;
&lt;p&gt;There might come a day when users expect their details, words, and actions could show up on other sites they weren’t aware of. ActivityPub and other forms of the Fediverse and federation might herald that future. But I don’t think we’re there yet, and I’m not convinced everyone will buy into it. I’ve seen enough threads on Bluesky where someone reacts with abject horror when they learn that &lt;a href=&quot;https://bsky.social/about/blog/5-19-2023-user-faq&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;basically &lt;em&gt;everything&lt;/em&gt; on Bluesky is public&lt;/a&gt;, and similar threads when someone learns just how far public posts on Mastodon can reach.&lt;/p&gt;
&lt;p&gt;By all means send and receive webmentions. Even backfeed interactions onto your site. Just be mindful of how you do it and whether displaying all the data you receive is the best thing to do.&lt;/p&gt;
&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://chrismcleod.dev/assets/images/_m67XhJpRc-320.avif 320w, https://chrismcleod.dev/assets/images/_m67XhJpRc-570.avif 570w&quot; sizes=&quot;(min-width: 55rem) 820px, 100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://chrismcleod.dev/assets/images/_m67XhJpRc-320.webp 320w, https://chrismcleod.dev/assets/images/_m67XhJpRc-570.webp 570w&quot; sizes=&quot;(min-width: 55rem) 820px, 100vw&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;https://chrismcleod.dev/assets/images/_m67XhJpRc-320.jpeg 320w, https://chrismcleod.dev/assets/images/_m67XhJpRc-570.jpeg 570w&quot; sizes=&quot;(min-width: 55rem) 820px, 100vw&quot; /&gt;&lt;img src=&quot;https://chrismcleod.dev/assets/images/_m67XhJpRc-570.jpeg&quot; width=&quot;570&quot; height=&quot;309&quot; alt=&quot;Dr Ian Malcolm in Jurassic park: You were so preoccupied with whether or not you could, you didn’t stop to think if you should&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;&quot; /&gt;&lt;/picture&gt;&lt;/figure&gt;
&lt;h2 id=&quot;a-quick-clarification-i-❤️-bridgy&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/more-words-on-webmentions-and-backfeed/#a-quick-clarification-i-%E2%9D%A4%EF%B8%8F-bridgy&quot;&gt;A quick clarification: I ❤️ &lt;a href=&quot;http://brid.gy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Brid.gy&lt;/a&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I wrote my last post pretty quickly and late at night, immediately before going to bed. After a re-read this morning I felt that it occasionally came across as me having a go at &lt;a href=&quot;http://brid.gy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Brid.gy&lt;/a&gt;. That wasn’t my intent; I admire the technical feat of what &lt;a href=&quot;http://brid.gy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Brid.gy&lt;/a&gt; achieves and could achieve in the future, and I respect that it is an open service helping to link disparate parts of the internet together, for free. My issues really lie with the &lt;em&gt;consumption&lt;/em&gt; of what &lt;a href=&quot;http://brid.gy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Brid.gy&lt;/a&gt; provides, rather than &lt;a href=&quot;http://brid.gy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Brid.gy&lt;/a&gt; itself.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Yes, this is a massive generalisation, but I like to think it comes from a place of experience. &lt;a href=&quot;https://chrismcleod.dev/blog/more-words-on-webmentions-and-backfeed/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I’ll use “we” and “us” a fair bit in this post. It’s largely the “&lt;a href=&quot;https://en.wikipedia.org/wiki/Royal_we&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;royal we&lt;/a&gt;”. As I’ve said before: I ain’t the boss of you. &lt;a href=&quot;https://chrismcleod.dev/blog/more-words-on-webmentions-and-backfeed/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content
    >
  </entry>
  <entry>
    <title>Some Words on Webmentions</title>
    <link href="https://chrismcleod.dev/blog/some-words-on-webmentions/" />
    <updated>2024-02-11T00:11:00Z</updated>
    <id>https://chrismcleod.dev/blog/some-words-on-webmentions/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;&lt;a href=&quot;https://indieweb.org/Webmention&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Webmentions&lt;/a&gt; are one of those things I like the &lt;em&gt;idea&lt;/em&gt; of, but not always the &lt;em&gt;practicalities&lt;/em&gt; of - particularly in the &lt;a href=&quot;https://indieweb.org/responses&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;extended IndieWeb sense of it&lt;/a&gt;. The idea of sending and receiving Likes and other interactions to/from other websites is pretty cool in theory, and helps bring that sense of community that is sometimes lacking in the modern version of the blogosphere. Time was you’d get a trackback/pingback from a response to a post you’d written, go check out the response post on the author’s site, then continue the conversation on your own blog. This back-and-forth was key to building up ties within groups and sub-groups on different topics. Webmentions are, at their core, just an iteration on the core technology behind that.&lt;/p&gt;
&lt;p&gt;However, there are pretty significant problems with Webmentions as they stand. &lt;a href=&quot;https://brainbaking.com/post/2023/05/why-i-retired-my-webmention-server/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Wouter lists a bunch of issues he came across&lt;/a&gt;, and I can empathise with all of them, even if I haven’t experienced each and every item on the list. The points about microformats and &lt;a href=&quot;http://brid.gy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Brid.gy&lt;/a&gt; (4 &amp;amp; 5) definitely hit home.&lt;/p&gt;
&lt;p&gt;Microformats is by far the biggest pain in the arse (in my humble opinion) to get right if you want to use the IndieWeb formats. Not insurmountable, but definitely fiddly. When I recently implemented my Bookmarks page, getting the requisite microformats &lt;em&gt;just right&lt;/em&gt; to show as a “bookmark-of” took by far the longest time of all the work involved. And absolutely, &lt;a href=&quot;http://brid.gy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Brid.gy&lt;/a&gt; far and away accounts for the vast majority of webmentions I receive; I guess it’s to be expected when it pulls in all the interactions from social media for you.&lt;/p&gt;
&lt;p&gt;However, &lt;a href=&quot;https://rknight.me/blog/mastodon-webmentions-and-privacy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Robb hits on the biggest potential issue with the way most sites implement webmentions: privacy&lt;/a&gt;. It’s something I first noticed on an old version of my blog: if &lt;a href=&quot;http://brid.gy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Brid.gy&lt;/a&gt; (or another bridge service) sends social media interactions to your site, and your site is setup to save and display those interactions, then the person interacting with you has - probably inadvertently - had their details and words published to your site. They then lose all control over what is or isn’t displayed. If they should later delete their interaction it is highly unlikely in the current implementations of the various bits of software involved that the deletion will be replicated back to your site. I had more than one instance where a Twitter mutual were shocked to discover some of their replies/likes at the bottom of some of my posts. Most didn’t mind, but a couple really didn’t like it so I went ahead and deleted the data for them manually.&lt;/p&gt;
&lt;p&gt;It was these interactions which led to me not adding webmentions at all to this site for a long time after it went live, despite wanting to. Even now I can send and receive, nothing is displayed anywhere on the site itself.&lt;/p&gt;
&lt;p&gt;So what do we do about these concerns around webmentions? Or rather, what am &lt;em&gt;I&lt;/em&gt; going to do&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/some-words-on-webmentions/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;? Well, I still hold to the promise of webmentions as a community building tool. I still want to receive them, even if the ones I care about get drowned out. They let me know where people are linking to my site from, and to an extent, what posts have resonated. In that vein, I still plan to send them - to let other people know when I link to them - though I might not spend any time sweating to get the microformats right. But what I’m &lt;em&gt;not going to do&lt;/em&gt; is display my webmentions publicly. I had been considering it for a while, but the recent discussion has made up my mind against it. At most I &lt;em&gt;might&lt;/em&gt; display a simple count of the different types of webmention a post has received.&lt;/p&gt;
&lt;p&gt;To be 100% transparent though: for my own convenience I am planning on adding a private dashboard that will display webmentions in a nice UI, but for my eyes only. Azure SWA lets me lock particular sections of my site behind role-based authentication, so I will be taking advantage of that. If only because the RSS feed from &lt;a href=&quot;http://webmention.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;webmention.io&lt;/a&gt; isn’t always the clearest thing to read. One feature I’m definitely implementing is a filter to let me hide &lt;a href=&quot;http://brid.gy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Brid.gy&lt;/a&gt; webmentions and focus on webmentions from blogs.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I ain’t the boss of you, so you do what you will. &lt;a href=&quot;https://chrismcleod.dev/blog/some-words-on-webmentions/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content
    >
  </entry>
  <entry>
    <title>Adventures in Containerising a Legacy PHP App for Azure</title>
    <link href="https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/" />
    <updated>2024-02-06T22:27:00Z</updated>
    <id>https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;h2 id=&quot;context&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#context&quot;&gt;Context&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For the last 18 years or so I’ve been looking after a small, bespoke, PHP application for a small local business. It started off as a “hey, can you help someone I know with a problem? There might be something in it for you…” at my then-employer and all these years later it’s still going.&lt;/p&gt;
&lt;p&gt;It’s best described as a “seasonal app”; once a year I’ll be asked to do a little bit of work on it - a combination of tweaks and data loading, then it will be used moderately for a few months. Sometimes during that time I’ll need to provide the occasional bit of support or bugfix. The owners don’t really think of the app outside of this timeframe and the “budget” is essentially operating expenses and a kickback to me for my time. That is to say, the core of the app is largely the same foundations as when it was first written.&lt;/p&gt;
&lt;p&gt;the app has been chugging along on a succession of DigitalOcean droplets for most of this time, somehow needing a more powerful spec every few years despite not becoming any more complex of an application. However, the major concern has been the increasing amount of hoop-jumping needed to keep it running on a new server. Without any real upgrades to speak of, it’s running old versions of the frameworks and libraries it’s built with, and these pretty much flat-out don’t work on PHP 8+. Without the time or budget to migrate to newer frameworks or fix the bugs, this is a major risk for future server moves. After a painful database crash last year, that nearly wiped out everything during “peak time” I was looking for a way to preserve the app as it is now, but allow me to run it in a less fragile manner.&lt;/p&gt;
&lt;p&gt;On top of this was a need to setup a proper local development environment that was consistent with “prod” and ran on Windows.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;So, what’s a aging, put-upon, web developer to do with a legacy application needing modernised, and little to no budget or time to do it in? Well, this sounds exactly like a scenario straight out of a “why you should use containers” sales pitch.&lt;/p&gt;
&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://chrismcleod.dev/assets/images/TERSueD2T1-320.avif 320w, https://chrismcleod.dev/assets/images/TERSueD2T1-570.avif 570w, https://chrismcleod.dev/assets/images/TERSueD2T1-820.avif 820w&quot; sizes=&quot;(min-width: 55rem) 820px, 100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://chrismcleod.dev/assets/images/TERSueD2T1-320.webp 320w, https://chrismcleod.dev/assets/images/TERSueD2T1-570.webp 570w, https://chrismcleod.dev/assets/images/TERSueD2T1-820.webp 820w&quot; sizes=&quot;(min-width: 55rem) 820px, 100vw&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;https://chrismcleod.dev/assets/images/TERSueD2T1-320.jpeg 320w, https://chrismcleod.dev/assets/images/TERSueD2T1-570.jpeg 570w, https://chrismcleod.dev/assets/images/TERSueD2T1-820.jpeg 820w&quot; sizes=&quot;(min-width: 55rem) 820px, 100vw&quot; /&gt;&lt;img src=&quot;https://chrismcleod.dev/assets/images/TERSueD2T1-820.jpeg&quot; width=&quot;820&quot; height=&quot;1108&quot; alt=&quot;It works on my machine / Then we&#39;ll ship your machine / And that is how Docker was born&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;&quot; /&gt;&lt;/picture&gt;&lt;/figure&gt;
&lt;h2 id=&quot;act-1-running-locally&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#act-1-running-locally&quot;&gt;Act 1: Running Locally&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I figured the best way to start was with a working, containerised, local environment, then adapt that to go into production. Ideally I’d split things up so there was the entry-point to the app (NGINX) in one container, and the processing part (PHP-FPM) in a second container. Locally I’d also need a MySQL database container, though the plan was to have a managed database in prod. To begin with, I set my initial project structure:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
├── conf
│   ├── nginx
│   ├── other
│   └── php-fpm
├── data
├── src
├── docker-compose.local.yml
└── docker-compose.yml&lt;/code&gt;&lt;/pre&gt;
&lt;ul class=&quot;list&quot;&gt;
&lt;li&gt;conf/* holds any configuration files I need to add to the containers&lt;/li&gt;
&lt;li&gt;data is where MySQL data will be persisted&lt;/li&gt;
&lt;li&gt;src is where the application source code will go&lt;/li&gt;
&lt;li&gt;the 2 docker-compose files were placeholders; docker-compose.local.yml is for the local environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Getting the local environment was, ultimately, reasonably straightforward. The NGINX container ended up being the official, Alpine-based, image. For PHP-FPM, I needed to create a custom Dockerfile based off an official PHP 7 image, then add a few extensions and PHP Composer. Then wire it up in the docker-compose file with suitable environment variables. The resulting file ended up more-or-less like this (some details changed to keep it anonymous):&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3.8&quot;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;alpine
    &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;8080:80&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./conf/nginx/site.conf&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/etc/nginx/conf.d/default.conf
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./src&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/var/www/html
    &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx
  &lt;span class=&quot;token key atrule&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; mysql&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; mysupersecurerootpwd&lt;span class=&quot;token tag&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3306:3306&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./data/db&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/var/lib/mysql
    &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;mysql
  &lt;span class=&quot;token key atrule&quot;&gt;php&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./src
      &lt;span class=&quot;token key atrule&quot;&gt;dockerfile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Dockerfile
    &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./src&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/var/www/html
    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;CI_ENV&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; development
      &lt;span class=&quot;token key atrule&quot;&gt;CI_DB_HOST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; db
      &lt;span class=&quot;token key atrule&quot;&gt;CI_DB_USER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; local&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;user
      &lt;span class=&quot;token key atrule&quot;&gt;CI_DB_PASS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; local&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;pwd
      &lt;span class=&quot;token key atrule&quot;&gt;CI_DB_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev_web
    &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;php&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No, those passwords aren’t the real thing 😉&lt;/p&gt;
&lt;p&gt;The gist of this configuration is that it runs the services, using volumes to link certain files or folders into the containers as necessary to enable things to run: configuration files, data folders, source code. Linking the app source code into the containers like this means I don’t need to rebuild the containers every time I make changes - everything will update in real time. I can run the application and edit it. So far, so good! This might be easier than I feared! I wonder what happens when I try to deploy it?&lt;/p&gt;
&lt;h2 id=&quot;act-2-attempting-deployment-aka-the-clown-show-begins&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#act-2-attempting-deployment-aka-the-clown-show-begins&quot;&gt;Act 2: Attempting Deployment, aka, The Clown Show Begins&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Obviously I couldn’t deploy as-is - where would the source code go? Changes will be needed.&lt;/p&gt;
&lt;p&gt;At this point I should explain what my deployment plans were: you’ll have guessed from the title that Azure is involved, but more specifically, my initial plan was to use the Container Apps. The idea was to deploy an NGINX container as the main app container, and have PHP-FPM as a supporting container. Each container would be packaged up with a copy of the app source code so that requests could be routed and processed as required&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. Microservices, &lt;em&gt;yeah&lt;/em&gt;!&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt; I knew running multiple containers as part of one Container App “app” was possible, because I’d seen all the options when I’d created some other container apps.&lt;/p&gt;
&lt;p&gt;With this plan in mind I created some new Dockerfiles to package up the required configs and source code for each container, built the images and pushed them up into an Azure Container Registry. So far, &lt;em&gt;somewhat&lt;/em&gt; so good. Let’s try to spin up the Container App…&lt;/p&gt;
&lt;p&gt;My friends: I had not understood how Container Apps works. Communication between multi-container setups is possible, but only using HTTPS or Dapr. Not over, say, port 9000 for FastCGI connections. However, I did not realise this at first, so spent approximately 6 hours over 2 evenings trying to make it work: tweaking configurations in Azure and in the containers, rebuilding and pushing new versions of the images. It was exhausting and frustrating.&lt;/p&gt;
&lt;p&gt;Then I tried to run the multi-container setup in the regular App Service - it’s possible to give App Service a docker-compose file and have it deploy everything for you, and it runs somewhat similarly to on a regular computer. Only App Service just flat out refused to deploy the containers, so I tore it down and abandoned that plan because I was starting to feel quite aggravated.&lt;/p&gt;
&lt;p&gt;It was at this point I realised I was going to have to bundle everything into one container image and run that instead of the two-container solution.&lt;/p&gt;
&lt;h2 id=&quot;act-3-return-of-the-clown&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#act-3-return-of-the-clown&quot;&gt;Act 3: Return of the Clown&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So, a single container it is then. That’s not too difficult to pull together? And it wasn’t, really. Took keep things as small as possible I started with the PHP:7-fpm-alpine&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt; image, added the dependencies I needed, added NGINX, added the source code and configs, tested it with the local database, and it… worked! The app loaded, features worked, and generally everything seemed A-OK.&lt;/p&gt;
&lt;p&gt;So I decided to test with the Production database, an Azure MySQL Flexible Server. MySQL on Azure requires using an SSL connection, so no problem: I downloaded the relevant certificate, added it to the container build, configured the connection in the app and started everything up again.&lt;/p&gt;
&lt;p&gt;Things didn’t just break, they broke so badly PHP would segfault the instant it tried to connect to the database. No error, no logs, just no response. Not just in the app itself, oh no; I tried manually connecting using the PHP interactive environment (&lt;code&gt;php -a&lt;/code&gt;) bypassing any library interference and still it would crash and burn. Delving into the logs using &lt;code&gt;dmesg | grep segfault | tail -n 10&lt;/code&gt; revealed the problem was “error 4 in libssl.so.1.1”. Noting this library wasn’t in the list of dependencies I’d added to the Dockerfile, I initially tried explicitly installing it and rebuilding. No dice.&lt;/p&gt;
&lt;p&gt;After a lot of &lt;s&gt;googling&lt;/s&gt; duck-duck-go-ing(?) I learned that there might be an incompatibility between the version of libssl linked to some of the libraries I was using and other components of Alpine. By this point I was tired, wanted the whole ordeal over with, and I didn’t have the energy to start recompiling libraries left and right, so I stopped paying attention and decided to give up on an Alpine image.&lt;/p&gt;
&lt;h2 id=&quot;act-4-old-faithful-to-the-rescue&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#act-4-old-faithful-to-the-rescue&quot;&gt;Act 4: Old Faithful To The Rescue&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Debian. Good old, reliable, Debian. It might be old, uncool, and slightly bigger than we’d like (to all three of which I can relate) but it’s rarely let me down.&lt;/p&gt;
&lt;p&gt;Starting the consolidated container image all over again, I went with PHP:7-fpm as the base, which is in turn based on the not-slimmed-down version of Debian. I briefly tried the slim version but it threw an error when running a simple &lt;code&gt;apt-get update&lt;/code&gt; so I immediately gave up on the idea. After a bit of tweaking to get config files in the right place - because of course different images have different ideas of where things go - I rebuilt and run the container locally &lt;em&gt;again&lt;/em&gt; and got an application error! &lt;a href=&quot;https://www.poetryfoundation.org/poems/42916/jabberwocky&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Callooh! Callay!&lt;/a&gt; It was progress. I can work with an application error, as it means there’s something I can fix.&lt;/p&gt;
&lt;h2 id=&quot;act-5-the-final-boss&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#act-5-the-final-boss&quot;&gt;Act 5: The Final Boss&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The problem was thus: the application was seemingly opening 2 database connections; one was successful, the other was failing to use SSL. I tracked this down to the main framework using its’ own database code (using the MySQLi driver) for session management, and all other database operations going through an ORM which used a separate connection. After a bit of trial and error I figured the ORM could be so old it might not have implemented the necessary bits and pieces to make an SSL connection. But in investigating this I discovered it was at least possible to pass an existing PDO-based connection into the ORM and it would use that. The docs suggested it would have reduced functionality but core features would work - which was all I needed. After a bit more spelunking I determined that 1) it was possible to use a PDO driver with the main framework (and that it still worked with SSL), and 2) that once setup I could access that connection and pass it to the ORM.&lt;/p&gt;
&lt;h2 id=&quot;act-6-deployment-but-for-real-this-time&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#act-6-deployment-but-for-real-this-time&quot;&gt;Act 6: Deployment, But For Real This Time?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With the necessary code changes made, and the container rebuilt, it was time to try deployment again. A new Container Apps instance was setup, all the secrets and other configuration entered, and the image deployed. After a moment for everything to spin up I checked the auto-generated URL to see if it worked - SUCCESS!&lt;/p&gt;
&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://chrismcleod.dev/assets/images/bzn7Qy1oeV-320.avif 320w, https://chrismcleod.dev/assets/images/bzn7Qy1oeV-570.avif 570w&quot; sizes=&quot;(min-width: 55rem) 820px, 100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://chrismcleod.dev/assets/images/bzn7Qy1oeV-320.webp 320w, https://chrismcleod.dev/assets/images/bzn7Qy1oeV-570.webp 570w&quot; sizes=&quot;(min-width: 55rem) 820px, 100vw&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;https://chrismcleod.dev/assets/images/bzn7Qy1oeV-320.jpeg 320w, https://chrismcleod.dev/assets/images/bzn7Qy1oeV-570.jpeg 570w&quot; sizes=&quot;(min-width: 55rem) 820px, 100vw&quot; /&gt;&lt;img src=&quot;https://chrismcleod.dev/assets/images/bzn7Qy1oeV-570.jpeg&quot; width=&quot;570&quot; height=&quot;243&quot; alt=&quot; A meme based on a scene from the 1995 James Bond movie GoldenEye. The person in the image is Boris Grishenko, a Russian hacker and one of the villains in the movie. He is played by actor Alan Cumming. He is standing in triumph, fists raised, and shouting &#39;Yes! I am invincible!&#39;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;&quot; /&gt;&lt;/picture&gt;&lt;/figure&gt;
&lt;p&gt;I’m not going to lie, I punched the air at this point, it had been such a slog. Cautiously I added the custom domain to ensure it all still worked under a new URL. Thankfully it did, and finally the app was containerised and fully switched over to its new cloud-based home.&lt;/p&gt;
&lt;p&gt;Container Apps runs with a serverless model, so instead of paying a flat fee for the app to go unused during “off-season”, like I was with the droplet, it will instead spin down after it’s not used for a while. Spinning up again seems to take ~20-30 seconds on the first load, but that’s acceptable enough, and it’s plenty fast once it’s up and running.&lt;/p&gt;
&lt;p&gt;Deploying any future bug fixes will be a case of running a few commands to build a new image, push it to the registry, then create a new revision in Container Apps. This can all be done through the CLI, so I should be able to script and automate it to some degree.&lt;/p&gt;
&lt;p&gt;While it was - frankly - a pain in the arse to get this app containerised, I’m glad I got it done. The new home on Azure gives me some real benefits above the two mentioned above, and maybe buys some time to get a fully modernised version in the works, as I won’t have to worry about aging out of compatibility any more or patching up the aging infrastructure any more. In an ideal world I’d have done this exercise sooner and used this year to start modernising it properly - but hindsight is a wonderful teacher!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Look, I’m not proud of how much I’ve been winging it with this app for the last couple of years. &lt;a href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;This should probably have been my first clue that this approach was not the right one. &lt;a href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;No, this isn’t actually Microservices. &lt;a href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I actually started with a base Alpine:latest image but had to jump back so many versions just to get PHP 7 libraries installing that I figured I was as well using the official PHP image even though it’s a year old. &lt;a href=&quot;https://chrismcleod.dev/blog/adventures-in-containerising-a-legacy-php-app-for-azure/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content
    >
  </entry>
  <entry>
    <title>Visiting Websites</title>
    <link href="https://chrismcleod.dev/blog/visiting-websites/" />
    <updated>2024-01-30T09:55:00Z</updated>
    <id>https://chrismcleod.dev/blog/visiting-websites/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;I love my RSS reader and the mountain of feeds it lets me read/skim on a daily basis. But some recent discussion gave me pause on how I use RSS, and made me reflect on whether it strips something fundamental from the web: visiting cool websites.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://darthmall.net/weblog/2023/rss/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Evan wrote about this in November&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I think RSS is great, but I sometimes feel like it’s at odds with one of my favorite things about the web. For one thing, I see less of my friends’ cool websites. A lot of the websites in my feed reader are really nice to look at. Some of their owners do periodic redesigns which are fun to see. I feel like I’m missing out a little on these lovely personal websites because I nearly always interact with them through my feed reader, which flattens all of the personality into a minimal, legible view.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.gr36.com/2024/01/29/visit-more-blogs.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Greg picked up this idea yesterday&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;this seemed like a crazy idea, but the more I thought about it, the more it made perfect sense. I read 99% of the blogs I follow in my &lt;a href=&quot;https://www.gr36.com/2022/12/14/matter-my-most.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;favourite app Matter&lt;/a&gt;. Which is great in that it boils websites down to the basic content and makes it easier to read. However, it removes all personality and expression from personal websites.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As an aside, I’d argue that the prevalence of “mobile” reading experiences also do a lot to strip the personality out of websites too, not just RSS or Read-It-Later services. It’s hard to be whimsical when you’re just a single screen-wide column of text. Maybe that’s just me?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.chamline.net/visiting-cool-websites/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Habib also wrote on this topic&lt;/a&gt;, and how he uses a feature in Reeder to get around this problem:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As much as I enjoy the Reeder reading experience within the app, I enjoy reading the content even more on the publisher’s website or blog. Thankfully, Reeder makes this process easy; once I hit the title of the post from the list it launches the content, and a simple swipe right to left launches the content on the publisher’s site using the In-app safari browser. Depending on the article — if long-form posts — I can tap on the article title link to launch the content in the default mobile Safari browser, bypassing the need to swipe right to left.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For myself, I know I’m guilty of just chucking a site into Inoreader and forgetting about visiting again unless I need to find something so deep in the archives that I can’t get it through RSS any more. I’d like to change that behaviour over time. I remember when we would “make the rounds” to the sites on our blogrolls or in our webrings. In part this was because there were just a lot less sites to follow/visit in those days, and also in part it was because you had to go to the site to leave a comment or similar to give your thoughts to the author. You could write a post on your own site but there was no guarantee they would ever see it.&lt;/p&gt;
&lt;p&gt;Regardless of &lt;em&gt;why&lt;/em&gt; you had to visit, you did, and it meant you’d see all the (frequent) redesigns, the new widgets and experiments, the new links in the sidebar you could explore after your visit was concluded.&lt;/p&gt;
&lt;p&gt;I feel the nostalgia is pulling me away from the point I was trying to get to, so let’s try to get back on track!&lt;/p&gt;
&lt;p&gt;I want to change this behaviour of only viewing sites through the lens of a feed reader (or similar). I want to visit sites more routinely when they are updated. As Evan put it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I wonder what the alternative looks like. A tool that helps you remember the sites you like to visit so that you can browse them at your leisure, but that doesn’t create a commitment to read—or at least look at—absolutely everything that is published on all of those sites.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I don’t know what it was, but this dragged &lt;a href=&quot;https://fraidyc.at/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;FraidyCat&lt;/a&gt; to the surface of my memory. FraidyCat is an alternative take on following websites, &lt;a href=&quot;https://www.kickscondor.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;created by Kicks Condor&lt;/a&gt;. It’s exactly what Evan describes: It keeps track of websites you want to follow, lets you see when they were last updated, but crucially for this purpose &lt;em&gt;links to the website instead of loading the feed content&lt;/em&gt;. If you want to read the post then you have to click through. There are no notifications to make you read everything to clear a badge, and you can organise sites by how important they are to you.&lt;/p&gt;
&lt;p&gt;I’ll admit that I never quite managed to “get” FraidyCat when I tried it out a few years back, but I’m going to give it another, more concerted try. I need to figure out my organisation structure and import some sites into it. It still might not stick. But if it gets me visiting websites again, and not only reading their feeds, then it will have been a worthwhile exercise.&lt;/p&gt;
</content
    >
  </entry>
  <entry>
    <title>Adding a Bookmarks Page</title>
    <link href="https://chrismcleod.dev/blog/adding-a-bookmarks-page/" />
    <updated>2024-01-29T17:00:00Z</updated>
    <id>https://chrismcleod.dev/blog/adding-a-bookmarks-page/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;I mentioned over on &lt;a href=&quot;https://theunderground.blog/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;The Underground&lt;/a&gt; that one thing I wanted to add to my site, to “do more” with it, was a space to collect bookmarks and fun links I com across. Well, I’ve added a quick and dirty “first draft” of the feature over on the new &lt;a href=&quot;https://chrismcleod.dev/bookmarks/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;/bookmarks&lt;/a&gt; page. It’s even got its &lt;a href=&quot;https://chrismcleod.dev/bookmarks/feed.xml&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;own feed&lt;/a&gt; you can use to follow along at home.&lt;/p&gt;
&lt;p&gt;I’d had the idea for the page a while ago, but the real motivation to get it done came with a recent push to get more people sharing links they’d enjoyed on their website more often (See, for example: &lt;a href=&quot;https://blog.cassidoo.co/post/human-curation/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Cassidy&lt;/a&gt;, &lt;a href=&quot;https://hidde.blog/sharing-links/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Hidde&lt;/a&gt;, and &lt;a href=&quot;https://joanwestenberg.com/blog/curation-is-the-last-best-hope-of-intelligent-discourse&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Joan&lt;/a&gt;). I wanted to have a way of sharing these links that in a way that didn’t need me to dedicate a whole blog post to them&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/adding-a-bookmarks-page/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, giving me the opportunity to be more spontaneous about it.&lt;/p&gt;
&lt;h2 id=&quot;fun-technical-notes&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/adding-a-bookmarks-page/#fun-technical-notes&quot;&gt;Fun(?) Technical Notes:&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When I add a bookmark it should trigger a webmention to the page in question (if it supports webmentions), and if the other site supports microformats then it should show up as an IndieWeb “bookmark-of”. This was by far the most fiddly bit to get right.&lt;/p&gt;
&lt;p&gt;In terms of adding bookmarks - I’m using a combination of IndieKit and a “bookmark on my site” iOS shortcut I made using the guide I wrote a few years back: &lt;a href=&quot;https://chrismcleod.dev/blog/indieweb-sharing-with-ios-shortcuts/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;IndieWeb Sharing with iOS Shortcuts&lt;/a&gt; . Hurrah for blog archives, otherwise I’d have had to figure this out again from scratch! I have a few minor niggles to iron out, but adding bookmarks is overall pretty easy for me to do.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I am thinking about automating some sort of “weekly round-up post” that will include bookmarks + other content I add to the site. But not yet, as I don’t quite have everything in place as I’d like it. &lt;a href=&quot;https://chrismcleod.dev/blog/adding-a-bookmarks-page/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content
    >
  </entry>
  <entry>
    <title>Generative AI for Blogging: Revisited</title>
    <link href="https://chrismcleod.dev/blog/generative-ai-for-blogging-revisited/" />
    <updated>2024-01-26T09:34:00Z</updated>
    <id>https://chrismcleod.dev/blog/generative-ai-for-blogging-revisited/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;About 6 months ago I did an experiment with using generative AI tools to write blog posts. It was part of a wider “understand if this stuff is useful and what its limitations are” exercise I needed to go through for work; it’s a hot topic and I needed to understand it - which involved using it, basically. I usually learn best by doing&lt;/p&gt;
&lt;p&gt;In the end I wrote 3 posts using “GenAI”, using a couple of different methods (ranked in order from least satisfying result to “most satisfying”):&lt;/p&gt;
&lt;ol class=&quot;list&quot;&gt;
&lt;li&gt;“AI take the wheel” - input a prompt, look at the output, refine the prompt until it was closer to what I wanted. Make slight tweaks to spelling and grammar. Post. This was the quickest way of using the tools. Partly done this way because I was in &lt;a href=&quot;https://chrismcleod.dev/blog/come-on-barbie-lets-go-party/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;the process of leaving the cinema&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;“Fill out this first draft outline” - write my own outline with some first draft points, then have the tools rewrite and expand on it. Again, make minor edits for clarity/remove Americanisms. &lt;a href=&quot;https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;This one was sorta-okay, but lacks polish and has a weird, disjointed tone and writing style&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;“Let’s collaborate” - the most involved process, taking much, much longer to write than if I’d just done it myself. More details in this post: &lt;a href=&quot;https://chrismcleod.dev/blog/how-i-use-generative-ai-to-write-blog-posts/&quot;&gt;How I use Generative AI to help write blog posts&lt;/a&gt;, which is actually the post which falls into this category.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And then, I promptly (albeit subconsciously&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/generative-ai-for-blogging-revisited/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;) decided using AI to help write blog posts wasn’t really for me, and I’ve never used it this context again, other than to check a couple of technical points in an upcoming post. It’s okay for search-like applications, so long as you make sure to confirm the citations say what it claims they say.&lt;/p&gt;
&lt;p&gt;When I was importing my historical posts I made a point of re-reading a &lt;em&gt;lot&lt;/em&gt; of my own posts, which ended up including the “AI-generated” posts. They stood out like a sore thumb, with the exception of the “collaborative” one. Honestly, reading them now makes me itch. They quite clearly aren’t written by me, and while I don’t regret doing the experiment or trying out the technology, I do regret publishing them.&lt;/p&gt;
&lt;p&gt;For the one that is okay-enough it doesn’t invoke an allergic reaction: was it worth it? Possibly, but probably not. It took &lt;em&gt;hours&lt;/em&gt; to put that post together - far more than I would normally spend on something of a similar length&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/generative-ai-for-blogging-revisited/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. Part of this could be put down to still being still not fully familiar with the inner-loop of writing with AI-assistance - and thus doing things in a sub-optimal manner - or by having to check technical details, but a damn lot of it was because I &lt;em&gt;had to rewrite so much stuff&lt;/em&gt;. I’ve been writing for myself for long enough I have a process I go through, as imperfect as it may be, and bringing a collaborator into that process was a &lt;em&gt;chore&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So, as it stands I am unlikely to be using generative AI tools to write or co-author my blog posts going forward. Of the 3 posts that were written in this way, I don’t feel it’s right to delete them now I’ve referenced here, but I am going to label them as having used AI and point to this post by way of explanation. I’ve seen people add “written by a human” to the end of their blog posts, so it feels only right to do similar for the inverse. If I do ever consciously involve The Machine again, I will make sure to label those posts too.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Honestly, I hadn’t really realised until today I was no longer using this thing I’d written a lengthy post about using. &lt;a href=&quot;https://chrismcleod.dev/blog/generative-ai-for-blogging-revisited/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;The latest post on The Underground is ~50% longer and took around about two-thirds of the time to write, for example. &lt;a href=&quot;https://chrismcleod.dev/blog/generative-ai-for-blogging-revisited/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content
    >
  </entry>
  <entry>
    <title>Sending Webmentions after Deploying to Azure Static Web Apps</title>
    <link href="https://chrismcleod.dev/blog/sending-webmentions-after-deploying-to-azure-static-web-apps/" />
    <updated>2024-01-21T10:51:00Z</updated>
    <id>https://chrismcleod.dev/blog/sending-webmentions-after-deploying-to-azure-static-web-apps/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;I recently switched this site from Netlify to &lt;a href=&quot;https://azure.microsoft.com/en-gb/products/app-service/static/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Azure Static Web Apps&lt;/a&gt; (SWA). For the most part it just worked - setup the site in the Azure portal, switch the DNS… done. You probably didn’t notice.&lt;/p&gt;
&lt;p&gt;There were a couple of things which didn’t immediately work because they were set up in a Netlify-specific way: HTTP headers, redirects, and sending webmentions after deploying a new version of the site. HTTP headers and redirects can be handled in &lt;a href=&quot;https://learn.microsoft.com/en-gb/azure/static-web-apps/configuration&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;the SWA configuration file&lt;/a&gt;, so thy were easy enough to fix. &lt;a href=&quot;https://rknight.me/blog/what-even-is-a-webmention/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Webmentions&lt;/a&gt; was looking to be a bit more tricky.&lt;/p&gt;
&lt;p&gt;On Netlify I was able to create a post-deployment event which called a webhook. This action called&lt;a href=&quot;https://remysharp.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; Remy’s&lt;/a&gt; &lt;a href=&quot;https://webmention.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Webmention.app service&lt;/a&gt; to scan the site feed and send to any discovered webmention targets. Unfortunately, SWA doesn’t have an obviously comparable feature, so I thought I was going to be stuck webmention-less for the foreseeable future.&lt;/p&gt;
&lt;p&gt;Thankfully, Sophie &lt;a href=&quot;https://localghost.dev/blog/sending-webmentions-from-a-static-site/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;posted a guide on how she sends webmentions from her static site&lt;/a&gt;. This reminded me that Remy also provides a CLI version of the Webmention tool, as an NPM package, that doesn’t rely on the web service. And then, to top it off, she posted &lt;a href=&quot;https://localghost.dev/blog/how-i-deploy-my-eleventy-site-to-neocities/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;how she deploys her Eleventy site to Neocities&lt;/a&gt; and reminded me of something else that let me put everything together: GitHub Actions.&lt;/p&gt;
&lt;p&gt;Hooking SWA up to your GitHub repository creates a GitHub Actions workflow file in your repository. This file handles the building of your site and deployment to SWA. GitHub Actions workflows can run commands, so I could use the CLI webmention tool after the auto-generated build-and-deploy step. All I needed to do was get the configuration right.&lt;/p&gt;
&lt;p&gt;After a bit of tinkering with the CLI running in “dry-run” mode, the addition to my workflow file looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Webmentions
        &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; webmentions
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          npx -y @remy/webmention dist/feed.xml --limit 1 --send&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Other than this addition, my workflow file is the default generated by the Azure portal. For context, my complete workflow file now looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Azure Static Web Apps CI/CD

&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; develop
  &lt;span class=&quot;token key atrule&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;opened&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; synchronize&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reopened&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; closed&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; develop

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;build_and_deploy_job&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.event_name == &#39;push&#39; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; (github.event_name == &#39;pull_request&#39; &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; github.event.action &lt;span class=&quot;token tag&quot;&gt;!=&lt;/span&gt; &#39;closed&#39;)
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Build and Deploy Job
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v3
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;submodules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;lfs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Build And Deploy
        &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; builddeploy
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Azure/static&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;web&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;apps&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;deploy@v1
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;azure_static_web_apps_api_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $
          &lt;span class=&quot;token key atrule&quot;&gt;repo_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $ &lt;span class=&quot;token comment&quot;&gt;# Used for Github integrations (i.e. PR comments)&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;upload&#39;&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;###### Repository/Build Configurations - These values can be configured to match your app requirements. ######&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;app_location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# App source code path&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;api_location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Api source code path - optional&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;output_location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/dist&#39;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Built app content directory - optional&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;###### End of Repository/Build Configurations ######&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Webmentions
        &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; webmentions
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          npx -y @remy/webmention dist/feed.xml --limit 1 --send&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;close_pull_request_job&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.event_name == &#39;pull_request&#39; &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; github.event.action == &#39;closed&#39;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Close Pull Request Job
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Close Pull Request
        &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; closepullrequest
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Azure/static&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;web&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;apps&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;deploy@v1
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;azure_static_web_apps_api_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $
          &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;close&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content
    >
  </entry>
  <entry>
    <title>Doing More With What You Already Have</title>
    <link href="https://chrismcleod.dev/blog/doing-more-with-what-you-already-have/" />
    <updated>2024-01-20T16:48:00Z</updated>
    <id>https://chrismcleod.dev/blog/doing-more-with-what-you-already-have/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;I’ve just posted the first post on &lt;a href=&quot;https://theunderground.blog/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;The Underground&lt;/a&gt; for the New Year; you’ll need to be subscribed to&lt;a href=&quot;https://theunderground.blog/feed.xml&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; the RSS feed&lt;/a&gt; to read the full thing, but I wanted to share my favourite part of the post here, for posterity:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An aside: I hesitate to air the nostalgia reason; a lot of the current &lt;a href=&quot;https://www.anildash.com/2024/01/03/human-web-renaissance/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;resurgence&lt;/a&gt; of the small/indie/open web is driven by &lt;a href=&quot;https://joanwestenberg.medium.com/i-miss-the-internet-c7e41544a8b9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;a nostalgia&lt;/a&gt; for &lt;a href=&quot;https://www.anildash.com/2012/12/13/the_web_we_lost/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;what we had&lt;/a&gt; before, and ultimately I’d like us collectively to move past that nostalgia and start imagining new ways of using the web on a personal level.&lt;/p&gt;
&lt;p&gt;But. But but &lt;em&gt;but&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I can’t deny there is an element of nostalgia driving this desire to do more with my own website. Way, way, way back… back before we standardised on blogs, I had a website and it was a melting pot of &lt;em&gt;everything&lt;/em&gt; I was interested in; it was a miniature “hub” online for me and my “IRL” friends (one of several hubs we used, all run by someone we knew in person). There was a feature where people could leave messages for each other. Meetups were arranged in the comments. If I decided I wanted to experiment with using maths to drive Adobe Flash animations, well I guess now there’s a new gallery page for that. Photo dumps of nights out before we started using Facebook for that purpose - before Facebook existed, even! Reviews of local band gigs that were as much about promoting those bands and venues as anything else, because I was in love with that world and wanted to share it… in short: anything I could think about putting online I experimented with doing so. Some of it didn’t work and got pulled down again quickly, and most of it would almost certainly make me cringe now - but isn’t that part of the point of being young? To embarrass your older self with how carefree you were?&lt;/p&gt;
&lt;p&gt;Now, I accept the world and I are very different in 2024 to how we were in 1999-2002, so it will never be exactly the same. But the ethos still clings to me. I still believe - even if my aging memory needs reminded and pushed into action - that our websites could and should be a reflection of everything we want to share about ourselves. Your website &lt;em&gt;could&lt;/em&gt; be your central hub on the web, instead of one part in a network of profiles across dozens of sites. It could be something so deeply &lt;em&gt;you&lt;/em&gt;, that even though you post prolifically in many places, it’s your website people remember about and read first.&lt;/p&gt;
&lt;/blockquote&gt;
</content
    >
  </entry>
  <entry>
    <title>Adding Site Search to Eleventy with &lt;pagefind-search&gt;</title>
    <link href="https://chrismcleod.dev/blog/adding-site-search-eleventy-pagefind-web-component/" />
    <updated>2024-01-07T15:27:00Z</updated>
    <id>https://chrismcleod.dev/blog/adding-site-search-eleventy-pagefind-web-component/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;I recently added some new ways to dig through the &lt;a href=&quot;https://chrismcleod.dev/blog/&quot;&gt;archives of this site&lt;/a&gt;, and chief among them was search. Search is something I’ve wanted to add for ages, and thankfully it was pretty straightforward, thanks to &lt;a href=&quot;https://pagefind.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Pagefind&lt;/a&gt; and the &lt;a href=&quot;https://www.zachleat.com/web/pagefind-search/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&amp;lt;pagefind-search&amp;gt; web component by Zach Leatherman&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;installation&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/adding-site-search-eleventy-pagefind-web-component/#installation&quot;&gt;Installation&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Installing the component starts with adding it to our &lt;code&gt;package.json&lt;/code&gt; as a regular dependency (&lt;em&gt;not&lt;/em&gt; a DevDependency as you might be used to for Eleventy):&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--save&lt;/span&gt; @zachleat/pagefind-search&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the package added, we need to add a reference to it into a JavaScript file referenced in our site/page HTML. The specifics of how you do this will vary depending on your setup/bundling process. For me, as I am using &lt;a href=&quot;https://eleventy-excellent.netlify.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Eleventy Excellent&lt;/a&gt; for the basis of my site, the easiest way for me to do do this was to add an import to the top of &lt;code&gt;assets/scripts/app.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;@zachleat/pagefind-search&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* ... rest of existing file contents ... */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that out of the way, we can add the component to which ever page or template you want to use it in. For me, this was my &lt;a href=&quot;https://github.com/mcleodchris/next.chrismcleod.dev/blob/develop/src/_layouts/archive.njk&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;archive Nunjucks file&lt;/a&gt;, with the additions looking like so:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Search&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;pagefind-search&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;pagefind-autofocus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://duckduckgo.com/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;min-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3.2em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- min-height to reduce CLS --&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	  Search for:
	  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;search&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;q&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autofocus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Put your searchable domain here --&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;sites&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;chrismcleod.dev&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Search&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;pagefind-search&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is more-or-less a copy of Zach’s &lt;a href=&quot;https://www.zachleat.com/web/pagefind-search/#usage&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;example usage&lt;/a&gt;. The only difference is I’ve added the &lt;code&gt;pagefind-autofocus&lt;/code&gt; attribute and changed the fallback content to reference my domain. There are &lt;a href=&quot;https://github.com/zachleat/pagefind-search/#extend-pagefind-options&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;other attributes&lt;/a&gt; you can play with to customise the component further.&lt;/p&gt;
&lt;p&gt;With these steps done, we’re nearly ready, but the search will not work until we generate the Pagefind bundle and index our site.&lt;/p&gt;
&lt;h2 id=&quot;indexing-our-site&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/adding-site-search-eleventy-pagefind-web-component/#indexing-our-site&quot;&gt;Indexing our site&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run your regular build command for your site, to generate the static files to be indexed. Indexing requires running another command:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt; pagefind &lt;span class=&quot;token parameter variable&quot;&gt;--site&lt;/span&gt; dist&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;--site&lt;/code&gt; option should point to the output directory of your build process. For me, that’s the &lt;code&gt;dist&lt;/code&gt; directory. Pagefind will go off and do it’s thing, then report back what it’s indexed. The end of the output will look something like this:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Building search indexes&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
Total: 
  Indexed &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; language
  Indexed &lt;span class=&quot;token number&quot;&gt;520&lt;/span&gt; pages
  Indexed &lt;span class=&quot;token number&quot;&gt;10092&lt;/span&gt; words
  Indexed &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; filters
  Indexed &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; sorts

Finished &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4.393&lt;/span&gt; seconds&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything should be generated now, you can go ahead and serve your site locally to try it out!&lt;/p&gt;
&lt;p&gt;If you want to customise the Pagefind index, you should refer to &lt;a href=&quot;https://pagefind.app/docs/indexing/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;the documentation&lt;/a&gt;. Personally, I’ve added a couple of &lt;a href=&quot;https://pagefind.app/docs/indexing/#removing-individual-elements-from-the-index&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;attributes&lt;/a&gt; to my templates to indicate what should or shouldn’t be indexed.&lt;/p&gt;
&lt;p&gt;This is all great so far, but we should automate things so the index is generated on every build - especially if we use any sort of automated process to build and deploy our site. To do this, you’ll need to modify whatever command you use to build your site. For me, I modified the scripts section of my &lt;code&gt;package.json&lt;/code&gt; to this (I’ve omitted irrelevant lines, for clarity):&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;clean&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rimraf dist&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ELEVENTY_PRODUCTION=true run-s clean build:*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build:11ty&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;eleventy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build:index&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npx -y pagefind --site dist&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;build&lt;/code&gt; entry is the one used by Netlify when it generates and deploys my site from any updates checked into GitHub. The important bit that changed here, from what it was before, is the addition of &lt;code&gt;build:index&lt;/code&gt;. This adds the step to the build process which indexes the site.&lt;/p&gt;
&lt;h2 id=&quot;customising-the-display-optional&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/adding-site-search-eleventy-pagefind-web-component/#customising-the-display-optional&quot;&gt;Customising the display (Optional)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pagefind comes with its’ own UI CSS, which the &amp;lt;pagefind-search&amp;gt; component loads when initialising. The defaults are perfectly acceptable - they work well, and look good. They did unfortunately stick out a bit as “wrong” when I initially added the component, so I wanted to make some adjustments.&lt;/p&gt;
&lt;p&gt;It took me a while to figure out how to get the results to display in a way that was more like the rest of my site - namely the typography and element spacing. It’s far from perfect, and I want to do some more work on this, but the following should help you customise things a bit to your liking.&lt;/p&gt;
&lt;p&gt;The main issue you’ll face is the Pagefind stylesheet will probably load after your main site stylesheets - so if you, say, &lt;a href=&quot;https://pagefind.app/docs/ui-usage/#customising-the-styles&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;follow the documentation and override the styling using CSS variables&lt;/a&gt;, the default stylesheet will override this. Thankfully, we can use the cascade and specificity to make our overrides take effect.&lt;/p&gt;
&lt;p&gt;As mentioned, I use Eleventy Excellent, which uses some build tools to bundle up multiple CSS files into one. To keep things organised I added a new &lt;code&gt;search.css&lt;/code&gt; file in the &lt;code&gt;src/assets/css/blocks&lt;/code&gt; directory (&lt;a href=&quot;https://github.com/mcleodchris/next.chrismcleod.dev/blob/develop/src/assets/css/blocks/search.css&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;link&lt;/a&gt;).  Your site will probably differ in the specifics - you should add any CSS in the same way you normally would.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* src/assets/css/blocks/search.css */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.pagefind-ui&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;margin-block-start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--flow-space&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;1em&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-primary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--color-fg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--color-fg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--color-bg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--color-fg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-tag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--color-primary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-border-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--border-radius&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-image-border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--border-radius&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-image-box-ratio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3 / 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--font-base&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.pagefind-ui__result&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #eeeeee&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.pagefind-ui__button&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-primary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--color-fg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--color-bg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;margin-block-start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--space-s-m&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.pagefind-ui__button:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-primary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--color-bg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--pagefind-ui-background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--color-fg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.pagefind-ui__search-input:focus-visible&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2px solid &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--color-primary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important bit to begin with is the &lt;code&gt;.pagefind-ui&lt;/code&gt; rule declaration. This is where we define the new generic values for the PageFind variables, and define any properties which aren’t already set. If you want to override properties which &lt;em&gt;are&lt;/em&gt; set (and aren’t influenced by the variables), you’ll need to get more into the weeds I’m afraid - which is outside the scope of this post. Maybe later, once I’ve revisited this topic.&lt;/p&gt;
&lt;p&gt;The reason this works is that the default PageFind variables are set on the &lt;code&gt;:root&lt;/code&gt; pseudo-element, which is less specific than &lt;code&gt;.pagefind-ui&lt;/code&gt;. In my case I set the values to variables set by Eleventy Excellent’s own stylesheet, which lets the colours follow my implementation of Dark Mode.&lt;/p&gt;
&lt;p&gt;From here, it’s a case of finding the class for the right elements you want to modify, and overriding the variables or adding new properties. For me, this was adjusting the border between results, and making the “load more results” button more like the other buttons on my site. One thing to remember: any variables you don’t override will use the defaults - so if you’re happy with everything but the font, for example, you can set the value of &lt;code&gt;--pagefind-ui-font&lt;/code&gt; in &lt;code&gt;.pagefind-ui&lt;/code&gt;, and leave it at that.&lt;/p&gt;
</content
    >
  </entry>
  <entry>
    <title>I Hate iCloud For Windows</title>
    <link href="https://chrismcleod.dev/blog/i-hate-icloud-for-windows/" />
    <updated>2024-01-06T14:33:00Z</updated>
    <id>https://chrismcleod.dev/blog/i-hate-icloud-for-windows/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;Hate is maybe too strong a word, but I can’t think of a better one in the moment. I like to think it’s not often I’ll piss-and-moan about a bit of software (these days), but the iCloud app for Windows is just so, so, so bad I’ve finally lost my patience with it. All I wanted it to do was sync my photos to my laptop, and occasionally let me grab a file from iCloud Drive. But for something that should be so simple, it’s caused nothing but trouble.&lt;/p&gt;
&lt;p&gt;Every few weeks it would require logging into the app again with the full 2FA flow. Not in itself too bad, but at one point last year it decided it was going to get in an endless login loop despite nothing changing with my Apple account. This resulted in Apple’s systems locking me out (across all Apple devices) and forced me to reset the password through the web portal - so I then had to go login to every device again.&lt;/p&gt;
&lt;p&gt;Syncing photos was hit-or-miss. Sometimes it would take days for a photo to show up on my laptop. The recently improved &lt;a href=&quot;https://blogs.windows.com/windowsexperience/2022/11/09/windows-11-makes-it-easier-to-connect-to-your-icloud-photos-right-in-the-photos-app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;integration with the Photos app on Windows 11&lt;/a&gt; didn’t work for &lt;em&gt;weeks&lt;/em&gt; after I installed all the relevant updates and toggled the settings. Instead of the integrated view I just had an extra folder view - something I could have setup before the update 😅 It eventually started working.&lt;/p&gt;
&lt;p&gt;And for today’s grumble? I updated my Apple account email address several weeks ago; iCloud for Windows finally caught up to the fact and asked me to login again&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/i-hate-icloud-for-windows/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. &lt;em&gt;Only, it won’t let me edit the email address in the login prompt&lt;/em&gt;. Which means every login attempt fails. I’d have signed out and back in again, but the rest of the UI - including the sign out button - is locked behind the login prompt, leaving me stuck. With the memories of last year’s tomfoolery still fresh enough in my mind, I don’t want to risk getting locked out again, so just… gave up and uninstalled the application.&lt;/p&gt;
&lt;p&gt;For photo sync I’ve discovered I can achieve much the same result using the OneDrive app on my iPhone, and I can also use OneDrive to get files to my laptop. It all requires some extra hops, but at least it seems to work &lt;em&gt;reliably&lt;/em&gt;.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;probably because I just had to reboot for a system update. &lt;a href=&quot;https://chrismcleod.dev/blog/i-hate-icloud-for-windows/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content
    >
  </entry>
  <entry>
    <title>New Year, Old Posts</title>
    <link href="https://chrismcleod.dev/blog/new-year-old-posts/" />
    <updated>2024-01-01T23:32:00Z</updated>
    <id>https://chrismcleod.dev/blog/new-year-old-posts/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;In a case of history repeating itself not &lt;a href=&quot;https://chrismcleod.dev/blog/regarding-the-thoughtful-cultivation-of-the-archived-internet/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;once&lt;/a&gt; - but &lt;a href=&quot;https://chrismcleod.dev/blog/reclaiming-history&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;three&lt;/a&gt; &lt;a href=&quot;https://chrismcleod.dev/rewriting-history&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;times&lt;/a&gt;! - I’ve been working through the process of adding “historical” blog posts to my blog. These are posts I have written on previous iterations of my blog, in this case on several different domains, and stretch back over 20 years. Most of the earliest posts have been pulled from The Internet Archive, because younger me didn’t think about backups - not that any I’d taken would have lasted this long!&lt;/p&gt;
&lt;p&gt;It’s a work-in-progress, but I’ve added in the region of 470 posts today. Most of the links and images should be working, but there’s a high chance something will be broken; I’ll be doing regular sweeps to fix up anything I find. There are still over 2000 “short form” and other, more esoteric, posts to sort through. Most of these are some form of&lt;a href=&quot;https://indieweb.org/posts#Types_of_Posts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; IndieWeb “post kind”&lt;/a&gt; that will need a bit more work to bring across, and may require some further changes to the site itself to accommodate.&lt;/p&gt;
&lt;p&gt;Beyond fixing formatting/images, I’m purposefully not editing the content itself. Even though I’m a very different person than I was when I some of these posts were written, it feels like it would be wrong to start rewriting the past. Instead, I’m putting a big disclaimer at the top of any post that’s older than 3 years. I’m still fiddling with the exact wording, but if you look at &lt;a href=&quot;https://chrismcleod.dev/blog/page-1/&quot;&gt;any old post&lt;/a&gt; you should see a representative example.&lt;/p&gt;
&lt;p&gt;For context, I was still a teenager when I wrote the earliest post I’ve been able to recover, and I don’t think the word “blog” had caught on yet.&lt;/p&gt;
&lt;p&gt;In terms of technical process, it was very similar to when &lt;a href=&quot;https://chrismcleod.dev/blog/i-rebuilt-my-blog-and-didnt-write-about-it/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;I migrated this site from Wordpress to Eleventy&lt;/a&gt;. It just required a bit of extra work up front. I’d lost the Wordpress backups I’d made of my previous blog&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/new-year-old-posts/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, but still had a disk image backup of the server itself. I ended up using this backup to create a Digital Ocean droplet that represented the old server before I shut it down. With a few configuration tweaks I was able to access Wordpress and make a new backup of the site content. While the server was online, I also grabbed a copy of the uploaded media.&lt;/p&gt;
&lt;p&gt;With that kerfuffle out of the way, I followed the same steps as my last migration - it just ended up with an order of magnitude more posts to sort through and clean up! It quickly became apparent I was missing a lot of images from 2017 and earlier - roughly when I had made another, earlier server migration. Thankfully, I &lt;em&gt;did&lt;/em&gt; still have a backup of those images to hand, so just had to match up the files to the right place.&lt;/p&gt;
&lt;p&gt;So. Will this be the last time I go through this exercise? The Magic 8-Ball says “eh, probably not…” but if nothing else it’s been a great excuse to go back, read, and reflect on some of the earliest things I’ve written online and just how far I’ve come since then.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;There &lt;em&gt;is&lt;/em&gt; also a &lt;a href=&quot;https://mrkapowski.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;static HTML archived copy&lt;/a&gt; I could refer to, but that wouldn’t help much with getting the content into Markdown for Eleventy. It was useful reference for fixing post where the export was missing some data due to the Wordpress plugins I was using. &lt;a href=&quot;https://chrismcleod.dev/blog/new-year-old-posts/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content
    >
  </entry>
  <entry>
    <title>Certified: DevOps Institute Site Reliability Engineering Fundamentals</title>
    <link href="https://chrismcleod.dev/blog/certified-devops-institute-site-reliability-engineering-fundamentals/" />
    <updated>2023-12-17T20:11:00Z</updated>
    <id>https://chrismcleod.dev/blog/certified-devops-institute-site-reliability-engineering-fundamentals/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;It’s been a very busy week, so I’m only managing to blog about this now, even though it happened last weekend. Last Saturday (9th December) I sat and passed &lt;a href=&quot;https://www.devopsinstitute.com/certifications/sre-foundation/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;the Foundation-level DevOps Institute certification for Site Reliability Engineering&lt;/a&gt;. This was a bit of a spur-of-the-moment certification to sit; it was instigated by a random conversation with a manager (not one of my own) who brought up Site Reliability Engineering, and the need to have more knowledge of it within our area of the organisation. It was a topic I was aware of through my various DevOps studies, but not as anything more than a passing mention. In order to be able to start fleshing out our internal wikis with at least some basic information (that other, more experienced colleagues could then bulk out further), I started looking at some of the internal training resources for more information. Fast forward another 2 weeks, and I had an unexpected opportunity to go on a 2-day external training course that included a voucher for sitting the certification exam. Wanting to strike while the iron was hot, I scheduled the exam for the day after the training, and that was that!&lt;/p&gt;
&lt;p&gt;Having the prior DevOps knowledge definitely helped, but I wouldn’t say it was essential. It was mostly a benefit in terms of knowing some of the general processes and concepts. Overall, the topic wasn’t much of a jump from what I’d done before, and being a foundation level exam, was broad rather than deep in terms of knowledge needed.&lt;/p&gt;
&lt;p&gt;In terms of resources used, I think most of the concepts can be learned from the many &lt;a href=&quot;https://youtube.com/playlist?list=PLIivdWyY5sqJrKl7D2u-gmis8h9K66qoj&amp;amp;si=-9facxE9yC6LD2SD&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;videos on YouTube&lt;/a&gt; that cover SRE, and the books at &lt;a href=&quot;https://sre.google/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://sre.google/&lt;/a&gt;. I do, however, recommend the virtual classroom course run by QA, if your employer will pay for it. It’s 2 days, gives you everything you need to pass the exam, and includes a voucher for the exam itself (which costs £220 otherwise).&lt;/p&gt;
&lt;p&gt;The exam itself was fairly standard for this level of certification - 40 multiple choice questions (4 possible answers per question), about an hour to complete the exam, and a pass rate of 65% (or, in SRE terms, an error budget of 14 wrong answers 😅). Interestingly, if you have the official hardcopy training materials, it’s an open book exam. I did not have them, so I had to go off memory. Having only sat Microsoft exams through PearsonVUE, I found the DevOps Institute exam - run by &lt;a href=&quot;https://www.peoplecert.org/browse-certifications/devops/DevOps-13/sre-foundation-3782&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PeopleCert&lt;/a&gt; - to be interestingly different. The best way to describe it would be the check-in was more “hands-on”. Instead of uploading some pictures of my space and waiting for approval to start the exam through what feels like automated processes, I had to interact with the real-life proctor and use my webcam to slowly pan around the room and over every part of my desk - including underneath. This did lead to the situation where I had to slightly adjust the angle of my desk + webcam in the room so myself, the entrance door, and a cupboard door were all visible at the same time, but it was only a minor inconvenience. The proctor was very pleasant to interact with and was only enforcing the examination policies, not being unreasonable. Once the exam is completed, you get your initial score but have to wait a couple of days before receiving your official results + certificate. Being used to receiving everything from Microsoft within a couple of hours, this threw me, but it’s not the end of the world. My confirmation arrived near the end of the business day on Monday, so it wasn’t like I had an an unreasonable length of time to wait, really.&lt;/p&gt;
&lt;p&gt;Overall, I’d recommend the exam and learning experience, for no other reason than simply having a verified understanding of the core concepts of SRE. Though obviously, it’s far from essential. I’m not sure if SRE is a topic I’m going to go deeper into, but we’ll see what next year holds!&lt;/p&gt;
&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://chrismcleod.dev/assets/images/qvY4X7Bivb-320.avif 320w&quot; sizes=&quot;(min-width: 55rem) 820px, 100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://chrismcleod.dev/assets/images/qvY4X7Bivb-320.webp 320w&quot; sizes=&quot;(min-width: 55rem) 820px, 100vw&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;https://chrismcleod.dev/assets/images/qvY4X7Bivb-320.jpeg 320w&quot; sizes=&quot;(min-width: 55rem) 820px, 100vw&quot; /&gt;&lt;img src=&quot;https://chrismcleod.dev/assets/images/qvY4X7Bivb-320.jpeg&quot; width=&quot;320&quot; height=&quot;320&quot; alt=&quot;DevOps Institute Site Reliability Engineering Fundamentals achievement badge&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;&quot; /&gt;&lt;/picture&gt;&lt;/figure&gt;
</content
    >
  </entry>
  <entry>
    <title>So About That Feed Reader-Only Blog Idea</title>
    <link href="https://chrismcleod.dev/blog/so-about-that-feed-reader-only-blog-idea/" />
    <updated>2023-12-04T12:32:00Z</updated>
    <id>https://chrismcleod.dev/blog/so-about-that-feed-reader-only-blog-idea/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;In my last post I included a footnote about a RSS-only blog idea I’d had ages ago and not done anything with:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This reminds me of the time I mused about setting up a site that was only an RSS feed. No web pages beyond the bare minimum needed for feed discovery, or maybe a splash to let people know what’s going on. The idea never came to anything, but &lt;em&gt;maybe&lt;/em&gt;…?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well, thanks to some encouragement from Robb, I’m giving it a try. Say hello to &lt;a href=&quot;https://theunderground.blog/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;theunderground.blog&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/so-about-that-feed-reader-only-blog-idea/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. As I mention in the first entry (issue?) there’s a lot I’m still figuring out, but the more boring technical aspects are largely in place. I literally took the Eleventy template for my site, stripped it back to just the Atom feed and a minimal home page, then called it a day. The most interesting part of it was getting Eleventy to delete all of the rendered posts after build so that there really wouldn’t be any permalinks that snuck through.&lt;/p&gt;
&lt;p&gt;I treated Entry #1 as if it were almost an old-style email newsletter&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/so-about-that-feed-reader-only-blog-idea/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;, with a quick introduction blurb and then some interesting links, and honestly - I really enjoyed the format and will probably continue with it. I haven’t figured out what cadence posts will be; I’m not really a daily blogger any more, but somewhere between twice weekly and twice monthly will be where we land. It will probably evolve over time too, but we’ll have to see. It’s very early days!&lt;/p&gt;
&lt;p&gt;The email newsletter comparison is probably the best mental model for a feed-only format. I can’t directly link to any entries, and I have little to no control over how it will be presented by clients. I just write, publish, then hope it lands in your feeds correctly. At least there’s no DKIM or SPF or spam filters to worry about. That “no linking” thing is the biggest brain twister I’ve come across so far. I’m so used to just tossing a link out to a post I’ve written it’s second nature, and I can’t do that here. It’s &lt;a href=&quot;https://theunderground.blog/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;the homepage&lt;/a&gt; or &lt;a href=&quot;https://theunderground.blog/feed.xml&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;the feed&lt;/a&gt;, or nothing! By its nature, entries might also be somewhat ephemeral. If you subscribe after, say, Entry #100, then I cannot be sure a feed reader will import right the way back to the beginning. It’s going to be an interesting journey of discovery.&lt;/p&gt;
&lt;p&gt;So please &lt;a href=&quot;https://theunderground.blog/feed.xml&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;subscribe&lt;/a&gt; to find out where this little experiment ends up going, and if you have any feedback, you &lt;em&gt;should&lt;/em&gt; be able to webmention this post now, or find other contact details at the bottom of the first entry over on theunderground.blog.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I’m still figuring out the “branding” - theunderground.blog? The Underground Blog? The Underground? TheUnderground? You get the idea! Some of them veer a bit too close to music “zine”. &lt;a href=&quot;https://chrismcleod.dev/blog/so-about-that-feed-reader-only-blog-idea/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Not your new-fangled ones like Substack or Buttondown that also give you a web version of each issue as an archive copy. &lt;a href=&quot;https://chrismcleod.dev/blog/so-about-that-feed-reader-only-blog-idea/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content
    >
  </entry>
  <entry>
    <title>Blogging is where it&#39;s at, again</title>
    <link href="https://chrismcleod.dev/blog/blogging-is-where-its-at-again/" />
    <updated>2023-11-30T11:09:00Z</updated>
    <id>https://chrismcleod.dev/blog/blogging-is-where-its-at-again/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;I don’t blog as much as I used to, or post about as wide an array of topics as I used to, but I’ve always believed the blog is the “natural form” of posting on the web: a site of your own, that you control&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/blogging-is-where-its-at-again/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; and set your own rules on content and discussion; where you can post whatever you like without worrying about “The Algorithm”. Everyone should have a blog, whether it’s on a free service like &lt;a href=&quot;https://wordpress.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Wordpress.com&lt;/a&gt; or &lt;a href=&quot;https://micro.blog/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Micro.blog&lt;/a&gt;, a &lt;a href=&quot;https://chrismcleod.dev/blog/i-rebuilt-my-blog-and-didnt-write-about-it/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;crafted custom setup&lt;/a&gt;, or something in-between.&lt;/p&gt;
&lt;p&gt;Blogs never really went away, but they did take a back seat to the social media era. I’ve been blogging on various iterations of my personal site (on different domains over the years) for around 24 years. This is &lt;em&gt;my&lt;/em&gt; space on the internet (pun intended); I’m not beholden to the whims of social media platforms here, or their rise and fall. But still, I’ve spent more time &lt;em&gt;and posted more&lt;/em&gt; on those platforms over the last several years than I have on my own site, so I’m not judging anyone about being, if we will, “social-first” (or only). For better or for worse, social media opened up the web to a lot more people for a number of reasons too long to get into here. I do like some social media platforms, some of the time. But deep down I feel having your own site is &lt;em&gt;better&lt;/em&gt;. For the web, and for you: the writer and the reader.&lt;/p&gt;
&lt;p&gt;Anyway. A couple of coincidental things happened over the last 2 days that got me jazzed for the future of blogging again. Maybe it’s going to make a comeback of sorts? That would be nice.&lt;/p&gt;
&lt;p&gt;First, the &lt;a href=&quot;https://chrismcleod.dev/blog/default-apps-for-2023/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Default Apps post&lt;/a&gt; from yesterday led me down a rabbit-hole. I was checking out the people who had also contributed to &lt;a href=&quot;https://defaults.rknight.me/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;the list&lt;/a&gt; when I noticed the &lt;a href=&quot;https://defaults.rknight.me/opml.xml&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OPML file&lt;/a&gt; of everyone with an RSS feed. Currently that’s over 200 people. It might sound silly, but I’ve not seen a list of active bloggers that long in &lt;strong&gt;years&lt;/strong&gt;, outside a few directories. It’s more than double the number of sites I had in my RSS reader’s “IndieWeb &amp;amp; Personal Blogs” folder after many years of slow curation.&lt;/p&gt;
&lt;p&gt;Second, &lt;a href=&quot;http://scripting.com/2023/11/27.html#a213710&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;by way of Dave Winer&lt;/a&gt;, I found the &lt;a href=&quot;https://daverupert.com/rss-club/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;RSS Club&lt;/a&gt;. Another list of bloggers — 32 this time — with the unique twist of committing to provide at lease some “RSS only” content&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/blogging-is-where-its-at-again/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. As the 2nd rule states: “Don’t Share on Social Media”. As luck would have it, the RSS Club also publishes &lt;a href=&quot;https://daverupert.com/rss-club/feeds.xml&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;an OPML file&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I keep mentioning &lt;a href=&quot;https://en.wikipedia.org/wiki/OPML&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OPML&lt;/a&gt; files. If you’re not aware, OPML files are used as a file for organising RSS feeds. If you export your subscriptions from a feed reader, what you’ll get is an OPML file ready to import into another service/app. So you could simply download the above linked files and import them directly into your feed reader of choice, subscribing you to everyone included when you initiated the download. The &lt;em&gt;really cool&lt;/em&gt; thing about OPML files, that some feed readers and clients support, is that you can subscribe to the OPML file itself. This means that you’ll not only get the posts of anyone included when you subscribed, you’ll also get the posts of anyone who is added to the file &lt;em&gt;at a later date&lt;/em&gt;. As the list of of feeds grows, so does the number of blogs and posts you’ll see.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.inoreader.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Inoreader&lt;/a&gt; (one of the feed readers I’m a paid user of) supports OPML subscriptions&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/blogging-is-where-its-at-again/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;, so I added subscriptions to both of the files above. Already it’s given me a big change in my reading experience. Blogs aren’t being drowned out by news sites anymore. I woke up this morning to a couple of hundred posts to read on a very wide variety of topics; some serious, some whimsical, some somewhere in the middle. It was magical. It got me thinking about all sorts of possibilities around social media, blogs, and feeds — and inspired this post 😅&lt;/p&gt;
&lt;p&gt;Maybe I’m reading too much into it, but stumbling into such a trove of &lt;em&gt;active&lt;/em&gt; blogs has enthused me about blogging as a medium again. It’s sparked a thought that through a combination of increased blogging activity, declining platforms, and increasing adoption of open standards to glue everything together, that maybe — just &lt;em&gt;maybe&lt;/em&gt; — we can swing the web back towards the blog again.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Hosting terms and conditions notwithstanding &lt;a href=&quot;https://chrismcleod.dev/blog/blogging-is-where-its-at-again/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;This reminds me of the time I mused about setting up a site that was only an RSS feed. No web pages beyond the bare minimum needed for feed discovery, or maybe a splash to let people know what’s going on. The idea never came to anything, but &lt;em&gt;maybe&lt;/em&gt;…? &lt;a href=&quot;https://chrismcleod.dev/blog/blogging-is-where-its-at-again/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://feedbin.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Feedbin&lt;/a&gt;, the other paid-for service I use, doesn’t support OPML subscriptions. I was able to import from the files just fine, it just means I won’t get any additional feeds. I’m still going back and forth over which service I prefer, but Inoreader now has a slight advantage. &lt;a href=&quot;https://chrismcleod.dev/blog/blogging-is-where-its-at-again/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content
    >
  </entry>
  <entry>
    <title>Default Apps for 2023</title>
    <link href="https://chrismcleod.dev/blog/default-apps-for-2023/" />
    <updated>2023-11-28T17:21:00Z</updated>
    <id>https://chrismcleod.dev/blog/default-apps-for-2023/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;I came across this collection of “what apps am I using?” posts via &lt;a href=&quot;https://www.manton.org/2023/11/28/default-apps-for.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Manton&lt;/a&gt;, and thought it would be fun take part. If you want to see what others have posted, &lt;a href=&quot;https://defaults.rknight.me/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Robb Knight is compiling a list&lt;/a&gt;.&lt;/p&gt;
&lt;ul class=&quot;list&quot;&gt;
&lt;li&gt;📨 Mail Client: Apple Mail + web&lt;/li&gt;
&lt;li&gt;📮 Mail Server: Fastmail&lt;/li&gt;
&lt;li&gt;📝 Notes: Obsidian (also trying out LogSeq)&lt;/li&gt;
&lt;li&gt;✅ To-Do: Apple Reminders&lt;/li&gt;
&lt;li&gt;📷 iPhone Photo Shooting: Apple Camera&lt;/li&gt;
&lt;li&gt;🌅 Photo Management: Apple Photos&lt;/li&gt;
&lt;li&gt;📆 Calendar: Apple Calendar linked to Google (with plans to switch to Fastmail)&lt;/li&gt;
&lt;li&gt;📁 Cloud File Storage: iCloud + OneDrive&lt;/li&gt;
&lt;li&gt;📖 RSS: NetNewsWire and Feedbin&lt;/li&gt;
&lt;li&gt;🙍🏻‍♂️ Contacts: Apple Contacts&lt;/li&gt;
&lt;li&gt;🌐 Browser: Edge&lt;/li&gt;
&lt;li&gt;💬 Chat: Work only - Teams; iMessage + WhatsApp, if they count&lt;/li&gt;
&lt;li&gt;🔖 Bookmarks: Micro.blog&lt;/li&gt;
&lt;li&gt;📑 Read It Later: Micro.blog&lt;/li&gt;
&lt;li&gt;📜 Word Processing: Word&lt;/li&gt;
&lt;li&gt;📈 Spreadsheets: Excel&lt;/li&gt;
&lt;li&gt;📊 Presentations: PowerPoint&lt;/li&gt;
&lt;li&gt;🛒 Shopping Lists: &lt;em&gt;none&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;🍴 Meal Planning: &lt;em&gt;none&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;💰 Budgeting and Personal Finance: Plum, banking app&lt;/li&gt;
&lt;li&gt;📰 News: Artifact, Apple News, RSS&lt;/li&gt;
&lt;li&gt;🎵 Music: Apple Music&lt;/li&gt;
&lt;li&gt;🎙️ Podcasts: Overcast&lt;/li&gt;
&lt;li&gt;🔐 Password Management: 1Password&lt;/li&gt;
&lt;li&gt;🤖 Code Editing: VS Code + Visual Studio&lt;/li&gt;
&lt;li&gt;📚 Books: Kindle, Audible&lt;/li&gt;
&lt;li&gt;🌎 Blogging: Micro.blog, Wordpress, Eleventy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall I’m a fan of sticking fairly close to the defaults on the platforms I use (Windows + iOS) simply because it usually leads to having an easier time of things. There are a couple of items on this list that probably won’t be on the list this time next year, but I’m still evaluating what their replacements might be.&lt;/p&gt;
</content
    >
  </entry>
  <entry>
    <title>How I use Generative AI to help write blog posts</title>
    <link href="https://chrismcleod.dev/blog/how-i-use-generative-ai-to-help-write-blog-posts/" />
    <updated>2023-08-31T00:00:00Z</updated>
    <id>https://chrismcleod.dev/blog/how-i-use-generative-ai-to-help-write-blog-posts/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;&lt;strong&gt;Editor’s Note, 26th January 2024&lt;/strong&gt; - This post was part of an experiment in using “generative AI” in my blogging process, and as such is at least in part written by one or more generative AI tools. &lt;a href=&quot;https://chrismcleod.dev/blog/generative-ai-for-blogging-revisited&quot;&gt;I am no longer using these tools&lt;/a&gt; and felt it important to come back and and apply this label to posts where such tools were used.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;The rise of large language models (LLMs) like &lt;a href=&quot;https://openai.com/blog/chatgpt/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ChatGPT&lt;/a&gt;, &lt;a href=&quot;https://www.anthropic.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Anthropic’s Claude&lt;/a&gt;, &lt;a href=&quot;https://blog.google/products/search/bard-google-ai/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Google Bard&lt;/a&gt;, and &lt;a href=&quot;https://www.microsoft.com/en-us/bing/bing-chat&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Bing Chat&lt;/a&gt;with GPT-4 has opened up exciting new possibilities for augmenting creativity. Recently I started using generative AI tools to help me draft my blog posts. I find that AI can be a great way to generate ideas, outlines, and even full drafts of my posts. Coming up with the initial draft is often the hardest and most time consuming part of the blogging process, and using generative tools can be a big win. So how do I do use these tools?&lt;/p&gt;
&lt;h2 id=&quot;the-process&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/how-i-use-generative-ai-to-help-write-blog-posts/#the-process&quot;&gt;The process:&lt;/a&gt;&lt;/h2&gt;
&lt;ol class=&quot;list&quot;&gt;
&lt;li&gt;I ask ChatGPT, Bing Chat, Bard, and Claude for an outline based on a prompt. The prompt is a short sentence or phrase that describes the main idea or theme of the blog post. For example, for this post, the prompt could be &lt;code&gt;write an outline for a blog post titled &amp;quot;how I use generative AI to draft blog posts&amp;quot;&lt;/code&gt;. Each tool will generate a different outline, with different headings and subheadings, based on the prompt.&lt;/li&gt;
&lt;li&gt;I compare the outlines from the four tools and choose the one that best suits my purpose and intended audience. If necessary I tweak the outline to the shape of the post I want. I may also combine elements from different outlines or add my own headings and subheadings.&lt;/li&gt;
&lt;li&gt;I then ask all the tools to write a first draft based on the outline. I provide each tool with the same outline prompt and ask them to generate a draft to a rough word count. Each tool will generate a different draft, with unique wording and style, based on the outline. Sometimes the drafts are &lt;em&gt;vastly&lt;/em&gt; different to each other, which is interesting to see.&lt;/li&gt;
&lt;li&gt;I compare and review the drafts. I read each draft carefully and evaluate its quality, coherence, readability, and tone. It’s fascinating to see the different tones and styles that emerge in these drafts. I may also ask for specific changes to individual drafts such as “use {X} writing style” or “include a paragraph about {Y}”.&lt;/li&gt;
&lt;li&gt;I select the draft I consider closest to what I had in mind when I started. I may also consider other factors such as originality, creativity, and relevance.&lt;/li&gt;
&lt;li&gt;I manually edit the draft to the final form, sometimes including elements from other drafts. I proofread and revise the draft to correct any errors, inconsistencies, or gaps in the content.&lt;/li&gt;
&lt;li&gt;If something still isn’t quite sitting right, I will ask one of the tools to review the text for suggested edits or to change the tone. I may also ask for feedback or suggestions from other tools or sources to improve the quality and clarity of the text.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’ve found that this process helps me reduce the time to write blog posts significantly. LLMs can generate a lot of ideas and content quickly, and I can then focus on editing and polishing the final product.&lt;/p&gt;
&lt;h2 id=&quot;lessons-learned&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/how-i-use-generative-ai-to-help-write-blog-posts/#lessons-learned&quot;&gt;Lessons learned&lt;/a&gt;&lt;/h2&gt;
&lt;ul class=&quot;list&quot;&gt;
&lt;li&gt;Be clear and specific in your prompts. The more specific you are, the better the AI tools will be able to understand what you want.&lt;/li&gt;
&lt;li&gt;Be willing to experiment. The AI tools are still under development, so you may need to try different prompts and settings to get the results you want.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Editing is a must.&lt;/strong&gt; The AI tools can generate some great words, but it’s important to edit and revise it to make sure it’s clear, concise, and error-free.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s also important to use the right tools for the job. Not all generative AI tools are created equal. Some are better at generating ideas, while others are better at writing full drafts. It’s also important to be aware of the limitations of these tools. They are also not perfect, and they can sometimes generate text that is inaccurate, biased, or offensive. Over time you will develop a sense of what each is good at.&lt;/p&gt;
&lt;p&gt;As an example, I usually find Claude to be the most “eloquent”, but equally, can get overly wordy and occasionally go on a tangent to meet a word count. Sometimes that works out for the better, but not always. On the other hand, Bard is usually the most “perfunctory”; it’s snappy and to-the-point, but can sometimes produce text that is blunt in tone to the point of rudeness. I also find Bard most likely to &lt;a href=&quot;https://zapier.com/blog/ai-hallucinations/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;hallucinate&lt;/a&gt; things that weren’t asked for&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/how-i-use-generative-ai-to-help-write-blog-posts/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. ChatGPT and its variants sit somewhere in the middle.&lt;/p&gt;
&lt;p&gt;Ultimately, the best way to use generative AI is to use it as a starting point. They can help you to generate ideas, outlines, and even full drafts, but you will still need to edit the final output to make sure it’s of a high quality to be posted under your name.&lt;/p&gt;
&lt;p&gt;While there are certainly &lt;a href=&quot;https://ukfinancialservicesinsights.deloitte.com/post/102i7s2/risks-and-ethical-considerations-of-generative-ai&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ethical&lt;/a&gt; &lt;a href=&quot;https://www.computer.org/publications/tech-news/trends/ethical-concerns-on-ai-content-creation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;concerns&lt;/a&gt; around AI content generation, I believe there are also responsible ways we can thoughtfully harness these models as tools. With the right human oversight, LLMs can expand our capabilities and knowledge, or just make certain jobs easier. My writing has already benefited from the interplay of AI and human creativity.&lt;/p&gt;
&lt;p&gt;Moving forward, I’m sure my processes will continue evolving as the AI capabilities advance. For now, this collaborative workflow allows me to tap into the strengths of both man and machine. I’m excited to see how generative AI continues to develop in the future.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;so-what-about-this-post&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/how-i-use-generative-ai-to-help-write-blog-posts/#so-what-about-this-post&quot;&gt;So what about this post?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;How meta! My process for writing this post was basically no different to what I described above, only this time my first prompt included the outline of what I wanted to say as I had already figured that out. The starting prompt was:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;write a first draft blog post of roughly 500 words about how I use generative AI/LLMs to draft blog posts, based on the outline below:

- I ask ChatGPT, Bing Chat, Bard, and Claude2 for an outline based on a prompt.
- I edit the outline to the shape of the post I want.
- I then ask the 4 tools to write a first draft based on the outline.
- I compare and review the drafts. I may ask for specific changes such as &quot;use {X} writing style&quot; or &quot;include a point about {Y}&quot;.
- I select the draft I consider best.
- I manually edit the draft to the final form, sometimes including elements from another draft.
- If something isn&#39;t quite right, I will ask the &quot;winning&quot; tool to review the text for suggested edits/tone&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The “winner” of the first round, this time, was Google Bard. It provided a nice and coherent draft post which expanded on my outline without going on too many tangents. I felt it was a little slim on details in places, so I edited in selections from Claude and Bing Chat where something needed expanded on. Finally, I asked Claude to review and edit for consistent tone and writing style with the following prompt:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;review and suggest edits for the attached Markdown text. Ensure it has a consistent, approachable tone. Correct any grammar and spelling mistakes using UK English. Add hyperlinks to any tools referenced in the text. The final output should be in Markdown format.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output was given one last proofread and minor edits such as adding the front-matter, before being posted as what you’re reading right now!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I once asked Bard to reword some positive performance review feedback from a bullet-point list into a couple of paragraphs. It did it, but also added a whole lot of unasked for and very negative text about poor time management and lack of research skills - both things that were completely unrelated to anything I had written. I did not use its output on that occasion! &lt;a href=&quot;https://chrismcleod.dev/blog/how-i-use-generative-ai-to-help-write-blog-posts/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content
    >
  </entry>
  <entry>
    <title>Microsoft Role-Based Exams Now (Semi) Open-Book</title>
    <link href="https://chrismcleod.dev/blog/microsoft-role-based-exams-now-semi-open-book/" />
    <updated>2023-08-23T00:00:00Z</updated>
    <id>https://chrismcleod.dev/blog/microsoft-role-based-exams-now-semi-open-book/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;Microsoft’s “role based” exams can be tough-going. There’s a &lt;em&gt;lot&lt;/em&gt; to learn, with a lot of questions where remembering the sometimes subtle nuances between things like different SKUs/pricing plans, or particular flags on a PowerShell command can make all of the difference. A lot of the time this is detail you wouldn’t normally need to memorise, but would just look it up in normal day-to-day work.&lt;/p&gt;
&lt;p&gt;It seems that Microsoft have noticed this, and are &lt;a href=&quot;https://chrismcleod.dev/blog/microsoft-role-based-exams-now-semi-open-book/blogpost&quot;&gt;adding a new Microsoft Learn button to official exams&lt;/a&gt;. You’ll be able to access and search &lt;a href=&quot;https://chrismcleod.dev/blog/microsoft-role-based-exams-now-semi-open-book/learn&quot;&gt;https://learn.microsoft.com/&lt;/a&gt; from within the exam, in a neat split-screen view. It’s somewhat limited, so you’ll still need to have some experience with the tools and topics, but it should help lower test anxiety for some people. From their blog post:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How often do you say in your daily life, “I’ll just look that up.”? Now, you can… with easy access to Microsoft Learn, another enhancement that we have made to improve the overall exam experience.&lt;/p&gt;
&lt;p&gt;Because this is an exam resource, much like a calculator, exam time will not be extended. We are not changing the way we write our questions; they will continue to focus on problems or scenarios that require real world experience to solve. As a result, this resource is intended to be used for those questions that describe problems where you may need to look something up on Microsoft Learn. It is &lt;strong&gt;not&lt;/strong&gt; something you should be leveraging to answer every question.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Renewal tests were already completely open-book, so this change brings the exams more into line with those. Overall I think it’s a great change and will put an end to a lot of those “is it the Standard SKU or the Premium SKU for that feature?” moments which always seem to trip me up the most.&lt;/p&gt;
&lt;p&gt;If you want a summary of all the changes in video form, Jon Savil has you covered:&lt;/p&gt;
  &lt;div class=&quot;youtube-embed&quot;&gt; &lt;lite-youtube videoid=&quot;iPiq3YuhnHc&quot; style=&quot;background-image: url(&#39;https://i.ytimg.com/vi/iPiq3YuhnHc/hqdefault.jpg&#39;);&quot;&gt;
  &lt;button type=&quot;button&quot; class=&quot;lty-playbtn&quot;&gt;
    &lt;span class=&quot;lyt-visually-hidden&quot;&gt;Microsoft Role-Based Exams Now Open-Book&lt;/span&gt;
  &lt;/button&gt;
&lt;/lite-youtube&gt;&lt;/div&gt;
</content
    >
  </entry>
  <entry>
    <title>I Rebuilt My Blog and Didn&#39;t Write About It</title>
    <link href="https://chrismcleod.dev/blog/i-rebuilt-my-blog-and-didnt-write-about-it/" />
    <updated>2023-08-20T00:00:00Z</updated>
    <id>https://chrismcleod.dev/blog/i-rebuilt-my-blog-and-didnt-write-about-it/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;Until now.&lt;/p&gt;
&lt;p&gt;Sorry, it’s tradition. If someone redesigns/redevelops their blog and doesn’t blog about it, &lt;em&gt;did it even happen&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;Regular visitors (if there is such a thing) will already know this, but if you follow via &lt;a href=&quot;https://chrismcleod.dev/feed.xml&quot;&gt;RSS&lt;/a&gt; you might not know that this site has a new lick of &lt;s&gt;paint&lt;/s&gt; CSS. Not only that, but there’s a new underlying system and hosting platform. In summary:&lt;/p&gt;
&lt;ul class=&quot;list&quot;&gt;
&lt;li&gt;WordPress is out; &lt;a href=&quot;https://www.11ty.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Eleventy&lt;/a&gt; is in.&lt;/li&gt;
&lt;li&gt;Instead of &lt;a href=&quot;https://www.digitalocean.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;hosting on my own VPS&lt;/a&gt; for ~£20 a month, I’m hosting for free on &lt;a href=&quot;https://www.netlify.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Netlify&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The site is based on the excellent &lt;a href=&quot;https://eleventy-excellent.netlify.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Eleventy Excellent&lt;/a&gt; starter, by &lt;a href=&quot;https://www.lenesaile.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Lene Saile&lt;/a&gt;. I’ve made a few tweaks here and there to make things &lt;em&gt;just so&lt;/em&gt; for my tastes, but full credit goes to Lene for providing such a good foundation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;why-eleventy&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/i-rebuilt-my-blog-and-didnt-write-about-it/#why-eleventy&quot;&gt;Why Eleventy?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In short: it’s very fast, flexible, and doesn’t railroad you into a particular way of building your site. The &lt;a href=&quot;https://www.11ty.dev/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;documentation&lt;/a&gt; is good, and ultimately: &lt;em&gt;I just really like working with it&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The community around Eleventy has come up with a variety of “&lt;a href=&quot;https://www.11ty.dev/docs/starter/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Starters&lt;/a&gt;”, which are a kind of template to help get your site up and running. They’re not just “templates” in the sense of how the site looks, but also how it is structured under the hood, build-time optimisations… the works! They can be as simple or as complicated as you want.&lt;/p&gt;
&lt;p&gt;I evaluated a few different Starters, Initially I was just looking for something simple, that optimised the output HTML and CSS at build-time, but not much more than that. Then I found Eleventy Excellent and new it was the one for me. In Lene’s own words:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This (opiniated) &lt;a href=&quot;https://www.11ty.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Eleventy&lt;/a&gt; starter is based on &lt;a href=&quot;https://mastodon.social/@andy@bell.bz&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Andy Bell’s&lt;/a&gt; talk ‘Be the browser’s mentor, not its micromanager’ and it’s companion website &lt;a href=&quot;http://buildexcellentwebsit.es/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;buildexcellentwebsit.es&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Andy’s talk was actually going to be the basis for how I built this site, so to find someone who had already done the hard work was fortuitous! That it looked good, was easily customisable, and fast, were all extra cherries on top 🥳&lt;/p&gt;
&lt;h2 id=&quot;getting-content-out-of-wordpress&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/i-rebuilt-my-blog-and-didnt-write-about-it/#getting-content-out-of-wordpress&quot;&gt;Getting content out of WordPress&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Getting my content out of WordPress and into nice, simple Markdown was probably the most time-consuming part of this whole process. Although, to be honest, it wasn’t that difficult compared to what I’d braced myself for.&lt;/p&gt;
&lt;p&gt;There are already a few WordPress to Markdown converters available. Most are a Node package that can be run against your WordPress export on the command-line. I tried out a couple, comparing the output, and finally settled on &lt;a href=&quot;https://github.com/flowershow/wordpress-to-markdown&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;flowershow/wordpress-to-markdown)&lt;/a&gt;. This generates a bunch of .md and downloads image files from posts into a directory structure.&lt;/p&gt;
&lt;p&gt;From there began the laborious job of cleaning up the output to match the blog setup, change the image references from Markdown to the &lt;a href=&quot;https://www.11ty.dev/docs/plugins/image/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Eleventy Image&lt;/a&gt; short-code, and update the image paths to point to the Azure storage blob I’m using to hold my master copies. After a couple of sessions working on it in a Starbucks, everything was building exactly as I wanted it&lt;/p&gt;
&lt;h2 id=&quot;why-netlify-and-not-azure-static-web-apps&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/i-rebuilt-my-blog-and-didnt-write-about-it/#why-netlify-and-not-azure-static-web-apps&quot;&gt;Why Netlify and not Azure Static Web Apps?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you’ve read &lt;a href=&quot;https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;my previous post on SWA&lt;/a&gt;, you’ll know I’m a huge fan of Azure Static Web Apps. So it might be a surprise that I opted for hosting with Netlify instead. In a lot of ways the services are functionally identical - free hosting for static sites, with a CI/CD pipeline from GitHub that automatically builds and deploys your site whenever changes are pushed to the repository.&lt;/p&gt;
&lt;p&gt;Honestly, it came down to just one thing - limits on the size of the compiled site “bundle”, including media files. SWA has a limit of 250MB per site on the Free tier. Netlify doesn’t. Although I knew the static HTML would be a tiny fraction of that limit, the cached images in multiple formats would take me closer. I don’t think I’m realistically I would hit the limit any time soon&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://chrismcleod.dev/blog/i-rebuilt-my-blog-and-didnt-write-about-it/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, but there would always be a ceiling I’d have to remember about as the content grows. So Netlify it was.&lt;/p&gt;
&lt;p&gt;I might revisit this, and have everything on Azure, but I’m not in a rush.&lt;/p&gt;
&lt;h2 id=&quot;interesting-technical-details&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/i-rebuilt-my-blog-and-didnt-write-about-it/#interesting-technical-details&quot;&gt;Interesting Technical Details&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Honestly, the majority of the technical underpinnings are pretty boring. To understand the HTML/CSS, I recommend taking a look at the starter repository and talk which inspired it. Both are linked at the top of this post. I think the most interesting thing is how I’m handling images:&lt;/p&gt;
&lt;ol class=&quot;list&quot;&gt;
&lt;li&gt;As mentioned above, masters are stored in Azure storage, with a CDN attached to make it easier to reference in posts.&lt;/li&gt;
&lt;li&gt;When writing a post I include a short-code which links out to the CDN version of the image.&lt;/li&gt;
&lt;li&gt;At build time, Eleventy Image downloads the master, resizes it to the specified responsive sizes, and generates optimised versions in various formats like AVIF. These are then cached by the build to prevent excessive work/traffic.&lt;/li&gt;
&lt;li&gt;The short-code is replaced with &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tags for proper responsive image support&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It’s pretty cool all-in-all, and makes posting images pretty trivial. I just have to upload to Azure, then link from a post, and Eleventy then does all the “right things” with regards to optimisations.&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/i-rebuilt-my-blog-and-didnt-write-about-it/#whats-next&quot;&gt;What’s next?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Hopefully just writing more! I’ve added a dark mode and links page generated from Feedbin subscriptions, so I think the only things I still want to add are:&lt;/p&gt;
&lt;ul class=&quot;list&quot;&gt;
&lt;li&gt;Search - I might end up using Azure Cognitive Search for this, but not 100% decided yet&lt;/li&gt;
&lt;li&gt;Bringing in content from old iterations of my blog. I’ve been writing online since before it was called “blogging”, and I’ve got a bunch of good stuff in the archives that’s not on this site. I’ll need to do some editorialising to bring in the “greatest hits” in a way that makes sense, but I’d like to expand the content so it’s not just the last couple of years.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;As it stands, the development build for the entire site is only ~21MB, including images - which take up &amp;gt;20MB. Yay HTML. &lt;a href=&quot;https://chrismcleod.dev/blog/i-rebuilt-my-blog-and-didnt-write-about-it/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content
    >
  </entry>
  <entry>
    <title>Come on Barbie, let’s go party</title>
    <link href="https://chrismcleod.dev/blog/come-on-barbie-lets-go-party/" />
    <updated>2023-07-24T00:00:00Z</updated>
    <id>https://chrismcleod.dev/blog/come-on-barbie-lets-go-party/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;&lt;strong&gt;Editor’s Note, 26th January 2024&lt;/strong&gt; - This post was part of an experiment in using “generative AI” in my blogging process, and as such is at least in part written by one or more generative AI tools. &lt;a href=&quot;https://chrismcleod.dev/blog/generative-ai-for-blogging-revisited&quot;&gt;I am no longer using these tools&lt;/a&gt; and felt it important to come back and and apply this label to posts where such tools were used.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;We went to the cinema recently, to watch Barbie, and I must say, it was an absolute blast! Going in, I had mixed expectations - essentially expecting something similar to The Lego Movie (funny, a few touching moments, but ultimately empty), but what I got was so much more.&lt;/p&gt;
&lt;p&gt;From the very start, I was hooked. I was pretty much smiling or laughing the whole way through. It weaved heartwarming moments throughout, making it a well-rounded and engaging experience. Margot Robbie and Ryan Gosling were undoubtedly the stars of the show, and they played their roles to perfection, adding an extra layer of charm to the movie.&lt;/p&gt;
&lt;p&gt;Margot Robbie’s portrayal of Barbie was exceptional, bringing the iconic character to life with a unique blend of humour, heart, and occasional obliviousness. Ryan Gosling’s performance as Ken was magnificent. I genuinely think I could watch an entire Ken movie based on his portrayal. Their chemistry on-screen was undeniable, and they truly seemed to be having a blast in their respective roles. Also - a shout-out to America Ferrera who adds the emotional core to the second half of the movie and who was brilliant in her role.&lt;/p&gt;
&lt;p&gt;There have been some “murmurs” online about the film being “anti-men,” but to my mind – this is nonsense, and most likely performative. Personally I found it very much “pro men”, particularly in the final act of the movie, if you actually pay attention. The core message of Barbie is positive and empowering for everyone, regardless of gender. It celebrates the importance of embracing individuality, being true to oneself, and supporting each other. The film’s underlying messages are of inclusivity and empowerment, and these are not bad things.&lt;/p&gt;
&lt;p&gt;As we left the cinema, I couldn’t help but &lt;a href=&quot;https://bsky.app/profile/chrismcleod.dev/post/3k34gi4hce62r&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;reflect&lt;/a&gt; on the amazing experience I had just had. Barbie far exceeded my expectations. Whether you’re part of the target audience, or just looking for a fun-filled and heart-warming escape, Barbie has something for everyone. So if you can, I recommend you take time to go see it.&lt;/p&gt;
</content
    >
  </entry>
  <entry>
    <title>Azure Static Web Apps are Awesome!</title>
    <link href="https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/" />
    <updated>2023-07-21T00:00:00Z</updated>
    <id>https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/</id>
    <content
      xml:lang=""
      type="html"
      >&lt;p&gt;&lt;strong&gt;Editor’s Note, 26th January 2024&lt;/strong&gt; - This post was part of an experiment in using “generative AI” in my blogging process, and as such is at least in part written by one or more generative AI tools. &lt;a href=&quot;https://chrismcleod.dev/blog/generative-ai-for-blogging-revisited&quot;&gt;I am no longer using these tools&lt;/a&gt; and felt it important to come back and apply this label to posts where such tools were used.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Azure Static Web Apps has become one of my new favourite tools for web development lately, so I wanted to do a quick write-up.&lt;/p&gt;
&lt;h2 id=&quot;what-is-azure-static-web-apps&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/#what-is-azure-static-web-apps&quot;&gt;What is Azure Static Web Apps?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So what is Azure Static Web Apps (SWA), and what can do for you? In a nutshell, it’s a modern solution for web development hosted on the Azure cloud. There’s no complex server setups and managing infrastructure - with Azure Static Web Apps, you can focus solely on building your front-end magic while Azure handles the rest. Sound useful, right? It absolutely is!&lt;/p&gt;
&lt;p&gt;One important thing to make clear - while the name is “Static”, and SWA doesn’t support pages built dynamically on the server (using PHP/Rails/whatever) - your pages do not need to be basic, un-interactive HTML if you don’t want them to be. Static Web Apps supports frameworks like React, Vue, Angular, .NET Blazor, static site generators like Gatsby and 11ty, and &lt;a href=&quot;https://learn.microsoft.com/en-gb/azure/static-web-apps/front-end-frameworks&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;more&lt;/a&gt;. On top of that, here’s a list of things you get on the &lt;strong&gt;free&lt;/strong&gt; tier that can spice up your app:&lt;/p&gt;
&lt;ul class=&quot;list&quot;&gt;
&lt;li&gt;Routing&lt;/li&gt;
&lt;li&gt;Authentication/Authorisation&lt;/li&gt;
&lt;li&gt;Serverless functions&lt;/li&gt;
&lt;li&gt;Integrate a database using GraphQL (currently in preview, but it’s awesome)&lt;/li&gt;
&lt;li&gt;Custom domains&lt;/li&gt;
&lt;li&gt;CI/CD integration&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://azure.github.io/static-web-apps-cli/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CLI tooling&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;learn-and-experiment-with-ideas&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/#learn-and-experiment-with-ideas&quot;&gt;Learn and Experiment with Ideas&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the coolest things about Azure Static Web Apps is how it encourages you to learn and experiment. As a developer, you love trying out new ideas, prototypes, and proof-of-concepts. In the last few weeks I’ve spun up several sites on SWA just to try out an idea or learn something. I’ll be talking more about those in later posts.&lt;/p&gt;
&lt;p&gt;Whether you want to build a personal website, an interactive landing page, API-driven frontend, or a progressive web app (PWA), Azure Static Web Apps can help you build it. And to top it off, getting started is a breeze!&lt;/p&gt;
&lt;h2 id=&quot;getting-started-made-super-simple&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/#getting-started-made-super-simple&quot;&gt;Getting Started Made Super Simple&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Deploying to Azure Static Web Apps is super simple. The first time you try it out, you’ll be pleasantly surprised by how smooth the process is. So, let me walk through the steps to get you started:&lt;/p&gt;
&lt;h3 id=&quot;step-1-create-your-azure-static-web-app&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/#step-1-create-your-azure-static-web-app&quot;&gt;Step 1: Create Your Azure Static Web App&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To begin, head over to the official Azure portal and create your very own Azure Static Web App instance. It’s free, and if it’s your first Azure account, you get some credits to put towards any paid Azure services that might take your fancy. Don’t worry about what’s involved in creating the instance; Microsoft have comprehensive &lt;a href=&quot;https://docs.microsoft.com/azure/static-web-apps/overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;documentation&lt;/a&gt; that will guide you through every step.&lt;/p&gt;
&lt;h3 id=&quot;step-2-set-up-the-deployment-pipeline&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/#step-2-set-up-the-deployment-pipeline&quot;&gt;Step 2: Set Up the Deployment Pipeline&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Next up is setting up your deployment pipeline. Azure integrates seamlessly with your version control system. Whether you prefer GitHub, Bitbucket, or Azure Repos, Azure will create a pipeline for you and add it to your repository. Once connected, Azure will automatically build and deploy your app whenever you push new changes.&lt;/p&gt;
&lt;h3 id=&quot;step-3-experiment-with-ideas&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/#step-3-experiment-with-ideas&quot;&gt;Step 3: Experiment with Ideas&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now comes the fun part - experimenting with your ideas! Since Azure Static Web Apps are perfect for prototypes and proof-of-concepts (and production apps too!), you can quickly test different concepts without worrying about server resources. This means you get more time to iterate and refine your ideas!&lt;/p&gt;
&lt;h2 id=&quot;embrace-the-power-of-serverless-architecture&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/#embrace-the-power-of-serverless-architecture&quot;&gt;Embrace the Power of Serverless Architecture&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another reason why you’ll love Azure Static Web Apps is its seamless integration with serverless architecture. With serverless APIs at your disposal, you can add dynamic functionalities to your static web apps without managing backend servers. It’s like having the best of both worlds - the simplicity of static sites with the power of dynamic APIs!&lt;/p&gt;
&lt;p&gt;As well as regular serverless functions, SWA now has “Database Connections” as a feature preview. With this you can easily hook up to, say, a free-tier Azure CosmosDB instance and - with a couple of configuration files - have a working GraphQL server for your site, including Role-Based Authentication, in minutes.&lt;/p&gt;
&lt;p&gt;Serverless Functions and Database Connections have full support for Role-Based Access Control, if that’s something you need.&lt;/p&gt;
&lt;h2 id=&quot;boost-your-productivity-with-continuous-integration&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/#boost-your-productivity-with-continuous-integration&quot;&gt;Boost Your Productivity with Continuous Integration&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As a developer, you value productivity, and Azure Static Web Apps delivers it in spades. The beauty of continuous integration is that you can focus on writing code without the distraction of deployment headaches. Plus, you get instant feedback on every change you make, making collaboration with teammates a breeze. Each Pull Request automatically spins-up a preview site with a unique URL - see your changes live before merging in and deploying to Production!&lt;/p&gt;
&lt;h2 id=&quot;learn-with-microsoft-training-materials&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/#learn-with-microsoft-training-materials&quot;&gt;Learn with Microsoft Training Materials&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Regular readers will know I’m a big fan of Microsoft learning resources. They offer a fantastic &lt;a href=&quot;https://learn.microsoft.com/azure/static-web-apps/?WT.mc_id=AZ-MVP-5004080&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;learning path&lt;/a&gt; for Azure Static Web Apps, and in-depth documentation. Even if you’re starting from scratch, you’ll get up-to-speed in no time with the well-crafted tutorials and guides.&lt;/p&gt;
&lt;h2 id=&quot;wrap-up&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;https://chrismcleod.dev/blog/azure-static-web-apps-are-awesome!/#wrap-up&quot;&gt;Wrap Up&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So, there you have it - a glimpse of why I think Azure Static Web Apps are genuinely awesome! They empower you to learn, experiment, and innovate with ease. With a user-friendly setup and seamless integration of serverless APIs, it’s a real joy to use. 🎉&lt;/p&gt;
</content
    >
  </entry>
</feed>
