diff --git a/.github/workflows/build-electron.yml b/.github/workflows/build-electron.yml new file mode 100644 index 0000000..98c2213 --- /dev/null +++ b/.github/workflows/build-electron.yml @@ -0,0 +1,77 @@ +name: Build Electron App + +on: + push: + branches: + - main + tags: + - 'v*' + workflow_dispatch: + +jobs: + build: + runs-on: windows-latest + + strategy: + matrix: + node-version: [20.x] + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + version: 10 + + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - name: Setup pnpm cache + uses: actions/cache@v3 + with: + path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build Vite project + run: pnpm run build + + - name: Compile Electron files + run: | + npx tsc electron/main.ts --outDir electron --module esnext --target es2020 --lib es2020 --moduleResolution node --esModuleInterop --skipLibCheck --strict false + npx tsc electron/preload.ts --outDir electron --module esnext --target es2020 --lib es2020 --moduleResolution node --esModuleInterop --skipLibCheck --strict false + + - name: Build Electron app + run: pnpm run electron-build + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: electron-builds + path: dist/electron/ + retention-days: 30 + + - name: Create Release and Upload Assets + if: startsWith(github.ref, 'refs/tags/v') + uses: softprops/action-gh-release@v1 + with: + files: | + dist/electron/*.exe + dist/electron/*.exe.blockmap + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/ELECTRON_BUILD.md b/ELECTRON_BUILD.md new file mode 100644 index 0000000..70aa53c --- /dev/null +++ b/ELECTRON_BUILD.md @@ -0,0 +1,272 @@ +# Electron 应用构建指南 + +本文档说明如何构建和发布高级时钟 Electron 应用为 Windows EXE 文件。 + +## 快速开始(Windows) + +### 使用自动化脚本 + +我们提供了两个自动化脚本来简化构建过程: + +#### 方式 1:使用批处理脚本(推荐) + +1. 在项目根目录找到 `build-electron.bat` +2. 双击运行脚本 +3. 脚本会自动完成所有构建步骤 +4. EXE 文件会生成在 `dist/electron/` 目录中 + +#### 方式 2:使用 PowerShell 脚本 + +1. 打开 PowerShell +2. 进入项目目录 +3. 运行命令: + ```powershell + Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + .\build-electron.ps1 + ``` +4. EXE 文件会生成在 `dist/electron/` 目录中 + +### 手动构建步骤 + +如果自动化脚本无法使用,可以手动执行以下步骤: + +```bash +# 1. 安装依赖 +pnpm install + +# 2. 构建 Vite 项目 +pnpm run build + +# 3. 编译 Electron 主进程 +npx tsc electron/main.ts --outDir electron --module esnext --target es2020 --lib es2020 --moduleResolution node --esModuleInterop --skipLibCheck --strict false + +# 4. 编译 Electron 预加载脚本 +npx tsc electron/preload.ts --outDir electron --module esnext --target es2020 --lib es2020 --moduleResolution node --esModuleInterop --skipLibCheck --strict false + +# 5. 构建 Electron 应用 +pnpm run electron-build +``` + +## 前置要求 + +- **Windows 系统**(Windows 10 或更高版本) +- **Node.js 20+**:从 https://nodejs.org/ 下载安装 +- **pnpm 10+**:通过 `npm install -g pnpm` 安装 + +## 输出文件 + +构建完成后,以下文件会生成在 `dist/electron/` 目录中: + +| 文件名 | 说明 | +|--------|------| +| `Advanced Clock.exe` | NSIS 安装程序(推荐用于发布) | +| `Advanced Clock Setup.exe` | 便携式可执行文件(无需安装) | +| `Advanced Clock Setup.exe.blockmap` | 增量更新文件 | + +## 发布到 GitHub Releases + +### 手动上传 + +1. 在 GitHub 仓库页面点击 **Releases** +2. 点击 **Create a new release** +3. 填写版本标签(如 `v1.0.0`)和发布说明 +4. 在 **Attach binaries** 部分上传 `Advanced Clock.exe` 文件 +5. 点击 **Publish release** + +### 使用 GitHub CLI + +```bash +# 创建发布并上传文件 +gh release create v1.0.0 dist/electron/Advanced\ Clock.exe --title "Advanced Clock v1.0.0" --notes "Release notes here" +``` + +## 开发模式 + +在开发过程中测试 Electron 应用: + +### 方式 1:使用两个终端 + +```bash +# 终端 1:启动 Vite 开发服务器 +pnpm run dev + +# 终端 2:启动 Electron 应用 +pnpm run electron-dev +``` + +### 方式 2:直接运行 + +```bash +# 构建后直接运行 Electron 应用 +pnpm run build +npx electron . +``` + +## 项目结构 + +``` +advanced-clock-site/ +├── client/ # React 应用源代码 +│ ├── src/ +│ │ ├── pages/ # 页面组件 +│ │ ├── components/ # UI 组件 +│ │ ├── contexts/ # React Context +│ │ ├── utils/ # 工具函数 +│ │ └── App.tsx # 主应用组件 +│ └── index.html # HTML 入口 +├── electron/ # Electron 相关文件 +│ ├── main.ts # Electron 主进程 +│ ├── main.js # 编译后的主进程 +│ ├── preload.ts # 预加载脚本 +│ └── preload.js # 编译后的预加载脚本 +├── dist/ # 构建输出 +│ ├── index.html # 打包的 Web 应用 +│ ├── assets/ # 静态资源 +│ └── electron/ # Electron 构建输出(EXE 文件) +├── build-electron.bat # Windows 批处理构建脚本 +├── build-electron.ps1 # PowerShell 构建脚本 +├── electron-builder.yml # Electron Builder 配置 +├── package.json # 项目配置和脚本 +├── vite.config.ts # Vite 配置 +└── tsconfig.json # TypeScript 配置 +``` + +## 配置说明 + +### electron-builder.yml + +定义了 Windows 安装程序的配置: + +```yaml +appId: com.advancedclock.app # 应用唯一标识符 +productName: Advanced Clock # 应用显示名称 +files: # 包含在打包中的文件 + - dist/**/* # 构建的 Web 应用 + - electron/**/* # Electron 文件 + - package.json +win: + target: # 构建目标 + - nsis # NSIS 安装程序 + - portable # 便携式可执行文件 +nsis: + oneClick: false # 允许自定义安装目录 + allowToChangeInstallationDirectory: true + createDesktopShortcut: true # 创建桌面快捷方式 + createStartMenuShortcut: true # 创建开始菜单快捷方式 +``` + +### package.json 中的脚本 + +```json +{ + "scripts": { + "electron-dev": "vite build && electron .", + "electron-build": "vite build && electron-builder" + }, + "build": { + "appId": "com.advancedclock.app", + "productName": "Advanced Clock", + "files": ["dist/**/*", "electron/**/*", "package.json"], + "win": { + "target": ["nsis", "portable"], + "arch": ["x64"] + }, + "nsis": { + "oneClick": false, + "allowToChangeInstallationDirectory": true, + "createDesktopShortcut": true, + "createStartMenuShortcut": true + } + } +} +``` + +## 常见问题 + +### Q: 构建时出现 "electron not found" 错误 + +A: 确保已安装依赖: +```bash +pnpm install +``` + +### Q: 如何自定义应用图标? + +A: 在项目根目录创建 `assets/` 文件夹,放入以下文件: +- `icon.png` - 应用图标(512x512 或更大) +- `icon.ico` - Windows 图标 + +然后重新构建应用。Electron Builder 会自动使用这些图标。 + +### Q: 如何签名 EXE 文件? + +A: 在 `electron-builder.yml` 中配置: +```yaml +win: + certificateFile: path/to/certificate.pfx + certificatePassword: your-password + signingHashAlgorithms: + - sha256 +``` + +### Q: 安装程序太大怎么办? + +A: 可以在 `electron-builder.yml` 中启用压缩: +```yaml +win: + certificateFile: null + signingHashAlgorithms: + - sha256 +``` + +### Q: 如何创建便携式版本(无需安装)? + +A: 便携式版本已经包含在构建输出中。使用 `Advanced Clock Setup.exe` 文件。 + +### Q: 如何自动检查更新? + +A: 可以集成 `electron-updater`: +```bash +pnpm add electron-updater +``` + +然后在 `electron/main.ts` 中配置更新逻辑。 + +## 故障排除 + +### 构建失败 + +1. 清除缓存: + ```bash + rm -r node_modules dist + pnpm install + ``` + +2. 检查 Node.js 版本: + ```bash + node --version # 应该是 v20 或更高 + ``` + +3. 检查磁盘空间:确保有至少 1GB 的可用空间 + +### 应用无法启动 + +1. 检查 `dist/index.html` 是否存在 +2. 查看 Electron 开发者工具(按 F12)中的错误信息 +3. 检查 `electron/main.js` 是否正确编译 + +### 安装程序无法运行 + +1. 确保 Windows 系统已更新 +2. 尝试以管理员身份运行安装程序 +3. 检查是否被防病毒软件阻止 + +## 更多信息 + +- [Electron 官方文档](https://www.electronjs.org/docs) +- [Electron Builder 文档](https://www.electron.build/) +- [NSIS 文档](https://nsis.sourceforge.io/Docs/) + +## 支持 + +如有问题,请在 GitHub Issues 中提报。 diff --git a/RELEASE_GUIDE.md b/RELEASE_GUIDE.md new file mode 100644 index 0000000..4ddfd37 --- /dev/null +++ b/RELEASE_GUIDE.md @@ -0,0 +1,213 @@ +# GitHub Release 发布指南 + +本指南说明如何将构建好的 EXE 文件发布到 GitHub Releases。 + +## 前置准备 + +1. **构建 EXE 文件**:按照 `ELECTRON_BUILD.md` 中的说明构建应用 +2. **GitHub 仓库访问权限**:确保您有仓库的推送权限 +3. **GitHub CLI**(可选):用于命令行发布 + +## 方式 1:通过 GitHub 网页界面(推荐) + +### 步骤 1:创建 Release + +1. 访问 GitHub 仓库:https://github.com/Yejack819/Advanced-clock-web +2. 点击右侧的 **Releases** 链接 +3. 点击 **Create a new release** 按钮 + +### 步骤 2:填写发布信息 + +| 字段 | 说明 | 示例 | +|------|------|------| +| **Tag version** | 版本标签 | `v1.0.0` | +| **Release title** | 发布标题 | `Advanced Clock v1.0.0` | +| **Describe this release** | 发布说明 | 功能说明、改进、已知问题等 | + +### 步骤 3:上传 EXE 文件 + +1. 在 **Attach binaries** 部分,点击 **Choose files** +2. 选择构建输出目录 `dist/electron/` 中的文件: + - `Advanced Clock.exe` - NSIS 安装程序(推荐) + - `Advanced Clock Setup.exe` - 便携式版本(可选) + +### 步骤 4:发布 + +1. 确认信息无误 +2. 点击 **Publish release** 按钮 +3. Release 创建完成,用户可以下载 EXE 文件 + +## 方式 2:使用 GitHub CLI + +### 安装 GitHub CLI + +```bash +# Windows (Chocolatey) +choco install gh + +# Windows (Scoop) +scoop install gh + +# macOS +brew install gh + +# Linux +# 详见 https://github.com/cli/cli#installation +``` + +### 登录 GitHub + +```bash +gh auth login +``` + +### 创建 Release 并上传文件 + +```bash +# 创建 Release 并上传单个文件 +gh release create v1.0.0 dist/electron/Advanced\ Clock.exe \ + --title "Advanced Clock v1.0.0" \ + --notes "Release notes here" + +# 创建 Release 并上传多个文件 +gh release create v1.0.0 \ + dist/electron/Advanced\ Clock.exe \ + dist/electron/Advanced\ Clock\ Setup.exe \ + --title "Advanced Clock v1.0.0" \ + --notes "Release notes here" + +# 创建草稿 Release(不立即发布) +gh release create v1.0.0 dist/electron/Advanced\ Clock.exe \ + --title "Advanced Clock v1.0.0" \ + --draft +``` + +### 查看已发布的 Release + +```bash +# 列出所有 Release +gh release list + +# 查看特定 Release 的详情 +gh release view v1.0.0 +``` + +## 方式 3:使用 Git 标签自动发布 + +如果配置了 GitHub Actions 工作流(需要 workflow 权限),可以通过 Git 标签自动发布: + +```bash +# 创建标签 +git tag v1.0.0 + +# 推送标签到 GitHub +git push origin v1.0.0 + +# GitHub Actions 会自动构建并创建 Release +``` + +## 发布说明模板 + +以下是一个发布说明的模板: + +```markdown +# Advanced Clock v1.0.0 + +## 新功能 +- ✨ 添加 Electron 桌面应用支持 +- 🎨 改进 UI 设计 +- 🚀 性能优化 + +## 改进 +- 📱 响应式设计改进 +- 🔧 代码重构和优化 +- 📚 文档更新 + +## 修复 +- 🐛 修复倒计时暂停/恢复问题 +- 🐛 修复日期显示格式问题 + +## 已知问题 +- 某些旧版 Windows 系统可能需要更新 .NET Framework + +## 下载 + +- **Advanced Clock.exe** - NSIS 安装程序(推荐) +- **Advanced Clock Setup.exe** - 便携式版本 + +## 安装说明 + +### 使用 NSIS 安装程序 +1. 下载 `Advanced Clock.exe` +2. 双击运行安装程序 +3. 按照提示完成安装 + +### 使用便携式版本 +1. 下载 `Advanced Clock Setup.exe` +2. 直接运行,无需安装 + +## 系统要求 +- Windows 10 或更高版本 +- 至少 100MB 可用磁盘空间 + +## 反馈 + +如有问题或建议,请在 [GitHub Issues](https://github.com/Yejack819/Advanced-clock-web/issues) 中提报。 +``` + +## 版本号规范 + +建议使用语义化版本号(Semantic Versioning): + +``` +v主版本号.次版本号.修订号 + +示例: +v1.0.0 - 初始版本 +v1.1.0 - 添加新功能 +v1.1.1 - 修复 bug +v2.0.0 - 重大更新 +``` + +## 发布清单 + +在发布前,请检查以下项目: + +- [ ] 代码已提交到 GitHub +- [ ] 版本号已更新(package.json) +- [ ] CHANGELOG 已更新 +- [ ] EXE 文件已成功构建 +- [ ] EXE 文件已测试可正常运行 +- [ ] Release 信息已准备好 +- [ ] 发布说明已编写 + +## 常见问题 + +### Q: 如何更新已发布的 Release? + +A: GitHub 不允许编辑已发布 Release 的文件。如需更新,请: +1. 删除旧 Release +2. 创建新 Release 并上传更新的文件 + +或者创建新版本号的 Release。 + +### Q: 如何删除 Release? + +A: 在 Release 页面点击 **Delete** 按钮。注意:删除 Release 不会删除 Git 标签。 + +### Q: 用户下载后如何安装? + +A: 用户可以: +1. 下载 EXE 文件 +2. 双击运行 +3. 按照安装程序提示完成安装 + +### Q: 如何提供更新检查功能? + +A: 可以集成 `electron-updater`,自动检查和下载更新。详见 `ELECTRON_BUILD.md`。 + +## 更多信息 + +- [GitHub Releases 文档](https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases) +- [GitHub CLI 文档](https://cli.github.com/manual/) +- [语义化版本](https://semver.org/lang/zh-CN/) diff --git a/build-electron.bat b/build-electron.bat new file mode 100644 index 0000000..f0b2426 --- /dev/null +++ b/build-electron.bat @@ -0,0 +1,84 @@ +@echo off +REM Advanced Clock - Electron Build Script for Windows +REM This script builds the Electron application and creates Windows installers + +echo. +echo ======================================== +echo Advanced Clock - Electron Build Script +echo ======================================== +echo. + +REM Check if Node.js is installed +where node >nul 2>nul +if %ERRORLEVEL% NEQ 0 ( + echo Error: Node.js is not installed or not in PATH + echo Please install Node.js from https://nodejs.org/ + pause + exit /b 1 +) + +REM Check if pnpm is installed +where pnpm >nul 2>nul +if %ERRORLEVEL% NEQ 0 ( + echo Error: pnpm is not installed + echo Installing pnpm... + npm install -g pnpm +) + +echo. +echo Step 1: Installing dependencies... +call pnpm install +if %ERRORLEVEL% NEQ 0 ( + echo Error: Failed to install dependencies + pause + exit /b 1 +) + +echo. +echo Step 2: Building Vite project... +call pnpm run build +if %ERRORLEVEL% NEQ 0 ( + echo Error: Failed to build Vite project + pause + exit /b 1 +) + +echo. +echo Step 3: Compiling Electron main process... +call npx tsc electron/main.ts --outDir electron --module esnext --target es2020 --lib es2020 --moduleResolution node --esModuleInterop --skipLibCheck --strict false +if %ERRORLEVEL% NEQ 0 ( + echo Error: Failed to compile main.ts + pause + exit /b 1 +) + +echo. +echo Step 4: Compiling Electron preload script... +call npx tsc electron/preload.ts --outDir electron --module esnext --target es2020 --lib es2020 --moduleResolution node --esModuleInterop --skipLibCheck --strict false +if %ERRORLEVEL% NEQ 0 ( + echo Error: Failed to compile preload.ts + pause + exit /b 1 +) + +echo. +echo Step 5: Building Electron application... +call pnpm run electron-build +if %ERRORLEVEL% NEQ 0 ( + echo Error: Failed to build Electron application + pause + exit /b 1 +) + +echo. +echo ======================================== +echo Build completed successfully! +echo ======================================== +echo. +echo Output files are located in: dist\electron\ +echo. +echo Files created: +echo - Advanced Clock.exe (NSIS Installer) +echo - Advanced Clock Setup.exe (Portable Executable) +echo. +pause diff --git a/build-electron.ps1 b/build-electron.ps1 new file mode 100644 index 0000000..34be358 --- /dev/null +++ b/build-electron.ps1 @@ -0,0 +1,86 @@ +# Advanced Clock - Electron Build Script for Windows (PowerShell) +# This script builds the Electron application and creates Windows installers + +Write-Host "" +Write-Host "========================================" +Write-Host "Advanced Clock - Electron Build Script" +Write-Host "========================================" +Write-Host "" + +# Check if Node.js is installed +try { + $nodeVersion = node --version + Write-Host "✓ Node.js found: $nodeVersion" +} catch { + Write-Host "✗ Error: Node.js is not installed or not in PATH" + Write-Host "Please install Node.js from https://nodejs.org/" + Read-Host "Press Enter to exit" + exit 1 +} + +# Check if pnpm is installed +try { + $pnpmVersion = pnpm --version + Write-Host "✓ pnpm found: $pnpmVersion" +} catch { + Write-Host "! pnpm not found, installing..." + npm install -g pnpm +} + +Write-Host "" +Write-Host "Step 1: Installing dependencies..." +pnpm install +if ($LASTEXITCODE -ne 0) { + Write-Host "✗ Error: Failed to install dependencies" + Read-Host "Press Enter to exit" + exit 1 +} + +Write-Host "" +Write-Host "Step 2: Building Vite project..." +pnpm run build +if ($LASTEXITCODE -ne 0) { + Write-Host "✗ Error: Failed to build Vite project" + Read-Host "Press Enter to exit" + exit 1 +} + +Write-Host "" +Write-Host "Step 3: Compiling Electron main process..." +npx tsc electron/main.ts --outDir electron --module esnext --target es2020 --lib es2020 --moduleResolution node --esModuleInterop --skipLibCheck --strict false +if ($LASTEXITCODE -ne 0) { + Write-Host "✗ Error: Failed to compile main.ts" + Read-Host "Press Enter to exit" + exit 1 +} + +Write-Host "" +Write-Host "Step 4: Compiling Electron preload script..." +npx tsc electron/preload.ts --outDir electron --module esnext --target es2020 --lib es2020 --moduleResolution node --esModuleInterop --skipLibCheck --strict false +if ($LASTEXITCODE -ne 0) { + Write-Host "✗ Error: Failed to compile preload.ts" + Read-Host "Press Enter to exit" + exit 1 +} + +Write-Host "" +Write-Host "Step 5: Building Electron application..." +pnpm run electron-build +if ($LASTEXITCODE -ne 0) { + Write-Host "✗ Error: Failed to build Electron application" + Read-Host "Press Enter to exit" + exit 1 +} + +Write-Host "" +Write-Host "========================================" +Write-Host "✓ Build completed successfully!" +Write-Host "========================================" +Write-Host "" +Write-Host "Output files are located in: dist\electron\" +Write-Host "" +Write-Host "Files created:" +Write-Host "- Advanced Clock.exe (NSIS Installer)" +Write-Host "- Advanced Clock Setup.exe (Portable Executable)" +Write-Host "" +Read-Host "Press Enter to exit" diff --git a/electron-builder.yml b/electron-builder.yml new file mode 100644 index 0000000..9cf371d --- /dev/null +++ b/electron-builder.yml @@ -0,0 +1,24 @@ +appId: com.advancedclock.app +productName: Advanced Clock +directories: + buildResources: assets + output: dist/electron +files: + - dist/**/* + - electron/**/* + - package.json + - node_modules/**/* +win: + target: + - nsis + - portable + certificateFile: null + certificatePassword: null + signingHashAlgorithms: + - sha256 +nsis: + oneClick: false + allowToChangeInstallationDirectory: true + createDesktopShortcut: true + createStartMenuShortcut: true + shortcutName: Advanced Clock diff --git a/electron/main.js b/electron/main.js new file mode 100644 index 0000000..0951c03 --- /dev/null +++ b/electron/main.js @@ -0,0 +1,83 @@ +import { app, BrowserWindow, Menu } from 'electron'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import isDev from 'electron-is-dev'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +let mainWindow = null; +function createWindow() { + mainWindow = new BrowserWindow({ + width: 1400, + height: 900, + minWidth: 800, + minHeight: 600, + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + nodeIntegration: false, + contextIsolation: true, + }, + icon: path.join(__dirname, '../assets/icon.png'), + }); + const startUrl = isDev + ? 'http://localhost:5173' + : `file://${path.join(__dirname, '../dist/index.html')}`; + mainWindow.loadURL(startUrl); + if (isDev) { + mainWindow.webContents.openDevTools(); + } + mainWindow.on('closed', () => { + mainWindow = null; + }); +} +app.on('ready', createWindow); +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit(); + } +}); +app.on('activate', () => { + if (mainWindow === null) { + createWindow(); + } +}); +// 创建应用菜单 +const menu = Menu.buildFromTemplate([ + { + label: 'File', + submenu: [ + { + label: 'Exit', + accelerator: 'CmdOrCtrl+Q', + click: () => { + app.quit(); + }, + }, + ], + }, + { + label: 'Edit', + submenu: [ + { role: 'undo' }, + { role: 'redo' }, + { type: 'separator' }, + { role: 'cut' }, + { role: 'copy' }, + { role: 'paste' }, + ], + }, + { + label: 'View', + submenu: [ + { role: 'reload' }, + { role: 'forceReload' }, + { role: 'toggleDevTools' }, + { type: 'separator' }, + { role: 'resetZoom' }, + { role: 'zoomIn' }, + { role: 'zoomOut' }, + { type: 'separator' }, + { role: 'togglefullscreen' }, + ], + }, +]); +Menu.setApplicationMenu(menu); diff --git a/electron/main.ts b/electron/main.ts new file mode 100644 index 0000000..75aa43b --- /dev/null +++ b/electron/main.ts @@ -0,0 +1,95 @@ +import { app, BrowserWindow, Menu, ipcMain } from 'electron'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import isDev from 'electron-is-dev'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +let mainWindow: BrowserWindow | null = null; + +function createWindow() { + mainWindow = new BrowserWindow({ + width: 1400, + height: 900, + minWidth: 800, + minHeight: 600, + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + nodeIntegration: false, + contextIsolation: true, + }, + icon: path.join(__dirname, '../assets/icon.png'), + }); + + const startUrl = isDev + ? 'http://localhost:5173' + : `file://${path.join(__dirname, '../dist/index.html')}`; + + mainWindow.loadURL(startUrl); + + if (isDev) { + mainWindow.webContents.openDevTools(); + } + + mainWindow.on('closed', () => { + mainWindow = null; + }); +} + +app.on('ready', createWindow); + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit(); + } +}); + +app.on('activate', () => { + if (mainWindow === null) { + createWindow(); + } +}); + +// 创建应用菜单 +const menu = Menu.buildFromTemplate([ + { + label: 'File', + submenu: [ + { + label: 'Exit', + accelerator: 'CmdOrCtrl+Q', + click: () => { + app.quit(); + }, + }, + ], + }, + { + label: 'Edit', + submenu: [ + { role: 'undo' }, + { role: 'redo' }, + { type: 'separator' }, + { role: 'cut' }, + { role: 'copy' }, + { role: 'paste' }, + ], + }, + { + label: 'View', + submenu: [ + { role: 'reload' }, + { role: 'forceReload' }, + { role: 'toggleDevTools' }, + { type: 'separator' }, + { role: 'resetZoom' }, + { role: 'zoomIn' }, + { role: 'zoomOut' }, + { type: 'separator' }, + { role: 'togglefullscreen' }, + ], + }, +]); + +Menu.setApplicationMenu(menu); diff --git a/electron/preload.js b/electron/preload.js new file mode 100644 index 0000000..704805b --- /dev/null +++ b/electron/preload.js @@ -0,0 +1,17 @@ +import { contextBridge, ipcRenderer } from 'electron'; +contextBridge.exposeInMainWorld('electron', { + ipcRenderer: { + send: (channel, args) => { + ipcRenderer.send(channel, args); + }, + on: (channel, func) => { + ipcRenderer.on(channel, (event, ...args) => func(...args)); + }, + once: (channel, func) => { + ipcRenderer.once(channel, (event, ...args) => func(...args)); + }, + invoke: (channel, args) => { + return ipcRenderer.invoke(channel, args); + }, + }, +}); diff --git a/electron/preload.ts b/electron/preload.ts new file mode 100644 index 0000000..83e2ebc --- /dev/null +++ b/electron/preload.ts @@ -0,0 +1,18 @@ +import { contextBridge, ipcRenderer } from 'electron'; + +contextBridge.exposeInMainWorld('electron', { + ipcRenderer: { + send: (channel: string, args: any) => { + ipcRenderer.send(channel, args); + }, + on: (channel: string, func: (...args: any[]) => void) => { + ipcRenderer.on(channel, (event, ...args) => func(...args)); + }, + once: (channel: string, func: (...args: any[]) => void) => { + ipcRenderer.once(channel, (event, ...args) => func(...args)); + }, + invoke: (channel: string, args: any) => { + return ipcRenderer.invoke(channel, args); + }, + }, +});