Files
TrueGrowth/vendor/aigcpanel/electron/main/index.ts
Jammy 52636c91ae
Some checks failed
CI / main (push) Has been cancelled
CI / release-e2e (push) Has been cancelled
Track bundled vendor runtime sources
2026-07-07 10:05:50 +08:00

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();
}
});