From 68312c613714ed9735a69ce42ba6d3f15d4291a2 Mon Sep 17 00:00:00 2001 From: mingyuansi Date: Wed, 10 Jun 2026 14:16:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81win=E6=89=93=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .npmrc | 3 + docs/MIRROR_QUICK_REFERENCE.md | 84 ++++ docs/QUICK_REFERENCE.md | 104 ++++ docs/electron-mirror-setup.md | 284 +++++++++++ docs/electron-packaging-guide.md | 269 ++++++++++ docs/electron-port-finder.md | 107 ++++ docs/electron-window-controls.md | 237 +++++++++ docs/mirror-setup-summary.md | 141 ++++++ electron/.npmrc | 11 + electron/main.js | 90 +++- electron/package-lock.json | 830 ++++++++++++++++++++++++++++++- electron/package.json | 12 +- electron/server.js | 65 +++ scripts/pack-mac.sh | 28 ++ scripts/pack-win.bat | 30 ++ scripts/prepare-electron.cjs | 47 ++ scripts/verify-mirrors.cjs | 35 ++ 17 files changed, 2360 insertions(+), 17 deletions(-) create mode 100644 .npmrc create mode 100644 docs/MIRROR_QUICK_REFERENCE.md create mode 100644 docs/QUICK_REFERENCE.md create mode 100644 docs/electron-mirror-setup.md create mode 100644 docs/electron-packaging-guide.md create mode 100644 docs/electron-port-finder.md create mode 100644 docs/electron-window-controls.md create mode 100644 docs/mirror-setup-summary.md create mode 100644 electron/.npmrc create mode 100644 electron/server.js create mode 100644 scripts/pack-mac.sh create mode 100644 scripts/pack-win.bat create mode 100644 scripts/prepare-electron.cjs create mode 100644 scripts/verify-mirrors.cjs diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..bfaf6ea --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +registry=https://registry.npmmirror.com +electron_mirror=https://npmmirror.com/mirrors/electron/ +electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ diff --git a/docs/MIRROR_QUICK_REFERENCE.md b/docs/MIRROR_QUICK_REFERENCE.md new file mode 100644 index 0000000..806900a --- /dev/null +++ b/docs/MIRROR_QUICK_REFERENCE.md @@ -0,0 +1,84 @@ +# Electron 镜像源快速参考 + +## 🚀 快速解决 + +### 已配置完成 ✅ + +项目已经配置了镜像源,直接打包即可: + +```bash +npm run pack:win # Windows +npm run pack:mac # Mac +``` + +## 📝 配置位置 + +### 1. 项目根目录 `.npmrc` +```ini +registry=https://registry.npmmirror.com +electron_mirror=https://npmmirror.com/mirrors/electron/ +electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ +``` + +### 2. electron 目录 `.npmrc` +```ini +electron_mirror=https://npmmirror.com/mirrors/electron/ +electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ +registry=https://registry.npmmirror.com +``` + +### 3. electron/package.json +```json +{ + "scripts": { + "pack:win": "cross-env ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ ..." + } +} +``` + +## 🔍 验证配置 + +```bash +npm config get electron_mirror +# 应该输出: https://npmmirror.com/mirrors/electron/ +``` + +## 🛠️ 故障排查 + +### 打包失败? + +```bash +# 1. 清除缓存 +npm cache clean --force + +# 2. 重新安装依赖 +cd electron +rm -rf node_modules +npm install + +# 3. 重新打包 +npm run pack:win +``` + +### 检查环境变量 + +```bash +# Windows +echo %ELECTRON_MIRROR% + +# Mac/Linux +echo $ELECTRON_MIRROR +``` + +## 📦 可用镜像源 + +| 镜像源 | 地址 | +|--------|------| +| 淘宝 | `https://npmmirror.com/mirrors/electron/` | +| 华为云 | `https://mirrors.huaweicloud.com/electron/` | +| 腾讯云 | `https://mirrors.cloud.tencent.com/npm/electron/` | + +## 📚 相关文档 + +- [完整指南](./electron-mirror-setup.md) +- [Electron 官方文档](https://www.electronjs.org/docs) diff --git a/docs/QUICK_REFERENCE.md b/docs/QUICK_REFERENCE.md new file mode 100644 index 0000000..d6c522d --- /dev/null +++ b/docs/QUICK_REFERENCE.md @@ -0,0 +1,104 @@ +# Electron 打包快速参考 + +## 🚀 快速开始 + +```bash +# 1. 安装依赖 +npm install +cd electron && npm install express + +# 2. 开发 +npm run dev + +# 3. 构建 +npm run build + +# 4. 打包 +npm run pack:win # Windows +npm run pack:mac # Mac +``` + +## 📦 核心特性 + +| 特性 | 说明 | +|------|------| +| ✅ 零源码修改 | 项目代码无需改动 | +| ✅ 自动端口查找 | 9527-9999 自动切换 | +| ✅ 本地服务器 | Express 提供静态文件 | +| ✅ 跨平台兼容 | 浏览器和 Electron 都能用 | + +## 🔧 配置文件 + +### electron/server.js +```javascript +const START_PORT = 9527 // 起始端口 +const MAX_PORT = 9999 // 最大端口 +``` + +### electron/main.js +```javascript +const win = new BrowserWindow({ + fullscreen: true, + autoHideMenuBar: true, + webPreferences: { + nodeIntegration: false, + contextIsolation: true + } +}) +``` + +## 📁 目录结构 + +``` +dist/ # 构建输出 +electron/ + ├── main.js # 主进程 + ├── server.js # 本地服务器 + └── package.json # Electron 依赖 +release/ # 打包输出 +``` + +## 🎯 工作流程 + +``` +开发: Vite (localhost:5173) → 浏览器 ✅ +打包: Express (localhost:9527) → Electron ✅ +``` + +## 🐛 常见问题 + +| 问题 | 解决方案 | +|------|---------| +| 白屏 | 检查 dist 是否复制到 electron | +| 端口冲突 | 系统自动处理,无需干预 | +| 资源加载失败 | 使用绝对路径 `/scenes/...` | +| 开发者工具 | 删除 `openDevTools()` 关闭 | + +## 📊 端口查找 + +``` +9527 → 可用 ✅ +9528 → 被占用,跳过 +9529 → 可用 ✅ +... +``` + +## 🔒 安全建议 + +```javascript +webPreferences: { + nodeIntegration: false, // ✅ 禁用 Node + contextIsolation: true // ✅ 隔离上下文 +} +``` + +## 📝 注意事项 + +1. 只监听 `127.0.0.1`,不暴露到外网 +2. 应用退出时自动关闭服务器 +3. 端口范围耗尽会抛出错误 + +## 📚 相关文档 + +- [完整指南](./electron-packaging-guide.md) +- [端口查找](./electron-port-finder.md) diff --git a/docs/electron-mirror-setup.md b/docs/electron-mirror-setup.md new file mode 100644 index 0000000..a3ac09a --- /dev/null +++ b/docs/electron-mirror-setup.md @@ -0,0 +1,284 @@ +# Electron 镜像源配置指南 + +## 问题描述 + +打包 Electron 应用时,如果未开启翻墙,会出现 "fetch failed" 错误,这是因为 Electron 和相关工具需要从国外的 GitHub releases 下载二进制文件。 + +## 解决方案 + +### 方案 1:使用 npm 配置(推荐) + +#### 1.1 全局配置 + +在项目根目录创建 `.npmrc` 文件: + +```ini +registry=https://registry.npmmirror.com +electron_mirror=https://npmmirror.com/mirrors/electron/ +electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ +``` + +#### 1.2 Electron 目录配置 + +在 `electron/` 目录创建 `.npmrc` 文件: + +```ini +electron_mirror=https://npmmirror.com/mirrors/electron/ +electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ +registry=https://registry.npmmirror.com +``` + +### 方案 2:使用环境变量(已配置) + +#### 2.1 Windows + +在 `electron/package.json` 中已经配置: + +```json +{ + "scripts": { + "pack:win": "cross-env ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ node ../scripts/prepare-electron.cjs && npx @electron/packager . MyGame --platform=win32 --arch=x64 --out=../release --overwrite" + } +} +``` + +#### 2.2 Mac/Linux + +```bash +export ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ +export ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ +``` + +### 方案 3:使用批处理脚本 + +#### Windows + +使用 `scripts/pack-win.bat`: + +```batch +@echo off +set ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ +set ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ +call npm run pack:win +``` + +#### Mac/Linux + +使用 `scripts/pack-mac.sh`: + +```bash +#!/bin/bash +export ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ +export ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ +npm run pack:mac +``` + +### 方案 4:使用系统环境变量 + +#### Windows + +```powershell +# 临时设置(当前会话) +set ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ +set ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ + +# 永久设置 +setx ELECTRON_MIRROR https://npmmirror.com/mirrors/electron/ +setx ELECTRON_BUILDER_BINARIES_MIRROR https://npmmirror.com/mirrors/electron-builder-binaries/ +``` + +#### Mac/Linux + +```bash +# 临时设置(当前会话) +export ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ +export ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ + +# 永久设置(添加到 ~/.bashrc 或 ~/.zshrc) +echo 'export ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/' >> ~/.bashrc +echo 'export ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/' >> ~/.bashrc +source ~/.bashrc +``` + +## 验证配置 + +### 检查 npm 配置 + +```bash +npm config get electron_mirror +npm config get electron_builder_binaries_mirror +``` + +应该输出: + +``` +https://npmmirror.com/mirrors/electron/ +https://npmmirror.com/mirrors/electron-builder-binaries/ +``` + +### 检查环境变量 + +#### Windows + +```cmd +echo %ELECTRON_MIRROR% +echo %ELECTRON_BUILDER_BINARIES_MIRROR% +``` + +#### Mac/Linux + +```bash +echo $ELECTRON_MIRROR +echo $ELECTRON_BUILDER_BINARIES_MIRROR +``` + +## 可用的镜像源 + +### 淘宝镜像(推荐) + +```ini +electron_mirror=https://npmmirror.com/mirrors/electron/ +electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ +``` + +### 华为云镜像 + +```ini +electron_mirror=https://mirrors.huaweicloud.com/electron/ +electron_builder_binaries_mirror=https://mirrors.huaweicloud.com/electron-builder-binaries/ +``` + +### 腾讯云镜像 + +```ini +electron_mirror=https://mirrors.cloud.tencent.com/npm/electron/ +``` + +## 常见问题 + +### Q1: 配置后仍然失败 + +**A:** 检查以下几点: + +1. 确认配置已生效 +2. 清除 npm 缓存:`npm cache clean --force` +3. 删除 node_modules 重新安装:`rm -rf node_modules && npm install` +4. 检查网络连接 + +### Q2: 某些包仍然需要翻墙 + +**A:** 可能是其他依赖包的问题,可以: + +1. 检查具体的错误信息 +2. 使用 `.npmrc` 配置其他镜像源 +3. 手动下载相关包 + +### Q3: 下载速度慢 + +**A:** 可以尝试: + +1. 更换其他镜像源 +2. 使用 CDN 加速 +3. 使用代理 + +### Q4: 如何恢复默认配置 + +**A:** 删除或注释掉 `.npmrc` 中的相关配置: + +```bash +npm config delete electron_mirror +npm config delete electron_builder_binaries_mirror +``` + +## 完整的打包流程 + +### 1. 首次配置 + +```bash +# 安装依赖 +npm install +cd electron +npm install + +# 配置镜像源(已在 .npmrc 中配置) +# 无需额外操作 +``` + +### 2. 日常打包 + +```bash +# Windows +npm run pack:win + +# Mac +npm run pack:mac +``` + +### 3. 故障排查 + +如果打包失败: + +```bash +# 1. 检查配置 +npm config get electron_mirror + +# 2. 清除缓存 +npm cache clean --force + +# 3. 重新安装依赖 +cd electron +rm -rf node_modules +npm install + +# 4. 重新打包 +npm run pack:win +``` + +## 其他工具的镜像配置 + +### yarn + +```bash +yarn config set electron_mirror https://npmmirror.com/mirrors/electron/ +yarn config set electron_builder_binaries_mirror https://npmmirror.com/mirrors/electron-builder-binaries/ +``` + +### pnpm + +```bash +pnpm config set electron_mirror https://npmmirror.com/mirrors/electron/ +pnpm config set electron_builder_binaries_mirror https://npmmirror.com/mirrors/electron-builder-binaries/ +``` + +## 性能优化 + +### 1. 使用缓存 + +Electron 下载的二进制文件会被缓存,后续打包会更快: + +```bash +# 查看缓存位置 +npm config get cache + +# Windows: C:\Users\\AppData\Roaming\npm-cache +# Mac: ~/.npm +# Linux: ~/.npm +``` + +### 2. 离线模式 + +如果已经下载过 Electron,可以设置离线模式: + +```bash +export ELECTRON_BUILDER_CACHE=~/.electron +``` + +## 总结 + +通过配置镜像源,你可以在不翻墙的情况下成功打包 Electron 应用。推荐使用: + +1. **`.npmrc` 配置** - 最简单,一次配置永久生效 +2. **环境变量** - 灵活,可以针对不同项目配置 +3. **批处理脚本** - 方便,可以一键打包 + +现在你可以愉快地打包 Electron 应用了!🎉 diff --git a/docs/electron-packaging-guide.md b/docs/electron-packaging-guide.md new file mode 100644 index 0000000..547f92b --- /dev/null +++ b/docs/electron-packaging-guide.md @@ -0,0 +1,269 @@ +# Electron 打包完整指南 + +## 概述 + +本指南介绍如何将 Vue/HTML 项目打包成 Electron 应用,采用**本地服务器方案**,无需修改源码。 + +## 核心优势 + +✅ **零源码修改** - 项目代码完全不需要改动 +✅ **自动端口查找** - 自动处理端口冲突(9527-9999) +✅ **跨平台兼容** - 浏览器和 Electron 都能正常工作 +✅ **易于维护** - 打包逻辑集中在 Electron 目录 +✅ **性能优秀** - 本地服务器响应快速 + +## 项目结构 + +``` +branch-engine/ +├── dist/ # 构建输出目录 +├── electron/ # Electron 相关文件 +│ ├── main.js # 主进程入口 +│ ├── server.js # 本地服务器 +│ └── package.json # Electron 依赖 +├── scripts/ +│ ├── prepare-electron.cjs # 打包前准备 +│ └── pack-html.cjs # HTML 打包 +├── src/ # Vue 源码 +├── editor/ # 编辑器 +└── package.json # 主项目配置 +``` + +## 工作原理 + +### 开发环境 +``` +npm run dev +↓ +Vite 开发服务器 (localhost:5173) +↓ +浏览器访问 → 正常工作 ✅ +``` + +### 打包环境 +``` +npm run pack:win +↓ +1. npm run build (生成 dist) +2. 复制 dist 到 electron +3. 启动 Express 服务器 (localhost:9527-9999) +4. Electron 加载 http://127.0.0.1:PORT ✅ +``` + +## 详细步骤 + +### 1. 安装依赖 + +```bash +# 主项目依赖 +npm install + +# Electron 依赖 +cd electron +npm install express +``` + +### 2. 开发 + +```bash +# 启动开发服务器 +npm run dev +``` + +### 3. 构建 + +```bash +# 构建 Vue 项目 +npm run build +``` + +### 4. 打包 + +```bash +# 打包 Windows 版本 +npm run pack:win + +# 打包 Mac 版本 +npm run pack:mac +``` + +### 5. 运行 + +打包完成后,在 `release/` 目录下找到生成的应用: +- Windows: `release/MyGame-win32-x64/MyGame.exe` +- Mac: `release/MyGame-mas-x64/MyGame.app` + +## 核心文件说明 + +### electron/main.js + +主进程入口,负责: +- 启动本地服务器 +- 创建浏览器窗口 +- 加载应用 +- 处理应用生命周期 + +### electron/server.js + +本地服务器,负责: +- 自动查找可用端口(9527-9999) +- 提供静态文件服务 +- 处理资源请求 + +### scripts/prepare-electron.cjs + +打包前准备脚本,负责: +- 将 dist 目录复制到 electron +- 验证构建文件 + +## 端口自动查找 + +### 功能特性 +- 起始端口:9527 +- 最大端口:9999 +- 自动检测:逐个检测端口可用性 +- 冲突处理:自动跳过被占用的端口 + +### 使用示例 + +#### 正常情况 +``` +✅ Local server running at http://127.0.0.1:9527 +🚀 Loading app from: http://127.0.0.1:9527/index.html +``` + +#### 端口冲突 +``` +🔒 Port 9527 is blocked +✅ Local server running at http://127.0.0.1:9528 +🚀 Loading app from: http://127.0.0.1:9528/index.html +``` + +## 配置选项 + +### 修改端口范围 + +编辑 `electron/server.js`: + +```javascript +const START_PORT = 9527 // 起始端口 +const MAX_PORT = 9999 // 最大端口 +``` + +### 修改窗口配置 + +编辑 `electron/main.js`: + +```javascript +const win = new BrowserWindow({ + fullscreen: true, // 全屏模式 + autoHideMenuBar: true, // 隐藏菜单栏 + webPreferences: { + nodeIntegration: false, // 禁用 node 集成 + contextIsolation: true // 启用上下文隔离 + } +}) +``` + +## 常见问题 + +### Q1: 打包后白屏 +**A:** 检查 dist 目录是否正确复制到 electron 目录。 + +### Q2: 端口被占用 +**A:** 系统会自动查找下一个可用端口,无需手动处理。 + +### Q3: 资源加载失败 +**A:** 确保使用绝对路径(如 `/scenes/config.json`),本地服务器会正确处理。 + +### Q4: 开发者工具如何关闭 +**A:** 删除 `electron/main.js` 中的 `win.webContents.openDevTools()` 行。 + +### Q5: 如何添加应用图标 +**A:** 在打包配置中添加图标文件路径。 + +## 性能优化 + +### 1. 减小包体积 +```bash +# 使用 electron-builder 替代 electron-packager +npm install --save-dev electron-builder +``` + +### 2. 启用代码压缩 +在 `vite.config.ts` 中配置: +```typescript +build: { + minify: 'terser', + terserOptions: { + compress: { + drop_console: true + } + } +} +``` + +### 3. 优化资源加载 +- 使用 CDN 加载第三方库 +- 启用 gzip 压缩 +- 懒加载非关键资源 + +## 安全建议 + +1. **禁用 Node 集成** + ```javascript + webPreferences: { + nodeIntegration: false, + contextIsolation: true + } + ``` + +2. **只监听本地地址** + ```javascript + server.listen(PORT, '127.0.0.1') + ``` + +3. **验证用户输入** + - 检查 URL 参数 + - 验证文件路径 + - 防止 XSS 攻击 + +## 调试技巧 + +### 查看服务器日志 +```javascript +console.log('Server started on port:', PORT) +``` + +### 查看网络请求 +打开开发者工具 → Network 标签,查看所有请求。 + +### 查看控制台错误 +打开开发者工具 → Console 标签,查看错误信息。 + +## 部署建议 + +### 1. 代码签名 +```bash +# Windows +electron-builder --win --x64 --publish never + +# Mac +electron-builder --mac --x64 --publish never +``` + +### 2. 自动更新 +使用 `electron-updater` 实现自动更新功能。 + +### 3. 安装包配置 +在 `electron-builder.yml` 中配置安装选项。 + +## 总结 + +本方案采用**本地服务器 + 自动端口查找**的方式,完美解决了 Electron 打包中的路径问题,同时保持了源码的纯净性。这是一个简单、优雅、可维护的解决方案。 + +## 相关文档 + +- [Electron 官方文档](https://www.electronjs.org/docs) +- [Express 文档](https://expressjs.com/) +- [Vite 文档](https://vitejs.dev/) diff --git a/docs/electron-port-finder.md b/docs/electron-port-finder.md new file mode 100644 index 0000000..e459407 --- /dev/null +++ b/docs/electron-port-finder.md @@ -0,0 +1,107 @@ +# Electron 端口自动查找功能 + +## 功能说明 + +Electron 应用启动时会自动在 9527-9999 端口范围内寻找可用端口,避免端口冲突问题。 + +## 工作原理 + +### 1. 端口检测 +使用 Node.js 内置的 `net` 模块检测端口是否可用: + +```javascript +function isPortAvailable(port) { + return new Promise((resolve) => { + const server = net.createServer() + server.listen(port, '127.0.0.1', () => { + server.once('close', () => resolve(true)) + server.close() + }) + server.on('error', () => resolve(false)) + }) +} +``` + +### 2. 端口查找 +从 9527 开始向上查找,直到找到可用端口: + +```javascript +async function findAvailablePort(startPort, maxPort) { + for (let port = startPort; port <= maxPort; port++) { + const available = await isPortAvailable(port) + if (available) { + return port + } + } + throw new Error(`No available port found between ${startPort} and ${maxPort}`) +} +``` + +### 3. 服务器启动 +找到可用端口后启动 Express 服务器: + +```javascript +const serverInfo = await startServer() +const { server, PORT } = serverInfo +``` + +## 配置参数 + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| `START_PORT` | 9527 | 起始端口 | +| `MAX_PORT` | 9999 | 最大端口 | + +## 使用示例 + +### 正常情况 +``` +✅ Local server running at http://127.0.0.1:9527 +🚀 Loading app from: http://127.0.0.1:9527/index.html +``` + +### 端口冲突情况 +``` +🔒 Port 9527 is blocked +✅ Local server running at http://127.0.0.1:9528 +🚀 Loading app from: http://127.0.0.1:9528/index.html +``` + +## 优势 + +1. **自动处理冲突** - 无需手动指定端口 +2. **范围可控** - 限制在 9527-9999 范围内 +3. **快速响应** - 端口检测速度快 +4. **零依赖** - 使用 Node.js 内置模块 + +## 注意事项 + +1. **端口范围** - 如果 9527-9999 都被占用,会抛出错误 +2. **监听地址** - 只监听 `127.0.0.1`,不暴露到外网 +3. **服务器关闭** - 应用退出时会自动关闭服务器 + +## 测试 + +### 测试端口查找 +```bash +node scripts/test-port-finder.cjs +``` + +### 测试端口冲突 +```bash +node scripts/test-port-conflict.cjs +``` + +## 故障排查 + +### 端口范围耗尽 +如果出现 "No available port found" 错误: +1. 检查是否有其他应用占用了大量端口 +2. 增加 `MAX_PORT` 的值 +3. 关闭不必要的应用释放端口 + +### 服务器启动失败 +如果服务器启动失败: +1. 检查 dist 目录是否存在 +2. 检查文件权限 +3. 查看控制台错误信息 diff --git a/docs/electron-window-controls.md b/docs/electron-window-controls.md new file mode 100644 index 0000000..67b6c92 --- /dev/null +++ b/docs/electron-window-controls.md @@ -0,0 +1,237 @@ +# Electron 窗口控制指南 + +## 窗口功能 + +### 基本控制 + +| 功能 | 操作 | +|------|------| +| 调整大小 | 拖动窗口边缘 | +| 最小化 | 点击窗口右上角的 `-` 按钮 | +| 最大化 | 点击窗口右上角的 `□` 按钮 | +| 还原 | 再次点击最大化按钮 | +| 关闭 | 点击窗口右上角的 `×` 按钮 | + +### 快捷键 + +| 快捷键 | 功能 | +|--------|------| +| `F11` | 切换全屏模式 | +| `Alt + F4` | 退出应用 | +| `Alt + F4` (Mac) | `Cmd + Q` | + +## 窗口配置 + +### 当前设置 + +```javascript +{ + width: 1280, // 初始宽度 + height: 720, // 初始高度 + minWidth: 800, // 最小宽度 + minHeight: 600, // 最小高度 + resizable: true, // 允许调整大小 + maximizable: true, // 允许最大化 + minimizable: true, // 允许最小化 + closable: true, // 允许关闭 + autoHideMenuBar: false // 显示菜单栏 +} +``` + +### 自定义窗口大小 + +如果你想修改默认窗口大小,编辑 `electron/main.js`: + +```javascript +const win = new BrowserWindow({ + width: 1920, // 修改为你想要的宽度 + height: 1080, // 修改为你想要的高度 + minWidth: 1024, // 修改为你想要的最小宽度 + minHeight: 768, // 修改为你想要的最小高度 + // ... 其他配置 +}) +``` + +### 启动时最大化 + +如果你希望应用启动时自动最大化,修改 `electron/main.js`: + +```javascript +const win = new BrowserWindow({ + // ... 其他配置 +}) + +// 在创建窗口后添加 +win.maximize() +``` + +### 启动时全屏 + +如果你希望应用启动时全屏,修改 `electron/main.js`: + +```javascript +const win = new BrowserWindow({ + // ... 其他配置 + fullscreen: true // 启用全屏 +}) +``` + +## 菜单栏 + +应用会显示 Electron 默认菜单栏,包含以下菜单: + +### File 菜单 +- **New Window** - 打开新窗口 +- **Close** - 关闭当前窗口 +- **Quit** - 退出应用 + +### Edit 菜单 +- **Undo** - 撤销 +- **Redo** - 重做 +- **Cut** - 剪切 +- **Copy** - 复制 +- **Paste** - 粘贴 +- **Select All** - 全选 + +### View 菜单 +- **Reload** - 重新加载页面 +- **Force Reload** - 强制重新加载 +- **Toggle Developer Tools** - 切换开发者工具 +- **Toggle Full Screen** - 切换全屏 +- **Zoom In** - 放大 +- **Zoom Out** - 缩小 +- **Reset Zoom** - 重置缩放 + +### Window 菜单 +- **Minimize** - 最小化 +- **Zoom** - 最大化/还原 +- **Front** - 置顶 + +## 隐藏菜单栏 + +如果你想隐藏菜单栏,修改 `electron/main.js`: + +```javascript +const win = new BrowserWindow({ + // ... 其他配置 + autoHideMenuBar: true // 隐藏菜单栏 +}) +``` + +## 自定义菜单栏 + +如果你想创建自定义菜单,在 `electron/main.js` 中添加: + +```javascript +const { Menu } = require('electron') + +const template = [ + { + label: '游戏', + submenu: [ + { label: '新游戏', click: () => { /* 新游戏逻辑 */ } }, + { label: '继续游戏', click: () => { /* 继续游戏逻辑 */ } }, + { type: 'separator' }, + { label: '退出', click: () => app.quit() } + ] + }, + { + label: '设置', + submenu: [ + { label: '全屏', click: () => { win.setFullScreen(!win.isFullScreen()) } }, + { label: '开发者工具', click: () => { win.webContents.toggleDevTools() } } + ] + } +] + +const menu = Menu.buildFromTemplate(template) +Menu.setApplicationMenu(menu) +``` + +## 窗口状态保存 + +如果你想记住窗口的大小和位置,可以使用以下代码: + +```javascript +const Store = require('electron-store') +const store = new Store() + +app.whenReady().then(async () => { + // 获取保存的窗口状态 + const windowState = store.get('windowState', { + width: 1280, + height: 720, + x: undefined, + y: undefined + }) + + const win = new BrowserWindow({ + width: windowState.width, + height: windowState.height, + x: windowState.x, + y: windowState.y, + // ... 其他配置 + }) + + // 保存窗口状态 + win.on('close', () => { + const bounds = win.getBounds() + store.set('windowState', { + width: bounds.width, + height: bounds.height, + x: bounds.x, + y: bounds.y + }) + }) +}) +``` + +## 边框窗口 + +如果你想要无边框窗口,修改 `electron/main.js`: + +```javascript +const win = new BrowserWindow({ + frame: false, // 无边框 + transparent: true, // 透明背景 + // ... 其他配置 +}) +``` + +注意:无边框窗口需要你自己实现窗口控制按钮。 + +## 常见问题 + +### Q: 如何在应用内切换全屏? +A: 按 `F11` 键或通过菜单栏 `View` → `Toggle Full Screen` + +### Q: 如何隐藏开发者工具? +A: 删除 `electron/main.js` 中的 `win.webContents.openDevTools()` 行 + +### Q: 如何设置应用图标? +A: 在 `electron/main.js` 中设置 `icon` 选项 + +### Q: 如何防止用户调整窗口大小? +A: 设置 `resizable: false` + +### Q: 如何设置窗口最小尺寸? +A: 设置 `minWidth` 和 `minHeight` 选项 + +## 性能建议 + +1. **合理的初始大小** - 设置合适的默认窗口大小 +2. **最小尺寸限制** - 防止窗口过小导致内容无法显示 +3. **响应式设计** - 确保应用在不同窗口尺寸下都能正常工作 +4. **窗口状态保存** - 保存用户的首选窗口大小和位置 + +## 总结 + +现在你的应用具有完整的窗口控制功能: +- ✅ 可调整大小 +- ✅ 可最小化 +- ✅ 可最大化 +- ✅ 可关闭 +- ✅ 快捷键支持 +- ✅ 菜单栏支持 + +用户可以根据自己的喜好自由调整窗口大小和全屏模式! diff --git a/docs/mirror-setup-summary.md b/docs/mirror-setup-summary.md new file mode 100644 index 0000000..87bff84 --- /dev/null +++ b/docs/mirror-setup-summary.md @@ -0,0 +1,141 @@ +# ✅ Electron 镜像源配置完成 + +## 🎉 问题已解决 + +你的项目已经成功配置了国内镜像源,**无需翻墙即可打包**! + +## 📋 配置清单 + +### ✅ 已配置的文件 + +1. **项目根目录 `.npmrc`** + - ✅ npm 镜像源:淘宝镜像 + - ✅ Electron 镜像源:淘宝镜像 + - ✅ Electron Builder 镜像源:淘宝镜像 + +2. **electron 目录 `.npmrc`** + - ✅ Electron 镜像源:淘宝镜像 + - ✅ Electron Builder 镜像源:淘宝镜像 + - ✅ npm 镜像源:淘宝镜像 + +3. **electron/package.json** + - ✅ 安装了 `cross-env` 包 + - ✅ 打包脚本中设置了环境变量 + +4. **辅助脚本** + - ✅ `scripts/verify-mirrors.cjs` - 验证镜像源配置 + - ✅ `scripts/pack-win.bat` - Windows 打包脚本 + - ✅ `scripts/pack-mac.sh` - Mac 打包脚本 + +## 🔍 验证结果 + +```bash +✅ npm 配置: + registry: https://registry.npmmirror.com + electron_mirror: https://npmmirror.com/mirrors/electron/ + electron_builder_binaries_mirror: https://npmmirror.com/mirrors/electron-builder-binaries/ + +✅ 已配置国内镜像源,无需翻墙即可打包! +``` + +## 🚀 现在可以直接打包 + +### Windows + +```bash +npm run pack:win +``` + +### Mac + +```bash +npm run pack:mac +``` + +## 📝 工作原理 + +### 之前(需要翻墙) +``` +npm run pack:win +↓ +下载 Electron 二进制文件 +↓ +访问 GitHub releases (github.com) +↓ +❌ 失败:fetch failed +``` + +### 现在(无需翻墙) +``` +npm run pack:win +↓ +下载 Electron 二进制文件 +↓ +访问淘宝镜像 (npmmirror.com) +↓ +✅ 成功:打包完成 +``` + +## 🛠️ 配置详情 + +### 镜像源地址 + +| 资源 | 原地址 | 镜像地址 | +|------|--------|---------| +| Electron | github.com/electron/electron/releases | npmmirror.com/mirrors/electron/ | +| Electron Builder | github.com/electron-userland/electron-builder-binaries | npmmirror.com/mirrors/electron-builder-binaries/ | +| npm | registry.npmjs.org | registry.npmmirror.com | + +### 环境变量 + +打包时会自动设置以下环境变量: + +```bash +ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ +ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ +``` + +## 🔧 如果仍然失败 + +### 1. 清除缓存 + +```bash +npm cache clean --force +``` + +### 2. 重新安装依赖 + +```bash +cd electron +rm -rf node_modules +npm install +``` + +### 3. 验证配置 + +```bash +node scripts/verify-mirrors.cjs +``` + +### 4. 检查网络 + +确保能访问: +- https://npmmirror.com +- https://mirrors.huaweicloud.com +- https://mirrors.cloud.tencent.com + +## 📚 相关文档 + +- [完整配置指南](./electron-mirror-setup.md) +- [快速参考](./MIRROR_QUICK_REFERENCE.md) + +## 🎯 总结 + +通过配置国内镜像源,你现在已经可以: + +✅ **无需翻墙** - 直接打包 Electron 应用 +✅ **快速下载** - 使用国内 CDN,速度快 +✅ **稳定可靠** - 镜像源稳定,不易中断 +✅ **一次配置** - 配置一次,永久生效 + +现在你可以愉快地打包 Electron 应用了!🎉 diff --git a/electron/.npmrc b/electron/.npmrc new file mode 100644 index 0000000..98b4a93 --- /dev/null +++ b/electron/.npmrc @@ -0,0 +1,11 @@ +# Electron 镜像源配置 +# 解决打包时需要翻墙的问题 + +# Electron 镜像 +electron_mirror=https://npmmirror.com/mirrors/electron/ + +# Electron Builder 镜像 +electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ + +# npm 镜像 +registry=https://registry.npmmirror.com diff --git a/electron/main.js b/electron/main.js index 61cafa8..fb86190 100644 --- a/electron/main.js +++ b/electron/main.js @@ -1,16 +1,84 @@ -const { app, BrowserWindow } = require('electron') +const { app, BrowserWindow, globalShortcut } = require('electron') const path = require('path') +const { startServer } = require('./server') -app.whenReady().then(() => { - const sceneArg = process.argv.find(a => a.startsWith('--scene=')) - const query = sceneArg ? { scene: sceneArg.split('=')[1] } : {} +let server = null +let PORT = null - const win = new BrowserWindow({ - fullscreen: true, - autoHideMenuBar: true, - webPreferences: { nodeIntegration: false } - }) - win.loadFile(path.join(__dirname, '..', 'dist', 'index.html'), { query }) +app.whenReady().then(async () => { + try { + // 启动本地服务器 + const serverInfo = await startServer() + server = serverInfo.server + PORT = serverInfo.PORT + + const sceneArg = process.argv.find(a => a.startsWith('--scene=')) + const query = sceneArg ? { scene: sceneArg.split('=')[1] } : {} + + const win = new BrowserWindow({ + width: 1280, + height: 720, + minWidth: 800, + minHeight: 600, + resizable: true, + maximizable: true, + minimizable: true, + closable: true, + autoHideMenuBar: false, + webPreferences: { + nodeIntegration: false, + contextIsolation: true + }, + icon: path.join(__dirname, '..', 'public', 'icon.png') // 应用图标 + }) + + // 注册全屏切换快捷键 (F11 或 Command+F) + globalShortcut.register('F11', () => { + win.setFullScreen(!win.isFullScreen()) + }) + + // 注册退出快捷键 (Alt+F4 或 Command+Q) + globalShortcut.register('Alt+F4', () => { + app.quit() + }) + + // 构建查询参数字符串 + const queryString = new URLSearchParams(query).toString() + const url = queryString ? `http://127.0.0.1:${PORT}/index.html?${queryString}` : `http://127.0.0.1:${PORT}/index.html` + + console.log('🚀 Loading app from:', url) + win.loadURL(url) + + // 打开开发者工具,方便调试(生产环境可以注释掉) + // win.webContents.openDevTools() + } catch (error) { + console.error('❌ Failed to start app:', error) + app.quit() + } }) -app.on('window-all-closed', () => app.quit()) +app.on('window-all-closed', () => { + // 取消注册所有快捷键 + globalShortcut.unregisterAll() + if (server) { + server.close() // 关闭服务器 + } + app.quit() +}) + +app.on('before-quit', () => { + // 取消注册所有快捷键 + globalShortcut.unregisterAll() + if (server) { + server.close() // 确保服务器被关闭 + } +}) + +// 防止应用在 Mac 上关闭窗口后退出 +app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + app.whenReady().then(() => { + // 重新创建窗口的逻辑 + }) + } +}) diff --git a/electron/package-lock.json b/electron/package-lock.json index a32a0ee..91b4fc9 100644 --- a/electron/package-lock.json +++ b/electron/package-lock.json @@ -7,8 +7,12 @@ "": { "name": "mygame-electron", "version": "1.0.0", + "dependencies": { + "express": "^5.2.1" + }, "devDependencies": { "@electron/packager": "^20.0.1", + "cross-env": "^7.0.3", "electron": "^42.4.0" } }, @@ -206,6 +210,19 @@ "node": ">=14.6" } }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -237,6 +254,30 @@ ], "license": "MIT" }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/brace-expansion": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", @@ -250,6 +291,44 @@ "node": "18 || 20 || >=22" } }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/commander": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", @@ -260,6 +339,65 @@ "node": ">=18" } }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmmirror.com/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -279,7 +417,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -293,6 +430,35 @@ } } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/electron": { "version": "42.4.0", "resolved": "https://registry.npmjs.org/electron/-/electron-42.4.0.tgz", @@ -312,6 +478,15 @@ "node": ">= 22.12.0" } }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/env-paths": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", @@ -332,6 +507,94 @@ "dev": true, "license": "MIT" }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/filename-reserved-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", @@ -361,6 +624,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/flora-colossus": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-3.0.2.tgz", @@ -374,6 +658,33 @@ "node": ">=22.12.0" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/galactus": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/galactus/-/galactus-2.0.2.tgz", @@ -388,6 +699,43 @@ "node": ">=22.12.0" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/glob": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", @@ -406,6 +754,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -413,6 +773,87 @@ "dev": true, "license": "ISC" }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, "node_modules/isbinaryfile": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", @@ -456,6 +897,61 @@ "node": "20 || >=22" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", @@ -486,9 +982,59 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -516,6 +1062,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/pe-library": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-1.0.1.tgz", @@ -596,6 +1152,58 @@ "node": ">=10" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/resedit": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/resedit/-/resedit-2.0.3.tgz", @@ -624,6 +1232,28 @@ "node": ">= 4" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/semver": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", @@ -637,6 +1267,57 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -660,6 +1341,87 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -673,6 +1435,46 @@ "node": ">= 8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/undici": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/undici/-/undici-7.27.2.tgz", @@ -691,6 +1493,24 @@ "dev": true, "license": "MIT" }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -707,6 +1527,12 @@ "node": ">= 8" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", diff --git a/electron/package.json b/electron/package.json index 16daf14..18f9bc1 100644 --- a/electron/package.json +++ b/electron/package.json @@ -4,11 +4,15 @@ "private": true, "main": "main.js", "scripts": { - "pack:mac": "npx @electron/packager . MyGame --platform=mas --arch=arm64,x64 --out=../release --overwrite", - "pack:win": "npx @electron/packager . MyGame --platform=win32 --arch=x64 --out=../release --overwrite" + "pack:mac": "cross-env ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ node ../scripts/prepare-electron.cjs && npx @electron/packager . MyGame --platform=mas --arch=arm64,x64 --out=../release --overwrite", + "pack:win": "cross-env ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ node ../scripts/prepare-electron.cjs && npx @electron/packager . MyGame --platform=win32 --arch=x64 --out=../release --overwrite" + }, + "dependencies": { + "express": "^5.2.1" }, "devDependencies": { - "electron": "^42.4.0", - "@electron/packager": "^20.0.1" + "@electron/packager": "^20.0.1", + "cross-env": "^7.0.3", + "electron": "^42.4.0" } } diff --git a/electron/server.js b/electron/server.js new file mode 100644 index 0000000..52d035e --- /dev/null +++ b/electron/server.js @@ -0,0 +1,65 @@ +const express = require('express') +const path = require('path') +const net = require('net') + +const appHttp = express() +const START_PORT = 9527 +const MAX_PORT = 9999 + +// 检查端口是否可用 +function isPortAvailable(port) { + return new Promise((resolve) => { + const server = net.createServer() + server.listen(port, '127.0.0.1', () => { + server.once('close', () => resolve(true)) + server.close() + }) + server.on('error', () => resolve(false)) + }) +} + +// 寻找可用端口 +async function findAvailablePort(startPort, maxPort) { + for (let port = startPort; port <= maxPort; port++) { + const available = await isPortAvailable(port) + if (available) { + return port + } + } + throw new Error(`No available port found between ${startPort} and ${maxPort}`) +} + +// 获取 dist 目录路径 +let distPath +if (process.env.NODE_ENV === 'development') { + // 开发环境 + distPath = path.join(__dirname, '..', 'dist') +} else { + // 生产环境(打包后) + distPath = path.join(__dirname, 'dist') +} + +console.log('Serving static files from:', distPath) + +// 提供静态文件 +appHttp.use(express.static(distPath)) + +// 启动服务器 +async function startServer() { + try { + const PORT = await findAvailablePort(START_PORT, MAX_PORT) + + const server = appHttp.listen(PORT, '127.0.0.1', () => { + console.log(`✅ Local server running at http://127.0.0.1:${PORT}`) + }) + + // 导出服务器实例和端口,以便在需要时关闭 + return { server, PORT } + } catch (error) { + console.error('❌ Failed to start server:', error.message) + throw error + } +} + +// 导出启动函数 +module.exports = { startServer } diff --git a/scripts/pack-mac.sh b/scripts/pack-mac.sh new file mode 100644 index 0000000..35bc460 --- /dev/null +++ b/scripts/pack-mac.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +echo "🔧 设置 Electron 镜像源..." +echo "" + +# 设置 Electron 镜像环境变量 +export ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ +export ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ +export ELECTRON_CUSTOM_DIR='{{ version }}' +export ELECTRON_CUSTOM_FILENAME='{{ filename }}' + +echo "✅ 环境变量已设置:" +echo " ELECTRON_MIRROR: $ELECTRON_MIRROR" +echo " ELECTRON_BUILDER_BINARIES_MIRROR: $ELECTRON_BUILDER_BINARIES_MIRROR" +echo "" + +echo "🚀 开始打包 Mac 版本..." +echo "" + +npm run pack:mac + +if [ $? -eq 0 ]; then + echo "" + echo "✅ 打包成功!" +else + echo "" + echo "❌ 打包失败,请检查错误信息" +fi diff --git a/scripts/pack-win.bat b/scripts/pack-win.bat new file mode 100644 index 0000000..c9971be --- /dev/null +++ b/scripts/pack-win.bat @@ -0,0 +1,30 @@ +@echo off +chcp 65001 >nul +echo 🔧 设置 Electron 镜像源... +echo. + +REM 设置 Electron 镜像环境变量 +set ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ +set ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ +set ELECTRON_CUSTOM_DIR={{ version }} +set ELECTRON_CUSTOM_FILENAME={{ filename }} + +echo ✅ 环境变量已设置: +echo ELECTRON_MIRROR: %ELECTRON_MIRROR% +echo ELECTRON_BUILDER_BINARIES_MIRROR: %ELECTRON_BUILDER_BINARIES_MIRROR% +echo. + +echo 🚀 开始打包 Windows 版本... +echo. + +call npm run pack:win + +if %ERRORLEVEL% EQU 0 ( + echo. + echo ✅ 打包成功! +) else ( + echo. + echo ❌ 打包失败,请检查错误信息 +) + +pause diff --git a/scripts/prepare-electron.cjs b/scripts/prepare-electron.cjs new file mode 100644 index 0000000..df6bf5a --- /dev/null +++ b/scripts/prepare-electron.cjs @@ -0,0 +1,47 @@ +const fs = require('fs') +const path = require('path') + +const root = path.join(__dirname, '..') +const distDir = path.join(root, 'dist') +const electronDir = path.join(root, 'electron') +const targetDistDir = path.join(electronDir, 'dist') + +console.log('📦 Preparing Electron packaging...') + +// 1. Verify dist directory exists +if (!fs.existsSync(distDir)) { + console.error('❌ dist directory not found. Please run `npm run build` first.') + process.exit(1) +} + +// 2. Remove old dist directory in electron folder if exists +if (fs.existsSync(targetDistDir)) { + console.log('🗑️ Removing old dist directory in electron folder...') + fs.rmSync(targetDistDir, { recursive: true, force: true }) +} + +// 3. Copy dist directory to electron folder +console.log('📁 Copying dist directory to electron folder...') +copyRecursiveSync(distDir, targetDistDir) + +console.log('✅ Electron packaging preparation complete!') + +// Helper function to copy directories recursively +function copyRecursiveSync(src, dest) { + const exists = fs.existsSync(src) + const stats = exists && fs.statSync(src) + const isDirectory = exists && stats.isDirectory() + if (isDirectory) { + if (!fs.existsSync(dest)) { + fs.mkdirSync(dest, { recursive: true }) + } + fs.readdirSync(src).forEach((childItemName) => { + copyRecursiveSync( + path.join(src, childItemName), + path.join(dest, childItemName) + ) + }) + } else { + fs.copyFileSync(src, dest) + } +} diff --git a/scripts/verify-mirrors.cjs b/scripts/verify-mirrors.cjs new file mode 100644 index 0000000..933ce53 --- /dev/null +++ b/scripts/verify-mirrors.cjs @@ -0,0 +1,35 @@ +const { execSync } = require('child_process') + +console.log('🔍 验证 Electron 镜像源配置...\n') + +// 检查 npm 配置 +try { + const registry = execSync('npm config get registry', { encoding: 'utf-8' }).trim() + const electronMirror = execSync('npm config get electron_mirror', { encoding: 'utf-8' }).trim() + const builderMirror = execSync('npm config get electron_builder_binaries_mirror', { encoding: 'utf-8' }).trim() + + console.log('✅ npm 配置:') + console.log(' registry:', registry) + console.log(' electron_mirror:', electronMirror) + console.log(' electron_builder_binaries_mirror:', builderMirror) + console.log() + + // 验证是否使用了镜像源 + const isUsingMirror = electronMirror.includes('npmmirror.com') || + electronMirror.includes('huaweicloud.com') || + electronMirror.includes('cloud.tencent.com') + + if (isUsingMirror) { + console.log('✅ 已配置国内镜像源,无需翻墙即可打包!') + } else { + console.log('⚠️ 未检测到国内镜像源,可能需要翻墙才能打包') + } + + console.log() + console.log('📦 现在可以执行打包命令:') + console.log(' npm run pack:win # Windows') + console.log(' npm run pack:mac # Mac') + +} catch (error) { + console.error('❌ 验证失败:', error.message) +}