Precode
52 Products in 52 Weeks

Week 13: MacroFlow — desktop and browser automation that doesn't break

·5 min read
Week 13: MacroFlow — desktop and browser automation that doesn't break

If you've ever recorded a macro to automate a repetitive workflow, you know the frustration: it works perfectly for two weeks, then the website you're automating updates their CSS classes, and everything breaks. You're back to manual clicking.

It's worse when your workflow spans both desktop apps and the browser. Copy data from Excel, paste it into a web form, click through a few screens, repeat. Most automation tools make you choose: automate the desktop OR automate the browser. Not both.

What we built

MacroFlow is a hybrid macro automation platform for macOS. It records your interactions across native desktop applications and Chrome browser tabs in a single unified timeline. Click through your workflow once, and MacroFlow captures everything — regardless of whether you're in Finder, a spreadsheet app, or a web-based CRM.

The standout feature is self-healing selectors. When you record a browser interaction, MacroFlow doesn't just store the CSS selector — it captures a fingerprint of the element: its ID, text content, aria-label, CSS classes, and position in the DOM. When playback runs and the original selector fails (because the website changed), MacroFlow scans the page and finds the best matching element using weighted attribute scoring. If confidence is above 70%, it auto-recovers and logs the healing for your review.

We also built Stream Deck integration, because if you're automating repetitive workflows, triggering them from a physical button feels genuinely satisfying. Press a button, watch the LED change colour as your macro executes, done.

How we built it

This was a three-day autonomous build sprint using our Ralph Wiggum loop — 35 user stories, implemented one at a time, each committed when TypeScript passes. The result: 40 commits, roughly 7,000 lines of strict TypeScript, zero any types.

The stack: Electron 33 for the desktop app, React 19 for the UI, SQLite with WAL mode for local storage, and a Chrome MV3 extension connected via Native Messaging. Stream Deck talks to the app over WebSocket.

A few decisions worth mentioning:

Zod discriminated unions everywhere. Every data type — macro steps, triggers, settings — is defined as a Zod schema. TypeScript types are inferred from schemas, never manually maintained. This catches invalid data at every boundary: IPC handlers, import validation, WebSocket messages. It's more upfront work, but it eliminates entire categories of runtime bugs.

Self-healing without machine learning. The healing algorithm uses weighted attribute matching: ID gets 40% weight, inner text 30%, aria-label 20%, class similarity (Jaccard index) 10%. Simple maths, predictable results, and you can actually debug it when something goes wrong. The healing log makes every decision auditable.

Native Messaging over WebSocket for Chrome. Chrome's MV3 service workers get terminated after 30 seconds of inactivity. Native Messaging (length-prefixed JSON over stdio) handles reconnection more gracefully than WebSocket would, and there's no port conflict headaches.

What went well

The self-healing approach works better than expected. Weighted attribute scoring is simple enough to explain in a sentence, yet robust enough to catch most CSS class rotations and ID changes that break traditional macro tools. Keeping the scan scoped to matching tag types keeps it under 50ms on typical pages.

The three-app architecture — Electron coordinator, Chrome extension, Stream Deck plugin — came together cleanly. Three different communication protocols (IPC, Native Messaging, WebSocket), but they feel like one cohesive system.

The code review at the end caught 12 issues: 6 security blockers (AppleScript injection vectors, IPC input validation gaps) and 6 improvements. All fixed same day. Having a thorough security pass before shipping isn't optional for a tool that executes commands on your machine.

What was harder than expected

Chrome MV3 service worker lifecycle is genuinely painful. The service worker can terminate at any moment, and your connection logic needs exponential backoff and clean reconnection handling. We got it working, but it's more code than it should be.

Semantic anchoring — handling dropdowns and modals that don't exist in the DOM until you trigger them — required recording parent-child relationships and adding a 'click anchor, wait for target, then interact' sequence during playback. It works, but it's more complex than capturing a simple click.

No tests yet. Type safety and the code review served as the primary quality gate for this sprint. It's tech debt we'll need to address — self-healing scoring and import validation are the obvious candidates for unit tests.

The methodology in practice

This is how we approach Build Sprints for clients: fixed scope, fixed timeline, working software at the end. Not a prototype, not a Figma file — a real application you can use.

MacroFlow went from blank repo to functional MVP in three days. That's not magic; it's the result of making decisions quickly, keeping scope tight, and shipping something that works rather than designing something perfect.

Try it

MacroFlow is available for early access at macroflow-web.vercel.app. If you're tired of your automations breaking every time a website updates, sign up for the waitlist and we'll let you know when it's ready.