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
+28 -17
View File
@@ -6,7 +6,7 @@ import { createServer as createViteServer } from "vite";
import multer from "multer";
// ═══ Uploads directory ═══
const UPLOADS_DIR = path.join(process.cwd(), "uploads");
const UPLOADS_DIR = process.env.BRADLY_UPLOADS_DIR || path.join(process.cwd(), "uploads");
if (!fs.existsSync(UPLOADS_DIR)) {
fs.mkdirSync(UPLOADS_DIR, { recursive: true });
}
@@ -40,6 +40,14 @@ const mediaUpload = multer({
});
async function startServer() {
const app = await createExpressApp();
const PORT = 3000;
app.listen(PORT, "0.0.0.0", () => {
console.log(`Server running on http://localhost:${PORT}`);
});
}
export async function createExpressApp() {
const app = express();
const PORT = 3000;
@@ -299,23 +307,26 @@ async function startServer() {
});
});
if (process.env.NODE_ENV !== "production") {
const vite = await createViteServer({
server: { middlewareMode: true },
appType: "spa",
});
app.use(vite.middlewares);
} else {
const distPath = path.join(process.cwd(), "dist");
app.use(express.static(distPath));
app.get("*", (req, res) => {
res.sendFile(path.join(distPath, "index.html"));
});
if (!process.env.BRADLY_ELECTRON) {
if (process.env.NODE_ENV !== "production") {
const vite = await createViteServer({
server: { middlewareMode: true },
appType: "spa",
});
app.use(vite.middlewares);
} else {
const distPath = path.join(process.cwd(), "dist");
app.use(express.static(distPath));
app.get("*", (req, res) => {
res.sendFile(path.join(distPath, "index.html"));
});
}
}
app.listen(PORT, "0.0.0.0", () => {
console.log(`Server running on http://localhost:${PORT}`);
});
return app;
}
startServer();
// Direct execution (web mode only)
if (!process.env.BRADLY_ELECTRON) {
startServer();
}