Creating Toolbox: From Four File Utilities to a Desktop App
Every so often, I needed to do one small thing to a file: remove a password from a PDF I could already open, convert an image, make it smaller, or resize it. There were websites that offered these tools for free, but documents can contain private information, and I didn't trust those sites with mine. I wanted the convenience of file utilities without sending my documents away. That became the privacy-first, on-device idea behind Toolbox.
The first version was deliberately small. It was a native macOS app with four tools: remove a PDF password, convert an image format, compress images, and resize them. I focused on making those few jobs dependable before adding more.
Start with the file, not the interface
I built the first version with SwiftUI, but kept the file-processing logic in a separate layer from the views. A processor took a file and options, then returned a result. That made the behavior testable without launching the app and kept interface changes from becoming file-processing changes.
I also wanted a bad input to be a small failure. If one file in a batch could not be processed, the rest should still finish. The app needed to show which files succeeded and which did not, without making users wonder what happened to their originals.
That led to a few rules that still guide Toolbox:
- Leave the input file untouched.
- Save each result as a separate file with a clear name.
- Never overwrite an existing file; choose a new output name when there is a collision.
- Keep processing a batch when one input fails.
These rules sound basic, but file utilities earn trust through their output behavior as much as through their buttons.
Real files exposed the hard parts
The first surprises came from testing with realistic PDFs and images rather than assuming a successful API call meant the result was right.
For example, writing an unlocked PDF back out could leave its encryption information in place. The copy still asked for a password. The fix was to rebuild the document from its decrypted pages, with a fallback for PDFs the usual path could not handle. The tests checked that the output text remained searchable too, so a workaround could not quietly turn the document into a pile of page images.
Image compression brought a different lesson: re-encoding does not guarantee a smaller file. Some formats are already efficient, and a supposedly lossless conversion can make a photo much larger. Toolbox now keeps the original format for lossless compression and falls back to copying the original bytes when a new encoding would increase the file size. The result is still a separate output; the source stays as it was.
Grow the shared workflow, then add tools
Once file selection, progress, batch handling, and results were shared parts of the app, new utilities could reuse them. I expanded the catalog into PDF page work, document conversion, image operations, GIFs, and TIFFs. Each tool still has its own job, while common behavior—like processing files independently—has one implementation.
This helped keep the app cohesive as the list grew. Toolbox was not meant to be a folder of unrelated scripts; I wanted one place to choose a task, work on a group of files, and find the results.
From a Mac app to a cross-platform project
The original SwiftUI app gave me a fast route to a focused Mac utility. As the project grew, I wanted Toolbox to serve Windows users too. I started a Tauri version and eventually made it the primary app. Today its interface is built with React and TypeScript, while Rust and bundled native helpers handle file operations. The same product principles still apply: process files on the device, keep originals intact, and make failures visible.
That move added work of its own. File formats do not behave identically across platform libraries, and native helpers need to be included and found in each installer. Tests need to check the produced files, not just whether a button was clicked. The project now uses UI automation and native file fixtures to check its tools.
What “local-first” means here
Toolbox does not upload the documents or images it processes. The current release does use the network to check for signed updates, and release builds record anonymous install and app-open events. Those events do not include file names, paths, or contents. I want the privacy description to say what happens in practice, rather than imply the app never makes a network request. More details are in the privacy policy.
The project is still evolving, and not every listed feature is available in every build. But the original goal remains useful: put the small file tasks I keep needing into a desktop app, make each operation predictable, and let me work on my files without sending them to an online converter.
You can try Toolbox or browse the source on GitHub.