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

> Easily build and package the Prompt-Optimizer desktop application for distribution. Follow simple steps to generate platform-specific installers from the linshenkx/prompt-optimizer repository.

- Repository: [且炼时光/prompt-optimizer](https://github.com/linshenkx/prompt-optimizer)
- Tags: how-to-guide
- Published: 2026-02-23

---

**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`](https://github.com/linshenkx/prompt-optimizer/blob/main/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`](https://github.com/linshenkx/prompt-optimizer/blob/main/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`](https://github.com/linshenkx/prompt-optimizer/blob/main/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`](https://github.com/linshenkx/prompt-optimizer/blob/main/pnpm-workspace.yaml)):

```bash
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`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/desktop/package.json):

```bash
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:

```bash
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`](https://github.com/linshenkx/prompt-optimizer/blob/main/main.js) loads via `loadFile()`:

```javascript
// 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`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/desktop/package.json):

```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`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/desktop/main.js), the main process collects all `VITE_*` variables and exposes them to the renderer:

```javascript
// 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:

```bash
pnpm --filter @prompt-optimizer/desktop dev

```

This executes `cross-env NODE_ENV=development electron .` from [`packages/desktop/package.json`](https://github.com/linshenkx/prompt-optimizer/blob/main/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`](https://github.com/linshenkx/prompt-optimizer/blob/main/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`](https://github.com/linshenkx/prompt-optimizer/blob/main/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`](https://github.com/linshenkx/prompt-optimizer/blob/main/main.js) process reads these variables and writes them to [`web-dist/config.js`](https://github.com/linshenkx/prompt-optimizer/blob/main/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`](https://github.com/linshenkx/prompt-optimizer/blob/main/web-dist/index.html) loaded by `mainWindow.loadFile()`.