PRA-only-demo/src/main/index.ts

126 lines
4.0 KiB
TypeScript

import { app, BrowserWindow, dialog, ipcMain, shell } from "electron";
import path from "node:path";
import type { AppInfo, FlowDefinition } from "../shared/types";
import "./capabilities";
import { catalogPath, loadCatalog } from "./catalog";
import { createFlow, deleteFlow, getFlow, listFlows, saveFlow } from "./flows";
import { capabilityCount, capabilityIds } from "./engine/registry";
import { closeBrowserSession, startRun, stopRun } from "./run/manager";
let mainWindow: BrowserWindow | null = null;
function createWindow(): void {
mainWindow = new BrowserWindow({
width: 1440,
height: 900,
minWidth: 1200,
minHeight: 760,
show: false,
backgroundColor: "#E7EDF3",
title: "RPA Studio",
titleBarStyle: "hidden",
titleBarOverlay: {
color: "#FFFFFF",
symbolColor: "#4E5A6B",
height: 58
},
webPreferences: {
preload: path.join(__dirname, "../preload/index.js"),
contextIsolation: true,
nodeIntegration: false,
sandbox: false
}
});
mainWindow.on("ready-to-show", () => mainWindow?.show());
mainWindow.on("closed", () => {
mainWindow = null;
});
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
if (/^https?:/i.test(url)) void shell.openExternal(url);
return { action: "deny" };
});
const devUrl = process.env.ELECTRON_RENDERER_URL;
if (devUrl) {
void mainWindow.loadURL(devUrl);
} else {
void mainWindow.loadFile(path.join(__dirname, "../renderer/index.html"));
}
}
function registerIpc(): void {
ipcMain.handle("app:info", (): AppInfo => ({
version: app.getVersion(),
electron: process.versions.electron,
chrome: process.versions.chrome,
node: process.versions.node,
userData: app.getPath("userData"),
catalogPath: catalogPath(),
packaged: app.isPackaged
}));
ipcMain.handle("catalog:get", () => loadCatalog());
ipcMain.handle("run:start", (event, flow: FlowDefinition) => startRun(flow, event.sender));
ipcMain.handle("run:stop", (_event, runId?: string) => stopRun(runId));
ipcMain.handle("run:capabilities", () => ({ count: capabilityCount(), ids: capabilityIds() }));
ipcMain.handle("flows:list", () => listFlows());
ipcMain.handle("flows:create", (_event, name?: string) => {
const flow = createFlow(name);
return saveFlow(flow);
});
ipcMain.handle("flows:get", (_event, id: string) => getFlow(id));
ipcMain.handle("flows:save", (_event, flow: FlowDefinition) => saveFlow(flow));
ipcMain.handle("flows:delete", (_event, id: string) => {
deleteFlow(id);
return true;
});
ipcMain.handle("dialog:open", async (_event, options: { title?: string; filters?: Electron.FileFilter[]; directories?: boolean }) => {
if (!mainWindow) return null;
const result = await dialog.showOpenDialog(mainWindow, {
title: options?.title || "选择文件",
properties: options?.directories ? ["openDirectory"] : ["openFile"],
filters: options?.filters
});
return result.canceled ? null : result.filePaths[0] || null;
});
ipcMain.handle("dialog:save", async (_event, options: { title?: string; defaultPath?: string; filters?: Electron.FileFilter[] }) => {
if (!mainWindow) return null;
const result = await dialog.showSaveDialog(mainWindow, {
title: options?.title || "保存文件",
defaultPath: options?.defaultPath,
filters: options?.filters
});
return result.canceled ? null : result.filePath || null;
});
}
const gotLock = app.requestSingleInstanceLock();
if (!gotLock) {
app.quit();
} else {
app.on("second-instance", () => {
if (!mainWindow) return;
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.focus();
});
app.whenReady().then(() => {
registerIpc();
createWindow();
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") app.quit();
});
app.on("before-quit", () => {
void closeBrowserSession();
});
}