How to Build and Package the Prompt-Optimizer Desktop Application for Distribution

Run pnpm run build from the repository root to automatically build the web assets, copy them to the Electron package, and generate platform-specific installers in packages/desktop/dist/.

The linshenkx/prompt-optimizer repository provides a desktop client that wraps the web UI in an Electron shell. To distribute this application to end users, you must build the Vue 3 frontend, bundle it with the Electron main process, and package everything into OS-specific installers. This process is orchestrated entirely through npm scripts defined in packages/desktop/package.json.

Understanding the Build Architecture

The Prompt-Optimizer desktop application is an Electron wrapper around the web UI located in packages/web. The build pipeline follows a three-stage architecture controlled by packages/desktop/package.json:

  1. Web Asset Compilation – Vite builds the Vue 3 application with Electron-specific base paths
  2. Asset Migration – Compiled files are copied from packages/web/dist to packages/desktop/web-dist
  3. Binary Packaging – electron-builder generates installers using the configuration stored in the build section of package.json

This design ensures the desktop client always ships with the latest web UI while maintaining separate concerns between the frontend build and Electron packaging logic.

Prerequisites and Initial Setup

Before building the desktop application, ensure you have the monorepo dependencies installed. From the repository root (the directory containing pnpm-workspace.yaml):

pnpm install

This installs all workspace dependencies including electron, electron-builder, and cross-env required for the packaging process.

Step-by-Step Build Process

The entire build and package workflow is triggered by a single command, but understanding the individual steps helps troubleshoot issues and customize the output.

Building the Web Assets

The first step compiles the Vue 3 web UI with Vite, using a specific base path required for Electron's file system loading. This is defined in the build:web script in packages/desktop/package.json:

cd ../web && cross-env ELECTRON_BUILD=true vite build --base=./

Key parameters:

  • --base=./ – Uses relative paths instead of absolute URLs, essential for loading from file:// protocols in Electron
  • ELECTRON_BUILD=true – Environment flag that may trigger Electron-specific build optimizations in the web package

This outputs compiled assets to packages/web/dist/.

Copying Assets to the Desktop Package

Immediately after the Vite build completes, a Node.js one-liner copies the compiled assets into the desktop package directory:

node -e "const fs=require('fs'),path=require('path'); const src='dist',dest='../desktop/web-dist'; if(fs.existsSync(dest)) fs.rmSync(dest,{recursive:true}); fs.cpSync(src,dest,{recursive:true}); console.log('Web files copied to desktop/web-dist');"

This ensures packages/desktop/web-dist/ contains the fresh build, which main.js loads via loadFile():

// From packages/desktop/main.js
mainWindow.loadFile(path.join(__dirname, 'web-dist/index.html'));

Packaging with Electron-Builder

The final step invokes electron-builder using the configuration stored in the build section of packages/desktop/package.json:

{
  "build": {
    "appId": "com.promptoptimizer.desktop",
    "productName": "PromptOptimizer",
    "directories": {
      "output": "dist"
    },
    "files": [
      "main.js",
      "preload.js",
      "web-dist/**/*"
    ],
    "win": {
      "target": ["nsis", "zip"],
      "icon": "icons/app-icon.ico"
    },
    "mac": {
      "target": ["dmg", "zip"],
      "icon": "icons/app-icon.icns"
    },
    "linux": {
      "target": ["AppImage", "zip"],
      "icon": "icons/app-icon.png"
    }
  }
}

This configuration:

  • Sets the application ID to com.promptoptimizer.desktop and product name to PromptOptimizer
  • Defines output directory as dist/ within the desktop package
  • Specifies which files to include (main process scripts and the web-dist folder)
  • Configures OS-specific targets: NSIS installers for Windows, DMG for macOS, AppImage for Linux, plus ZIP archives for all platforms

Runtime Configuration and Environment Variables

The desktop application handles API keys and configuration through environment variables injected at runtime. In packages/desktop/main.js, the main process collects all VITE_* variables and exposes them to the renderer:

// Lines 18-30 and 55-65 in main.js
const config = {};
for (const [key, value] of Object.entries(process.env)) {
  if (key.startsWith('VITE_')) {
    config[key] = value;
  }
}

// Write to config.js that the renderer loads
fs.writeFileSync(
  path.join(__dirname, 'web-dist/config.js'),
  `window.__CONFIG__ = ${JSON.stringify(config)};`
);

This mechanism ensures sensitive credentials are not hardcoded into the packaged application but are available at runtime through window.__CONFIG__.

Output Artifacts and Distribution

After running pnpm run build, the distributable files appear in packages/desktop/dist/ with the following naming convention:

  • Windows: PromptOptimizer-{version}-windows-x64.exe (NSIS installer) and .zip (portable)
  • macOS: PromptOptimizer-{version}-mac-x64.dmg, PromptOptimizer-{version}-mac-arm64.dmg (Apple Silicon), and corresponding .zip files
  • Linux: PromptOptimizer-{version}.AppImage and .zip

These artifacts are ready for upload to GitHub Releases, enterprise software distribution systems, or direct download servers.

Development vs. Production Builds

For local testing without generating installers, use the development mode:

pnpm --filter @prompt-optimizer/desktop dev

This executes cross-env NODE_ENV=development electron . from packages/desktop/package.json, launching the Electron app with hot-reload capabilities while using the freshly built web assets.

Summary

  • Install dependencies once with pnpm install from the repository root.
  • Trigger the full build with pnpm run build, which chains the web compilation, asset copying, and Electron packaging.
  • Locate installers in packages/desktop/dist/ as NSIS executables (Windows), DMG files (macOS), and AppImages (Linux).
  • Configure runtime variables via VITE_* environment variables injected by main.js into the renderer process.
  • Develop locally using pnpm --filter @prompt-optimizer/desktop dev to test without packaging overhead.

Frequently Asked Questions

What is the minimum Node.js version required to build the desktop application?

The build system relies on modern Node.js features used by Vite and Electron Builder. While the repository does not specify an explicit minimum version in the analyzed files, you should use Node.js 18 or higher to ensure compatibility with the pnpm workspace, Vite 5+, and Electron 28+ dependencies typically found in modern Electron Vue stacks.

Can I customize the installer icons and application metadata?

Yes. Edit the build section in packages/desktop/package.json to modify the productName, appId, and icon paths. Place your custom icons in packages/desktop/icons/ using the required formats: .ico for Windows, .icns for macOS, and .png for Linux. Re-run pnpm run build to generate installers with the new branding.

How do I include API keys or configuration in the packaged application without hardcoding them?

The application uses a runtime injection mechanism. Set your configuration as environment variables prefixed with VITE_ (e.g., VITE_API_KEY=sk-...) before launching the built binary. The main.js process reads these variables and writes them to web-dist/config.js, exposing them to the renderer via window.__CONFIG__. This keeps secrets out of the compiled source code while making them available at runtime.

Why does the web build use --base=./ instead of absolute paths?

Electron loads the UI from the local file system using the file:// protocol rather than a web server. Absolute base paths (like /) would cause the renderer to look for assets at the root of the file system (e.g., file:///index.html), which fails. The --base=./ setting ensures Vite generates relative paths (e.g., ./assets/...), allowing Electron to resolve them correctly relative to web-dist/index.html loaded by mainWindow.loadFile().

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →