Why I built Textorium
600 Hugo posts, one file tree, and the moment I decided grep wasn't a content management strategy.
Duration: 5:19 | Size: 6.1 MB
Over 600 posts across several Hugo sites. For years, the content management workflow was: open VS Code, expand the file tree, squint at filenames, grep when something wasn’t turning up. Checking which posts were still in draft meant running a shell one-liner against the frontmatter. Sorting by date or word count meant piping things through awk.
It worked. It always works, technically. But one morning, trying to find a post about infrastructure, I opened six files before I found it. The filename wasn’t what I expected. That was the moment: this is a database problem, and a file browser is the wrong tool.
The gap
Static site generators are brilliant. Hugo builds my sites in milliseconds. The content lives as plain Markdown files, no database, no vendor lock-in, no sync conflicts. Wouldn’t trade that for anything.
But once you have a few hundred posts, the filesystem stops being useful for browsing. You need to search across titles, filter by status, sort by date. You need metadata at a glance, not buried in YAML at the top of each file.
Web-based CMSs like Forestry and Netlify CMS solve this, but they want to own the workflow. APIs, sync processes, cloud dependencies. They add a layer between you and your files. The content management part is good. The middleman part isn’t.
Why native
It had to be a Mac app. Too many Electron editors take 3 seconds to launch and eat 400MB of RAM to display text. For a writing tool, something you open quickly and use for a few minutes, startup time matters. So does feeling like it belongs on your machine.
SwiftUI made this practical. A table view with sortable columns, a split-pane editor, a metadata panel: built-in components, not custom widgets to maintain. The whole app is under 10MB. It launches instantly.
The tradeoff is platform lock-in. Textorium is macOS only. A mediocre cross-platform app helps nobody. A focused Mac app helps the people who use Macs. That tradeoff is fine.
The table view
The feature that made the project click was the table view. All posts as rows: title, date, draft status, word count, folder. Sort by date to find recent work. Filter to drafts only. Search across titles and body text. See word counts without opening each file.
It sounds simple. Going from “open file, read frontmatter” to “scan a table” is the difference between a filing cabinet and a spreadsheet.
Customizable columns came next. Not every site uses the same frontmatter fields. Some have categories, some have tags, some have custom taxonomies. The app auto-discovers frontmatter fields and lets you add them as columns.
Two interfaces
About a month in, terminal access started to seem necessary too. When you’re already in the terminal running hugo server, switching to a GUI app to find a post creates friction. But building a TUI in SwiftUI doesn’t make sense.
The first terminal version was Python. It worked, but it took over a second to start on a site with 621 posts. For a terminal tool, that’s an eternity. The Rust rewrite with ratatui brought startup down to 28 milliseconds. Navigation is instant. Search is real-time.
The two interfaces share no code but they share a philosophy: your files are the source of truth, the app is just a lens.
What shipping taught me
First commit: December 15, 2025. App Store: January 27, 2026. Rust TUI on Homebrew a week after that. About six weeks, start to finish.
Some things that surprised me:
SwiftUI’s Table has real limitations. You can’t drag to reorder columns. You can’t customize column headers with icons. Workarounds exist (a column customization sheet, display names instead of field keys), but the constraints are real. SwiftUI’s Table is adequate for v1. “Adequate” is working hard in that sentence.
The first release had a silent data corruption bug in nested frontmatter. When you saved a post with nested YAML structures (dictionaries inside dictionaries), Swift’s string interpolation would convert them to debug output instead of preserving the YAML. Your frontmatter would go from author: {name: "Paul"} to author: [AnyHashable("name"): Paul]. Caught it in v1.0.1. Type safety isn’t optional when you’re writing to someone’s files.
macOS app sandboxing initially felt restrictive. Every file access needs a security-scoped bookmark. But it forces good design. The user explicitly picks their content folder. The app can’t silently access anything else. When someone trusts you with their unpublished writing, that boundary matters.
Where it’s going
Textorium is a content browser and editor. There are great Git tools already. Hugo doesn’t need me to replicate its build system. The value is in the layer between: managing the content itself.
Some light Git integration is coming. Enough to commit and sync without switching to the terminal. Click a button, your changes are saved. No staging areas, no branch management.
What does a writing tool owe its users? Speed. Don’t corrupt my files. Show me what’s happening. And do one thing well.
Static site generators got the hard part right: fast builds, plain files, no lock-in. The authoring experience just needed to catch up.
Why customer tools are organized wrong
This article reveals a fundamental flaw in how customer support tools are designed—organizing by interaction type instead of by customer—and explains why this fragmentation wastes time and obscures the full picture you need to help users effectively.
Infrastructure shapes thought
The tools you build determine what kinds of thinking become possible. On infrastructure, friction, and building deliberately for thought rather than just throughput.
Server-side dashboard architecture: Why moving data fetching off the browser changes everything
How choosing server-side rendering solved security, CORS, and credential management problems I didn't know I had.
The work of being available now
A book on AI, judgment, and staying human at work.
The practice of work in progress
Practical essays on how work actually gets done.
The day the strategy became a price tag
Most strategies die in the gap between "we should do this" and "here's what it costs." The ones that survive are the ones that hit a number before lunch.
The moment your team starts talking without you
The most important thing a leader can build is the conversation that happens when they leave the room. Today, five departments started sharing fixes, cracking jokes, and solving each other's problems — without being asked.
I ran my AI agency's first real engagement. Here's everything that happened.
Five AI personas. One client onboarding. Fifteen minutes of things going wrong in instructive ways.
Textorium is live on the app store
Textorium launches on Mac App Store - a native editor for Hugo, Jekyll & Eleventy that manages hundreds of posts with table views and smart filtering.
How i eliminated networking complexity: Docker tailscale sidecar patterns
Simplify your container networking with Docker Tailscale sidecar patterns, eliminating complexity and enhancing security for your self-hosted projects.
When teaching stops being bounded
AI removes the constraints that gave teaching its shape—one teacher, thirty students, limited time. But lifting constraints doesn't make the work easier. It makes it different. Teachers trained for a bounded classroom now face an unbounded role that requires judgment, discernment, and presence in ways we haven't yet mapped.