# Potatofield Image Toolkit: sharp in the main process, Vue 3 in the renderer

> The Potatofield Image Toolkit is a rewrite of an Electron 11 and Vue 2 tool onto Electron 44, Vue 3 and electron-vite, with eleven image utilities running through sharp in the main process over IPC. Its update compatibility rules and its separated data model are the parts a maintainer has to respect.

**CNOliverZhang/PotatofieldImageToolkit** — 一个适用于摄影从业者/爱好者、设计师等创意行业从业者的图像工具箱。

- Repository: https://github.com/CNOliverZhang/PotatofieldImageToolkit
- Stars: 1,016 · Forks: 126
- Language: Vue
- License: not declared
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/cnoliverzhang-potatofieldimagetoolkit

## A second rewrite of the same product, on a much newer Electron

This is the modern rewrite of an older project, PotatofieldImageToolkit, which was built on Electron 11 and Vue 2. The current version is 5.1.0, and the release history shows three tags landed on the same day in October 2026, with 5.1.0 and 5.0.0 both dated 2026-10-10 alongside 3.5.1, and the last push on 2026-10-12.

The stack in the current version is a big jump. The runtime is Electron 44, which requires Node 22.12 or newer, and the build tool is electron-vite 5. The frontend is Vue 3 with the script setup syntax and TypeScript, Vue Router 4, and Pinia for state. The UI layer uses the Fluent UI web components at version 3 with the matching token package, plus Font Awesome icons.

The product itself is an image toolbox aimed at photographers, hobbyists, designers and other creative workers, and it ships eleven tool modules: watermarking including a fullscreen watermark mode, long image stitching, cropping, splitting, resizing, compression, format conversion, EXIF reading, colour extraction, font management, and watermark templates. A rich text image editor feature was moved out into a separate product with only a jump link left inside the app, and several single image tools also exist in a batch form that opens in a separate window.

The description and product name in package.json are in Chinese, and the README is written in Chinese as well, which is worth knowing before you plan to contribute.

## sharp runs in the main process, and that is the whole architectural point

The single most important decision in the codebase is where image processing happens. The image work uses sharp, and it runs in the main process. The renderer process calls it over IPC, and the stated reason is to avoid blocking the UI.

That is the correct call for this kind of application. sharp is a native module, and native work in a renderer process either blocks the interface or has to be pushed to a worker anyway. Putting the codec and pixel operations on the main process side and treating the renderer as a thin view means a batch conversion of a few hundred images does not freeze the window.

The directory layout reinforces the separation. Under src/ there is main/ for window management, IPC, the sharp image processing, updates and file IO, preload/ where the contextBridge exposes window.api, shared/ for types shared between main and renderer, and renderer/ which holds the router, stores, composables, utils, components and pages. That shared/ directory is a small thing that pays off: the types used across the process boundary are declared once, so a change to an IPC payload breaks the compiler in both places at once rather than at runtime in one of them.

The preload script exposing a single window.api surface is the other half of the discipline. The renderer does not reach into Node directly, it calls through the bridge, which keeps the context isolation that Electron expects.

## State belongs in Pinia, and shared data belongs in the main process

State persistence is handled by Pinia together with pinia-plugin-persistedstate, which writes to localStorage. The README then attaches a warning that matters more than it looks: data shared across windows, such as the template library, must be kept in the main process, and multi-window shared localStorage must not be relied on.

This is a specific and well known Electron failure mode. Each renderer window has its own localStorage in the sense that writes are not synchronised between windows, so two windows open on the template library can diverge, and the last write wins for whoever reads next. The project's rule sidesteps the problem rather than trying to synchronise it, and the comment in the README is explicit that this is a design rule for contributors, not a suggestion.

The same shape shows up in the tool list. Single image tools such as watermarking, resizing, compression, format conversion and cropping support a batch form, and that batch form runs in a separate window. A batch window is precisely the case where the template library or the font list has to be consistent across the application, which is why the rule about the main process is stated next to the feature list rather than buried.

There is also a documentation file dedicated to the difference between single image and batch behaviour, docs/TOOL_CONSISTENCY.md, which records the decisions on how the two modes should diverge. That is the kind of file that saves an afternoon when someone asks why a setting applies in one mode and not the other.

## The packaging constants that must never change

The most operationally important part of the README is the update compatibility section, and it is marked as important.

For a client already running an old version to update automatically to a new one, several values have to stay fixed. The appId must remain cn.potatofield.imagetoolkit, the publish url must remain the files.potatofield.cn ImageToolkit Packages directory, the Windows target must stay nsis and the macOS target must stay dmg, and asar must stay false. The stated reason for keeping the publish url is compatibility with the old version's update source.

That is a genuine constraint rather than a stylistic preference. electron-updater identifies a build by app id and fetches from a publish location, so changing any of these values silently breaks the upgrade path for every user on an older build, and the failure mode is an application that simply stops receiving updates. A fork that wants to distribute under its own identity therefore has to accept losing the existing update path, or set up its own publish location and coordinate a migration.

The rest of the packaging configuration is visible in package.json, where the product name is set, the artefact name template is potatofield-image-toolkit-${version}.${ext}, and there are separate package:win and package:mac scripts on top of the general package script. electron-notarize is in the dev dependencies, and the packaging configuration names icons in build/icons/ that are copied over from the old project: icon.ico for Windows, icon.icns for macOS, and icon.png for the tray.

## Development scripts and the one setup trap

Getting the project running is four commands:

```bash
npm install
npm run dev        # 启动 Vite + Electron 开发模式
npm run build      # 构建主进程 / 预加载 / 渲染进程
npm run package    # 打包（win/nsis + mac/dmg）
```

The dev script starts Vite and Electron together through electron-vite dev, the build script compiles the main process, the preload and the renderer, and the package script produces a Windows NSIS installer or a macOS disk image.

The one trap is documented, which saves a lot of time. On a new machine the dev command can fail to start because the Electron 44 binary has to be downloaded separately, and npm rebuild does not trigger that download. The fix given is to set the Electron mirror and run the install script by hand:

```bash
$env:ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"; node node_modules/electron/install.js
```

The shell syntax there is PowerShell, and the mirror is the npmmirror.com mirror of the Electron binaries, which is a sensible choice for anyone behind a slow link to the default download host. On a POSIX shell the equivalent would set the same variable with an export, but that is not written down in the project.

The repository also carries a .nvmrc file, so the intended Node version is pinned rather than left to the system default, which matters given the Node 22.12 requirement.

## The documentation set, and the licence that is declared but not filed

The project keeps several documents at the top level and in docs/, and the README tells contributors what to read first. PROGRESS.md holds the current progress and unfinished items, with the log in reverse order, and the instruction is that a session continuing the work should read it first. docs/DEVELOPMENT_GUIDE.md covers unified components, style conventions and development maintenance rules, marked as required reading before starting. docs/TOOL_CONSISTENCY.md records the single image versus batch differences and the decisions behind them, and docs/LEGACY_COMPARISON.md compares the implementation and performance against the old version. PLANNING.md holds the early architecture planning and is explicitly marked as partly outdated, with the development guide taking precedence.

That last note is the most useful signal in the whole set. A planning document that says part of it has expired is better than one that pretends to be current, but it does mean a reader has to check which document governs a given decision before acting on it.

Licensing is where the repository is weakest. The package.json declares an MIT licence and the author as Potatofield, but there is no LICENSE file among the top level entries, and the repository metadata itself lists the licence as unknown. For a codebase you intend to reuse, that gap should be closed by the maintainer, since a licence string in package.json is a declaration rather than the text of the grant. DESIGN.md, RELEASE_NOTES.md, an electron.vite.config.ts and a scripts/ directory complete the top level.

## Conclusion

The Potatofield Image Toolkit is a reasonable base for a desktop image utility written on Electron and Vue 3, because the sharp-in-the-main-process rule and the shared-types directory remove two common sources of bugs, and the MIT declaration in package.json keeps reuse simple; it is the wrong base if you need a plugin architecture or a tool suite outside the image domain, and before you fork it you should check that the appId, publish url and packaging targets are still the ones you want, because the project states these must stay unchanged or existing clients will not receive your update.

## FAQ

### How do I run the Potatofield Image Toolkit in development mode?

Run npm install and then npm run dev, which starts Vite and Electron together through electron-vite dev. The build is npm run build and the installer is produced with npm run package, which targets Windows NSIS and macOS DMG.

### Which image processing library does the toolkit use, and where does it run?

It uses sharp, and it runs in the Electron main process. The renderer process calls it over IPC specifically so that image work does not block the user interface.

### What tools are included in the Potatofield Image Toolkit?

Eleven modules: watermarking with a fullscreen mode, long image stitching, cropping, splitting, resizing, compression, format conversion, EXIF reading, colour extraction, font management, and watermark templates. The rich text image feature has been moved to a separate product with only a link left in the app.

### What must stay unchanged when forking this project?

The appId cn.potatofield.imagetoolkit, the publish url at files.potatofield.cn, the nsis and dmg packaging targets, and the asar false setting. Changing any of them breaks the automatic update path for clients running an older build, since electron-updater identifies builds by app id and fetches from the publish location.

### Why does npm run dev fail on a fresh machine?

The Electron 44 binary has to be downloaded separately and npm rebuild does not trigger that download. The project suggests setting the Electron mirror to the npmmirror.com one and running node node_modules/electron/install.js.

## Sources

- [CNOliverZhang/PotatofieldImageToolkit on GitHub](https://github.com/CNOliverZhang/PotatofieldImageToolkit)
- [Issues](https://github.com/CNOliverZhang/PotatofieldImageToolkit/issues)
- [README](https://github.com/CNOliverZhang/PotatofieldImageToolkit/blob/master/README.md)
- [Releases](https://github.com/CNOliverZhang/PotatofieldImageToolkit/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cnoliverzhang-potatofieldimagetoolkit
