- electron/builder.js: 修复 Linux 平台 npm 命令、增加 .deb 安装包支持 - electron/main.js: 按平台动态设置保存对话框默认扩展名 - package.json: 添加 Linux deb 打包目标 - 新增 Dockerfile 用于 Linux 交叉构建 - 新增 build-linux-docker.ps1 / build-linux.sh 构建脚本 - 新增 site/ 项目官网页面 - .gitignore: 全面忽略二进制文件 - 新增 GitHub Actions CI 工作流
169 lines
4.6 KiB
JavaScript
169 lines
4.6 KiB
JavaScript
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 defaultFilters = process.platform === 'win32'
|
|
? [{ name: '安装包', extensions: ['exe'] }]
|
|
: process.platform === 'darwin'
|
|
? [{ name: '安装包', extensions: ['dmg'] }]
|
|
: [{ name: '安装包', extensions: ['AppImage', 'deb'] }]
|
|
const result = await dialog.showSaveDialog(mainWindow, {
|
|
defaultPath: options?.defaultPath,
|
|
filters: options?.filters || defaultFilters
|
|
})
|
|
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()
|
|
})
|