This document explains how Prompt Optimizer's codebase is architected to run on three distinct platforms—web browsers, browser extensions, and Electron desktop—from a single shared codebase. It covers the abstraction layers that enable platform independence, the CORS handling strategies that differ between platforms, and the storage/IPC mechanisms specific to each deployment target.
For detailed monorepo structure and package dependencies, see Monorepo Structure and Packages. For platform-specific implementation details, see Web Application, Browser Extension, and Desktop Application (Electron).
The multi-platform architecture follows a strict layering principle where business logic is completely separated from platform-specific concerns. The @prompt-optimizer/core package contains all domain logic and is platform-agnostic. The @prompt-optimizer/ui package provides Vue components that depend on core services through dependency injection, avoiding direct coupling to any platform. Finally, three application packages (web, extension, desktop) initialize the appropriate platform-specific implementations and compose the UI.
Sources: packages/core/src/services/storage packages/ui/src/PromptOptimizerApp.vue packages/desktop/main.js528-600 packages/desktop/preload.js1-50
The web application runs entirely in the browser as a Single Page Application (SPA). It imports @prompt-optimizer/core directly in the renderer thread and uses IndexedDB via the Dexie library for data persistence.
| Aspect | Implementation |
|---|---|
| Entry Point | packages/web/src/main.ts |
| Storage Backend | DexieStorageProvider → IndexedDB |
| Core Services | Direct imports from @prompt-optimizer/core |
| Build Tool | Vite |
| CORS Constraints | Subject to browser CORS policies |
| Mixed Content | HTTPS pages cannot call HTTP APIs |
Key Limitation: The web version cannot bypass CORS restrictions or mixed content policies. This means it cannot connect to local services like Ollama (HTTP) from an HTTPS-hosted page, or to APIs that don't set proper CORS headers.
Sources: packages/web/package.json1-36 README.md75-94
The browser extension follows a similar architecture to the web application but runs in the extension's isolated context. It uses chrome.storage.local as its persistence layer through the same DexieStorageProvider wrapper (Dexie can use chrome.storage as a backend).
| Aspect | Implementation |
|---|---|
| Entry Point | packages/extension/src/main.ts |
| Manifest | Manifest V3 (packages/extension/public/manifest.json) |
| Storage Backend | chrome.storage.local via Dexie |
| Core Services | Direct imports from @prompt-optimizer/core |
| CORS Constraints | Slightly relaxed compared to web pages |
| Permissions | None required (offline-capable) |
The extension manifest declares no special permissions, making it lightweight and privacy-friendly. It runs as a popup UI with the same component tree as the web application.
Sources: packages/extension/public/manifest.json1-35 packages/extension/package.json1-37
The desktop application uses Electron's multi-process architecture to achieve maximum flexibility and bypass browser restrictions. The main process (Node.js) runs all core services with full system access, while the renderer process (Chromium) displays the UI. Communication between processes occurs through IPC (Inter-Process Communication) via a secure preload script.
| Aspect | Implementation |
|---|---|
| Main Process | packages/desktop/main.js (Node.js runtime) |
| Renderer Process | packages/desktop/web-dist/index.html (built from @prompt-optimizer/web) |
| IPC Bridge | packages/desktop/preload.js (contextBridge) |
| Storage Backend | FileStorageProvider → JSON files in userData directory |
| Core Services | Run in main process with full Node.js access |
| CORS Constraints | None (Node.js bypasses browser restrictions) |
| Proxy System | undici global dispatcher for system proxy support |
The desktop architecture solves critical issues that web/extension cannot:
localhost services like OllamaSources: packages/desktop/main.js1-100 packages/desktop/package.json1-99 README.md96-106
The IStorageProvider interface defines platform-agnostic storage operations. Each platform implements this interface using its native storage mechanism.
| Feature | Web/Extension (Dexie) | Desktop (File) |
|---|---|---|
| Implementation | DexieStorageProvider | FileStorageProvider |
| Backend | IndexedDB or chrome.storage.local | JSON files in app.getPath('userData') |
| Persistence | Browser-managed | File system (survives app uninstall) |
| Backup Strategy | None (browser handles) | Atomic write with .backup.json |
| Capacity | Browser quota limits | Disk space |
| Flush Strategy | Immediate | Debounced (1000ms) |
| Recovery | Browser auto-recovery | Backup file fallback |
The desktop's FileStorageProvider implements a robust write strategy with atomic file operations and backup rotation to prevent data corruption on crashes or power loss.
Sources: packages/core/src/services/storage packages/desktop/main.js589-600
Cross-Origin Resource Sharing (CORS) restrictions are a fundamental difference between platforms. The following table summarizes how each platform handles CORS:
| Platform | CORS Status | Solution | Trade-offs |
|---|---|---|---|
| Web (Online) | Blocked | User must use CORS-compliant APIs or proxy services | Cannot connect to most local services |
| Web (Docker HTTP) | Limited | Same-origin if accessed via HTTP | HTTP-only, no HTTPS security |
| Extension | Partial | Extension context has relaxed policies | Still cannot access many APIs |
| Desktop | Bypassed | Node.js in main process ignores CORS | Full access to all APIs |
Problem: User wants to connect to local Ollama (http://localhost:11434) from the online web app (https://prompt.always200.com).
Web (Online):
Access-Control-Allow-OriginDesktop:
Problem: User wants to connect to a commercial API with strict CORS policies (e.g., Nvidia NIM, Volcano Engine).
Web:
Desktop:
Solution Recommendation: The README explicitly recommends desktop app for users needing full API compatibility, or using API proxy services like OneAPI/NewAPI for web deployments.
Sources: README.md349-409 packages/desktop/main.js190-276
The UI layer uses Vue's provide/inject mechanism to decouple from platform-specific service implementations. This allows the same UI components to work across all platforms without modification.
In web/extension, services are created directly:
In desktop, renderer process uses proxy objects that communicate with main process:
UI components use the same interface:
This pattern ensures UI components never directly import platform-specific code.
Sources: packages/ui/src/types/services.ts packages/ui/src/plugins/i18n.ts1-121 packages/ui/src/components/DataManager.vue254-272
Electron's security model enforces context isolation between the main process (trusted, full Node.js access) and renderer process (untrusted, Chromium sandbox). Communication occurs through a preload script that uses contextBridge to expose a limited API.
The main process registers IPC handlers for all core services:
The preload script exposes these via contextBridge:
Key Security Features:
Sources: packages/desktop/main.js243-450 packages/desktop/preload.js88-239 packages/ui/src/types/electron.d.ts1-275
All platforms use environment variables prefixed with VITE_ for configuration. Vite automatically exposes these to the frontend code via import.meta.env. The desktop application additionally injects them at runtime into the renderer process.
The system supports unlimited custom models using a naming pattern:
The pattern matching and validation logic is shared across packages:
This pattern is used identically in:
packages/core/src/services/model/model-utils.ts (core logic)packages/desktop/main.js:555-561 (desktop scanning)packages/mcp-server/src/config/models.ts (MCP server)Sources: packages/core/src/utils/environment.ts packages/desktop/main.js354-378 packages/core/src/services/model/defaults.ts1-119 env.local.example42-71
vercel.json configuration/mcp path (Docker only)"offline_enabled": true in manifest"incognito": "split" creates separate storage per profileelectron-updater with GitHub Releases integrationsession.resolveProxy() and undici global dispatchercut, copy, paste)userData directoryDesktop Shutdown Sequence:
Sources: packages/desktop/main.js170-188 packages/desktop/main.js472-526 packages/extension/public/manifest.json24-34 vercel.json1-33 docker-compose.yml1-44
The multi-platform architecture achieves code reuse through three key patterns:
Layered Abstraction: Core logic is completely platform-agnostic, UI components use dependency injection, and only application entry points are platform-specific.
Storage Provider Pattern: The IStorageProvider interface allows the same business logic to work with IndexedDB (web/extension), chrome.storage (extension), or file system (desktop).
IPC Proxy Pattern: Desktop uses proxy objects in the renderer that forward calls to main process services via IPC, maintaining the same API surface as direct imports.
The desktop application provides the most complete functionality by running core services in a Node.js process that bypasses all browser restrictions (CORS, mixed content, local service access). Web and extension versions are lightweight but subject to browser security policies. All three share 95%+ of their codebase through the monorepo architecture.
Refresh this wiki