main.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. import { Menu, app, BrowserWindow, WebContentsView, ipcMain } from "electron";
  2. import { fileURLToPath } from "node:url";
  3. import path from "node:path";
  4. import fs from "node:fs";
  5. const __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
  6. process.env.APP_ROOT = path.join(__dirname$1, "..");
  7. const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"];
  8. const MAIN_DIST = path.join(process.env.APP_ROOT, "dist-electron");
  9. const RENDERER_DIST = path.join(process.env.APP_ROOT, "dist");
  10. process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL ? path.join(process.env.APP_ROOT, "public") : RENDERER_DIST;
  11. let mainWindow;
  12. let childView;
  13. let isSideOpen = true;
  14. let childViewAspectRatio = 0;
  15. const SIDE_WIDTH = 250;
  16. const EXPAND_VIEW_SIZE = 40;
  17. const LOADING_VIEW_WIDTH = 150;
  18. const LOADING_VIEW_HEIGHT = 100;
  19. function loadWindowPage(window, subPath) {
  20. if (VITE_DEV_SERVER_URL) {
  21. window.loadURL(VITE_DEV_SERVER_URL + "#" + subPath);
  22. } else {
  23. window.loadFile(path.join(RENDERER_DIST, "index.html") + "#" + subPath);
  24. }
  25. }
  26. function loadViewUrl(view, subPath) {
  27. if (!mainWindow) {
  28. return;
  29. }
  30. if (VITE_DEV_SERVER_URL) {
  31. view.webContents.loadURL(VITE_DEV_SERVER_URL + "#" + subPath);
  32. } else {
  33. view.webContents.loadFile(path.join(RENDERER_DIST, "index.html") + "#" + subPath);
  34. }
  35. }
  36. Menu.setApplicationMenu(null);
  37. function createWindow() {
  38. mainWindow = new BrowserWindow({
  39. icon: path.join(process.env.VITE_PUBLIC, "icon.ico"),
  40. webPreferences: {
  41. preload: path.join(__dirname$1, "preload.mjs"),
  42. contextIsolation: true,
  43. allowRunningInsecureContent: true,
  44. partition: "persist:minnan-demo-app"
  45. },
  46. width: 1200,
  47. height: 800
  48. });
  49. childView = new WebContentsView({
  50. webPreferences: {
  51. partition: "persist:minnan-demo-app",
  52. allowRunningInsecureContent: true,
  53. enableBlinkFeatures: "PasswordManager"
  54. }
  55. });
  56. const expandButtonView = new WebContentsView({
  57. webPreferences: {
  58. preload: path.join(__dirname$1, "preload.mjs"),
  59. contextIsolation: true
  60. }
  61. });
  62. const loadingView = new BrowserWindow({
  63. skipTaskbar: true,
  64. width: LOADING_VIEW_WIDTH,
  65. height: LOADING_VIEW_HEIGHT,
  66. parent: mainWindow,
  67. thickFrame: true,
  68. titleBarStyle: "hidden",
  69. webPreferences: {
  70. preload: path.join(__dirname$1, "preload.mjs"),
  71. contextIsolation: true
  72. }
  73. });
  74. mainWindow.contentView.addChildView(childView);
  75. mainWindow.contentView.addChildView(expandButtonView);
  76. expandButtonView.setVisible(false);
  77. childView.webContents.on("did-start-loading", () => {
  78. loadingView.show();
  79. });
  80. childView.webContents.on("did-stop-loading", () => {
  81. loadingView.hide();
  82. });
  83. childView.webContents.on("did-fail-load", (_, errorCode, errorDescription) => {
  84. loadingView.hide();
  85. loadWindowPage(loadingView, "/error?code=" + errorCode + "&message=" + errorDescription);
  86. });
  87. function updateChildWindowBounds() {
  88. const bounds = mainWindow.getBounds();
  89. expandButtonView.setBounds({
  90. x: 0,
  91. y: bounds.height - 100,
  92. width: EXPAND_VIEW_SIZE,
  93. height: EXPAND_VIEW_SIZE
  94. });
  95. loadingView.setBounds({
  96. x: bounds.x + (bounds.width - LOADING_VIEW_WIDTH) / 2,
  97. y: bounds.y + (bounds.height - LOADING_VIEW_HEIGHT) / 2,
  98. width: LOADING_VIEW_WIDTH,
  99. height: LOADING_VIEW_HEIGHT
  100. });
  101. if (childViewAspectRatio) {
  102. const rect = {
  103. x: isSideOpen ? SIDE_WIDTH : 0,
  104. y: 0,
  105. width: bounds.width - (isSideOpen ? SIDE_WIDTH : 0),
  106. height: bounds.height
  107. };
  108. const availableRatio = rect.width / rect.height;
  109. let childWidth, childHeight;
  110. if (availableRatio > childViewAspectRatio) {
  111. childHeight = rect.height;
  112. childWidth = childHeight * childViewAspectRatio;
  113. } else {
  114. childWidth = rect.width;
  115. childHeight = childWidth / childViewAspectRatio;
  116. }
  117. const childX = rect.x + (rect.width - childWidth) / 2;
  118. const childY = rect.y + (rect.height - childHeight) / 2;
  119. childView.setBounds({
  120. x: Math.round(childX),
  121. y: Math.round(childY),
  122. width: Math.round(childWidth),
  123. height: Math.round(childHeight)
  124. });
  125. } else {
  126. childView.setBounds({
  127. x: isSideOpen ? SIDE_WIDTH : 0,
  128. y: 0,
  129. width: bounds.width - (isSideOpen ? SIDE_WIDTH : 0),
  130. height: bounds.height
  131. });
  132. }
  133. }
  134. loadWindowPage(mainWindow, "/");
  135. loadWindowPage(loadingView, "/loading");
  136. loadViewUrl(childView, "/hello");
  137. loadViewUrl(expandButtonView, "/expand");
  138. updateChildWindowBounds();
  139. mainWindow.on("resize", () => {
  140. updateChildWindowBounds();
  141. });
  142. mainWindow.webContents.on("did-finish-load", () => {
  143. mainWindow == null ? void 0 : mainWindow.webContents.send("main-process-message", (/* @__PURE__ */ new Date()).toLocaleString());
  144. });
  145. function handleWindowFullScreenKeys(event, input) {
  146. if (input.key === "F11" && input.type === "keyDown") {
  147. event.preventDefault();
  148. mainWindow == null ? void 0 : mainWindow.setFullScreen(!(mainWindow == null ? void 0 : mainWindow.isFullScreen()));
  149. } else if (input.key === "F12" && input.type === "keyDown") {
  150. event.preventDefault();
  151. mainWindow == null ? void 0 : mainWindow.webContents.toggleDevTools();
  152. }
  153. }
  154. mainWindow.webContents.on("before-input-event", (event, input) => {
  155. handleWindowFullScreenKeys(event, input);
  156. });
  157. childView.webContents.on("before-input-event", (event, input) => {
  158. handleWindowFullScreenKeys(event, input);
  159. });
  160. ipcMain.on("exit-app", () => {
  161. app.quit();
  162. });
  163. ipcMain.on("toggle-fullscreen", (_event, isFullScreen) => {
  164. if (mainWindow) {
  165. if (isFullScreen) {
  166. mainWindow.setFullScreen(true);
  167. } else {
  168. mainWindow.setFullScreen(false);
  169. }
  170. }
  171. });
  172. ipcMain.on("load-child-url", (_event, url, aspectRatio) => {
  173. if (childView)
  174. childView.webContents.loadURL(url);
  175. childViewAspectRatio = aspectRatio;
  176. updateChildWindowBounds();
  177. });
  178. ipcMain.on("toggle-child-side", (_event, value) => {
  179. isSideOpen = value;
  180. expandButtonView.setVisible(!isSideOpen);
  181. mainWindow == null ? void 0 : mainWindow.webContents.send("main-side-state-changed", isSideOpen);
  182. updateChildWindowBounds();
  183. });
  184. ipcMain.handle("get-app-path", () => {
  185. return app.getAppPath();
  186. });
  187. ipcMain.on("open-window", (_event, url) => {
  188. const newWin = new BrowserWindow({
  189. icon: path.join(process.env.VITE_PUBLIC, "icon.ico"),
  190. webPreferences: {
  191. preload: path.join(__dirname$1, "preload.mjs"),
  192. contextIsolation: true,
  193. allowRunningInsecureContent: true
  194. },
  195. fullscreenable: true,
  196. maximizable: true,
  197. width: 1200,
  198. height: 800
  199. });
  200. newWin.loadURL(url);
  201. newWin.maximize();
  202. newWin.webContents.on("before-input-event", (event, input) => {
  203. if (input.key === "F11" && input.type === "keyDown") {
  204. event.preventDefault();
  205. newWin == null ? void 0 : newWin.setFullScreen(!(newWin == null ? void 0 : newWin.fullScreen));
  206. } else if (input.key === "F12" && input.type === "keyDown") {
  207. event.preventDefault();
  208. newWin == null ? void 0 : newWin.webContents.toggleDevTools();
  209. }
  210. });
  211. });
  212. ipcMain.handle("load-apps-json", async () => {
  213. const appPath = process.cwd();
  214. const appsJsonPath = path.join(appPath, "apps.json");
  215. try {
  216. if (fs.existsSync(appsJsonPath)) {
  217. const data = fs.readFileSync(appsJsonPath, "utf8");
  218. return JSON.parse(data);
  219. } else {
  220. const devAppsJsonPath = path.join(process.env.VITE_PUBLIC || "", "apps.json");
  221. if (fs.existsSync(devAppsJsonPath)) {
  222. const data = fs.readFileSync(devAppsJsonPath, "utf8");
  223. return JSON.parse(data);
  224. }
  225. throw new Error("apps.json not found");
  226. }
  227. } catch (error) {
  228. console.error("Error loading apps.json:", error);
  229. throw error;
  230. }
  231. });
  232. ipcMain.handle("load-default-apps-json", async () => {
  233. const devAppsJsonPath = path.join(process.env.VITE_PUBLIC || "", "apps.json");
  234. if (fs.existsSync(devAppsJsonPath)) {
  235. const data = fs.readFileSync(devAppsJsonPath, "utf8");
  236. return JSON.parse(data);
  237. }
  238. throw new Error("apps.json not found");
  239. });
  240. ipcMain.on("show-config", () => {
  241. const configWindow = new BrowserWindow({
  242. icon: path.join(process.env.VITE_PUBLIC, "icon.ico"),
  243. webPreferences: {
  244. preload: path.join(__dirname$1, "preload.mjs"),
  245. contextIsolation: true,
  246. allowRunningInsecureContent: true
  247. },
  248. title: "列表配置",
  249. parent: mainWindow || void 0,
  250. skipTaskbar: true,
  251. minimizable: false,
  252. maximizable: false,
  253. modal: true,
  254. width: 800,
  255. height: 600
  256. });
  257. loadWindowPage(configWindow, "/config");
  258. });
  259. ipcMain.on("save-apps-json", (_event, appsJson) => {
  260. const appPath = process.cwd();
  261. const appsJsonPath = path.join(appPath, "apps.json");
  262. try {
  263. fs.writeFileSync(appsJsonPath, appsJson);
  264. mainWindow == null ? void 0 : mainWindow.webContents.send("main-config-changed");
  265. } catch (error) {
  266. console.error("Error saving apps.json:", error);
  267. throw error;
  268. }
  269. });
  270. ipcMain.on("show-about", () => {
  271. const aboutWindow = new BrowserWindow({
  272. icon: path.join(process.env.VITE_PUBLIC, "icon.ico"),
  273. webPreferences: {
  274. preload: path.join(__dirname$1, "preload.mjs"),
  275. contextIsolation: true,
  276. allowRunningInsecureContent: true
  277. },
  278. parent: mainWindow || void 0,
  279. title: "关于程序",
  280. skipTaskbar: true,
  281. maximizable: false,
  282. minimizable: false,
  283. modal: true,
  284. width: 450,
  285. height: 470
  286. });
  287. loadWindowPage(aboutWindow, "/about");
  288. });
  289. }
  290. app.on("window-all-closed", () => {
  291. if (process.platform !== "darwin") {
  292. app.quit();
  293. mainWindow = null;
  294. }
  295. });
  296. app.on("activate", () => {
  297. if (BrowserWindow.getAllWindows().length === 0) {
  298. createWindow();
  299. }
  300. });
  301. app.whenReady().then(createWindow);
  302. export {
  303. MAIN_DIST,
  304. RENDERER_DIST,
  305. VITE_DEV_SERVER_URL
  306. };