setup electron multi-window architecture (#102)

* prototype with two windows

* use deps in separate window render process

* enhance autoplay into desktop overlay

* fix: autoplay window not applying tailwind styles
This commit was merged in pull request #102.
This commit is contained in:
Arjun Patel
2026-03-31 15:05:59 -07:00
committed by GitHub
parent 7f490b4596
commit 3de3ca7cef
21 changed files with 428 additions and 133 deletions
+112 -4
View File
@@ -1,4 +1,4 @@
import { app, BrowserWindow, ipcMain, session, shell } from 'electron';
import { app, BrowserWindow, ipcMain, screen, session, shell } from 'electron';
import path from 'node:path';
import started from 'electron-squirrel-startup';
import { updateElectronApp, UpdateSourceType } from 'update-electron-app';
@@ -22,9 +22,12 @@ if (process.platform === 'darwin' && !app.isPackaged) {
app.dock?.setIcon(path.join(__dirname, '../../assets/icon.png'));
}
let mainWindow: BrowserWindow | null = null;
let autoplayWindow: BrowserWindow | null = null;
let huddleWindow: BrowserWindow | null = null;
const createWindow = () => {
// Create the browser window.
const mainWindow = new BrowserWindow({
mainWindow = new BrowserWindow({
width: 800,
height: 600,
resizable: true,
@@ -35,7 +38,6 @@ const createWindow = () => {
},
});
// and load the index.html of the app.
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
mainWindow.webContents.openDevTools();
@@ -44,8 +46,80 @@ const createWindow = () => {
path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`),
);
}
mainWindow.on('closed', () => {
mainWindow = null;
});
};
const createAutoplayWindow = () => {
if (autoplayWindow) return;
autoplayWindow = new BrowserWindow({
width: 320,
height: 88,
resizable: false,
frame: false,
alwaysOnTop: true,
skipTaskbar: true,
focusable: false,
show: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
if (AUTOPLAY_WINDOW_VITE_DEV_SERVER_URL) {
autoplayWindow.loadURL(AUTOPLAY_WINDOW_VITE_DEV_SERVER_URL);
} else {
autoplayWindow.loadFile(
path.join(__dirname, `../renderer/${AUTOPLAY_WINDOW_VITE_NAME}/index.html`),
);
}
autoplayWindow.on('closed', () => {
autoplayWindow = null;
});
};
const createHuddleWindow = () => {
if (huddleWindow) {
huddleWindow.focus();
return;
}
huddleWindow = new BrowserWindow({
width: 1024,
height: 768,
resizable: true,
fullscreenable: true,
frame: true,
title: 'Huddle',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
if (HUDDLE_WINDOW_VITE_DEV_SERVER_URL) {
huddleWindow.loadURL(HUDDLE_WINDOW_VITE_DEV_SERVER_URL);
} else {
huddleWindow.loadFile(
path.join(__dirname, `../renderer/${HUDDLE_WINDOW_VITE_NAME}/index.html`),
);
}
huddleWindow.on('closed', () => {
huddleWindow = null;
});
};
function positionAutoplayWindow() {
if (!autoplayWindow) return;
const { width } = screen.getPrimaryDisplay().workAreaSize;
const [winW] = autoplayWindow.getSize();
autoplayWindow.setPosition(width - winW - 16, 16);
}
// Window control IPC handlers
ipcMain.on('window:minimize', (event) => {
BrowserWindow.fromWebContents(event.sender)?.minimize();
@@ -66,6 +140,39 @@ ipcMain.on('window:fullscreen', (event) => {
if (win) win.setFullScreen(!win.isFullScreen());
});
// Secondary window IPC handlers
ipcMain.on('window:open-huddle', () => {
createHuddleWindow();
});
ipcMain.on('window:close-huddle', () => {
huddleWindow?.close();
});
// Autoplay IPC handlers
ipcMain.on('autoplay:play', (_event, payload) => {
if (!autoplayWindow) createAutoplayWindow();
if (!autoplayWindow) return;
const isVideo = typeof payload?.mimeType === 'string' && payload.mimeType.startsWith('video/');
autoplayWindow.setSize(320, isVideo ? 260 : 88);
positionAutoplayWindow();
autoplayWindow.webContents.send('autoplay:play', payload);
autoplayWindow.showInactive();
});
ipcMain.on('autoplay:dismiss', () => {
autoplayWindow?.hide();
});
ipcMain.on('autoplay:navigate', (_event, data) => {
autoplayWindow?.hide();
if (mainWindow) {
mainWindow.webContents.send('autoplay:navigate', data);
mainWindow.focus();
}
});
// --- Link metadata ---
const metadataCache = new Map<string, LinkMetadata>();
@@ -212,6 +319,7 @@ app.on('ready', () => {
);
createWindow();
createAutoplayWindow();
});
// Quit when all windows are closed, except on macOS. There, it's common