main.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. const electron_1 = require("electron");
  7. const http_1 = require("http");
  8. const next_1 = __importDefault(require("next"));
  9. const path_1 = __importDefault(require("path"));
  10. const fs_1 = __importDefault(require("fs"));
  11. const net_1 = __importDefault(require("net"));
  12. // ─── Globals ─────────────────────────────────────────────────────────────────
  13. const isDev = process.env.NODE_ENV === 'development' || !electron_1.app.isPackaged;
  14. // 开发模式下直接连接已运行的 Next.js dev server,不自己启动
  15. let nextApp = null;
  16. let handle = null;
  17. if (!isDev) {
  18. nextApp = (0, next_1.default)({ dev: false, dir: path_1.default.join(__dirname, '..') });
  19. handle = nextApp.getRequestHandler();
  20. }
  21. let httpServer = null;
  22. let mainWindow = null;
  23. // ─── Port Discovery ───────────────────────────────────────────────────────────
  24. function findAvailablePort(startPort) {
  25. return new Promise((resolve, reject) => {
  26. const server = net_1.default.createServer();
  27. server.listen(startPort, '127.0.0.1', () => {
  28. const address = server.address();
  29. const port = typeof address === 'object' && address ? address.port : startPort;
  30. server.close(() => resolve(port));
  31. });
  32. server.on('error', () => {
  33. // Port in use, try next one
  34. findAvailablePort(startPort + 1).then(resolve).catch(reject);
  35. });
  36. });
  37. }
  38. // ─── App Config (userData) ────────────────────────────────────────────────────
  39. function getAppConfigPath() {
  40. return path_1.default.join(electron_1.app.getPath('userData'), 'app-config.json');
  41. }
  42. function readAppConfig() {
  43. const configPath = getAppConfigPath();
  44. try {
  45. if (fs_1.default.existsSync(configPath)) {
  46. const raw = fs_1.default.readFileSync(configPath, 'utf-8');
  47. return JSON.parse(raw);
  48. }
  49. }
  50. catch {
  51. // Return default config if file is missing or malformed
  52. }
  53. return { recentProjects: [] };
  54. }
  55. function writeAppConfig(config) {
  56. const configPath = getAppConfigPath();
  57. const dir = path_1.default.dirname(configPath);
  58. if (!fs_1.default.existsSync(dir)) {
  59. fs_1.default.mkdirSync(dir, { recursive: true });
  60. }
  61. // Atomic write: write to temp file then rename
  62. const tmpPath = configPath + '.tmp';
  63. fs_1.default.writeFileSync(tmpPath, JSON.stringify(config, null, 2), 'utf-8');
  64. fs_1.default.renameSync(tmpPath, configPath);
  65. }
  66. // ─── IPC Handlers ─────────────────────────────────────────────────────────────
  67. // Select a folder using the native OS dialog
  68. electron_1.ipcMain.handle('select-folder', async () => {
  69. const result = await electron_1.dialog.showOpenDialog({
  70. properties: ['openDirectory'],
  71. });
  72. return result.canceled ? null : result.filePaths[0];
  73. });
  74. // Read global app config from userData
  75. electron_1.ipcMain.handle('get-app-config', () => {
  76. return readAppConfig();
  77. });
  78. // Write global app config to userData
  79. electron_1.ipcMain.handle('save-app-config', (_event, config) => {
  80. writeAppConfig(config);
  81. return { success: true };
  82. });
  83. // ─── App Startup ──────────────────────────────────────────────────────────────
  84. async function startApp() {
  85. let appPort = 3000;
  86. if (isDev) {
  87. // 开发模式:直接连接已运行的 Next.js dev server (localhost:3000)
  88. appPort = 3000;
  89. }
  90. else {
  91. // 生产模式:自己启动内嵌 Next.js 服务器
  92. await nextApp.prepare();
  93. appPort = await findAvailablePort(3000);
  94. httpServer = (0, http_1.createServer)((req, res) => {
  95. handle(req, res);
  96. });
  97. await new Promise((resolve) => {
  98. httpServer.listen(appPort, '127.0.0.1', () => {
  99. resolve();
  100. });
  101. });
  102. }
  103. mainWindow = new electron_1.BrowserWindow({
  104. width: 1400,
  105. height: 900,
  106. webPreferences: {
  107. contextIsolation: true,
  108. nodeIntegration: false,
  109. preload: path_1.default.join(__dirname, 'preload.js'),
  110. },
  111. });
  112. mainWindow.loadURL(`http://localhost:${appPort}/features/novel-writer`);
  113. mainWindow.on('closed', () => {
  114. mainWindow = null;
  115. });
  116. }
  117. // ─── Lifecycle ────────────────────────────────────────────────────────────────
  118. electron_1.app.whenReady().then(startApp).catch((err) => {
  119. console.error('Failed to start app:', err);
  120. electron_1.app.quit();
  121. });
  122. electron_1.app.on('window-all-closed', () => {
  123. if (process.platform !== 'darwin') {
  124. electron_1.app.quit();
  125. }
  126. });
  127. electron_1.app.on('activate', () => {
  128. if (mainWindow === null) {
  129. startApp().catch((err) => {
  130. console.error('Failed to restart app:', err);
  131. });
  132. }
  133. });
  134. electron_1.app.on('before-quit', () => {
  135. if (httpServer) {
  136. httpServer.close();
  137. httpServer = null;
  138. }
  139. });
  140. //# sourceMappingURL=main.js.map