feat: integrate Electron for desktop app

- Add electron-vite + Electron Forge tooling
- Create Electron main process with embedded Express server
- Create preload script with native dialog IPC bridge
- Refactor server.ts to export createExpressApp() (dual web/electron)
- Adapt renderQueue.ts for packaged binaries + pre-built bundle
- Add ensureBrowser() for Chrome Headless Shell pre-download
- Add scripts/bundle-remotion.js for packaging
- Data persists in ~/Library/Application Support/Bradly/
- Web mode preserved via npm run dev:web
This commit is contained in:
2026-06-02 03:57:17 -05:00
parent b135a70cc7
commit 92a8cf78a9
10 changed files with 6137 additions and 26 deletions
+72
View File
@@ -0,0 +1,72 @@
/**
* electron-vite Configuration — Bradly Desktop App
*
* Configures three build targets:
* - main: Electron main process (Node.js)
* - preload: Sandboxed bridge scripts
* - renderer: React app (browser)
*/
import { defineConfig, externalizeDepsPlugin } from 'electron-vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
import path from 'path';
export default defineConfig({
// ═══ Main Process (Node.js environment) ═══
main: {
plugins: [externalizeDepsPlugin()],
build: {
outDir: 'out/main',
rollupOptions: {
input: {
index: path.resolve(__dirname, 'src/electron/main.ts'),
},
// Keep Remotion + Express packages external (they have native deps)
external: [
'@remotion/bundler',
'@remotion/renderer',
/^@remotion\/compositor/,
'express',
'multer',
'vite',
],
},
},
},
// ═══ Preload Scripts (sandboxed bridge) ═══
preload: {
plugins: [externalizeDepsPlugin()],
build: {
outDir: 'out/preload',
rollupOptions: {
input: {
index: path.resolve(__dirname, 'src/electron/preload.ts'),
},
},
},
},
// ═══ Renderer (React app — browser environment) ═══
renderer: {
root: '.',
plugins: [react(), tailwindcss()],
build: {
outDir: 'out/renderer',
rollupOptions: {
input: {
index: path.resolve(__dirname, 'index.html'),
},
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
},
},
server: {
hmr: process.env.DISABLE_HMR !== 'true',
watch: process.env.DISABLE_HMR === 'true' ? null : {},
},
},
});