273 lines
8.6 KiB
TypeScript
273 lines
8.6 KiB
TypeScript
import { app, BrowserWindow, desktopCapturer, session, shell } from "electron";
|
|
import { optimizer } from "@electron-toolkit/utils";
|
|
import path from "node:path";
|
|
import fs from "node:fs";
|
|
|
|
/** process.js 必须位于非依赖项的顶部 */
|
|
import { isDummy } from "../lib/process";
|
|
import { AppEnv, AppRuntime } from "../mapi/env";
|
|
import { MAPI } from "../mapi/main";
|
|
|
|
import { WindowConfig } from "../config/window";
|
|
import { AppConfig } from "../../src/config";
|
|
import Log from "../mapi/log/main";
|
|
import { ConfigMenu } from "../config/menu";
|
|
import { ConfigLang, t } from "../config/lang";
|
|
import { ConfigContextMenu } from "../config/contextMenu";
|
|
import { preloadDefault, rendererLoadPath } from "../lib/env-main";
|
|
import { Page } from "../page";
|
|
import { ConfigTray } from "../config/tray";
|
|
import { icnsLogoPath, icoLogoPath, logoPath } from "../config/icon";
|
|
import { isDev, isMac, isPackaged } from "../lib/env";
|
|
import { executeHooks } from "../lib/hooks";
|
|
import { DevToolsManager } from "../lib/devtools";
|
|
import { AppsMain } from "../mapi/app/main";
|
|
import { ServerMain } from "../mapi/server/main";
|
|
import { HttpServerMain } from "../mapi/httpserver/main";
|
|
import ConfigMain from "../mapi/config/main";
|
|
import { reportError } from "../mapi/log/beacon";
|
|
|
|
const isDummyNew = isDummy;
|
|
|
|
if (process.env["ELECTRON_ENV_PROD"]) {
|
|
DevToolsManager.setEnable(false);
|
|
}
|
|
|
|
process.on("uncaughtException", (reason) => {
|
|
let error: any = reason;
|
|
if (error instanceof Error) {
|
|
error = [error.message, error.stack].join("\n");
|
|
}
|
|
Log.error("UncaughtException", error);
|
|
reportError(
|
|
reason instanceof Error ? reason.message : String(reason),
|
|
reason instanceof Error ? reason.stack : undefined,
|
|
);
|
|
});
|
|
|
|
process.on("unhandledRejection", (reason) => {
|
|
Log.error("UnhandledRejection", reason);
|
|
let error: any = reason;
|
|
if (error instanceof Error) {
|
|
error = [error.message, error.stack].join("\n");
|
|
}
|
|
Log.error("UnhandledRejection", error);
|
|
reportError(
|
|
reason instanceof Error ? (reason as Error).message : String(reason),
|
|
reason instanceof Error ? (reason as Error).stack : undefined,
|
|
);
|
|
});
|
|
|
|
// Ensure consistent app name (macOS uses Info.plist CFBundleName from the dev binary,
|
|
// which would be "Electron" instead of "aigcpanel" without this explicit override)
|
|
if (app.getName() !== "aigcpanel") {
|
|
app.setName("aigcpanel");
|
|
}
|
|
|
|
app.disableHardwareAcceleration();
|
|
|
|
// Set application name for Windows 10+ notifications
|
|
if (process.platform === "win32") app.setAppUserModelId(app.getName());
|
|
|
|
if (!app.requestSingleInstanceLock()) {
|
|
app.quit();
|
|
process.exit(0);
|
|
}
|
|
|
|
const hasSplashWindow = true;
|
|
|
|
AppEnv.appRoot = process.env.APP_ROOT;
|
|
AppEnv.appData = app.getPath("appData");
|
|
AppEnv.userData = app.getPath("userData");
|
|
AppEnv.dataRoot = path.join(AppEnv.userData, "data");
|
|
|
|
if (!fs.existsSync(AppEnv.dataRoot)) {
|
|
fs.mkdirSync(AppEnv.dataRoot, { recursive: true });
|
|
}
|
|
for (const dir of ["logs", "storage"]) {
|
|
if (!fs.existsSync(path.join(AppEnv.dataRoot, dir))) {
|
|
fs.mkdirSync(path.join(AppEnv.dataRoot, dir), { recursive: true });
|
|
}
|
|
}
|
|
|
|
AppEnv.isInit = true;
|
|
|
|
MAPI.init();
|
|
ConfigContextMenu.init();
|
|
|
|
Log.info("Starting");
|
|
Log.info("LaunchInfo", {
|
|
isPackaged,
|
|
userData: AppEnv.userData,
|
|
dataRoot: AppEnv.dataRoot,
|
|
});
|
|
|
|
async function createWindow() {
|
|
let icon = logoPath;
|
|
if (process.platform === "win32") {
|
|
icon = icoLogoPath;
|
|
} else if (process.platform === "darwin") {
|
|
icon = icnsLogoPath;
|
|
}
|
|
if (hasSplashWindow) {
|
|
AppRuntime.splashWindow = new BrowserWindow({
|
|
title: AppConfig.title,
|
|
width: 600,
|
|
height: 350,
|
|
transparent: true,
|
|
frame: false,
|
|
alwaysOnTop: true,
|
|
hasShadow: true,
|
|
skipTaskbar: true,
|
|
});
|
|
rendererLoadPath(AppRuntime.splashWindow, "splash.html");
|
|
}
|
|
AppRuntime.mainWindow = new BrowserWindow({
|
|
show: !hasSplashWindow,
|
|
title: AppConfig.title,
|
|
...(!isPackaged ? { icon } : {}),
|
|
frame: false,
|
|
transparent: false,
|
|
hasShadow: true,
|
|
center: true,
|
|
minWidth: WindowConfig.initWidth,
|
|
minHeight: WindowConfig.initHeight,
|
|
width: WindowConfig.initWidth,
|
|
height: WindowConfig.initHeight,
|
|
backgroundColor: await AppsMain.defaultDarkModeBackgroundColor(),
|
|
titleBarStyle: "hidden",
|
|
trafficLightPosition: { x: 10, y: 11 },
|
|
webPreferences: {
|
|
preload: preloadDefault,
|
|
// Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
|
|
nodeIntegration: true,
|
|
webSecurity: false,
|
|
webviewTag: true,
|
|
// Consider using contextBridge.exposeInMainWorld
|
|
// Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation
|
|
contextIsolation: false,
|
|
// sandbox: false,
|
|
},
|
|
});
|
|
|
|
AppRuntime.mainWindow.on("closed", () => {
|
|
AppRuntime.mainWindow = null;
|
|
});
|
|
AppRuntime.mainWindow.on("show", async () => {
|
|
await executeHooks(AppRuntime.mainWindow, "Show");
|
|
});
|
|
AppRuntime.mainWindow.on("hide", async () => {
|
|
await executeHooks(AppRuntime.mainWindow, "Hide");
|
|
});
|
|
|
|
if (isMac) {
|
|
AppRuntime.mainWindow.on("close", (event) => {
|
|
// @ts-ignore
|
|
if (!app.quitForce && !isDev) {
|
|
executeHooks(AppRuntime.mainWindow, "ShowQuitConfirmDialog");
|
|
event.preventDefault();
|
|
}
|
|
});
|
|
}
|
|
AppRuntime.mainWindow.on("enter-full-screen", () => {
|
|
executeHooks(AppRuntime.mainWindow, "EnterFullScreen");
|
|
});
|
|
AppRuntime.mainWindow.on("leave-full-screen", () => {
|
|
executeHooks(AppRuntime.mainWindow, "LeaveFullScreen");
|
|
});
|
|
|
|
rendererLoadPath(AppRuntime.mainWindow, "index.html");
|
|
DevToolsManager.register("Main", AppRuntime.mainWindow);
|
|
|
|
AppRuntime.mainWindow.webContents.on("did-finish-load", () => {
|
|
if (hasSplashWindow) {
|
|
AppRuntime.mainWindow?.show();
|
|
setTimeout(() => {
|
|
try {
|
|
AppRuntime.splashWindow?.close();
|
|
AppRuntime.splashWindow = null;
|
|
// AppRuntime.mainWindow.webContents.openDevTools({
|
|
// mode: 'detach',
|
|
// })
|
|
} catch (e) {}
|
|
}, 1000);
|
|
}
|
|
Page.ready("main");
|
|
DevToolsManager.autoShow(AppRuntime.mainWindow);
|
|
});
|
|
AppRuntime.mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
|
if (url.startsWith("https:")) shell.openExternal(url);
|
|
return { action: "deny" };
|
|
});
|
|
}
|
|
|
|
app.whenReady()
|
|
.then(() => {
|
|
session.defaultSession.setDisplayMediaRequestHandler(
|
|
(request, callback) => {
|
|
desktopCapturer
|
|
.getSources({ types: ["screen"] })
|
|
.then((sources) => {
|
|
// Grant access to the first screen found.
|
|
callback({ video: sources[0], audio: "loopback" });
|
|
});
|
|
},
|
|
);
|
|
})
|
|
.then(ConfigLang.readyAsync)
|
|
.then(() => {
|
|
MAPI.ready();
|
|
ConfigMenu.ready();
|
|
ConfigTray.ready();
|
|
HttpServerMain.start().catch((err: any) => {
|
|
Log.error("HttpServer auto-start failed", err);
|
|
});
|
|
app.on("browser-window-created", (_, window) => {
|
|
optimizer.watchWindowShortcuts(window);
|
|
});
|
|
createWindow().then();
|
|
});
|
|
|
|
app.on("before-quit", (event) => {
|
|
const localServerRunningCount = ServerMain.getRunningServerCount();
|
|
if (localServerRunningCount > 0) {
|
|
// && isPackaged
|
|
event.preventDefault();
|
|
AppsMain.toast(
|
|
t("有 {count} 个本地模型服务正在运行,请停止后再关闭应用", {
|
|
count: localServerRunningCount,
|
|
}),
|
|
);
|
|
}
|
|
});
|
|
|
|
app.on("will-quit", () => {
|
|
MAPI.destroy();
|
|
});
|
|
|
|
app.on("window-all-closed", () => {
|
|
if (process.platform !== "darwin") app.quit();
|
|
});
|
|
|
|
app.on("second-instance", () => {
|
|
if (AppRuntime.mainWindow) {
|
|
if (AppRuntime.mainWindow.isMinimized()) {
|
|
AppRuntime.mainWindow.restore();
|
|
}
|
|
AppRuntime.mainWindow.show();
|
|
AppRuntime.mainWindow.focus();
|
|
}
|
|
});
|
|
|
|
app.on("activate", () => {
|
|
const allWindows = BrowserWindow.getAllWindows();
|
|
if (allWindows.length) {
|
|
if (!AppRuntime.mainWindow.isVisible()) {
|
|
AppRuntime.mainWindow.show();
|
|
}
|
|
AppRuntime.mainWindow.focus();
|
|
} else {
|
|
createWindow().then();
|
|
}
|
|
});
|