This document describes the desktop application packaging and distribution system for Prompt Optimizer. It covers the electron-builder configuration, platform-specific build targets, artifact generation, and the auto-update mechanism. For information about the Electron application architecture and IPC handlers, see Desktop Application (Electron). For deployment environment configuration, see Environment Configuration and API Keys.
The desktop application is packaged using electron-builder, which produces native installers for Windows, macOS, and Linux from a single codebase. The distribution system supports:
package.jsonSources: package.json1-107 packages/desktop/package.json1-99
Key Build Steps:
ELECTRON_BUILD=true flag, --base=./ for relative paths (packages/desktop/package.json12)packages/desktop/web-dist/ (packages/desktop/package.json12)Sources: packages/desktop/package.json11-16 package.json12-19
The electron-builder configuration is embedded in packages/desktop/package.json32-91 It defines how the application is packaged for each platform.
| Configuration Key | Value | Purpose |
|---|---|---|
appId | com.promptoptimizer.desktop | macOS bundle identifier, Windows app ID |
productName | PromptOptimizer | Display name in installers and shortcuts |
directories.output | dist | Output directory for built installers |
files | See table below | Files to bundle in the app |
publish.provider | github | Auto-update source (GitHub Releases) |
publish.owner | linshenkx | GitHub repository owner |
publish.repo | prompt-optimizer | GitHub repository name |
Sources: packages/desktop/package.json32-50
The files array specifies which files are bundled into the final application:
| Pattern | Purpose |
|---|---|
main.js | Electron main process entry point |
preload.js | Renderer context bridge script |
config/**/* | Configuration files (e.g., logging, updates) |
web-dist/**/* | Bundled web application UI |
node_modules/**/* | Runtime dependencies (pruned by electron-builder) |
Sources: packages/desktop/package.json38-44
Primary Target: NSIS installer (.exe)
oneClick: false - Multi-step installation wizardallowToChangeInstallationDirectory: true - User can choose install locationperMachine: false - Per-user installation (no admin required)Additional Output: Portable ZIP archive (.zip)
Icon Format: Multi-resolution ICO file at packages/desktop/icons/app-icon.ico
Runtime Icon Selection: packages/desktop/main.js383-396
Sources: packages/desktop/package.json51-57 .github/workflows/release.yml119-154
Primary Target: DMG disk image (.dmg)
Architecture Support:
darwin-x64.dmg)darwin-arm64.dmg)Additional Output: ZIP archive per architecture (.zip)
Icon Format: ICNS file at packages/desktop/icons/app-icon.icns
Code Signing: Configurable via CSC_LINK and CSC_KEY_PASSWORD environment variables (optional)
Sources: packages/desktop/package.json59-77 .github/workflows/release.yml156-280
Primary Target: AppImage (.AppImage)
chmod +x and runAdditional Output: Portable ZIP archive (.zip)
Icon Format: PNG files at packages/desktop/icons/ (512x512.png, 256x256.png, app-icon.png)
Sources: packages/desktop/package.json79-86 .github/workflows/release.yml282-405
electron-builder generates artifacts with platform-specific naming patterns:
| Platform | Installer Type | Naming Pattern | Example (v2.5.3) |
|---|---|---|---|
| Windows | NSIS Setup | PromptOptimizer Setup ${version}.exe | PromptOptimizer Setup 2.5.3.exe |
| Windows | Portable ZIP | PromptOptimizer-${version}-win-${arch}.zip | PromptOptimizer-2.5.3-win-x64.zip |
| macOS | DMG (x64) | PromptOptimizer-${version}-darwin-x64.dmg | PromptOptimizer-2.5.3-darwin-x64.dmg |
| macOS | DMG (arm64) | PromptOptimizer-${version}-darwin-arm64.dmg | PromptOptimizer-2.5.3-darwin-arm64.dmg |
| macOS | ZIP (x64) | PromptOptimizer-${version}-darwin-x64.zip | PromptOptimizer-2.5.3-darwin-x64.zip |
| macOS | ZIP (arm64) | PromptOptimizer-${version}-darwin-arm64.zip | PromptOptimizer-2.5.3-darwin-arm64.zip |
| Linux | AppImage | PromptOptimizer-${version}-linux-x86_64.AppImage | PromptOptimizer-2.5.3-linux-x86_64.AppImage |
| Linux | Portable ZIP | PromptOptimizer-${version}-linux-x64.zip | PromptOptimizer-2.5.3-linux-x64.zip |
electron-updater requires platform-specific YAML files for update checking:
| Platform | File | Purpose |
|---|---|---|
| Windows | latest.yml | Contains version, file URLs, SHA512 checksums for Windows builds |
| macOS | latest-mac.yml | Contains version, file URLs, SHA512 checksums for macOS builds (both architectures) |
| Linux | latest-linux.yml | Contains version, file URLs, SHA512 checksums for Linux builds |
These files are automatically generated by electron-builder and must be uploaded to GitHub Releases alongside installers.
Sources: packages/desktop/package.json56-84 .github/workflows/release.yml148-404
A complete release for version v2.5.3 includes:
PromptOptimizer Setup 2.5.3.exe
PromptOptimizer-2.5.3-win-x64.zip
latest.yml
PromptOptimizer-2.5.3-darwin-x64.dmg
PromptOptimizer-2.5.3-darwin-x64.zip
PromptOptimizer-2.5.3-darwin-arm64.dmg
PromptOptimizer-2.5.3-darwin-arm64.zip
latest-mac.yml
PromptOptimizer-2.5.3-linux-x86_64.AppImage
PromptOptimizer-2.5.3-linux-x64.zip
latest-linux.yml
Sources: .github/workflows/release.yml460-665
The auto-update system uses multiple configuration layers:
publish Configuration (packages/desktop/package.json45-50):
Update Config Module (packages/desktop/config/update-config.js):
IPC_EVENTS: Event names for main ↔ renderer communicationPREFERENCE_KEYS: Storage keys for ignored versionsDEFAULT_CONFIG: Default update check settingsbuildReleaseUrl(): Constructs GitHub API URLs for version checkingvalidateVersion(): Validates version string formatSources: packages/desktop/package.json45-50 packages/desktop/main.js27-33
The application supports checking for both stable and prerelease versions simultaneously:
Renderer → Main (invoke):
| Event | Parameters | Returns | Handler |
|---|---|---|---|
updater-check-update | - | {hasUpdate, version, ...} | main.js1332-1350 |
updater-check-all-versions | - | {currentVersion, stable, prerelease} | main.js1352-1430 |
updater-download-specific-version | `versionType: 'stable' | 'prerelease'` | {hasUpdate, version, reason} |
updater-install-update | - | void | main.js1482-1497 |
updater-ignore-version | version: string, versionType?: string | void | main.js1499-1505 |
updater-unignore-version | `versionType: 'stable' | 'prerelease'` | void |
updater-get-ignored-versions | - | `{stable: string | null, prerelease: string |
Main → Renderer (send):
| Event | Payload | Trigger |
|---|---|---|
update-available-info | {version, releaseDate, releaseNotes} | New version detected |
update-not-available | {version, reason} | No updates found |
update-download-progress | {percent, bytesPerSecond, transferred, total} | Download in progress |
update-downloaded | {version} | Download complete |
update-error | {message, code} | Update check/download failed |
updater-download-started | {versionType, version} | Download initiated |
Sources: packages/desktop/preload.js4-21 packages/ui/src/types/electron.d.ts40-107 packages/desktop/main.js1332-1516
Users can suppress update notifications for specific versions:
Storage: Ignored versions stored in PreferenceService under keys:
UPDATER_IGNORED_STABLE_VERSIONUPDATER_IGNORED_PRERELEASE_VERSIONBehavior:
update-available-info eventsUpdaterModal UIImplementation: packages/desktop/main.js1499-1516
Sources: packages/desktop/config/update-config.js packages/ui/src/components/UpdaterModal.vue
The auto-update system is configured in packages/desktop/package.json45-50:
| Key | Value | Description |
|---|---|---|
publish.provider | github | Use GitHub Releases as update source |
publish.owner | linshenkx | Repository owner |
publish.repo | prompt-optimizer | Repository name |
publish.private | false | Public repository (no auth needed) |
electronAPI.updater.checkUpdate() (packages/desktop/preload.js4-21)latest.yml from GitHub ReleasesUpdaterModal.vue (packages/ui/src/components/UpdaterModal.vue1)autoUpdater.quitAndInstall() replaces the applicationThe preload bridge exposes update events to the renderer:
| Event | Direction | Payload | Purpose |
|---|---|---|---|
update-available-info | Main → Renderer | { version, releaseNotes } | New version detected |
update-not-available | Main → Renderer | { version, reason } | No updates found |
update-download-progress | Main → Renderer | { percent, transferred } | Download progress |
update-downloaded | Main → Renderer | { version } | Ready to install |
update-error | Main → Renderer | { message, code } | Update failed |
Sources: packages/desktop/preload.js4-21 packages/ui/src/types/electron.d.ts100-107
Users can ignore specific versions to suppress update notifications:
electronAPI.updater.ignoreVersion(version, versionType)electronAPI.updater.unignoreVersion(versionType)electronAPI.updater.getIgnoredVersions()Ignored versions are stored in PreferenceService and checked before showing update notifications.
Sources: packages/ui/src/types/electron.d.ts74-79 packages/desktop/main.js27-32
The release process is fully automated via GitHub Actions and triggered by git tags matching v*.*.* pattern.
Stage 1: Test Gate
Stage 2: Parallel Platform Builds
packages/desktop/package.json:
v prefix)repository.url to current repositorybuild.publish for electron-updaterStage 3: Artifact Verification
Stage 4: Release Creation
builder-debug.yml).yml files)Sources: .github/workflows/release.yml1-670 .github/workflows/test.yml1-49
The workflow dynamically modifies packages/desktop/package.json at build time to inject repository information:
| Field | Source | Example Value |
|---|---|---|
version | Git tag (strip v) | 2.5.3 |
repository.url | ${{ github.repository }} | https://github.com/linshenkx/prompt-optimizer.git |
build.publish.provider | Static | github |
build.publish.owner | Git repo owner | linshenkx |
build.publish.repo | Git repo name | prompt-optimizer |
build.publish.private | Static | false |
This enables the same codebase to work with any fork by automatically detecting the repository context.
Sources: .github/workflows/release.yml44-115 .github/workflows/release.yml177-241
Local Development:
| Command | Purpose | Implementation |
|---|---|---|
pnpm run dev:desktop | Start dev server + Electron in watch mode | npm-run-all clean:dist build:core build:ui dev:desktop:parallel |
pnpm run dev:desktop:parallel | Parallel: web dev server + Electron launcher | concurrently "pnpm dev:web" "wait-on ... && pnpm -F ... dev" |
Production Build:
| Command | Purpose | Implementation |
|---|---|---|
pnpm run build:desktop | Full build with all dependencies | npm-run-all build:core build:ui build:web build:desktop-only |
pnpm run build:desktop-only | Package Electron app only | pnpm -F @prompt-optimizer/desktop build |
pnpm run build:web | Build web UI for Electron with ELECTRON_BUILD=true | Sets base: './' for relative paths |
CI/CD (GitHub Actions):
| Workflow | Trigger | Command |
|---|---|---|
test.yml | Push to main/master, PR | pnpm test:gate + pnpm test:gate:e2e |
release.yml | Git tag v*.*.* | pnpm install + pnpm build:desktop |
docker.yml | After successful test | docker build (includes web app + MCP server) |
Sources: package.json18-26 .github/workflows/release.yml116-122 .github/workflows/test.yml36-46
The build process uses a version sync script to ensure consistency:
package.json (package.json3)scripts/sync-versions.js copies version to all packagespnpm version (package.json46)pnpm run version:tag creates git tag v{version} (package.json48)Sources: package.json3-49
The desktop application uses platform-specific icon formats to ensure optimal display quality across operating systems.
The main process dynamically selects icons based on platform (packages/desktop/main.js383-396):
Sources: packages/desktop/main.js383-396
The desktop application uses a file-based storage system instead of browser IndexedDB, with automatic backups and data recovery.
The desktop app initializes FileStorageProvider with the standard user data directory (packages/desktop/main.js587-590):
This ensures compatibility with auto-updates (updates preserve user data directory).
The UI package provides desktop-specific storage utilities in DataManager.vue:
| Feature | Method | Purpose |
|---|---|---|
| Storage Info | electronAPI.data.getStorageInfo() | Returns file paths, sizes (main/backup/total) |
| Open Directory | electronAPI.data.openStorageDirectory() | Opens storage folder in file explorer |
| Refresh Info | Manual refresh button | Updates storage size display |
Sources: packages/ui/src/components/DataManager.vue300-324 packages/ui/src/types/electron.d.ts176-194 packages/desktop/main.js587-590
The build system supports both development and production modes with different configurations:
Trigger: NODE_ENV=development environment variable
Characteristics:
http://localhost:18181 (packages/desktop/main.js455-458)Command: pnpm run dev:desktop (starts both web dev server and Electron)
Trigger: Default (no NODE_ENV or packaged app)
Characteristics:
web-dist/index.html (packages/desktop/main.js461-468)Command: pnpm run build:desktop (full production build)
Sources: packages/desktop/main.js455-469 package.json24-26
Before releasing a new version, ensure:
package.json version incremented (package.json3)packages/desktop/icons/v{version} (package.json48)latest.yml files for auto-updateSources: package.json45-49 packages/desktop/package.json45-50
Refresh this wiki