MoreRSS

site iconJim NielsenModify

Designer. Engineer. Writer.20+ years at the intersection of design & code on the web.
Please copy the RSS to your reader, or quickly subscribe to:

Inoreader Feedly Follow Feedbin Local Reader

Rss preview of Blog of Jim Nielsen

Data Storage As Files on Disk Paired With an LLM

2025-11-17 03:00:00

I recently added a bunch of app icons from macOS Tahoe to my collection.

Screenshot of macosicongallery where the entire page is filled with app icons from macOS Tahoe.

Afterwards, I realized some of them were missing relational metadata.

For example, I have a collection of iMove icons through the years which are related in my collection by their App Store ID. However, the latest iMovie icon I added didn’t have this ID.

This got me thinking, "Crap, I really want this metadata so I can see apps over time. Am I gonna have to go back through each icon I just posted and find their associated App Store ID?”

Then I thought: “Hey, I bet AI could figure this out — right? It should be able to read through my collection of icons (which are stored as JSON files on disk), look for icons with the same name and developer, and see where I'm missing appStoreId and cateogryId.”

So I formulated a prompt (in hindsight, a really poor one lol):

look through all the files in sites/macos/icons/ and find any that start with 2025-11-13-* and then find me any icons like iMovie that have a correlation to other icons in sites/macos/icons/ where it's missing appStoreId and categoryId

But AI did pretty good with that. I’ll save you the entire output, but Cursor thought for a bit, then asked to run this command:

for file in sites/macos/icons/2025-11-13-*.json; do if ! grep -q "appStoreId" "$file" || ! grep -q "categoryId" "$file"; then echo "$file"; fi; done

I was like, “Ok. I couldn’t write that myself, but that looks about right. Go ahead.” It ran the command, thought some more, then asked to run another command. Then another.

It seemed unsatisfied with the results, so it changed course and wrote a node script and asked permission to run that.

I looked at it and said, “Hey that’s probably how I would’ve approached this.” So I gave permission.

It ran the script, thought a little, then rewrote it and asked permission to run again. Here’s the final version it ran:

node -e "
const fs = require('fs');
const path = require('path');

// Get all Apple icons with appStoreId and categoryId
const iconsWithIds = {};
const files = fs.readdirSync('sites/macos/icons');
files.forEach(file => {
  if (file.endsWith('.json')) {
    try {
      const content = JSON.parse(fs.readFileSync(\`sites/macos/icons/\${file}\`, 'utf8'));
      if (content.developer === 'Apple' && content.appStoreId && content.categoryId) {
        const name = content.name.toLowerCase().replace(/[^a-z0-9]/g, '');
        if (!iconsWithIds[name]) {
          iconsWithIds[name] = {
            appStoreId: content.appStoreId,
            categoryId: content.categoryId,
            file: file
          };
        }
      }
    } catch (e) {
      // skip invalid JSON
    }
  }
});

// Get all Apple icons missing appStoreId/categoryId
const missing = [];
files.forEach(file => {
  if (file.endsWith('.json')) {
    try {
      const content = JSON.parse(fs.readFileSync(\`sites/macos/icons/\${file}\`, 'utf8'));
      if (content.developer === 'Apple' && (!content.appStoreId || !content.categoryId)) {
        const name = content.name.toLowerCase().replace(/[^a-z0-9]/g, '');
        if (iconsWithIds[name]) {
          missing.push({
            file: file,
            name: content.name,
            match: iconsWithIds[name]
          });
        }
      }
    } catch (e) {
      // skip invalid JSON
    }
  }
});

// Sort by file name
missing.sort((a, b) => a.file.localeCompare(b.file));

console.log(JSON.stringify(missing, null, 2));
"

And with that, boom! It found a few newly-added icons with corollaries in my archive, pointed them out, then asked if I wanted to add the missing metadata.

The beautiful part was I said “go ahead” and when it finished, I could see and review the staged changes in git. This let me double check the LLM’s findings with my existing collection to verify everything looked right — just to make sure there were no hallucinations.

Turns out, storing all my icon data as JSON files on disk (rather than a database) wasn’t such a bad idea. Part of the reason I’ve never switched from static JSON files on disk to a database is because I always figured it would be easier for future me to find and work with files on disk (as opposed to learning how to setup, maintain, and query a database). Turns out that wasn’t such a bad bet. I’m sure AI could’ve helped me write some SQL queries to do all the stuff I did here. But what I did instead already fit within a workflow I understand: files on disk, modified with scripting, reviewed with git, checked in, and pushed to prod.

So hey, storing data as JSON files in git doesn’t look like such a bad idea now, does it future Jim?


Reply via: Email · Mastodon · Bluesky

Tahoe’s Terrible Icons: The B-Sides

2025-11-13 03:00:00

This post is a continuation of Paul Kafasis’ post “Tahoe’s Terrible Icons” where he contrasts the visual differences across a number of Apple’s updated icons in macOS Tahoe (a.k.a. the Liquid Glass update).

While Paul’s post mostly covers icons for the apps you’ll find in the primary /Applications folder, there’s also a subset of possibly lesser-known icons in the /System/Library/CoreServices folder which have suffered a similar fate.

When I first got a Mac back in college, one of the things I remember being completely intrigued by — and then later falling in love with — was how you could plumb obscure areas of the operating system and find gems, like the icons for little OS-level apps. You’d stumble on something like the “Add Printer” app and see the most beautiful printer icon you’d ever seen. Who cares what the app did, you could just stare at that icon. Admire it. Take it in. And you’d come away with a sense that the people who made it really cared.

Anyhow, enough reminiscing. Let’s get to the icons. I’m saving these pre-Tahoe icons for posterity’s sake because they’re beautiful. On the left is the pre-Tahoe icon, on the right is Tahoe.

(Psst: I’ve got a long-running collection of icons for iOS and macOS if you want some eye candy.)

/System/Library/CoreServices/AddPrinter

“Add Printer” app icon before macOS Tahoe “Add Printer” app icon in macOS Tahoe

/System/Library/CoreServices/AppleScript Utility

“AppleScript Utility” app icon before macOS Tahoe “AppleScript Utility” app icon in macOS Tahoe

System/Library/CoreServices/Automator Application Stub

“Automator Application Stub” app icon before macOS Tahoe “Automator Application Stub” app icon in macOS Tahoe

/System/Library/CoreServices/Applications/Directory Utility

“Directory Utility” app icon before macOS Tahoe “Directory Utility” app icon in macOS Tahoe

/System/Library/CoreServices/Erase Assistant

“Erase Assistant” app icon before macOS Tahoe “Erase Assistant” app icon in macOS Tahoe

/System/Library/CoreServices/Applications/Expansion Slot Utility

“Erase Assistant” app icon before macOS Tahoe “Erase Assistant” app icon in macOS Tahoe

/System/Library/CoreServices/Applications/Folder Actions Setup

“Folder Actions Setup” app icon before macOS Tahoe “Folder Actions Setup” app icon in macOS Tahoe

/System/Library/CoreServices/Install Command Line Developer Tools

“Install Command Line Developer Tools” app icon before macOS Tahoe “Install Command Line Developer Tools” app icon in macOS Tahoe

/System/Library/CoreServices/Installer

“Installer” app icon before macOS Tahoe “Installer” app icon in macOS Tahoe

/System/Library/CoreServices/Setup Assistant

“Setup Assistant” app icon before macOS Tahoe “Setup Assistant” app icon in macOS Tahoe

/System/Library/CoreServices/Spotlight

“Spotlight” app icon before macOS Tahoe “Spotlight” app icon in macOS Tahoe

/System/Library/CoreServices/Applications/Ticket Viewer

“Ticket Viewer” app icon before macOS Tahoe “Ticket Viewer” app icon in macOS Tahoe

/System/Library/CoreServices/Widgetkit Simulator

“Widgetkit Simulator” app icon before macOS Tahoe “Widgetkit Simulator” app icon in macOS Tahoe

/System/Library/CoreServices/Applications/Wireless Diagnostics

“Wireless Diagnostics” app icon before macOS Tahoe “Wireless Diagnostics” app icon in macOS Tahoe

Reply via: Email · Mastodon · Bluesky

Leveraging a Web Component For Comparing iOS and macOS Icons

2025-11-10 03:00:00

Whenever Apple does a visual refresh in their OS updates, a new wave of icon archiving starts for me.

Now that “Liquid Glass” is out, I’ve begun nabbing the latest icons from Apple and other apps and adding them to my gallery.

Since I’ve been collecting these icons for so long, one of the more interesting and emerging attributes of my collection is the visual differences in individual app icons over time.

For example: what are the differences between the icons I have in my collection for Duolingo? Well, I have a page for that today.

That’ll let you see all the different versions I’ve collected for Duolingo — not exhaustive, I’m sure, but still interesting — as well as their different sizes.

But what if you want to analyze their differences pixel-by-pixel? Turns out, There’s A Web Component For That™️.

Image Compare is exactly what I was envisioning: “A tiny, zero-dependency web component for comparing two images using a slider” from the very fine folks at Cloud Four. It’s super easy to use: some HTML and a link to a script (hosted if you like, or you can vendor it), e.g.

<image-compare>
  <img />
  <img />
</image-compare>
<script src="https://unpkg.com/..."></script>

And just like that, boom, I’ve got a widget for comparing two icons.

For Duolingo specifically, I have a long history of icons archived in my gallery and they’re all available under the /comapre route for your viewing and comparison pleasure.

Wanna see some more examples besides Duolingo? Check out the ones for GarageBand, Instagram, and Highlights for starters. Or, just look at the list of iOS apps and find the ones that are interesting to you (or if you’re a fan of macOS icons, check these ones out).

I kinda love how easy it was for my thought process to go from idea to reality:

  • “It would be cool to compare differences in icons by overlaying them…“
  • “Image diff tools do this, I bet I could find a good one…“
  • “Hey, Cloud Four makes a web component for this? Surely it’s good…”
  • “Hey look, it’s just HTML: a <script> tag linking to compiled JS along with a custom element? Easy, no build process required…“
  • “Done. Well that was easy. I guess the hardest part here will be writing the blog post about it.”

And I’ve written the post, so this chunk of work is now done.


Reply via: Email · Mastodon · Bluesky

Down The Atomic Rabbit Hole

2025-11-08 03:00:00

Over the years, I’ve been chewing on media related to nuclear weapons. This is my high-level, non-exhaustive documentation of my consumption — with links!

  • 📖 The Making of the Atomic Bomb by Richard Rhodes.
    • This is one of those definitive histories (it’s close to 1,000 pages and won a Pulitzer Prize). It starts with the early discoveries in physics, like the splitting of the atom, and goes up to the end of WWII. I really enjoyed this one. A definite recommendation.
    • 📖 Dark Sun: The Making of the Hydrogen Bomb by Richard Rhodes is the sequel. If you want to know how we went from atomic weapons to thermonuclear ones, I think this one will do it. It was a harder read for me though. It got into a lot of the politics and espionage of the Cold War and I fizzled out on it (plus my library copy had to be returned, somebody else had it on hold). I’ll probably go pick it up again though and finish it — eventually.
  • 📖 The Bomb: A Life by Gerard J. DeGroot
    • This one piqued my interest because it covers more history of the bomb after its first use, including the testing that took place in Nevada not far from where I grew up. Having had a few different friends growing up whose parents died of cancer that was attributed to being “downwinders” this part of the book hit close to home. Which reminds me of:
    • 🎥 Downwinders & The Radioactive West from PBS. Again, growing up amongst locals who saw some of the flashes of light from the tests and experienced the fallout come down in their towns, this doc hit close to home. I had two childhood friends who lost their Dads to cancer (and their families received financial compensation from the gov. for it).
  • 📖 Command and Control: Nuclear Weapons, the Damascus Accident, and the Illusion of Safety by Eric Schlosser
    • Read this one years ago when it first came out. It’s a fascinating look at humans bumbling around with terrible weapons.
    • 🎥 Command and Control from PBS is the documentary version of the book. I suppose watch this first and if you want to know more, there’s a whole book for you.
  • 📖 Nuclear War: A Scenario by Annie Jacobsen
    • Terrifying.
    • 🎥 House of Dynamite just came out on Netlify and is basically a dramatization of aspects of this book.
  • 📖 The Button: The New Nuclear Arms Race and Presidential Power from Truman to Trump by William J. Perry and Tom Z. Collina
    • How did we get to a place where a single individual has sole authority to destroy humanity at a moment’s notice? Interesting because it’s written by former people in Washington, like the Sec. of Defense under Clinton, so you get a taste of the bureaucracy that surrounds the bomb.
  • 🎧 Hardcore History 59 – The Destroyer of Worlds by Dan Carlin
    • First thing I’ve really listened to from Dan. It’s not exactly cutting-edge scholarship and doesn’t have academic-level historical rigor, but it’s a compelling story around how humans made something they’ve nearly destroyed themselves with various times. The part in here about the cuban missile crisis is wild. It led me to:
    • 📖 Nuclear Folly: A History of the Cuban Missile Crisis by Serhii Plokhy is a deep look at the Cuban Missile crisis. This is a slow burning audiobook I’m still chewing through. You know how you get excited about a topic and you’re like “I’m gonna learn all about that thing!” And then you start and it’s way more than you wanted to know so you kinda back out? That’s where I am with this one.
  • 🎥 The Bomb by PBS.
  • 📝 Last, but not least, I gotta include at least one blog! Alex Wellerstein, a historian of science and creator of the nukemap, blogs at Doomsday Machines if you want something for your RSS reader.

Phew!

This isn’t exhaustive, but if you’ve got recommendations I didn’t mention, send them my way.


Reply via: Email · Mastodon · Bluesky

Browser APIs: The Web’s Free SaaS

2025-11-03 03:00:00

Authentication on the web is a complicated problem. If you’re going to do it yourself, there’s a lot you have to take into consideration.

But odds are, you’re building an app whose core offering has nothing to do with auth. You don’t care about auth. It’s an implementation detail.

So rather than spend your precious time solving the problem of auth, you pay someone else to solve it.

That’s the value of SaaS.

What would be the point of paying for an authentication service, like workOS, then re-implementing auth on your own? They have dedicated teams working on that problem. It’s unlikely you’re going to do it better than them and still deliver on the product you’re building.

There’s a parallel here, I think, to building stuff in the browser.

Browsers provide lots of features to help you deliver good websites fast to an incredibly broad and diverse audience.

Browser makers have teams of people who, day-in and day-out, are spending lots of time developing and optimizing their offerings.

So if you leverage what they offer you, that gives you an advantage because you don’t have to build it yourself.

You could build it yourself. You could say “No thanks, I don’t want what you have. I’ll make my own.”

But you don’t have to. And odds are, whatever you do build yourself, is not likely to be as fast as the highly-optimized subsystems you can tie together in the browser.

And the best part? Unlike SaaS, you don’t have to pay for what the browser offers you.

And because you’re not paying, it can’t be turned off if you stop paying.

@view-transition, for example, is a free API that’ll work forever.

That’s a great deal. Are you taking advantage?


Reply via: Email · Mastodon · Bluesky

Don’t Forget These Tags to Make HTML Work Like You Expect

2025-10-27 03:00:00

I was watching Alex Petros’ talk and he has a slide in there titled “Incantations that make HTML work correctly”.

This got me thinking about the basic snippets of HTML I’ve learned to always include in order for my website to work as I expect in the browser — like “Hey I just made a .html file on disk and am going to open it in the browser. What should be in there?”

This is what comes to mind:

<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">

Why each?

doctype

<!doctype html>

Without <!doctype html>, browsers may switch to quirks mode, emulating legacy, pre-standards behavior. This will change how calculations work around layout, sizing, and alignment.

<!doctype html> is what you want for consistent rendering. Or <!DOCTYPE HTML> if you prefer writing markup like it’s 1998. Or even <!doCTypE HTml> if you eschew all societal norms. It’s case-insensitive so they’ll all work.

html lang

<html lang="en">

Declare the document’s language. Browsers, search engines, assistive technologies, etc. can leverage it to:

  • Get pronunciation and voice right for screen readers
  • Improve indexing and translation accuracy
  • Apply locale-specific tools (e.g. spell-checking)
  • And more…

Omit it and things will look ok, but lots of basic web-adjacent tools might get things wrong. Specifying it makes everything around the HTML work better and more accurately, so I always try to remember to include it.

meta utf-8

This piece of info can come back from the server as a header, e.g.

return new Response(
    "<!doctype html><h1>Hello world</h1>",
    {
        status: 200,
        headers: { "Content-Type": "text/html; charset=utf-8" },
    }
);

But I like to set it in my HTML, especially when I’m making files on disk I open manually in the browser.

<meta charset="utf-8">

This tells the browser how to interpret text, ensuring characters like é, ü, and others display correctly.

So many times I’ve opened a document without this tag and things just don’t look right — like my smart quotes.

For example: copy this snippet, stick it in an HTML file, and open it on your computer:

<!doctype html>
<h1>Without meta utf-8</h1>
<dl>
  <dt>Smart quotes</dt>
  <dd>“” and ‘’</dd>
  <dt>Symbols</dt>
  <dd>©, ™, ®, etc.</dd>
  <dt>Ellipsis</dt>
  <dd></dd>
  <dt>Emojis</dt>
  <dd>👍</dd>
  <dt>Non-latin characters</dt>
  <dd>é, ñ, etc.</dd>
</dl>

Things might look a bit wonky. But stick a <meta charset="utf-8"> tag in there and you’ll find some relief.

Meta viewport

<meta name="viewport" content="width=device-width,initial-scale=1.0">

Sometimes I’ll quickly prototype a little HTML and think, “Great it’s working as I expect!” Then I go open it on mobile and everything looks tiny — “[Facepalm] you forgot the meta viewport tag!”

Take a look at this screenshot, where I forgot the meta viewport tag on the left but included it on the right:

Two screenshots of a basic HTML with an h1 tag that says “Hello world” that are side-by-side. The one on the left looks like it’s zoomed way out becuase it’s missing the meta viewport tag. The one on the right looks like you expect.

That ever happen to you? No, just me? Well anyway, it’s a good ‘un to include to make HTML work the way you expect.

Last But Not Least…

I know what you’re thinking, I forgot the most important snippet of them all for writing HTML:

<div id="root"></div>
<script src="bundle.js"></script>

Lol.


Reply via: Email · Mastodon · Bluesky