Matthew Lang avatar

Margin: my own Markdown editor

I’ll be honest: I didn’t start the week planning to build my own Markdown editor for macOS, but by Thursday night, I already had something that worked.

A dual-pane Markdown editor app called Margin, showing raw Markdown text on the left and rendered preview on the right.

I’ve jumped the gun. Let’s rewind a bit.

My skill set is in web development: Ruby, Rails, Hanami, CSS, JS, and HTML. It’s been my bread and butter for the last 15 years. Building a macOS app has always been on my radar, but I never got round to starting one.

At the start of the week, I was split between VS Code and Obsidian when editing a file in my personal work wiki. I record the tickets I work on, any information that might be useful later, and other important bits. For a while, I’ve wanted a Markdown editor to maintain a few Markdown files separate from the wiki, something other than VS Code or Obsidian. They’re scratchpad files I keep around and usually clear out at the end of the week. Sure, I could use another text editor like Zed, but I wanted a plain-text editor, not a code editor.

Dozens of Markdown editors could serve my purpose. Then I thought, “What if I tried to build my own?”

And so, Margin was born. I didn’t spend long on the name. All I knew was that I planned a feature involving the margin itself, so the name stuck; the feature will come later.

Armed with Claude, I decided to build the absolute basics: type Markdown on the left and see it render on the right. The first prompt delivered a rather basic but working Markdown editor. After an hour or two, I had a small macOS app with a couple of additional changes: themes and file loading and saving . That was version 1.0.0.

After a few more tweaks, I added menu bar formatting options and syntax highlighting in the text editor, and fixed a couple of bugs with dashes and quotes. Not bad for two evenings. The experience has been quite an eye-opener, and I am slowly getting used to reading Swift code. Don’t get me wrong: I’m never going to build the next big thing for macOS, but being able to build my own app, even slowly, has been quite liberating.

The application itself is mostly SwiftUI. The only exception is the text editor, an AppKit view embedded in the SwiftUI layout. For the most part, Swift isn’t far removed from other languages I’ve used, but it will definitely take a while to get familiar with it.

For those interested, Margin is open source, but it’s not a typical open-source application. You can read the code, download it, and even run it if you choose, but I am building Margin just for me, so I’m not looking for contributions.

I’ll keep working on this a couple of hours a week and see where it takes me.

It’s hard to believe we have a weather warning for later in the day when the day starts like this.

Vivid red and orange sunset sky over silhouetted suburban houses and trees in a residential neighborhood.

Sun low in the sky, a roast in the oven, apples stewing for later tonight, the Packers on television shortly. Pretty much a perfect evening ahead.

My /photo page

After reading about Robert Birming’s photo page, I decided to add one to my own blog, with one change. My own photo page turned out pretty well. Here’s what I did.

Adding a new page in Jekyll is straightforward. It’s just a static page after all. Rendering a single photo in Jekyll wasn’t the challenge, though. I wanted to include archives of all the photos I will eventually share on this page, so I needed a way to build an image archive.

Jekyll is a static site generator, so it doesn’t have a database to query for archive photos. I needed another Jekyll-friendly database. Enter the photos.yml file.

This file contains the URLs for each photo’s full-sized and thumbnail versions, the caption, the alt text and the date the photo was taken. Each uploaded photo is added to the top of this file and acts as the source for the photo page and the archive.

I now needed a means to update this. I have my own publishing app, Postpress, that updates my Jekyll site. It’s a Rails application that lets me publish articles, upload images, and manage a few other data collections on my website. I already could upload images for my posts, so the photo upload uses the same process but saves to a separate photos folder.

I then collect the uploaded photo URLs, caption, date and LLM-generated alt text (which I always review), and add these to the top of the photos.yml file on my blog. Postpress commits these changes and pushes them back to my source code management host, where a new build of the website is triggered.

The resulting photos.yml file will look something like this:

---
- url: https://notarealhost.com/photos/2026/10/golf-course-full.jpg
  thumbnail: https://notarealhost.com/photos/2026/10/golf-course-thumb.jpg
  alt: A golf course putting green with a white flag pin, lush fairway, scattered trees, and a partly cloudy blue sky.
  caption: "Another view of the golf course. I wish I was more organised. I would have brought my clubs and played."
  date: 2026-10-10
- url: https://notarealhost.com/photos/2026/10/nuphy-keyboard-full.jpg
  thumbnail: https://notarealhost.com/photos/2026/10/nuphy-keyboard-thumb.jpg
  alt: Close-up of a mechanical keyboard's upper-right corner featuring dark gray, white, and teal keycaps with an RGB light strip.
  caption: I switched back to the original keycaps on my Nuphy Air75 keyboard. I quite like the default keycaps.
  date: 2026-10-09

I have a handful of layouts and views, plus a plugin that aggregates all the data for the photo pages, letting you drill down into the archives by year and navigate back. As the photos.yml is located in the _data folder in Jekyll, I can reference these photos by using site.data.photos.

A photo archive page from 2026 showing two thumbnail images: a golf course on October 10 and a mechanical keyboard on October 9.

Here are the files I needed to build this.

  • photo.md - The photo page itself, which will grab the first photo from the site.data.photos collection.
  • photo/archive.html - The archive page showing a photo for each year I have posted.
  • photo-year.html - The page that displays all the photos for a year.
  • photo_archive.rb - A plugin that generates a page for each year.

This took a bit of time today, but it was time well spent. I like to think of this as my own personal Instagram baked into my blog, without the hassle of actually having to post to Instagram.

I am really intrigued by Enkihost, a new host for Ruby applications by Albert Oliva. Pricing seems very competitive, but I haven’t run an application there yet, let alone five. It’s certainly cheaper than my current DigitalOcean and Hatchbox setup.

It turned out to be a pretty good day for Drew’s coaching at Castle Park Golf Club today. Sunny, but also quite windy.

Golf course green with red flag, lined by autumn-colored trees and rolling green hills under a partly cloudy sky.

Starting a week off work tomorrow to take Drew to an all-day golf coaching session at Castle Park Golf Club. I’ll be pitching up in the clubhouse all day. Expect a few posts tomorrow, and maybe a few blog changes.