Notes

Cobalt Next for Zed

Having recently switched from Cursor back to VSCode, I figured it was probably worth taking another look at Zed – if only so I have a second text editor configured how I like it. Zed comes with a lot of the features I want baked-in and it was pretty trivial to port my VSCode config over. But I was surprised to discover that nobody seems to have ported the Cobalt Next theme to Zed’s theme format.

So I went ahead and did that as best I could…

screenshot of Cobalt Next theme in Zed

You can grab it on GitHub.

copy / view as markdown

Switching back to VSCode

I switched from VSCode to Cursor as my main editor sometime in early 2025, primarily for the much better tab completion it had at the time. As my workflow has evolved over the last six months I’ve found myself using Cursor’s AI features less and less, to the point where for the most part I just use it as a normal text editor. While AI tab-completion is still sometimes useful, I’m finding more and more that anywhere I’m manually writing or editing code, normal non-AI completion tends to do the job just fine.

Cursor 3 was announced on April 3, and as expected it isn’t really a text editor anymore. I’ve yet to spend much time with it but I do know that there’s no longer a good reason for me to use Cursor as a text editor.

How I use a text editor in April 2026

My usual workflow at the moment involves having a project (or worktree) directory open in Ghostty with two panes: one for Claude Code TUI and one for a standard terminal. I have the same directory open in my editor, primarily for five things:

  1. Visually reviewing & tweaking uncommitted changes made by Claude.
  2. Manually staging and committing changes – I still mostly prefer to make my own commits on feature branches.
  3. Manually exploring & referencing the codebase as I’ve always done, only now it’s often so I can provide input to Claude rather than make edits myself.
  4. Manually writing or editing markdown files which describe requirements, tasks or plans before I have Claude read them.
  5. Occasionally, writing or editing actual code.

This means I basically need a normal editor like VSCode configured how I like it, with a few specific additions:

  • “Standard” language extensions, as well as things like prettier, path-intellisense etc.
  • Just enough git-related extensions that it’s well-integrated with git and GitHub.
  • Extensions and configuration which makes it easy to work with markdown and CSV files.

My “new” VSCode setup

My guiding principle is to keep VSCode as close to its defaults as possible. I add cosmetic tweaks, formatting and linting, a fairly heavy markdown layer, and a deliberately small amount of AI tooling. Everything else is left alone.

The theme is Cobalt Next with a custom version of Operator Mono that includes coding ligatures, set in 15px 200 weight. I’ve moved the activity bar to the top of the window rather than its default spot on the left, and the cursor blinks with the “expand” animation and has smooth movement on. The minimap auto-hides and renders as colour blocks without characters. I use Atom-style keybindings (muscle memory from years ago that I’ve never managed to shake) and set a few other bits:

  • Tabs are 2 spaces.
  • Final newlines get inserted on save and trailing whitespace gets trimmed (except in markdown and YAML).
  • Prettier is the default formatter except for markdown, configured globally with single quotes, no semicolons, and proseWrap: preserve so it doesn’t reflow my paragraphs.
  • ESLint runs on save rather than as I type.

Format-on-save is deliberately off to avoid accidental big diffs when working with AI-generated code – I toggle it on or run it manually when I need it.

Markdown is the most heavily customised part of the whole config, because I write a lot of markdown and the defaults do things I actively dislike. Prettier is too aggressive with markdown – it reflows paragraphs, fights with my line break conventions and reformats lists in annoying ways. So .md files use yzhang.markdown-all-in-one as their default formatter instead, which is much more conservative.

I disable markdown link validation, soft-wrap at column 100, and preserve trailing whitespace. I also customise the editor token colours so H1s render in cyan and H2s in magenta, which makes long documents much easier to scan. cmd+b and cmd+i are remapped to toggle bold and italic when in markdown or MDX files. I also have the following extensions installed:

  • Markdown All in One - Provides formatting, TOC generation, list continuation, keyboard shortcuts, basic table editing, and is the default formatter.
  • Markdown Extended - Adds a bunch of other niceties.
  • Markdown Table - Makes working with tables a little easier.

For AI I have exactly three extensions installed:

  • The official Claude Code IDE extension, which connects the editor to a Claude Code session running in the terminal.
  • GitHub Copilot – purely for inline tab completion, disabled for plaintext, markdown etc.
  • GitHub Copilot Chat – installed only for the “Generate commit message” sparkle button in the Source Control view. I don’t use the chat panel, but the button is a feature of this extension and there’s no way to get it without installing the whole thing.

Git settings are minimal: smart commit is on (so I can commit without staging first), auto-fetch is on, and the “confirm sync” dialog is off. I have the GitHub PR extension and a file-history extension installed, but most of my git work happens in the terminal with git and gh – the extensions are just for the cases where in-editor review is genuinely faster.

I also have language extensions for the things I work with regularly (Astro, Tailwind, Bun, Rust, Tauri, TOML, Vitest), plus a CSV editor and a PDF viewer.

The full description lives in vscode-setup.md in my dotfiles repo, along with the contents of my settings.json if you want to copy any of it.

copy / view as markdown

Quoting: An appreciation for (technical) architecture (Interconnected)

But we want AI agents to solve coding problems quickly and in a way that is maintainable and adaptive and composable (benefiting from improvements elsewhere), and where every addition makes the whole stack better.

So at the bottom is really great libraries that encapsulate hard problems, with great interfaces that make the “right” way the easy way for developers building apps with them. Architecture!

While I’m vibing (I call it vibing now, not coding and not vibe coding) while I’m vibing, I am looking at lines of code less than ever before, and thinking about architecture more than ever before.

I am sweating developer experience even though human developers are unlikely to ever be my audience.

copy / view as markdown

Roberts Radios

istream-radio.jpg

I recently bought a Roberts Revival iStream 3L radio having wanted one for some time. It’s a beautiful thing. I grew up in a house where Radio 4 was on constantly, mostly playing on an old Roberts which my mum carried about the house with her.

While I have Amazon Echos in my kitchen, living room and office/bedroom, I still regularly find myself using my phone to listen to podcasts and radio shows because I can take it with me as I potter about the house and garden. I have two problems with this:

  1. Phone speakers are rubbish and wearing headphones at home is both isolating for me and anyone else I’m sharing a room with.
  2. I’m trying to spend more time with my phone tucked away in a drawer and out of mind.

What I really want here is a dedicated portable device which can play the radio without requiring my phone or laptop to even be switched on. I want it to sound warm like Radio 4 should, regardless of the volume. I want it to feel good and make me smile. I basically want my mum’s old Roberts radio.

But I also want a few modern features – I want it to:

  • Play podcasts while my phone is off.
  • Have an alarm clock and sleep timer.
  • Act as a bluetooth speaker if I need it.
  • Recharge itself when plugged in to mains power.

Which is exactly what the Revival iStream 3L does. It looks, feels and sounds like a classic Roberts and supports FM & DAB+ radio as you’d expect, plus bluetooth, 3.5mm jack and USB stick inputs. It has an alarm and sleep timer. It runs on mains power or six normal AA batteries, but with the flip of a switch and six 2000mAh NiMH rechargable batteries it becomes rechargable like any other modern device. It is exactly the kind of high-quality, well-thought-out, future-proof product that Roberts built their reputation on decades ago.

And importantly for me, it also supports Internet Radio & Podcasts via Frontier Smart’s tech with Airable providing the online catalog, so I can search & stream online radio stations and podcasts while my phone and laptop are both switched off. It also supports streaming services like Spotify, Deezer & Amazon Music, though they all require another device to search and select songs.

All-in-all it’s a cracking piece of kit. And so long as I keep the firmware updated, I expect it to remain so for a lot longer than most modern devices.

It also runs a webserver!

The useguide recommends using the UNDOK app as an easier interface for configuring & managing the radio, which got me wondering how the two communicated. Turns out the radio exposes a simple HTTP API on your local network called FSAPI. Given you know the radio’s IP address, you can establish a session and send simple GET requests to it which return XML:

curl "http://192.168.1.72/fsapi/GET/netRemote.sys.audio.volume?pin=1234"

will return

<fsapiResponse>
<status>FS_OK</status>
<value>
<u8>2</u8>
</value>
</fsapiResponse>

showing that the current volume is 2.

There are quite a few pre-existing libraries for interacting with FSAPI devices, but the interface is small & simple enough that they’re not really necessary.

Building a macOS menubar app

After digging about with curl for a while I asked Claude Code to make a SwiftUI macOS menubar app which shows what’s playing on the radio and lets me control it from my mac. After a couple of hours’ iteration we ended up with this:

screenshot.png

If you have an iStream3L – or possibly any other FS-based radio – you can download a zip containing the menubar app from here. It should auto-discover your radio when you first run it.

copy / view as markdown

Older Notes