Setup and context: The Resurgence of Desktop AI Applications
While cloud-based AI services dominate the landscape, there's a growing resurgence of desktop applications driven by three critical needs: privacy, offline capability, and ultra-low latency .
Tools like Cursor and Windsurf—both Electron-based AI-powered IDEs—exemplify this shift. Alongside them, local AI assistant applications and privacy-first tools are gaining traction. Desktop applications offer distinct advantages over web-based solutions:
Complete Privacy : User data remains on-machine, never transmitted to external servers
Offline-First Operation : Run sophisticated AI models without internet connectivity
Sub-Millisecond Latency : Direct GPU/NPU access with minimal network overhead
Deep System Integration : File system access, tray icons, global hotkeys, hardware integration
Persistent Learning : Local embedding models enable personalization and context retention across sessions
This guide walks you through building production-grade desktop AI applications by combining Electron and Antigravity , Google's AI-powered IDE. You'll learn architectural patterns, IPC design, local model integration, security hardening, and deployment strategies used by professional AI tool developers.
Electron × AI Application Architecture Design
Building robust desktop AI applications requires understanding multi-process models and clean separation of concerns .
The Electron Process Model
Electron operates as a three-tier architecture:
1. Main Process (Full Node.js Access)
Window, menu, and tray management
File system and native module access
IPC message coordination with renderer processes
Application lifecycle control
2. Renderer Process (Sandboxed)
UI rendering using React, Vue, or Svelte
User interaction handling
IPC requests to main process
Runs in restricted security context
3. Worker Process (Optional)
GPU-intensive operations
LLM inference without blocking main thread
Image processing, heavy computations
Prevents UI freezing during long operations
Unified AI Integration Architecture
┌─────────────────────────────────────────────┐
│ UI Layer (React/Vue) │
│ ┌─────────────────────────────┐ │
│ │ Chat Interface │ │
│ │ File Drop Zone │ │
│ │ Settings & Model Loader │ │
│ └──────────┬──────────────────┘ │
└─────────────────┼──────────────────────────┘
│ IPC (async/await)
┌─────────────────▼──────────────────────────┐
│ Main Process │
│ ┌──────────────────────────────┐ │
│ │ IPC Handler Registry │ │
│ │ Model Manager & Cache │ │
│ │ Database Connections │ │
│ │ Filesystem Operations │ │
│ └──────────────────────────────┘ │
└─────────────────┬──────────────────────────┘
│
┌─────────┼─────────┐
│ │ │
┌───▼──┐ ┌───▼──┐ ┌────▼───┐
│LLM │ │Text │ │Computer│
│Model │ │Embed │ │ Vision │
└──────┘ └──────┘ └────────┘
This architecture ensures smooth UI responsiveness while enabling real-time AI inference feedback to the frontend.
Project Scaffolding and Antigravity Development Workflow
Antigravity dramatically accelerates Electron × Node.js × Python stack development by providing intelligent code generation and multi-file refactoring.
Recommended Project Structure
electron-ai-app/
├── src/
│ ├── main/ # Main process
│ │ ├── ipc/ # IPC message handlers
│ │ │ ├── model.ts
│ │ │ ├── file.ts
│ │ │ └── system.ts
│ │ ├── services/ # AI service layer
│ │ │ ├── llm.ts
│ │ │ ├── embedding.ts
│ │ │ └── inference.ts
│ │ └── main.ts # Entry point
│ ├── renderer/ # Renderer process
│ │ ├── components/ # React components
│ │ ├── hooks/ # Custom React hooks
│ │ ├── pages/
│ │ └── App.tsx
│ └── shared/ # Shared type definitions
│ ├── types.ts
│ └── constants.ts
├── python/ # AI inference scripts
│ ├── inference.py
│ ├── embedding.py
│ └── requirements.txt
├── resources/ # Static assets
│ ├── icons/
│ └── models/ # LLM weights
├── vite.config.ts
├── electron-builder.json
└── package.json
Antigravity AI-Assisted Workflow
Leverage Antigravity's contextual AI to dramatically speed up development:
1. Type-Safe IPC Interface Generation
Antigravity infers IPC contracts from shared/types.ts
Auto-generates renderer ↔ main process type bindings
Catches mismatches during development instead of runtime
2. Multi-File Refactoring at Scale
Change a type in shared layer
Antigravity updates all dependent IPC handlers simultaneously
Reduces manual coordination by 80%
3. Python-to-Node Bridge Completion
Map Python model outputs to Node.js types
Auto-generates child_process invocation patterns
Handles JSON serialization edge cases
4. Consistent Error Handling Templates
Write IPC handler signature
Antigravity expands try-catch-log boilerplate
Enforces uniform error reporting across your app
Practical Development Flow:
1. Open Antigravity: Command Palette → "Generate IPC Handler"
↓
2. Input: handler name, input/output types
↓
3. Antigravity generates across three files:
- main/ipc/xxx.ts (handler implementation)
- renderer/hooks/useXxx.ts (React hook wrapper)
- shared/types.ts (TypeScript contracts)
↓
4. In renderer component: const { data, loading } = useIpc('xxx', args)
This workflow reduces boilerplate by 60% while maintaining type safety across process boundaries.
Main Process and Renderer Process IPC Design
Core IPC Patterns
Electron IPC operates in two modes: invoke (request-response) and send (one-way notification) .
Pattern 1: Invoke (Awaitable)
// main/ipc/llm.ts
ipcMain. handle ( 'llm:generate' , async ( event , prompt : string ) => {
try {
const response = await llmService. generate (prompt)
return { success: true , data: response }
} catch (error) {
return { success: false , error: error.message }
}
})
// renderer/hooks/useLLM.ts
async function generateText ( prompt : string ) {
const result = await ipcRenderer. invoke ( 'llm:generate' , prompt)
if (result.success) {
setResponse (result.data)
} else {
setError (result.error)
}
}
Pattern 2: Send (Streaming)
// main/ipc/llm.ts - stream inference in chunks
ipcMain. on ( 'llm:stream' , async ( event , prompt : string ) => {
const stream = llmService. generateStream (prompt)
for await ( const chunk of stream) {
event.sender. send ( 'llm:stream:chunk' , chunk)
}
event.sender. send ( 'llm:stream:done' )
})
// renderer/components/ChatBox.tsx
useEffect (() => {
ipcRenderer. on ( 'llm:stream:chunk' , ( chunk ) => {
setMessages ( prev => [ ... prev, chunk])
})
return () => ipcRenderer. removeAllListeners ( 'llm:stream:chunk' )
}, [])
Timeout Management and Error Recovery
AI inference operations can take seconds to minutes, making timeout handling critical:
// main/services/inference.ts
const INFERENCE_TIMEOUT = 5 * 60 * 1000 // 5 minutes
async function invokeWithTimeout < T >(
name : string ,
args : unknown [],
timeout : number = INFERENCE_TIMEOUT
) : Promise < T > {
return Promise . race ([
ipcRenderer. invoke (name, ... args),
new Promise (( _ , reject ) =>
setTimeout (
() => reject ( new Error ( `${ name } timed out after ${ timeout }ms` )),
timeout
)
),
])
}
// Renderer usage
try {
const result = await invokeWithTimeout ( 'llm:generate' , [prompt], 30000 )
} catch (error) {
if (error.message. includes ( 'timed out' )) {
showNotification ( 'Inference timed out. Try a shorter prompt.' )
}
}
Real-Time State Synchronization
Stream model loading status (downloading, ready, error) to renderer in real time:
// main/services/model-manager.ts
class ModelManager extends EventEmitter {
private models : Map < string , AIModel > = new Map ()
async loadModel ( modelId : string ) {
this . emit ( 'model:loading' , modelId)
try {
const model = await downloadAndCacheModel (modelId)
this .models. set (modelId, model)
this . emit ( 'model:loaded' , modelId)
} catch (error) {
this . emit ( 'model:error' , { modelId, error: error.message })
}
}
}
// main/ipc/model.ts - forward events to renderer
modelManager. on ( 'model:loading' , ( modelId ) => {
mainWindow.webContents. send ( 'model:status' , { modelId, status: 'loading' })
})
// renderer/hooks/useModelStatus.ts
export function useModelStatus () {
const [ models , setModels ] = useState ({})
useEffect (() => {
ipcRenderer. on ( 'model:status' , ( status ) => {
setModels ( prev => ({
... prev,
[status.modelId]: status.status
}))
})
}, [])
return models
}
Local AI Model Integration and Inference Pipelines
Model Distribution Strategies
Local LLMs introduce file size challenges . Here are three production approaches:
Option 1: On-Demand Download (Recommended)
Don't bundle models with the installer
Download on first use when user needs inference
Cache at app.getPath('userData')/models/
Show progress UI during download
Option 2: App Bundle (Small Models Only)
Include only quantized models (GGUF format)
Trade app size for zero-download startup
Best for 2-7B parameter models
Option 3: Intelligent Fallback
Attempt local inference first
Fall back to cloud API on failure
Enables offline mode without sacrificing reliability
// main/services/model-manager.ts
const MODEL_CACHE_DIR = path. join (app. getPath ( 'userData' ), 'models' )
async function ensureModelExists ( modelId : string ) : Promise < string > {
const modelPath = path. join ( MODEL_CACHE_DIR , modelId, 'model.gguf' )
// Already cached
if (fs. existsSync (modelPath)) {
return modelPath
}
// Start download
mainWindow.webContents. send ( 'model:download:start' , { modelId })
try {
await downloadModelWithProgress (modelId, modelPath, ( progress ) => {
mainWindow.webContents. send ( 'model:download:progress' , {
modelId,
progress, // 0-100
})
})
mainWindow.webContents. send ( 'model:download:complete' , { modelId })
return modelPath
} catch (error) {
mainWindow.webContents. send ( 'model:download:error' , {
modelId,
error: error.message,
})
throw error
}
}
Python Inference Engine Integration
Node.js alone cannot run large LLMs efficiently. Use Python + llama-cpp-python as a child process:
// main/services/inference.ts
import { spawn } from 'child_process'
import { EventEmitter } from 'events'
class LLMInference extends EventEmitter {
private pythonProcess : ChildProcess | null = null
async initialize ( modelPath : string ) {
this .pythonProcess = spawn ( 'python' , [ 'python/inference.py' , modelPath], {
stdio: [ 'pipe' , 'pipe' , 'pipe' ],
env: {
... process.env,
CUDA_VISIBLE_DEVICES: '0' , // GPU index
PYTHONUNBUFFERED: '1' ,
},
})
this .pythonProcess.stdout ! . on ( 'data' , ( data ) => {
const message = JSON . parse (data. toString ())
if (message.type === 'ready' ) {
this . emit ( 'ready' )
}
})
}
async generate ( prompt : string , options : GenerateOptions ) {
return new Promise (( resolve , reject ) => {
const requestId = crypto. randomUUID ()
this .pythonProcess ! .stdin ! . write (
JSON . stringify ({ requestId, prompt, options }) + ' \n '
)
const handleMessage = ( data ) => {
const message = JSON . parse (data. toString ())
if (message.requestId === requestId) {
if (message.type === 'complete' ) {
this .pythonProcess ! .stdout ! . removeListener ( 'data' , handleMessage)
resolve (message.output)
} else if (message.type === 'error' ) {
reject ( new Error (message.error))
}
// Stream chunks to renderer
this . emit ( 'chunk' , message)
}
}
this .pythonProcess ! .stdout ! . on ( 'data' , handleMessage)
})
}
}
Python Inference Script (python/inference.py):
import json
import sys
from llama_cpp import Llama
model_path = sys.argv[ 1 ]
llm = Llama(model_path, n_gpu_layers =- 1 , verbose = False )
# Signal readiness
sys.stdout.write(json.dumps({ "type" : "ready" }) + " \n " )
sys.stdout.flush()
# Request processing loop
for line in sys.stdin:
request = json.loads(line)
request_id = request[ 'requestId' ]
prompt = request[ 'prompt' ]
options = request.get( 'options' , {})
try :
output = ""
# Stream generation
for chunk in llm(
prompt,
max_tokens = options.get( 'max_tokens' , 256 ),
temperature = options.get( 'temperature' , 0.7 ),
stream = True ,
):
token = chunk[ 'choices' ][ 0 ][ 'text' ]
output += token
# Send chunk
sys.stdout.write(json.dumps({
'requestId' : request_id,
'type' : 'chunk' ,
'token' : token,
}) + " \n " )
sys.stdout.flush()
# Signal completion
sys.stdout.write(json.dumps({
'requestId' : request_id,
'type' : 'complete' ,
'output' : output,
}) + " \n " )
sys.stdout.flush()
except Exception as e:
sys.stdout.write(json.dumps({
'requestId' : request_id,
'type' : 'error' ,
'error' : str (e),
}) + " \n " )
sys.stdout.flush()
Native Module Integration (Node.js Addons / N-API)
When to Use Native Modules
JavaScript performance sometimes isn't sufficient. Consider native modules for:
GPU Processing : CUDA/Metal matrix operations at 10-100x JS speed
Real-Time Audio : Low-latency noise cancellation, speech recognition
Image Processing : OpenCV filters, computer vision pipelines
Hardware Integration : Custom USB devices, hardware accelerators
N-API Implementation
// native/src/gpu_buffer.cc
#include <napi.h>
#include <cuda_runtime.h>
Napi :: Value AllocateGPUBuffer ( const Napi :: CallbackInfo & info ) {
Napi ::Env env = info. Env ();
if (info. Length () < 1 ) {
Napi :: TypeError :: New (env, "Expected 1 argument" )
. ThrowAsJavaScriptException ();
return env. Null ();
}
size_t size = info[ 0 ].As < Napi ::Number > (). Uint32Value ();
float* device_ptr;
cudaMalloc ( & device_ptr, size * sizeof ( float ));
// Return as buffer handle
return Napi :: Buffer < float >:: New (env, device_ptr, size);
}
Napi :: Object Init ( Napi :: Env env , Napi :: Object exports ) {
exports. Set (
Napi :: String :: New (env, "allocateGPUBuffer" ),
Napi :: Function :: New (env, AllocateGPUBuffer)
);
return exports;
}
NODE_API_MODULE (gpu_buffer, Init)
binding.gyp :
{
"targets" : [
{
"target_name" : "gpu_buffer" ,
"sources" : [ "src/gpu_buffer.cc" ],
"include_dirs" : [ "<!@(node -p \" require('node-addon-api').include \" )" ],
"dependencies" : [ "<!(node -p \" require('node-addon-api').gyp \" )" ],
"cflags_cc" : [ "-fexceptions" ],
"xcode_settings" : {
"GCC_ENABLE_CPP_EXCEPTIONS" : "YES"
}
}
]
}
Usage :
const gpu = require ( './build/Release/gpu_buffer.node' )
const buffer = gpu. allocateGPUBuffer ( 1024 * 1024 * 4 ) // 4MB GPU memory
Dynamic Library Binding (node-ffi)
For calling C libraries without compilation overhead:
import Library from 'ffi-napi'
import ref from 'ref-napi'
const libCuda = new Library ( 'libcuda.so.1' , {
cuInit: [ 'int' , [ 'int' ]],
cuDeviceGetCount: [ 'int' , [ref. refType (ref.types.int)]],
})
const count = ref. alloc (ref.types.int)
libCuda. cuDeviceGetCount (count)
console. log ( `Detected ${ count . deref () } CUDA devices` )
Secure Context Bridge and Sandbox Design
Enabling Sandboxing and Permission Management
Electron should enforce strict process isolation by disabling direct Node.js access:
// main.ts
const win = new BrowserWindow ({
webPreferences: {
nodeIntegration: false ,
contextIsolation: true ,
enableRemoteModule: false ,
preload: path. join (__dirname, 'preload.ts' ),
sandbox: true ,
},
})
Preload script exposes only whitelisted IPC channels:
// main/preload.ts
import { contextBridge, ipcRenderer } from 'electron'
const allowedChannels = {
// LLM channels
'llm:generate' : true ,
'llm:stream' : true ,
'model:status' : true ,
// File operations
'file:open' : true ,
'file:save' : true ,
// System info
'system:info' : true ,
}
contextBridge. exposeInMainWorld ( 'electronAPI' , {
ipcInvoke : ( channel : string , args : any ) => {
if (allowedChannels[channel]) {
return ipcRenderer. invoke (channel, args)
}
throw new Error ( `IPC channel "${ channel }" is not allowed` )
},
ipcOn : ( channel : string , callback : ( data : any ) => void ) => {
if (allowedChannels[channel]) {
ipcRenderer. on (channel, ( _ , data ) => callback (data))
}
},
})
declare global {
interface Window {
electronAPI : {
ipcInvoke : ( channel : string , args : any ) => Promise < any >
ipcOn : ( channel : string , callback : ( data : any ) => void ) => void
}
}
}
Renderer Usage :
// renderer/hooks/useIPC.ts
export function useIPC () {
return {
async invoke ( channel : string , args : any ) {
return window.electronAPI. ipcInvoke (channel, args)
},
on ( channel : string , callback : ( data : any ) => void ) {
window.electronAPI. ipcOn (channel, callback)
},
}
}
Filesystem Access Control and Path Traversal Prevention
Always enforce explicit user permission and prevent directory traversal:
// main/ipc/file.ts
ipcMain. handle ( 'file:open-dialog' , async ( event ) => {
const { filePaths } = await dialog. showOpenDialog (mainWindow, {
properties: [ 'openFile' ],
filters: [
{ name: 'Text Files' , extensions: [ 'txt' , 'md' ] },
{ name: 'All Files' , extensions: [ '*' ] },
],
})
return filePaths
})
ipcMain. handle ( 'file:read' , async ( event , filePath : string ) => {
// Prevent path traversal attacks
const normalizedPath = path. normalize (path. resolve (filePath))
const userDir = app. getPath ( 'userData' )
if ( ! normalizedPath. startsWith (userDir)) {
throw new Error ( 'Access denied: file is outside user data directory' )
}
return fs.promises. readFile (normalizedPath, 'utf-8' )
})
ipcMain. handle ( 'file:save' , async ( event , filePath : string , content : string ) => {
const normalizedPath = path. normalize (path. resolve (filePath))
const userDir = app. getPath ( 'userData' )
if ( ! normalizedPath. startsWith (userDir)) {
throw new Error ( 'Access denied: cannot write outside user data directory' )
}
await fs.promises. writeFile (normalizedPath, content, 'utf-8' )
})
Content Security Policy Headers
// main.ts
const win = new BrowserWindow ({
webPreferences: {
contentSecurityPolicy: "default-src 'self'; script-src 'self'" ,
},
})
Auto-Update System Implementation
electron-updater for Seamless Updates
Use electron-updater for differential updates and rollback support:
// main/services/auto-updater.ts
import { autoUpdater } from 'electron-updater'
export class AutoUpdater {
constructor ( private mainWindow : BrowserWindow ) {
this . setupAutoUpdater ()
}
private setupAutoUpdater () {
autoUpdater. checkForUpdatesAndNotify ()
autoUpdater. on ( 'update-available' , ( info ) => {
console. log ( `New version available: ${ info . version }` )
this .mainWindow.webContents. send ( 'update:available' , {
version: info.version,
releaseDate: info.releaseDate,
})
})
autoUpdater. on ( 'update-not-available' , () => {
console. log ( 'App is up to date' )
})
autoUpdater. on ( 'download-progress' , ( progress ) => {
this .mainWindow.webContents. send ( 'update:progress' , {
percent: progress.percent,
bytesPerSecond: progress.bytesPerSecond,
})
})
autoUpdater. on ( 'update-downloaded' , () => {
this .mainWindow.webContents. send ( 'update:downloaded' )
})
autoUpdater. on ( 'error' , ( error ) => {
console. error ( 'Auto-updater error:' , error)
this .mainWindow.webContents. send ( 'update:error' , {
error: error.message,
})
})
}
async checkForUpdates () {
return autoUpdater. checkForUpdates ()
}
async installUpdates () {
autoUpdater. quitAndInstall ()
}
}
IPC Handlers :
// main/ipc/updater.ts
ipcMain. handle ( 'updater:check' , async () => {
return autoUpdater. checkForUpdates ()
})
ipcMain. handle ( 'updater:install' , async () => {
autoUpdater. quitAndInstall ()
})
Renderer UI Component :
// renderer/components/UpdatePrompt.tsx
import { useEffect, useState } from 'react'
export function UpdatePrompt () {
const [ updateInfo , setUpdateInfo ] = useState ( null )
const [ downloadProgress , setDownloadProgress ] = useState ( 0 )
useEffect (() => {
window.electronAPI. ipcOn ( 'update:available' , ( info ) => {
setUpdateInfo (info)
})
window.electronAPI. ipcOn ( 'update:progress' , ( progress ) => {
setDownloadProgress (progress.percent)
})
window.electronAPI. ipcOn ( 'update:downloaded' , () => {
setDownloadProgress ( 100 )
})
}, [])
if ( ! updateInfo) return null
return (
< div className = "update-banner" >
< h3 >New version available ({updateInfo.version}) </ h3 >
< div className = "progress-bar" >
< div style = {{ width : `${ downloadProgress }%` }} />
</ div >
< p >{downloadProgress.toFixed( 0 )} % downloading ...</ p >
{ downloadProgress === 100 && (
< button onClick = {() => window.electronAPI.ipcInvoke( 'updater:install' )}>
Restart and install
</button>
)}
</div>
)
}
package.json Configuration :
{
"build" : {
"publish" : {
"provider" : "github" ,
"owner" : "your-org" ,
"repo" : "your-app"
}
}
}
Packaging and Cross-Platform Building
electron-builder for Distribution Packages
# macOS (DMG + App Store)
npm run build -- --mac dmg mas
# Windows (NSIS + Portable)
npm run build -- --win nsis portable
# Linux (AppImage + deb)
npm run build -- --linux appimage deb
electron-builder.json :
{
"appId" : "com.example.electron-ai-app" ,
"productName" : "AI Assistant" ,
"directories" : {
"buildResources" : "resources" ,
"output" : "dist"
},
"mac" : {
"target" : [ "dmg" , "zip" ],
"hardenedRuntime" : true ,
"entitlements" : "resources/entitlements.mac.plist" ,
"gatekeeper-assess" : false
},
"win" : {
"target" : [
{
"target" : "nsis" ,
"arch" : [ "x64" , "arm64" ]
},
{
"target" : "portable" ,
"arch" : [ "x64" ]
}
],
"signingHashAlgorithms" : [ "sha256" ],
"certificateFile" : "resources/cert.pfx" ,
"certificatePassword" : "YOUR_PASSWORD"
},
"linux" : {
"target" : [ "AppImage" , "deb" ],
"category" : "Utility"
},
"nsis" : {
"oneClick" : false ,
"allowToChangeInstallationDirectory" : true ,
"createDesktopShortcut" : true ,
"createStartMenuShortcut" : true
}
}
GitHub Actions CI/CD Pipeline
name : Build and Release
on :
push :
tags :
- 'v*'
jobs :
build :
runs-on : ${{ matrix.os }}
strategy :
matrix :
os : [ ubuntu-latest , windows-latest , macos-latest ]
steps :
- uses : actions/checkout@v3
- name : Setup Node.js
uses : actions/setup-node@v3
with :
node-version : '18'
- name : Setup Python
uses : actions/setup-python@v4
with :
python-version : '3.10'
- name : Install dependencies
run : |
npm ci
pip install -r python/requirements.txt
- name : Build
env :
GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
APPLE_ID : ${{ secrets.APPLE_ID }}
APPLE_PASSWORD : ${{ secrets.APPLE_PASSWORD }}
run : npm run build
- name : Upload artifacts
uses : softprops/action-gh-release@v1
with :
files : dist/*
Performance Optimization and Memory Management
Memory Leak Detection and Prevention
Common memory leak sources in Electron and prevention strategies:
// ❌ Incorrect: listener persists after unmount
function setupListener () {
ipcRenderer. on ( 'model:status' , ( status ) => {
updateUI (status)
})
// No cleanup → memory leak
}
// ✅ Correct: cleanup in useEffect dependency
useEffect (() => {
function handleModelStatus ( status ) {
updateUI (status)
}
ipcRenderer. on ( 'model:status' , handleModelStatus)
return () => {
ipcRenderer. removeListener ( 'model:status' , handleModelStatus)
}
}, [])
Memory Profiling with DevTools :
// renderer/hooks/useMemoryMonitor.ts
export function useMemoryMonitor ( interval : number = 5000 ) {
useEffect (() => {
const timer = setInterval (() => {
const usage = process. getProcessMemoryInfo ()
console. log ( `Memory: ${ ( usage . heapUsed / 1024 / 1024 ). toFixed ( 2 ) } MB` )
if (usage.heapUsed > 500 * 1024 * 1024 ) {
console. warn ( 'High memory usage detected!' )
}
}, interval)
return () => clearInterval (timer)
}, [interval])
}
Inference Request Queuing
Prevent memory spikes from simultaneous requests with intelligent queuing:
// main/services/inference-queue.ts
class InferenceQueue {
private queue : QueueItem [] = []
private processing = false
private concurrency = 1
async enqueue ( prompt : string , options : GenerateOptions ) {
return new Promise (( resolve , reject ) => {
this .queue. push ({ prompt, options, resolve, reject })
this . processQueue ()
})
}
private async processQueue () {
if ( this .processing || this .queue. length === 0 ) {
return
}
this .processing = true
while ( this .queue. length > 0 ) {
const item = this .queue. shift () !
try {
const result = await this .llmService. generate (
item.prompt,
item.options
)
item. resolve (result)
} catch (error) {
item. reject (error)
}
}
this .processing = false
}
}
V8 Snapshot for Faster Startup
Create binary snapshots for instant application launch (used by Cursor, Windsurf):
// electron.vms.config.js
module . exports = {
snapshotCompiler: true ,
snapshotImage: 'snapshot.bin' ,
// Include preload script in snapshot
v8Code: 'src/main/preload.ts' ,
}