← back to log
Day 007 2026-07-30 shipped

Rewrote the IPC layer, deleted 400 lines

The desktop tool had nodeIntegration: true since day one because it was faster to build that way. It also meant the renderer could call fs directly, which meant every feature had grown its own ad-hoc file access scattered across components.

Today I turned it off.

What it took

Every Node call in the renderer had to move behind contextBridge. I expected this to be tedious and it was, but the surprise was how much code just evaporated. Four different components had each written their own “read the project JSON, parse it, handle the missing file case” logic. Now there’s one:

// preload.js
contextBridge.exposeInMainWorld('api', {
  readProject: (id) => ipcRenderer.invoke('project:read', id),
  writeProject: (id, data) => ipcRenderer.invoke('project:write', id, data),
  listProjects: () => ipcRenderer.invoke('project:list'),
});

Net: -412 lines, and the renderer no longer knows what a file path is.

What broke

The file watcher. It was using fs.watch in the renderer to refresh the project list, and there’s no clean way to push events across the bridge without setting up the reverse channel. Ended up with ipcRenderer.on('projects:changed', ...) and a webContents.send from main.

Worth noting for later: the watcher fires twice on Windows for a single save. Debounced it at 120ms, which is a bandaid, not a fix.

Tomorrow

Same treatment for the ffmpeg calls. Those are worse — they stream progress, so it’s not a simple request/response.


Contact

Recognise this problem?

Open to freelance and contract work — desktop tools, automation, video pipelines.