const { app, BrowserWindow, ipcMain, dialog } = require('electron') const path = require('path') const fs = require('fs') const { createProject, loadProject, saveProject, importFiles, removeFile, setProjectIcon } = require('./project') const { startPack, cancelPack } = require('./builder') let mainWindow function createWindow() { mainWindow = new BrowserWindow({ width: 1200, height: 800, minWidth: 900, minHeight: 600, webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false }, title: 'Web2App', show: false }) if (process.env.NODE_ENV === 'development' || process.argv.includes('--dev')) { mainWindow.loadURL('http://localhost:5173') mainWindow.webContents.openDevTools() } else { mainWindow.loadFile(path.join(__dirname, '..', 'dist', 'index.html')) } mainWindow.once('ready-to-show', () => { mainWindow.show() }) } ipcMain.handle('dialog:selectDirectory', async () => { const result = await dialog.showOpenDialog(mainWindow, { properties: ['openDirectory'] }) if (!result.canceled && result.filePaths.length > 0) { return result.filePaths[0] } return null }) ipcMain.handle('dialog:selectFile', async (_event, options) => { const result = await dialog.showOpenDialog(mainWindow, { properties: ['openFile', 'multiSelections'], filters: options?.filters || [ { name: '所有文件', extensions: ['*'] } ] }) if (!result.canceled) { return result.filePaths } return [] }) ipcMain.handle('dialog:selectIcon', async () => { const result = await dialog.showOpenDialog(mainWindow, { properties: ['openFile'], filters: [ { name: '图标文件', extensions: ['ico', 'png', 'icns'] } ] }) if (!result.canceled && result.filePaths.length > 0) { return result.filePaths[0] } return null }) ipcMain.handle('dialog:saveFile', async (_event, options) => { const result = await dialog.showSaveDialog(mainWindow, { defaultPath: options?.defaultPath, filters: options?.filters || [{ name: '安装包', extensions: ['exe', 'dmg', 'AppImage'] }] }) if (!result.canceled && result.filePath) { return result.filePath } return null }) ipcMain.handle('dir:readTree', async (_event, dirPath) => { return readDirectoryTree(dirPath) }) ipcMain.handle('project:create', async (_event, name, dir) => { return createProject(name, dir) }) ipcMain.handle('project:load', async (_event, projectPath) => { return loadProject(projectPath) }) ipcMain.handle('project:save', async (_event, projectPath, data) => { return saveProject(projectPath, data) }) ipcMain.handle('project:importFiles', async (_event, projectPath, files) => { return importFiles(projectPath, files) }) ipcMain.handle('project:removeFile', async (_event, projectPath, relativePath) => { return removeFile(projectPath, relativePath) }) ipcMain.handle('project:setIcon', async (_event, projectPath, iconPath) => { return setProjectIcon(projectPath, iconPath) }) ipcMain.handle('pack:start', async (_event, projectPath, outputPath) => { const result = await startPack(projectPath, outputPath, (progress) => { if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.webContents.send('pack:progress', progress) } }) return result }) ipcMain.handle('pack:cancel', () => { cancelPack() return true }) function readDirectoryTree(dirPath) { const items = fs.readdirSync(dirPath, { withFileTypes: true }) const result = [] for (const item of items) { if (item.name.startsWith('.')) continue const fullPath = path.join(dirPath, item.name) if (item.isDirectory()) { result.push({ name: item.name, path: fullPath, type: 'directory', children: readDirectoryTree(fullPath) }) } else { result.push({ name: item.name, path: fullPath, type: 'file', size: fs.statSync(fullPath).size, ext: path.extname(item.name).toLowerCase() }) } } return result.sort((a, b) => { if (a.type !== b.type) return a.type === 'directory' ? -1 : 1 return a.name.localeCompare(b.name) }) } app.whenReady().then(() => { createWindow() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) }) app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() })