Skip to content

Commit b0b1f0f

Browse files
committed
feat: window management/label apis for electron
1 parent a370086 commit b0b1f0f

3 files changed

Lines changed: 111 additions & 1 deletion

File tree

src-electron/main-window-ipc.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
const { ipcMain, BrowserWindow } = require('electron');
2+
const path = require('path');
3+
4+
const PHOENIX_WINDOW_PREFIX = 'phcode-';
5+
const PHOENIX_EXTENSION_WINDOW_PREFIX = 'extn-';
6+
const MAX_WINDOWS = 30;
7+
8+
// Window registry: windowLabel -> BrowserWindow
9+
const windowRegistry = new Map();
10+
// Reverse lookup: webContentsId -> windowLabel
11+
const webContentsToLabel = new Map();
12+
13+
function getNextLabel(prefix) {
14+
for (let i = 1; i <= MAX_WINDOWS; i++) {
15+
const label = `${prefix}${i}`;
16+
if (!windowRegistry.has(label)) {
17+
return label;
18+
}
19+
}
20+
throw new Error(`No free window label available for prefix: ${prefix}`);
21+
}
22+
23+
function registerWindow(win, label) {
24+
windowRegistry.set(label, win);
25+
webContentsToLabel.set(win.webContents.id, label);
26+
27+
win.on('closed', () => {
28+
windowRegistry.delete(label);
29+
webContentsToLabel.delete(win.webContents.id);
30+
});
31+
}
32+
33+
function registerWindowIpcHandlers() {
34+
// Get all window labels (mirrors Tauri's _get_window_labels)
35+
ipcMain.handle('get-window-labels', () => {
36+
return Array.from(windowRegistry.keys());
37+
});
38+
39+
// Get current window's label
40+
ipcMain.handle('get-current-window-label', (event) => {
41+
return webContentsToLabel.get(event.sender.id) || null;
42+
});
43+
44+
// Create new window (mirrors openURLInPhoenixWindow for Electron)
45+
ipcMain.handle('create-phoenix-window', async (event, url, options) => {
46+
const { windowTitle, fullscreen, resizable, height, minHeight, width, minWidth, isExtension } = options || {};
47+
48+
const prefix = isExtension ? PHOENIX_EXTENSION_WINDOW_PREFIX : PHOENIX_WINDOW_PREFIX;
49+
const label = getNextLabel(prefix);
50+
51+
const win = new BrowserWindow({
52+
width: width || 1366,
53+
height: height || 900,
54+
minWidth: minWidth || 800,
55+
minHeight: minHeight || 600,
56+
fullscreen: fullscreen || false,
57+
resizable: resizable !== false,
58+
title: windowTitle || label,
59+
webPreferences: {
60+
preload: path.join(__dirname, 'preload.js'),
61+
contextIsolation: true,
62+
nodeIntegration: false
63+
}
64+
});
65+
66+
registerWindow(win, label);
67+
await win.loadURL(url);
68+
69+
return label;
70+
});
71+
72+
// Close current window
73+
ipcMain.handle('close-window', async (event) => {
74+
const win = BrowserWindow.fromWebContents(event.sender);
75+
if (win) {
76+
win.close();
77+
}
78+
});
79+
80+
// Quit app (for last window scenario)
81+
ipcMain.handle('quit-app', (event, exitCode) => {
82+
const { app } = require('electron');
83+
app.exit(exitCode || 0);
84+
});
85+
86+
// Focus current window
87+
ipcMain.handle('focus-window', (event) => {
88+
const win = BrowserWindow.fromWebContents(event.sender);
89+
if (win) {
90+
win.setAlwaysOnTop(true);
91+
win.focus();
92+
win.setAlwaysOnTop(false);
93+
}
94+
});
95+
}
96+
97+
module.exports = { registerWindowIpcHandlers, registerWindow, windowRegistry };

src-electron/main.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const fs = require('fs');
55
const { registerAppIpcHandlers, terminateAllProcesses } = require('./main-app-ipc');
66
const { registerFsIpcHandlers, getAppDataDir } = require('./main-fs-ipc');
77
const { registerCredIpcHandlers, cleanupWindowTrust } = require('./main-cred-ipc');
8+
const { registerWindowIpcHandlers, registerWindow } = require('./main-window-ipc');
89

910
// In-memory key-value store shared across all windows (mirrors Tauri's put_item/get_all_items)
1011
// Used for multi-window storage synchronization
@@ -24,6 +25,9 @@ async function createWindow() {
2425
icon: path.join(__dirname, '..', 'src-tauri', 'icons', 'icon.png')
2526
});
2627

28+
// Register main window with label 'main' (mirrors Tauri's window labeling)
29+
registerWindow(mainWindow, 'main');
30+
2731
// Load the test page from the http-server
2832
mainWindow.loadURL('http://localhost:8000/src/');
2933

@@ -46,6 +50,7 @@ async function gracefulShutdown(exitCode = 0) {
4650
registerAppIpcHandlers();
4751
registerFsIpcHandlers();
4852
registerCredIpcHandlers();
53+
registerWindowIpcHandlers();
4954

5055
/**
5156
* IPC handlers for electronAPI

src-electron/preload.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,13 @@ contextBridge.exposeInMainWorld('electronAPI', {
8787
removeTrustWindowAesKey: (key, iv) => ipcRenderer.invoke('remove-trust-window-aes-key', key, iv),
8888
storeCredential: (scopeName, secretVal) => ipcRenderer.invoke('store-credential', scopeName, secretVal),
8989
getCredential: (scopeName) => ipcRenderer.invoke('get-credential', scopeName),
90-
deleteCredential: (scopeName) => ipcRenderer.invoke('delete-credential', scopeName)
90+
deleteCredential: (scopeName) => ipcRenderer.invoke('delete-credential', scopeName),
91+
92+
// Window management APIs (mirrors Tauri's window labeling scheme)
93+
getWindowLabels: () => ipcRenderer.invoke('get-window-labels'),
94+
getCurrentWindowLabel: () => ipcRenderer.invoke('get-current-window-label'),
95+
createPhoenixWindow: (url, options) => ipcRenderer.invoke('create-phoenix-window', url, options),
96+
closeWindow: () => ipcRenderer.invoke('close-window'),
97+
quitApp: (exitCode) => ipcRenderer.invoke('quit-app', exitCode),
98+
focusWindow: () => ipcRenderer.invoke('focus-window')
9199
});

0 commit comments

Comments
 (0)