1.4.1 并且不至于1.4.1
|
|
@ -0,0 +1,11 @@
|
|||
node_modules/
|
||||
dist/
|
||||
build/
|
||||
out/
|
||||
.env
|
||||
.env.local
|
||||
.DS_Store
|
||||
*.log
|
||||
start.bat
|
||||
push.bat
|
||||
GoodPlanCraftLauncher/
|
||||
24
README.md
|
|
@ -1,29 +1,22 @@
|
|||
# GoodPlanCraftLaunCher (GPCL)
|
||||
# GoodPlanCraftLauncher (GPCL)
|
||||
|
||||
简洁高效的 Minecraft 启动器。
|
||||
|
||||
代码优先挂
|
||||
|
||||
https://git.caellab.com/yunyun/GoodPlanCraftLaunCher
|
||||
|
||||
这个可能很久都没更新了
|
||||
|
||||
|
||||
## 制作方法
|
||||
|
||||
使用[Electron](https://www.electronjs.org/zh/docs/latest/)进行制作。
|
||||
使用 [Node.js](https://nodejs.org) + [Electron](https://www.electronjs.org/zh/docs/latest/) 进行制作。
|
||||
|
||||
## 安装
|
||||
|
||||
### 从源码构建
|
||||
|
||||
```bash
|
||||
git clone https://github.com/yunyun-3782/GoodPlanCraftLaunCher.git
|
||||
git clone https://git.caellab.com/yunyun/GoodPlanCraftLauncher.git
|
||||
```
|
||||
|
||||
### 预编译版本
|
||||
|
||||
前往 [Releases](https://github.com/yunyun-3782/GoodPlanCraftLaunCher/releases/) 下载对可执行文件。
|
||||
前往 [Releases](https://git.caellab.com/yunyun/GoodPlanCraftLauncher/releases) 下载对可执行文件。
|
||||
|
||||
## 使用
|
||||
|
||||
|
|
@ -49,14 +42,17 @@ npm run build
|
|||
|
||||
## 贡献
|
||||
|
||||
欢迎提交 不同意见,同时欢迎开发 衍生 版本。
|
||||
欢迎提交 [不同意见](https://f.caellab.com/t/problem) 或 [Issue](https://git.caellab.com/yunyun/GoodPlanCraftLauncher/issues) ,同时欢迎开发 衍生 版本。
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目采用 [CaelLab BY-SA Code License](https://www.caellab.com/license/bysa-code) 授权。
|
||||
|
||||
具体可查看 [LICENSE](https://github.com/yunyun-3782/GoodPlanCraftLaunCher/blob/main/LICENSE) 。
|
||||
具体可查看 [LICENSE](https://git.caellab.com/yunyun/GoodPlanCraftLauncher/src/branch/main/LICENSE) 。
|
||||
|
||||
---
|
||||
很高兴能帮助到你!祝你在MC中玩的愉快!
|
||||
|
||||
---
|
||||
|
||||
**GoodPlanCraftLaunCher** — 让 Minecraft 启动更简单。
|
||||
**GoodPlanCraftLauncher** — 让 Minecraft 启动更简单。
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3408 @@
|
|||
/*
|
||||
* CaelLab BY-SA Code License
|
||||
* Copyright (c) 2026 Yunyun(云云) By 虚舟实验室(CaelLab) / CaelLabGameTS
|
||||
* Source: https://git.caellab.com/yunyun/GoodPlanCraftLauncher
|
||||
* Source: https://github.com/yunyun-3782/GoodPlanCraftLauncher
|
||||
*/
|
||||
|
||||
|
||||
|
||||
const { app, BrowserWindow, ipcMain, net, dialog, Menu, shell } = require('electron');
|
||||
const path = require('path');
|
||||
const { spawn, spawnSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const { tmpdir } = require('os');
|
||||
const { pipeline } = require('stream/promises');
|
||||
|
||||
process.env.TZ = 'Asia/Shanghai';
|
||||
|
||||
app.commandLine.appendSwitch('js-flags', '--compile-hints-always');
|
||||
app.commandLine.appendSwitch('disk-cache-size', '134217728');
|
||||
|
||||
function formatTimestamp() {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
const hours = String(now.getHours()).padStart(2, '0');
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
const ms = String(now.getMilliseconds()).padStart(3, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}`;
|
||||
}
|
||||
|
||||
function checkDebuggerAttached() {
|
||||
try {
|
||||
if (process.execArgv.some(arg => arg.includes('--inspect') || arg.includes('--debug-brk'))) {
|
||||
return true;
|
||||
}
|
||||
if (process.env.ELECTRON_ENABLE_STACK_DUMPING) {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
let isDeveloperMode = false;
|
||||
|
||||
let BASE_DIR;
|
||||
let GAME_DIR;
|
||||
let CACHE_DIR;
|
||||
let LOG_DIR;
|
||||
let LOG_FILE;
|
||||
let APP_VERSION;
|
||||
let CONFIG_DIR;
|
||||
|
||||
const DEFAULT_SETTINGS = {
|
||||
game: {
|
||||
memory: '2',
|
||||
playerName: 'GPCL_Player',
|
||||
javaPath: '',
|
||||
jvmArgs: '',
|
||||
windowMode: 'windowed'
|
||||
},
|
||||
appearance: {
|
||||
theme: 'light',
|
||||
scale: '110'
|
||||
},
|
||||
download: {
|
||||
maxConcurrent: 64,
|
||||
javaMirror: 'tsinghua',
|
||||
customJavaMirror: ''
|
||||
},
|
||||
advanced: {
|
||||
autoCheckUpdate: true,
|
||||
preventMultipleLaunch: true,
|
||||
autoClearLogs: true,
|
||||
logRetentionValue: 7,
|
||||
logRetentionUnit: 'day',
|
||||
playStartupAnimation: true,
|
||||
developerMode: false
|
||||
}
|
||||
};
|
||||
|
||||
function parseIni(text) {
|
||||
const result = {};
|
||||
let currentSection = null;
|
||||
const lines = text.split(/\r?\n/);
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed || trimmed.startsWith(';') || trimmed.startsWith('#')) {
|
||||
continue;
|
||||
}
|
||||
const sectionMatch = trimmed.match(/^\[(.+)\]$/);
|
||||
if (sectionMatch) {
|
||||
currentSection = sectionMatch[1].toLowerCase();
|
||||
if (!result[currentSection]) {
|
||||
result[currentSection] = {};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const keyValueMatch = trimmed.match(/^([^=]+)=(.*)$/);
|
||||
if (keyValueMatch && currentSection) {
|
||||
const key = keyValueMatch[1].trim();
|
||||
let value = keyValueMatch[2].trim();
|
||||
if (value.toLowerCase() === 'true') {
|
||||
value = true;
|
||||
} else if (value.toLowerCase() === 'false') {
|
||||
value = false;
|
||||
} else {
|
||||
const num = Number(value);
|
||||
if (!isNaN(num)) {
|
||||
value = num;
|
||||
}
|
||||
}
|
||||
result[currentSection][key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function stringifyIni(obj) {
|
||||
let result = '';
|
||||
for (const [section, sectionData] of Object.entries(obj)) {
|
||||
result += `[${section}]\n`;
|
||||
for (const [key, value] of Object.entries(sectionData)) {
|
||||
result += `${key}=${value}\n`;
|
||||
}
|
||||
result += '\n';
|
||||
}
|
||||
return result.trim();
|
||||
}
|
||||
|
||||
function mergeWithDefaults(settings) {
|
||||
const merged = JSON.parse(JSON.stringify(DEFAULT_SETTINGS));
|
||||
for (const [section, sectionData] of Object.entries(settings)) {
|
||||
if (merged[section]) {
|
||||
for (const [key, value] of Object.entries(sectionData)) {
|
||||
if (key in merged[section]) {
|
||||
merged[section][key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
function loadSettings() {
|
||||
try {
|
||||
const configPath = path.join(BASE_DIR, 'gpcl', 'config', 'setting.ini');
|
||||
if (fs.existsSync(configPath)) {
|
||||
const content = fs.readFileSync(configPath, 'utf8');
|
||||
const parsed = parseIni(content);
|
||||
const merged = mergeWithDefaults(parsed);
|
||||
writeLog('配置已加载');
|
||||
return merged;
|
||||
}
|
||||
} catch (e) {
|
||||
writeLog(`加载配置失败: ${e.message}`);
|
||||
}
|
||||
writeLog('使用默认配置');
|
||||
return JSON.parse(JSON.stringify(DEFAULT_SETTINGS));
|
||||
}
|
||||
|
||||
function saveSettings(settings) {
|
||||
try {
|
||||
const configDir = path.join(BASE_DIR, 'gpcl', 'config');
|
||||
const configPath = path.join(configDir, 'setting.ini');
|
||||
if (!fs.existsSync(configDir)) {
|
||||
fs.mkdirSync(configDir, { recursive: true });
|
||||
}
|
||||
const content = stringifyIni(settings);
|
||||
fs.writeFileSync(configPath, content, 'utf8');
|
||||
writeLog('配置已保存');
|
||||
return true;
|
||||
} catch (e) {
|
||||
writeLog(`保存配置失败: ${e.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function initPaths() {
|
||||
BASE_DIR = app.getPath('userData');
|
||||
GAME_DIR = path.join(BASE_DIR, '.minecraft');
|
||||
CACHE_DIR = path.join(BASE_DIR, 'gpcl', 'cache');
|
||||
LOG_DIR = path.join(BASE_DIR, 'gpcl', 'log');
|
||||
CONFIG_DIR = path.join(BASE_DIR, 'gpcl', 'config');
|
||||
const startupTime = new Date().toISOString().replace(/:/g, '-').replace('.', '_');
|
||||
LOG_FILE = path.join(LOG_DIR, `${startupTime}.log`);
|
||||
|
||||
const requiredDirs = [
|
||||
path.join(BASE_DIR, 'gpcl', 'config'),
|
||||
path.join(BASE_DIR, 'gpcl', 'users'),
|
||||
LOG_DIR,
|
||||
CACHE_DIR
|
||||
];
|
||||
|
||||
for (const dir of requiredDirs) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cleanupExpiredLogs() {
|
||||
try {
|
||||
const settings = loadSettings();
|
||||
const autoClearLogs = settings.advanced?.autoClearLogs;
|
||||
|
||||
if (!autoClearLogs) {
|
||||
writeLog('[日志清理] 自动清理日志已禁用');
|
||||
return;
|
||||
}
|
||||
|
||||
const retentionValue = settings.advanced?.logRetentionValue || 7;
|
||||
const retentionUnit = settings.advanced?.logRetentionUnit || 'day';
|
||||
|
||||
// 计算过期时间
|
||||
const now = Date.now();
|
||||
let expireTime;
|
||||
|
||||
switch (retentionUnit) {
|
||||
case 'hour':
|
||||
expireTime = now - retentionValue * 60 * 60 * 1000;
|
||||
break;
|
||||
case 'day':
|
||||
expireTime = now - retentionValue * 24 * 60 * 60 * 1000;
|
||||
break;
|
||||
case 'month':
|
||||
expireTime = now - retentionValue * 30 * 24 * 60 * 60 * 1000;
|
||||
break;
|
||||
case 'year':
|
||||
expireTime = now - retentionValue * 365 * 24 * 60 * 60 * 1000;
|
||||
break;
|
||||
default:
|
||||
expireTime = now - retentionValue * 24 * 60 * 60 * 1000;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(LOG_DIR)) {
|
||||
writeLog('[日志清理] 日志目录不存在');
|
||||
return;
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(LOG_DIR);
|
||||
let cleanedCount = 0;
|
||||
|
||||
for (const file of files) {
|
||||
if (!file.endsWith('.log')) continue;
|
||||
|
||||
const filePath = path.join(LOG_DIR, file);
|
||||
try {
|
||||
const stat = fs.statSync(filePath);
|
||||
if (stat.mtime.getTime() < expireTime) {
|
||||
fs.unlinkSync(filePath);
|
||||
cleanedCount++;
|
||||
}
|
||||
} catch (e) {
|
||||
writeLog(`[日志清理] 清理文件失败: ${file} - ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (cleanedCount > 0) {
|
||||
writeLog(`[日志清理] 已清理 ${cleanedCount} 个过期日志文件`);
|
||||
} else {
|
||||
writeLog('[日志清理] 没有需要清理的过期日志');
|
||||
}
|
||||
} catch (e) {
|
||||
writeLog(`[日志清理] 清理过程出错: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
let mainWindow;
|
||||
APP_VERSION = require('./package.json').version;
|
||||
|
||||
function writeLog(message) {
|
||||
const timestamp = formatTimestamp();
|
||||
const logLine = `[${timestamp}] ${message}\n`;
|
||||
try {
|
||||
if (LOG_FILE) {
|
||||
fs.appendFileSync(LOG_FILE, logLine, 'utf8');
|
||||
}
|
||||
console.log(`[${timestamp}] LOG:`, message);
|
||||
} catch (e) {
|
||||
console.error(`[${formatTimestamp()}] 写入日志失败:`, e);
|
||||
}
|
||||
}
|
||||
|
||||
let isDownloading = false;
|
||||
let currentDownloadVersion = null;
|
||||
let shouldCancelDownload = false;
|
||||
let currentDownloadType = null; // 'game' or 'java'
|
||||
let currentDownloadPath = null; // 下载的文件或文件夹路径
|
||||
let javaDownloadResolve = null; // Java下载对话框的Promise resolve
|
||||
let javaDownloadReject = null; // Java下载对话框的Promise reject
|
||||
let isJavaDownloading = false; // Java下载中标志
|
||||
|
||||
function createWindow() {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1000,
|
||||
height: 700,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
maxWidth: 1920,
|
||||
maxHeight: 1080,
|
||||
frame: false,
|
||||
title: 'GoodPlanCraftLauncher',
|
||||
icon: path.join(__dirname, 'gpcl', 'logo.ico'),
|
||||
backgroundColor: '#0d0d15',
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
devTools: true
|
||||
}
|
||||
});
|
||||
|
||||
try { Menu.setApplicationMenu(null); } catch (e) {}
|
||||
mainWindow.setMenuBarVisibility(false);
|
||||
mainWindow.setAutoHideMenuBar(true);
|
||||
|
||||
mainWindow.loadFile(path.join(__dirname, 'renderer', 'splash.html'));
|
||||
|
||||
// 关闭窗口前拦截
|
||||
mainWindow.on('close', (event) => {
|
||||
if (isDownloading || isJavaDownloading) {
|
||||
event.preventDefault();
|
||||
// 发送询问到渲染进程
|
||||
mainWindow.webContents.send('confirm-close-while-downloading');
|
||||
}
|
||||
});
|
||||
|
||||
mainWindow.webContents.on('before-input-event', (event, input) => {
|
||||
if (isDeveloperMode) return;
|
||||
const key = input.key && input.key.toLowerCase();
|
||||
const ctrlOrCmd = input.control || input.meta;
|
||||
if ((ctrlOrCmd && key === 'r') || input.key === 'F5') event.preventDefault();
|
||||
if ((input.control && input.shift && key === 'i') || input.key === 'F12' || (input.meta && input.alt && key === 'i')) event.preventDefault();
|
||||
});
|
||||
|
||||
mainWindow.webContents.on('context-menu', (e) => {
|
||||
if (!isDeveloperMode) e.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
app.on('web-contents-created', (event, contents) => {
|
||||
try {
|
||||
contents.on('context-menu', (e) => {
|
||||
if (!isDeveloperMode) e.preventDefault();
|
||||
});
|
||||
contents.on('before-input-event', (event, input) => {
|
||||
if (isDeveloperMode) return;
|
||||
const key = input.key && input.key.toLowerCase();
|
||||
const ctrlOrCmd = input.control || input.meta;
|
||||
if ((ctrlOrCmd && key === 'r') || input.key === 'F5') event.preventDefault();
|
||||
if ((input.control && input.shift && key === 'i') || input.key === 'F12' || (input.meta && input.alt && key === 'i')) event.preventDefault();
|
||||
});
|
||||
} catch (e) {}
|
||||
});
|
||||
|
||||
// 单实例锁:当检测到第二个实例时,凸显已有窗口
|
||||
app.on('second-instance', () => {
|
||||
if (mainWindow) {
|
||||
if (mainWindow.isMinimized()) mainWindow.restore();
|
||||
mainWindow.focus();
|
||||
}
|
||||
});
|
||||
|
||||
function createGPCLShortcut() {
|
||||
try {
|
||||
const startMenuDir = path.join(app.getPath('appData'), 'Microsoft', 'Windows', 'Start Menu', 'Programs');
|
||||
const shortcutPath = path.join(startMenuDir, 'GPCL.lnk');
|
||||
|
||||
if (fs.existsSync(shortcutPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (app.isPackaged) {
|
||||
const exePath = process.execPath;
|
||||
shell.writeShortcutLink(shortcutPath, 'create', {
|
||||
target: exePath,
|
||||
description: 'GoodPlanCraftLauncher - Minecraft 启动器',
|
||||
icon: exePath,
|
||||
iconIndex: 0
|
||||
});
|
||||
writeLog('[快捷方式] 已创建 GPCL 快捷方式');
|
||||
}
|
||||
} catch (e) {
|
||||
writeLog(`[快捷方式] 创建 GPCL 快捷方式失败: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
// 检测是否是 electron-builder 的测试运行
|
||||
const isTesting = process.argv.includes('--squirrel-install') ||
|
||||
process.argv.includes('--squirrel-obsolete') ||
|
||||
process.argv.includes('--squirrel-updated') ||
|
||||
process.argv.includes('--testing');
|
||||
|
||||
if (isTesting) {
|
||||
console.log('[启动] 检测到构建测试模式,跳过窗口创建');
|
||||
app.quit();
|
||||
return;
|
||||
}
|
||||
|
||||
// 提前初始化 BASE_DIR 以读取配置文件
|
||||
BASE_DIR = app.getPath('userData');
|
||||
|
||||
// 检查是否启用防止多次启动
|
||||
let preventMultipleLaunch = true;
|
||||
try {
|
||||
const configPath = path.join(BASE_DIR, 'gpcl', 'config', 'setting.ini');
|
||||
if (fs.existsSync(configPath)) {
|
||||
const content = fs.readFileSync(configPath, 'utf8');
|
||||
const parsed = parseIni(content);
|
||||
if (parsed.advanced) {
|
||||
if (parsed.advanced.preventMultipleLaunch === false || parsed.advanced.preventmultiplelaunch === false) {
|
||||
preventMultipleLaunch = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
if (preventMultipleLaunch) {
|
||||
const gotLock = app.requestSingleInstanceLock();
|
||||
if (!gotLock) {
|
||||
writeLog('检测到已有实例运行,退出');
|
||||
app.quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
initPaths();
|
||||
|
||||
cleanupExpiredLogs();
|
||||
|
||||
try {
|
||||
const devConfigPath = path.join(BASE_DIR, 'gpcl', 'config', 'setting.ini');
|
||||
if (fs.existsSync(devConfigPath)) {
|
||||
const devContent = fs.readFileSync(devConfigPath, 'utf8');
|
||||
const devParsed = parseIni(devContent);
|
||||
if (devParsed.advanced && devParsed.advanced.developerMode === true) {
|
||||
isDeveloperMode = true;
|
||||
writeLog('[开发者模式] 启动时已启用');
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
createGPCLShortcut();
|
||||
|
||||
writeLog(`GPCL v${APP_VERSION} 启动`);
|
||||
console.log('日志文件路径:', LOG_FILE);
|
||||
createWindow();
|
||||
});
|
||||
app.on('window-all-closed', () => app.quit());
|
||||
|
||||
ipcMain.handle('show-message-dialog', async (_, options) => {
|
||||
if (!mainWindow) return { success: false };
|
||||
const result = await dialog.showMessageBox(mainWindow, {
|
||||
type: options.type || 'info',
|
||||
title: options.title || '提示',
|
||||
message: options.message || '',
|
||||
detail: options.detail || '',
|
||||
buttons: options.buttons || ['确定'],
|
||||
defaultId: options.defaultId || 0,
|
||||
cancelId: options.cancelId || 0,
|
||||
noLink: true
|
||||
});
|
||||
return { success: true, response: result.response };
|
||||
});
|
||||
|
||||
// Java版本 → 官方runtime目录名映射
|
||||
const JAVA_RUNTIME_MAP = {
|
||||
"8": "jre-legacy",
|
||||
"16": "java-runtime-beta",
|
||||
"17": "java-runtime-gamma",
|
||||
"21": "java-runtime-delta",
|
||||
"25": "java-runtime-epsilon"
|
||||
};
|
||||
|
||||
// Java版本 → 下载URL的key映射
|
||||
const JAVA_DOWNLOAD_KEYS = {
|
||||
"8": "jre-legacy",
|
||||
"17": "java-runtime-gamma",
|
||||
"21": "java-runtime-delta",
|
||||
"25": "java-runtime-epsilon"
|
||||
};
|
||||
|
||||
// 可选择的Java版本列表
|
||||
const JAVA_VERSIONS_AVAILABLE = ["8", "17", "21", "25"];
|
||||
|
||||
// 获取Java运行时目录(官方目录结构,使用绝对路径)
|
||||
function getJavaRuntimeDir(javaVersion) {
|
||||
const runtimeName = JAVA_RUNTIME_MAP[javaVersion] || `jre${javaVersion}`;
|
||||
// 使用官方Minecraft运行时目录:C:\Users\用户名\AppData\Roaming\.minecraft\runtime
|
||||
return path.join(os.homedir(), 'AppData', 'Roaming', '.minecraft', 'runtime', runtimeName);
|
||||
}
|
||||
|
||||
// 在目录中查找java.exe(自动拼接\bin\,使用java.exe以便捕获错误输出)
|
||||
function findJavaExecutable(runtimeDir) {
|
||||
// 优先使用 bin\java.exe(可以捕获控制台输出用于调试)
|
||||
const javaPath = path.join(runtimeDir, 'bin', 'java.exe');
|
||||
if (fs.existsSync(javaPath)) {
|
||||
try {
|
||||
fs.accessSync(javaPath, fs.constants.R_OK);
|
||||
return javaPath;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// 兜底:递归查找
|
||||
return findJavaExecutableInDir(runtimeDir);
|
||||
}
|
||||
|
||||
// 检查指定版本的Java是否已安装
|
||||
async function checkJavaInstalled(javaVersion) {
|
||||
const runtimeDir = getJavaRuntimeDir(javaVersion);
|
||||
if (fs.existsSync(runtimeDir)) {
|
||||
const exe = findJavaExecutable(runtimeDir);
|
||||
if (exe) {
|
||||
return { installed: true, javaPath: exe };
|
||||
}
|
||||
}
|
||||
return { installed: false, javaPath: null };
|
||||
}
|
||||
|
||||
// 获取指定Minecraft版本所需的Java版本
|
||||
function getJavaVersionForMCVersion(versionId) {
|
||||
// Minecraft 1.16及以下: Java 8
|
||||
// Minecraft 1.17: Java 16
|
||||
// Minecraft 1.18-1.20: Java 17
|
||||
// Minecraft 1.21+: Java 21
|
||||
const parts = versionId.split('.');
|
||||
let major = parseInt(parts[0], 10);
|
||||
let minor = parseInt(parts[1], 10);
|
||||
// 如果版本号是 "26" 这种没有点的格式
|
||||
if (isNaN(major)) major = 1;
|
||||
// 处理像 "26" 这种格式(没有 minor)
|
||||
if (isNaN(minor)) {
|
||||
// 26 及以上使用 Java 25
|
||||
if (major >= 26) return '25';
|
||||
// 21-25 使用 Java 21
|
||||
if (major >= 21) return '21';
|
||||
// 否则默认 Java 17
|
||||
return '17';
|
||||
}
|
||||
// 处理 1.x.x 的旧版本
|
||||
if (major === 1) {
|
||||
if (minor < 17) return '8';
|
||||
if (minor === 17) return '16';
|
||||
if (minor <= 20) return '17';
|
||||
return '21';
|
||||
}
|
||||
// 处理 2.x.x 和更大的版本(21, 24, 26, ...)
|
||||
// 26 及以上使用 Java 25
|
||||
if (major >= 26) return '25';
|
||||
// 21-25 使用 Java 21
|
||||
if (major >= 21) return '21';
|
||||
return '17';
|
||||
}
|
||||
|
||||
// 获取版本数据中指定的Java版本
|
||||
function getJavaVersionFromVersionData(versionData) {
|
||||
if (versionData?.javaVersion?.majorVersion) {
|
||||
return String(versionData.javaVersion.majorVersion);
|
||||
}
|
||||
if (versionData?.javaVersion?.component) {
|
||||
const m = versionData.javaVersion.component.match(/\d+/);
|
||||
if (m) return m[0];
|
||||
}
|
||||
return getJavaVersionForMCVersion(versionData.id);
|
||||
}
|
||||
|
||||
// 查找本地Java运行时(只查找,不下载)
|
||||
async function ensureJavaRuntime(version) {
|
||||
const javaVersion = typeof version === 'string' ? version : String(version);
|
||||
const runtimeDir = getJavaRuntimeDir(javaVersion);
|
||||
|
||||
writeLog(`[Java] 查找 Java ${javaVersion} 运行时: ${runtimeDir}`);
|
||||
|
||||
if (fs.existsSync(runtimeDir)) {
|
||||
const exe = findJavaExecutable(runtimeDir);
|
||||
if (exe) {
|
||||
writeLog(`[Java] 找到 Java ${javaVersion}: ${exe}`);
|
||||
return exe;
|
||||
}
|
||||
}
|
||||
|
||||
writeLog(`[Java] 未找到 Java ${javaVersion} 运行时`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 完整的Java运行时保证(包括下载和安装)
|
||||
async function ensureJavaRuntimeWithInstall(version, mainWindow) {
|
||||
const javaVersion = typeof version === 'string' ? version : String(version);
|
||||
|
||||
// 先检查是否已安装
|
||||
const installed = await checkJavaInstalled(javaVersion);
|
||||
if (installed.installed && installed.javaPath) {
|
||||
writeLog(`[Java] Java ${javaVersion} 已安装: ${installed.javaPath}`);
|
||||
return { success: true, javaPath: installed.javaPath };
|
||||
}
|
||||
|
||||
// 如果未安装,触发下载流程
|
||||
writeLog(`[Java] Java ${javaVersion} 未安装,开始下载流程`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 用户确认关闭并清理
|
||||
ipcMain.handle('confirm-close-download', async () => {
|
||||
if (isDownloading && currentDownloadVersion) {
|
||||
shouldCancelDownload = true;
|
||||
|
||||
try {
|
||||
// 清理游戏版本下载
|
||||
const versionDir = currentDownloadPath || path.join(GAME_DIR, 'versions', currentDownloadVersion);
|
||||
const cancelFilePath = path.join(versionDir, 'CancelDownload.txt');
|
||||
const cancelContent = `GoodPlanCraftLauncher ${APP_VERSION}\n取消时间:${formatTimestamp()}\n\n正确的关闭\n您在下载完成前正确地关闭了GPCL。`;
|
||||
|
||||
if (fs.existsSync(versionDir)) {
|
||||
fs.writeFileSync(cancelFilePath, cancelContent, 'utf8');
|
||||
const files = fs.readdirSync(versionDir);
|
||||
for (const file of files) {
|
||||
const filePath = path.join(versionDir, file);
|
||||
if (file !== 'CancelDownload.txt') {
|
||||
if (fs.statSync(filePath).isDirectory()) {
|
||||
fs.rmSync(filePath, { recursive: true, force: true });
|
||||
} else {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
writeLog(`已清理游戏版本下载目录: ${versionDir}`);
|
||||
} catch (e) {
|
||||
console.error('清理下载失败:', e);
|
||||
writeLog(`清理下载失败: ${e.message}`);
|
||||
}
|
||||
|
||||
isDownloading = false;
|
||||
currentDownloadVersion = null;
|
||||
currentDownloadType = null;
|
||||
currentDownloadPath = null;
|
||||
}
|
||||
if (mainWindow) {
|
||||
mainWindow.destroy();
|
||||
}
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
// 取消关闭
|
||||
ipcMain.handle('cancel-close', () => {
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
// 取消启动游戏
|
||||
let launchCancelFlag = false;
|
||||
ipcMain.handle('cancel-launch', () => {
|
||||
launchCancelFlag = true;
|
||||
writeLog('[启动] 用户取消启动');
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
// 检查更新(使用Node.js网络请求避免CORS问题)
|
||||
const https = require('https');
|
||||
ipcMain.handle('check-for-updates', async () => {
|
||||
return new Promise((resolve) => {
|
||||
const url = 'https://gamets.caellab.com/gpcl/data.json';
|
||||
writeLog('[更新] 开始检查更新...');
|
||||
|
||||
https.get(url, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const json = JSON.parse(data);
|
||||
writeLog('[更新] 获取到数据: ' + data);
|
||||
resolve({ success: true, data: json });
|
||||
} catch (e) {
|
||||
writeLog('[更新] JSON解析失败: ' + e.message);
|
||||
resolve({ success: false, error: 'JSON解析失败: ' + e.message });
|
||||
}
|
||||
});
|
||||
}).on('error', (e) => {
|
||||
writeLog('[更新] 请求失败: ' + e.message);
|
||||
resolve({ success: false, error: e.message });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 获取当前版本号(从package.json读取)
|
||||
ipcMain.handle('get-app-version', () => {
|
||||
try {
|
||||
const packagePath = path.join(__dirname, 'package.json');
|
||||
if (fs.existsSync(packagePath)) {
|
||||
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
||||
return pkg.version || '1.0.0';
|
||||
}
|
||||
} catch (e) {
|
||||
writeLog('[版本] 读取package.json失败: ' + e.message);
|
||||
}
|
||||
return '1.0.0';
|
||||
});
|
||||
|
||||
// 打开外部链接
|
||||
ipcMain.handle('open-external', (_, url) => {
|
||||
try {
|
||||
shell.openExternal(url);
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
writeLog('[外部链接] 打开失败: ' + e.message);
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
});
|
||||
|
||||
// IPC: 卸载Java运行时
|
||||
ipcMain.handle('uninstall-java', async (_, javaVersion) => {
|
||||
try {
|
||||
const runtimeDir = getJavaRuntimeDir(javaVersion);
|
||||
writeLog(`[Java] 开始卸载 Java ${javaVersion},目录: ${runtimeDir}`);
|
||||
|
||||
if (!fs.existsSync(runtimeDir)) {
|
||||
return { success: false, error: `Java ${javaVersion} 未安装` };
|
||||
}
|
||||
|
||||
fs.rmSync(runtimeDir, { recursive: true, force: true });
|
||||
writeLog(`[Java] Java ${javaVersion} 已卸载,目录已删除: ${runtimeDir}`);
|
||||
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('java-uninstalled', { javaVersion });
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
writeLog(`[Java] 卸载 Java ${javaVersion} 失败: ${e.message}`);
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
});
|
||||
|
||||
// 获取Java镜像源设置
|
||||
ipcMain.handle('get-java-mirror-settings', () => {
|
||||
try {
|
||||
const settingsPath = path.join(GAME_DIR, 'settings', 'java_mirror.json');
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
return JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
||||
}
|
||||
return {
|
||||
mirror: 'tsinghua',
|
||||
customUrl: ''
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
mirror: 'tsinghua',
|
||||
customUrl: ''
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 获取Java版本对应的下载URL
|
||||
ipcMain.handle('get-java-download-url', async (_, javaVersion) => {
|
||||
try {
|
||||
const settingsPath = path.join(GAME_DIR, 'settings', 'java_mirror.json');
|
||||
let settings = {
|
||||
mirror: 'tsinghua',
|
||||
customUrl: ''
|
||||
};
|
||||
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
||||
}
|
||||
|
||||
// 根据镜像源生成URL
|
||||
const url = getJavaDownloadUrl(javaVersion, settings.mirror, settings.customUrl);
|
||||
writeLog(`[Java] 获取下载URL: Java ${javaVersion} - ${url}`);
|
||||
return { success: true, url };
|
||||
} catch (e) {
|
||||
writeLog(`[Java] 获取下载URL失败: ${e.message}`);
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
});
|
||||
|
||||
// 根据镜像源生成下载URL
|
||||
function getJavaDownloadUrl(javaVersion, mirror, customUrl) {
|
||||
// 清华大学镜像
|
||||
const tsinghuaMirrors = {
|
||||
"8": "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/8/jre/x64/windows/OpenJDK8U-jre_x64_windows_hotspot_8u492b09.zip",
|
||||
"17": "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/17/jre/x64/windows/OpenJDK17U-jre_x64_windows_hotspot_17.0.19_10.zip",
|
||||
"21": "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/21/jre/x64/windows/OpenJDK21U-jre_x64_windows_hotspot_21.0.11_10.zip",
|
||||
"25": "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/25/jre/x64/windows/OpenJDK25U-jre_x64_windows_hotspot_25.0.3_9.zip"
|
||||
};
|
||||
|
||||
// 如果使用清华大学镜像
|
||||
if (mirror === 'tsinghua') {
|
||||
return tsinghuaMirrors[javaVersion] || tsinghuaMirrors["17"];
|
||||
}
|
||||
|
||||
// 如果使用自定义镜像
|
||||
if (mirror === 'custom' && customUrl) {
|
||||
// 替换{v}变量
|
||||
let url = customUrl;
|
||||
|
||||
// Java版本号(8, 17, 21等)
|
||||
url = url.replace(/\{v\}/gi, javaVersion);
|
||||
|
||||
// 完整版本号的映射
|
||||
const versionMap = {
|
||||
"8": "8u492b09",
|
||||
"17": "17.0.19_10",
|
||||
"21": "21.0.11_10",
|
||||
"25": "25.0.3_9"
|
||||
};
|
||||
url = url.replace(/\{version\}/gi, versionMap[javaVersion] || javaVersion);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
// 默认使用清华大学镜像
|
||||
return tsinghuaMirrors[javaVersion] || tsinghuaMirrors["17"];
|
||||
}
|
||||
|
||||
// 保存Java镜像源设置
|
||||
function saveJavaMirrorSettings(mirror, customUrl) {
|
||||
try {
|
||||
const settingsDir = path.join(GAME_DIR, 'settings');
|
||||
if (!fs.existsSync(settingsDir)) {
|
||||
fs.mkdirSync(settingsDir, { recursive: true });
|
||||
}
|
||||
const settingsPath = path.join(settingsDir, 'java_mirror.json');
|
||||
fs.writeFileSync(settingsPath, JSON.stringify({
|
||||
mirror,
|
||||
customUrl
|
||||
}, null, 2));
|
||||
writeLog(`[Java] 镜像源设置已保存: ${mirror}`);
|
||||
return true;
|
||||
} catch (e) {
|
||||
writeLog(`[Java] 保存镜像源设置失败: ${e.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 取消下载(只清理文件,不关闭窗口)
|
||||
ipcMain.handle('cancel-download-only', async () => {
|
||||
writeLog('[取消下载] 开始取消下载流程');
|
||||
|
||||
// 设置取消标志
|
||||
shouldCancelDownload = true;
|
||||
|
||||
// 清空下载队列
|
||||
const pendingCount = downloadQueue.length;
|
||||
downloadQueue = [];
|
||||
writeLog(`[取消下载] 已清空 ${pendingCount} 个待处理下载任务`);
|
||||
|
||||
// 等待当前活跃的下载完成(最多等待2秒)
|
||||
const waitStart = Date.now();
|
||||
while (activeDownloads > 0 && (Date.now() - waitStart) < 2000) {
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
}
|
||||
writeLog(`[取消下载] 活跃下载数: ${activeDownloads}`);
|
||||
|
||||
if ((isDownloading && currentDownloadVersion) || isJavaDownloading) {
|
||||
try {
|
||||
if (currentDownloadType === 'game' && currentDownloadPath) {
|
||||
// 清理游戏版本下载
|
||||
const versionDir = currentDownloadPath;
|
||||
if (fs.existsSync(versionDir)) {
|
||||
const files = fs.readdirSync(versionDir);
|
||||
for (const file of files) {
|
||||
const filePath = path.join(versionDir, file);
|
||||
try {
|
||||
if (fs.statSync(filePath).isDirectory()) {
|
||||
fs.rmSync(filePath, { recursive: true, force: true });
|
||||
} else {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
} catch (e) {
|
||||
writeLog(`清理文件失败: ${filePath} - ${e.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
writeLog(`[取消下载] 已清理游戏版本下载目录: ${versionDir}`);
|
||||
} else if (currentDownloadType === 'java' && currentDownloadPath) {
|
||||
// 清理Java下载
|
||||
const javaFile = currentDownloadPath;
|
||||
if (fs.existsSync(javaFile)) {
|
||||
try {
|
||||
fs.unlinkSync(javaFile);
|
||||
writeLog(`[取消下载] 已清理Java下载文件: ${javaFile}`);
|
||||
} catch (e) {
|
||||
writeLog(`清理Java文件失败: ${e.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('清理下载失败:', e);
|
||||
writeLog(`清理下载失败: ${e.message}`);
|
||||
}
|
||||
|
||||
// 重置所有下载状态
|
||||
isDownloading = false;
|
||||
isJavaDownloading = false;
|
||||
currentDownloadVersion = null;
|
||||
currentDownloadType = null;
|
||||
currentDownloadPath = null;
|
||||
activeDownloads = 0;
|
||||
totalDownloadFiles = 0;
|
||||
completedDownloadFiles = 0;
|
||||
}
|
||||
|
||||
// 通知渲染进程下载已取消
|
||||
for (const w of BrowserWindow.getAllWindows()) {
|
||||
w.webContents.send('download-cancelled');
|
||||
}
|
||||
|
||||
writeLog('[取消下载] 取消下载流程完成');
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
// 显示Java版本选择对话框
|
||||
ipcMain.handle('show-java-install-dialog', async (_, options) => {
|
||||
try {
|
||||
const { mcVersion, recommendedJava } = options;
|
||||
|
||||
// 构建版本选择列表,按推荐程度排序
|
||||
const versionPriority = {
|
||||
[recommendedJava]: 0,
|
||||
"17": 1,
|
||||
"21": 2,
|
||||
"25": 3,
|
||||
"8": 4
|
||||
};
|
||||
|
||||
const sortedVersions = [...JAVA_VERSIONS_AVAILABLE].sort((a, b) => {
|
||||
return (versionPriority[a] || 99) - (versionPriority[b] || 99);
|
||||
});
|
||||
|
||||
const result = await new Promise((resolve, reject) => {
|
||||
javaDownloadResolve = resolve;
|
||||
javaDownloadReject = reject;
|
||||
|
||||
// 发送消息给渲染进程显示对话框
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('show-java-install-dialog', {
|
||||
mcVersion,
|
||||
recommendedJava,
|
||||
availableVersions: sortedVersions
|
||||
});
|
||||
} else {
|
||||
reject(new Error('窗口未初始化'));
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (e) {
|
||||
writeLog(`Java安装对话框错误: ${e.message}`);
|
||||
return { success: false, cancelled: true };
|
||||
}
|
||||
});
|
||||
|
||||
// 用户在对话框中选择Java版本
|
||||
ipcMain.handle('select-java-version', async (_, data) => {
|
||||
const { selectedVersion, cancelled } = data;
|
||||
|
||||
if (cancelled) {
|
||||
if (javaDownloadResolve) {
|
||||
javaDownloadResolve({ success: false, cancelled: true });
|
||||
javaDownloadResolve = null;
|
||||
javaDownloadReject = null;
|
||||
}
|
||||
return { success: false, cancelled: true };
|
||||
}
|
||||
|
||||
if (selectedVersion && javaDownloadResolve) {
|
||||
javaDownloadResolve({ success: true, selectedVersion });
|
||||
javaDownloadResolve = null;
|
||||
javaDownloadReject = null;
|
||||
return { success: true, selectedVersion };
|
||||
}
|
||||
|
||||
return { success: false, cancelled: true };
|
||||
});
|
||||
|
||||
// 下载并安装Java运行时
|
||||
async function downloadAndInstallJava(javaVersion) {
|
||||
const runtimeDir = getJavaRuntimeDir(javaVersion);
|
||||
|
||||
writeLog(`[Java] 开始安装 Java ${javaVersion} 运行时`);
|
||||
|
||||
try {
|
||||
// 检查是否已存在
|
||||
if (fs.existsSync(runtimeDir)) {
|
||||
const exe = findJavaExecutable(runtimeDir);
|
||||
if (exe) {
|
||||
writeLog(`[Java] Java ${javaVersion} 已存在,无需下载`);
|
||||
return { success: true, javaPath: exe };
|
||||
}
|
||||
}
|
||||
|
||||
// 获取Java运行时的下载URL(支持自定义镜像源)
|
||||
const settingsPath = path.join(GAME_DIR, 'settings', 'java_mirror.json');
|
||||
let settings = { mirror: 'tsinghua', customUrl: '' };
|
||||
|
||||
try {
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
const downloadUrl = getJavaDownloadUrl(javaVersion, settings.mirror, settings.customUrl);
|
||||
writeLog(`[Java] 使用镜像源: ${settings.mirror}, URL: ${downloadUrl}`);
|
||||
|
||||
// 使用Electron webContents下载(更可靠)
|
||||
const result = await downloadJavaWithWebContents(javaVersion, downloadUrl);
|
||||
return result;
|
||||
|
||||
} catch (e) {
|
||||
writeLog(`[Java] Java ${javaVersion} 安装失败: ${e.message}`);
|
||||
|
||||
// 清理失败的安装
|
||||
if (fs.existsSync(runtimeDir)) {
|
||||
try {
|
||||
fs.rmSync(runtimeDir, { recursive: true, force: true });
|
||||
} catch (e2) {}
|
||||
}
|
||||
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
// 使用Electron webContents下载Java(推荐方法)
|
||||
async function downloadJavaWithWebContents(javaVersion, downloadUrl) {
|
||||
const runtimeName = JAVA_RUNTIME_MAP[javaVersion];
|
||||
const runtimeDir = getJavaRuntimeDir(javaVersion);
|
||||
const tempDir = path.join(CACHE_DIR, 'java_temp');
|
||||
const tempFile = path.join(tempDir, `java-${javaVersion}.zip`);
|
||||
|
||||
if (!fs.existsSync(tempDir)) {
|
||||
fs.mkdirSync(tempDir, { recursive: true });
|
||||
}
|
||||
|
||||
writeLog(`[Java] 使用Electron下载 Java ${javaVersion}: ${downloadUrl}`);
|
||||
|
||||
// 设置下载路径,以便取消时能正确删除
|
||||
currentDownloadVersion = `java-${javaVersion}`;
|
||||
currentDownloadType = 'java';
|
||||
currentDownloadPath = tempFile;
|
||||
shouldCancelDownload = false;
|
||||
isJavaDownloading = true;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const win = BrowserWindow.getFocusedWindow() || BrowserWindow.getAllWindows()[0];
|
||||
if (!win) {
|
||||
isJavaDownloading = false;
|
||||
currentDownloadVersion = null;
|
||||
currentDownloadType = null;
|
||||
currentDownloadPath = null;
|
||||
reject(new Error('没有可用的窗口'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用webContents.downloadURL
|
||||
win.webContents.downloadURL(downloadUrl, true);
|
||||
|
||||
// 监听下载开始
|
||||
win.webContents.session.once('will-download', (event, item) => {
|
||||
// 设置下载路径
|
||||
item.setSavePath(tempFile);
|
||||
|
||||
const totalBytes = item.getTotalBytes();
|
||||
writeLog(`[Java] 开始下载,总大小: ${totalBytes} bytes`);
|
||||
|
||||
// 监听进度
|
||||
item.on('updated', (event, state) => {
|
||||
if (state === 'progressing') {
|
||||
const received = item.getReceivedBytes();
|
||||
const percent = totalBytes > 0 ? Math.floor((received / totalBytes) * 100) : 0;
|
||||
|
||||
for (const w of BrowserWindow.getAllWindows()) {
|
||||
w.webContents.send('download-progress', {
|
||||
label: `Java ${javaVersion}`,
|
||||
percent: percent,
|
||||
bytesDownloaded: received,
|
||||
current: completedDownloadFiles,
|
||||
total: totalDownloadFiles
|
||||
});
|
||||
}
|
||||
|
||||
if (shouldCancelDownload) {
|
||||
item.cancel();
|
||||
writeLog(`[Java] 下载已取消`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 监听完成
|
||||
item.on('done', (event, state) => {
|
||||
if (state === 'cancelled') {
|
||||
isJavaDownloading = false;
|
||||
currentDownloadVersion = null;
|
||||
currentDownloadType = null;
|
||||
currentDownloadPath = null;
|
||||
reject(new Error('下载已取消'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (state === 'interrupted') {
|
||||
isJavaDownloading = false;
|
||||
currentDownloadVersion = null;
|
||||
currentDownloadType = null;
|
||||
currentDownloadPath = null;
|
||||
reject(new Error('下载中断'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (state === 'completed') {
|
||||
writeLog(`[Java] 下载完成,开始解压...`);
|
||||
|
||||
// 验证文件
|
||||
const fileStats = fs.statSync(tempFile);
|
||||
const fileSize = fileStats.size;
|
||||
|
||||
if (fileSize < 1024) {
|
||||
reject(new Error(`下载失败:文件太小 (${fileSize} bytes)`));
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查ZIP头
|
||||
const header = Buffer.alloc(4);
|
||||
const fd = fs.openSync(tempFile, 'r');
|
||||
fs.readSync(fd, header, 0, 4, 0);
|
||||
fs.closeSync(fd);
|
||||
|
||||
if (header.toString('hex') !== '504b0304') {
|
||||
reject(new Error('下载失败:文件不是有效的ZIP格式'));
|
||||
return;
|
||||
}
|
||||
|
||||
writeLog(`[Java] 文件验证通过,大小: ${fileSize} bytes`);
|
||||
|
||||
// 解压
|
||||
const parentDir = path.dirname(runtimeDir);
|
||||
if (!fs.existsSync(parentDir)) {
|
||||
fs.mkdirSync(parentDir, { recursive: true });
|
||||
}
|
||||
|
||||
if (fs.existsSync(runtimeDir)) {
|
||||
fs.rmSync(runtimeDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
try {
|
||||
const AdmZip = require('adm-zip');
|
||||
const zip = new AdmZip(tempFile);
|
||||
zip.extractAllTo(parentDir, false);
|
||||
writeLog(`[Java] 解压完成`);
|
||||
} catch (zipError) {
|
||||
reject(new Error(`解压失败: ${zipError.message}`));
|
||||
return;
|
||||
}
|
||||
|
||||
// 重命名目录
|
||||
const entries = fs.readdirSync(parentDir);
|
||||
let extractedDir = null;
|
||||
for (const entry of entries) {
|
||||
const entryPath = path.join(parentDir, entry);
|
||||
if (fs.statSync(entryPath).isDirectory()) {
|
||||
if (entry.includes('jdk') || entry.includes('jre')) {
|
||||
const binPath = path.join(entryPath, 'bin', 'java.exe');
|
||||
if (fs.existsSync(binPath)) {
|
||||
extractedDir = entryPath;
|
||||
writeLog(`[Java] 找到目录: ${entry}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (extractedDir && extractedDir !== runtimeDir) {
|
||||
if (fs.existsSync(runtimeDir)) {
|
||||
fs.rmSync(runtimeDir, { recursive: true, force: true });
|
||||
}
|
||||
fs.renameSync(extractedDir, runtimeDir);
|
||||
writeLog(`[Java] 已重命名为: ${runtimeName}`);
|
||||
}
|
||||
|
||||
// 清理临时文件
|
||||
try {
|
||||
fs.unlinkSync(tempFile);
|
||||
} catch (e) {}
|
||||
|
||||
// 验证安装
|
||||
const javaPath = findJavaExecutable(runtimeDir);
|
||||
if (!javaPath) {
|
||||
reject(new Error('Java运行时安装失败,未找到java.exe'));
|
||||
return;
|
||||
}
|
||||
|
||||
writeLog(`[Java] Java ${javaVersion} 安装成功: ${javaPath}`);
|
||||
isJavaDownloading = false;
|
||||
currentDownloadVersion = null;
|
||||
currentDownloadType = null;
|
||||
currentDownloadPath = null;
|
||||
resolve({ success: true, javaPath });
|
||||
} else {
|
||||
isJavaDownloading = false;
|
||||
currentDownloadVersion = null;
|
||||
currentDownloadType = null;
|
||||
currentDownloadPath = null;
|
||||
reject(new Error(`下载失败: ${state}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 超时处理(10分钟)
|
||||
setTimeout(() => {
|
||||
if (isJavaDownloading) {
|
||||
writeLog(`[Java] 下载超时`);
|
||||
isJavaDownloading = false;
|
||||
currentDownloadVersion = null;
|
||||
currentDownloadType = null;
|
||||
currentDownloadPath = null;
|
||||
reject(new Error('下载超时'));
|
||||
}
|
||||
}, 600000);
|
||||
}).catch((e) => {
|
||||
writeLog(`[Java] Java ${javaVersion} 安装失败: ${e.message}`);
|
||||
if (fs.existsSync(runtimeDir)) {
|
||||
try {
|
||||
fs.rmSync(runtimeDir, { recursive: true, force: true });
|
||||
} catch (e2) {}
|
||||
}
|
||||
return { success: false, error: e.message };
|
||||
});
|
||||
}
|
||||
|
||||
// 从Minecraft元数据获取Java运行时的下载信息
|
||||
async function getJavaRuntimeDownloadInfo(javaVersion) {
|
||||
try {
|
||||
const runtimeKey = JAVA_DOWNLOAD_KEYS[javaVersion];
|
||||
if (!runtimeKey) {
|
||||
writeLog(`[Java] 不支持的Java版本: ${javaVersion}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
writeLog(`[Java] 正在查找 Java ${javaVersion} (${runtimeKey}) 的下载信息...`);
|
||||
|
||||
// 使用清华大学开源软件镜像站(TUNA)的Adoptium镜像
|
||||
const runtimeInfo = {
|
||||
"8": {
|
||||
id: "jre-legacy",
|
||||
version: "8",
|
||||
url: "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/8/jre/x64/windows/OpenJDK8U-jre_x64_windows_hotspot_8u492b09.zip"
|
||||
},
|
||||
"17": {
|
||||
id: "java-runtime-gamma",
|
||||
version: "17",
|
||||
url: "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/17/jre/x64/windows/OpenJDK17U-jre_x64_windows_hotspot_17.0.19_10.zip"
|
||||
},
|
||||
"21": {
|
||||
id: "java-runtime-delta",
|
||||
version: "21",
|
||||
url: "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/21/jre/x64/windows/OpenJDK21U-jre_x64_windows_hotspot_21.0.11_10.zip"
|
||||
},
|
||||
"25": {
|
||||
id: "java-runtime-epsilon",
|
||||
version: "25",
|
||||
url: "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/25/jre/x64/windows/OpenJDK25U-jre_x64_windows_hotspot_25.0.3_9.zip"
|
||||
}
|
||||
};
|
||||
|
||||
const javaInfo = runtimeInfo[javaVersion];
|
||||
if (javaInfo) {
|
||||
writeLog(`[Java] ✓ 找到 Java ${javaVersion} 下载信息`);
|
||||
writeLog(`[Java] URL: ${javaInfo.url}`);
|
||||
return {
|
||||
url: javaInfo.url,
|
||||
name: javaInfo.id,
|
||||
sha1: null,
|
||||
size: null
|
||||
};
|
||||
}
|
||||
|
||||
writeLog(`[Java] ✗ 无法找到 Java ${javaVersion} 的下载信息`);
|
||||
return null;
|
||||
|
||||
} catch (e) {
|
||||
writeLog(`[Java] 获取Java运行时下载信息失败: ${e.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取MC版本推荐的Java版本
|
||||
function getRecommendedJavaVersion(mcVersion) {
|
||||
// Minecraft 1.16.5及以下: Java 8
|
||||
// Minecraft 1.17: Java 16/17
|
||||
// Minecraft 1.18-1.20.4: Java 17
|
||||
// Minecraft 1.20.5+: Java 21
|
||||
// Minecraft 1.21+: Java 21/25
|
||||
// Minecraft 26+: Java 21
|
||||
|
||||
const parts = mcVersion.split('.');
|
||||
let major = parseInt(parts[0], 10);
|
||||
let minor = parseInt(parts[1], 10);
|
||||
let patch = parts.length > 2 ? parseInt(parts[2], 10) : 0;
|
||||
|
||||
// 如果版本号是 "26" 这种没有点的格式
|
||||
if (isNaN(major)) major = 1;
|
||||
|
||||
// 处理像 "26" 这种格式(只有 major 没有 minor)
|
||||
if (isNaN(minor)) {
|
||||
// 26 及以上使用 Java 25
|
||||
if (major >= 26) return '25';
|
||||
// 21-25 使用 Java 21
|
||||
if (major >= 21) return '21';
|
||||
return '17';
|
||||
}
|
||||
|
||||
// 处理 1.x.x 的旧版本
|
||||
if (major === 1) {
|
||||
if (minor < 17) return '8';
|
||||
if (minor === 17) return '17';
|
||||
if (minor <= 20) return '17';
|
||||
if (minor === 21 && patch <= 4) return '21';
|
||||
if (minor === 21 && patch > 4) return '21';
|
||||
if (minor >= 22) return '21';
|
||||
return '17';
|
||||
}
|
||||
|
||||
// 处理 2.x.x 和更大的版本(21, 24, 26, ...)
|
||||
// 26 及以上使用 Java 25
|
||||
if (major >= 26) return '25';
|
||||
// 21-25 使用 Java 21
|
||||
if (major >= 21) return '21';
|
||||
return '17';
|
||||
}
|
||||
|
||||
function findJavaExecutableInDir(rootDir) {
|
||||
const stack = [rootDir];
|
||||
while (stack.length > 0) {
|
||||
const current = stack.pop();
|
||||
if (!fs.existsSync(current)) continue;
|
||||
const javaw = path.join(current, 'bin', 'javaw.exe');
|
||||
const java = path.join(current, 'bin', 'java.exe');
|
||||
if (fs.existsSync(javaw)) return javaw;
|
||||
if (fs.existsSync(java)) return java;
|
||||
try {
|
||||
const entries = fs.readdirSync(current, { withFileTypes: true });
|
||||
for (const e of entries) {
|
||||
if (e.isDirectory()) stack.push(path.join(current, e.name));
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function fetchText(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let finished = false;
|
||||
|
||||
const fallbackToHttps = (reason) => {
|
||||
if (finished) return;
|
||||
try {
|
||||
const https = require('https');
|
||||
const req2 = https.get(url, { timeout: 20000 }, res2 => {
|
||||
let body = '';
|
||||
res2.on('data', d => body += d);
|
||||
res2.on('end', () => { finished = true; resolve(body); });
|
||||
res2.on('error', err2 => { if (!finished) { finished = true; reject(err2); } });
|
||||
});
|
||||
req2.on('timeout', () => { req2.destroy(); if (!finished) { finished = true; reject('超时'); } });
|
||||
req2.on('error', err2 => { if (!finished) { finished = true; reject(err2); } });
|
||||
} catch (e2) {
|
||||
if (!finished) { finished = true; reject(e2); }
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const req = net.request(url);
|
||||
const to = setTimeout(() => { try { req.abort(); } catch {} ; fallbackToHttps('timeout'); }, 20000);
|
||||
|
||||
req.on('response', res => {
|
||||
clearTimeout(to);
|
||||
let body = '';
|
||||
res.on('data', d => body += d);
|
||||
res.on('end', () => { if (!finished) { finished = true; resolve(body); } });
|
||||
res.on('error', err => { if (!finished) fallbackToHttps(err); });
|
||||
});
|
||||
|
||||
req.on('error', err => { if (!finished) fallbackToHttps(err); });
|
||||
req.end();
|
||||
} catch (e) {
|
||||
fallbackToHttps(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let MAX_CONCURRENT = 128;
|
||||
let downloadQueue = [];
|
||||
let activeDownloads = 0;
|
||||
let totalDownloadFiles = 0;
|
||||
let completedDownloadFiles = 0;
|
||||
let currentDownloadSpeed = 0;
|
||||
|
||||
async function downloadWithPool(url, dest, label) {
|
||||
return new Promise((resolve, reject) => {
|
||||
downloadQueue.push({ url, dest, label, resolve, reject });
|
||||
processDownloadQueue();
|
||||
});
|
||||
}
|
||||
|
||||
async function processDownloadQueue() {
|
||||
while (downloadQueue.length > 0 && activeDownloads < MAX_CONCURRENT && !shouldCancelDownload) {
|
||||
const task = downloadQueue.shift();
|
||||
activeDownloads++;
|
||||
downloadFileInternal(task.url, task.dest, task.label)
|
||||
.then(() => {
|
||||
task.resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
task.reject(err);
|
||||
})
|
||||
.finally(() => {
|
||||
activeDownloads--;
|
||||
completedDownloadFiles++;
|
||||
processDownloadQueue();
|
||||
});
|
||||
}
|
||||
|
||||
// 如果取消下载,拒绝队列中所有剩余任务
|
||||
if (shouldCancelDownload) {
|
||||
while (downloadQueue.length > 0) {
|
||||
const task = downloadQueue.shift();
|
||||
task.reject(new Error('下载已取消'));
|
||||
}
|
||||
// 通知渲染进程下载已取消
|
||||
for (const w of BrowserWindow.getAllWindows()) {
|
||||
w.webContents.send('download-cancelled');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadFile(url, dest, label) {
|
||||
if (fs.existsSync(dest)) {
|
||||
const stat = fs.statSync(dest);
|
||||
if (stat.size > 1024) {
|
||||
for (const w of BrowserWindow.getAllWindows()) {
|
||||
w.webContents.send('download-progress', {
|
||||
label,
|
||||
percent: 100,
|
||||
current: completedDownloadFiles,
|
||||
total: totalDownloadFiles
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
await downloadWithPool(url, dest, label);
|
||||
}
|
||||
|
||||
async function downloadFileInternal(url, dest, label) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const dir = path.dirname(dest);
|
||||
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
||||
|
||||
// 确保使用二进制模式写入
|
||||
const file = fs.createWriteStream(dest, { encoding: null });
|
||||
let loaded = 0;
|
||||
let total = 0;
|
||||
|
||||
const fallbackToHttps = (reason) => {
|
||||
writeLog(`[下载] ${label} HTTP请求失败: ${reason},尝试HTTPS`);
|
||||
try {
|
||||
const https = require('https');
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
||||
}
|
||||
};
|
||||
|
||||
const req2 = https.get(url, options, res2 => {
|
||||
// 处理重定向
|
||||
if (res2.statusCode >= 300 && res2.statusCode < 400 && res2.headers.location) {
|
||||
writeLog(`[下载] ${label} 重定向到: ${res2.headers.location}`);
|
||||
file.close();
|
||||
// 递归处理重定向
|
||||
downloadFileInternal(res2.headers.location, dest, label).then(resolve).catch(reject);
|
||||
return;
|
||||
}
|
||||
|
||||
total = parseInt(res2.headers['content-length'] || '0', 10);
|
||||
|
||||
// 检查内容类型
|
||||
const contentType = res2.headers['content-type'] || '';
|
||||
if (!contentType.includes('zip') && !contentType.includes('octet-stream')) {
|
||||
writeLog(`[下载] ${label} 警告: 内容类型是 ${contentType},可能不是ZIP文件`);
|
||||
}
|
||||
|
||||
writeLog(`[下载] ${label} 开始下载,大小: ${total} bytes`);
|
||||
|
||||
res2.on('data', d => {
|
||||
if (shouldCancelDownload) {
|
||||
req2.destroy();
|
||||
file.destroy();
|
||||
reject(new Error('下载已取消'));
|
||||
return;
|
||||
}
|
||||
loaded += d.length;
|
||||
file.write(d);
|
||||
const p = total > 0 ? Math.floor((loaded / total) * 100) : 0;
|
||||
for (const w of BrowserWindow.getAllWindows()) {
|
||||
w.webContents.send('download-progress', {
|
||||
label,
|
||||
percent: p,
|
||||
bytesDownloaded: loaded,
|
||||
current: completedDownloadFiles,
|
||||
total: totalDownloadFiles
|
||||
});
|
||||
}
|
||||
});
|
||||
res2.on('end', () => {
|
||||
file.end();
|
||||
writeLog(`[下载] ${label} 下载完成,实际大小: ${loaded} bytes`);
|
||||
resolve();
|
||||
});
|
||||
res2.on('error', err2 => {
|
||||
file.destroy();
|
||||
reject(err2);
|
||||
});
|
||||
});
|
||||
req2.on('timeout', () => { req2.destroy(); reject('超时'); });
|
||||
req2.on('error', err2 => {
|
||||
file.destroy();
|
||||
reject(err2);
|
||||
});
|
||||
} catch (e2) {
|
||||
file.destroy();
|
||||
reject(e2);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const req = net.request(url);
|
||||
const to = setTimeout(() => { try { req.abort(); } catch {} ; fallbackToHttps('timeout'); }, 60000);
|
||||
|
||||
// 设置User-Agent头
|
||||
req.setHeader('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
|
||||
|
||||
req.on('response', res => {
|
||||
clearTimeout(to);
|
||||
|
||||
// 处理重定向
|
||||
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||
writeLog(`[下载] ${label} 重定向到: ${res.headers.location}`);
|
||||
file.close();
|
||||
// 递归处理重定向
|
||||
downloadFileInternal(res.headers.location, dest, label).then(resolve).catch(reject);
|
||||
return;
|
||||
}
|
||||
|
||||
total = parseInt(res.headers['content-length'] || '0', 10);
|
||||
|
||||
// 检查内容类型
|
||||
const contentType = res.headers['content-type'] || '';
|
||||
if (!contentType.includes('zip') && !contentType.includes('octet-stream')) {
|
||||
writeLog(`[下载] ${label} 警告: 内容类型是 ${contentType},可能不是ZIP文件`);
|
||||
}
|
||||
|
||||
writeLog(`[下载] ${label} 开始下载,大小: ${total} bytes`);
|
||||
|
||||
res.on('data', d => {
|
||||
if (shouldCancelDownload) {
|
||||
try { req.abort(); } catch {}
|
||||
file.destroy();
|
||||
reject(new Error('下载已取消'));
|
||||
return;
|
||||
}
|
||||
loaded += d.length;
|
||||
file.write(d);
|
||||
const p = total > 0 ? Math.floor((loaded / total) * 100) : 0;
|
||||
for (const w of BrowserWindow.getAllWindows()) {
|
||||
w.webContents.send('download-progress', {
|
||||
label,
|
||||
percent: p,
|
||||
bytesDownloaded: loaded,
|
||||
current: completedDownloadFiles,
|
||||
total: totalDownloadFiles
|
||||
});
|
||||
}
|
||||
});
|
||||
res.on('end', () => {
|
||||
file.end();
|
||||
writeLog(`[下载] ${label} 下载完成,实际大小: ${loaded} bytes`);
|
||||
resolve();
|
||||
});
|
||||
res.on('error', err => {
|
||||
file.destroy();
|
||||
fallbackToHttps(err);
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', err => {
|
||||
file.destroy();
|
||||
fallbackToHttps(err);
|
||||
});
|
||||
|
||||
req.end();
|
||||
} catch (e) {
|
||||
file.destroy();
|
||||
fallbackToHttps(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 带超时的下载包装器,超时后会抛出错误让调用方尝试下一个源
|
||||
async function downloadFileInternalWithTimeout(url, dest, label, timeoutMs) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let settled = false;
|
||||
const timer = setTimeout(() => {
|
||||
if (!settled) {
|
||||
settled = true;
|
||||
reject(new Error(`连接超时 (${timeoutMs / 1000}秒)`));
|
||||
}
|
||||
}, timeoutMs);
|
||||
|
||||
downloadFileInternal(url, dest, label)
|
||||
.then(result => {
|
||||
if (!settled) {
|
||||
settled = true;
|
||||
clearTimeout(timer);
|
||||
resolve(result);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
if (!settled) {
|
||||
settled = true;
|
||||
clearTimeout(timer);
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function installVersion(versionId, maxConcurrent = 20, downloadPath = null) {
|
||||
const targetDir = downloadPath || path.join(GAME_DIR, 'versions', versionId);
|
||||
const versionDir = targetDir;
|
||||
const cancelFilePath = path.join(versionDir, 'CancelDownload.txt');
|
||||
if (fs.existsSync(cancelFilePath)) {
|
||||
try { fs.unlinkSync(cancelFilePath); } catch (e) {}
|
||||
}
|
||||
|
||||
// 重置取消标志
|
||||
shouldCancelDownload = false;
|
||||
isDownloading = true;
|
||||
currentDownloadVersion = versionId;
|
||||
currentDownloadType = 'game';
|
||||
currentDownloadPath = targetDir;
|
||||
writeLog(`开始下载:${versionId} 到 ${targetDir}`);
|
||||
|
||||
try {
|
||||
MAX_CONCURRENT = maxConcurrent;
|
||||
downloadQueue = [];
|
||||
activeDownloads = 0;
|
||||
totalDownloadFiles = 0;
|
||||
completedDownloadFiles = 0;
|
||||
|
||||
const versionDir = path.join(GAME_DIR, 'versions', versionId);
|
||||
if (!fs.existsSync(versionDir)) fs.mkdirSync(versionDir, { recursive: true });
|
||||
|
||||
const versionJsonPath = path.join(versionDir, versionId + '.json');
|
||||
const versionJarPath = path.join(versionDir, versionId + '.jar');
|
||||
|
||||
// 获取版本清单
|
||||
const manifest = JSON.parse(await fetchText('https://piston-meta.mojang.com/mc/game/version_manifest_v2.json'));
|
||||
const vInfo = manifest.versions.find(v => v.id === versionId);
|
||||
if (!vInfo) throw new Error('版本不存在: ' + versionId);
|
||||
|
||||
// 下载版本JSON
|
||||
if (!fs.existsSync(versionJsonPath)) {
|
||||
const vData = await fetchText(vInfo.url);
|
||||
fs.writeFileSync(versionJsonPath, vData);
|
||||
}
|
||||
|
||||
const versionData = JSON.parse(fs.readFileSync(versionJsonPath, 'utf8'));
|
||||
|
||||
// 下载客户端JAR
|
||||
if (!fs.existsSync(versionJarPath)) {
|
||||
await downloadFile(versionData.downloads.client.url, versionJarPath, '客户端');
|
||||
}
|
||||
|
||||
// 检查是否取消
|
||||
if (shouldCancelDownload) {
|
||||
writeLog(`下载取消:${versionId}`);
|
||||
throw new Error('下载已取消');
|
||||
}
|
||||
|
||||
// 下载资源索引
|
||||
const assetIndexPath = path.join(GAME_DIR, 'assets', 'indexes', versionData.assetIndex.id + '.json');
|
||||
if (!fs.existsSync(assetIndexPath)) {
|
||||
await downloadFile(versionData.assetIndex.url, assetIndexPath, '资源索引');
|
||||
}
|
||||
|
||||
// 检查是否取消
|
||||
if (shouldCancelDownload) {
|
||||
writeLog(`下载取消:${versionId}`);
|
||||
throw new Error('下载已取消');
|
||||
}
|
||||
|
||||
// 等待资源索引下载完成并读取
|
||||
let assetIndexData;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
try {
|
||||
assetIndexData = JSON.parse(fs.readFileSync(assetIndexPath, 'utf8'));
|
||||
break;
|
||||
} catch (e) {
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
}
|
||||
}
|
||||
if (!assetIndexData) throw new Error('无法读取资源索引');
|
||||
|
||||
// 收集所有需要下载的文件
|
||||
const toDownload = [];
|
||||
|
||||
// 资源文件
|
||||
for (const [name, obj] of Object.entries(assetIndexData.objects)) {
|
||||
const hash = obj.hash;
|
||||
const prefix = hash.substring(0, 2);
|
||||
const assetPath = path.join(GAME_DIR, 'assets', 'objects', prefix, hash);
|
||||
if (!fs.existsSync(assetPath)) {
|
||||
toDownload.push({
|
||||
url: `https://resources.download.minecraft.net/${prefix}/${hash}`,
|
||||
dest: assetPath,
|
||||
label: `资源: ${name}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 库文件
|
||||
for (const lib of versionData.libraries) {
|
||||
if (lib.downloads?.artifact) {
|
||||
const libPath = path.join(GAME_DIR, 'libraries', lib.downloads.artifact.path);
|
||||
if (!fs.existsSync(libPath)) {
|
||||
toDownload.push({
|
||||
url: lib.downloads.artifact.url,
|
||||
dest: libPath,
|
||||
label: `库: ${lib.name}`
|
||||
});
|
||||
}
|
||||
}
|
||||
if (lib.downloads?.classifiers) {
|
||||
for (const [cls, info] of Object.entries(lib.downloads.classifiers)) {
|
||||
if (cls.includes('natives-windows')) {
|
||||
const libPath = path.join(GAME_DIR, 'libraries', info.path);
|
||||
if (!fs.existsSync(libPath)) {
|
||||
toDownload.push({
|
||||
url: info.url,
|
||||
dest: libPath,
|
||||
label: `原生库: ${lib.name}`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
totalDownloadFiles = toDownload.length;
|
||||
completedDownloadFiles = 0;
|
||||
|
||||
// 并发下载所有文件
|
||||
const downloadPromises = toDownload.map(item => downloadFile(item.url, item.dest, item.label));
|
||||
await Promise.all(downloadPromises);
|
||||
|
||||
// 解压原生库
|
||||
const nativesDir = path.join(GAME_DIR, 'versions', versionId, 'natives');
|
||||
if (!fs.existsSync(nativesDir)) fs.mkdirSync(nativesDir, { recursive: true });
|
||||
|
||||
for (const lib of versionData.libraries) {
|
||||
if (lib.downloads?.classifiers) {
|
||||
for (const [cls, info] of Object.entries(lib.downloads.classifiers)) {
|
||||
if (cls.includes('natives-windows')) {
|
||||
const libPath = path.join(GAME_DIR, 'libraries', info.path);
|
||||
if (fs.existsSync(libPath)) {
|
||||
const AdmZip = require('adm-zip');
|
||||
const zip = new AdmZip(libPath);
|
||||
zip.extractAllTo(nativesDir, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldCancelDownload) {
|
||||
writeLog(`下载取消:${versionId}`);
|
||||
throw new Error('下载已取消');
|
||||
}
|
||||
|
||||
writeLog(`下载完成:${versionId}`);
|
||||
|
||||
// 更新版本最后启动时间
|
||||
updateVersionLastPlayed(versionId, targetDir);
|
||||
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
writeLog(`下载失败:${versionId} - ${e.message}`);
|
||||
return { success: false, error: e.message };
|
||||
} finally {
|
||||
isDownloading = false;
|
||||
currentDownloadVersion = null;
|
||||
currentDownloadType = null;
|
||||
currentDownloadPath = null;
|
||||
shouldCancelDownload = false;
|
||||
downloadQueue = [];
|
||||
activeDownloads = 0;
|
||||
totalDownloadFiles = 0;
|
||||
completedDownloadFiles = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新版本的最后启动时间
|
||||
function updateVersionLastPlayed(versionId, targetDir) {
|
||||
try {
|
||||
const versionDir = targetDir || path.join(GAME_DIR, 'versions', versionId);
|
||||
const gpclDir = path.join(versionDir, 'gpcl');
|
||||
const configPath = path.join(gpclDir, 'config.json');
|
||||
|
||||
let settings = {};
|
||||
|
||||
// 读取现有配置
|
||||
if (fs.existsSync(configPath)) {
|
||||
try {
|
||||
settings = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
} catch (e) {
|
||||
settings = {};
|
||||
}
|
||||
}
|
||||
|
||||
// 更新最后启动时间
|
||||
settings.lastPlayed = new Date().toISOString();
|
||||
|
||||
// 确保目录存在
|
||||
if (!fs.existsSync(gpclDir)) {
|
||||
fs.mkdirSync(gpclDir, { recursive: true });
|
||||
}
|
||||
|
||||
// 保存配置
|
||||
fs.writeFileSync(configPath, JSON.stringify(settings, null, 2), 'utf8');
|
||||
writeLog(`[版本] ${versionId} 最后启动时间已更新: ${settings.lastPlayed}`);
|
||||
|
||||
} catch (e) {
|
||||
writeLog(`[版本] 更新 ${versionId} 最后启动时间失败: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
ipcMain.handle('scan-versions', async (_, gameDir, silent = false) => {
|
||||
const targetDir = gameDir ? path.resolve(gameDir) : GAME_DIR;
|
||||
const versionsDir = path.join(targetDir, 'versions');
|
||||
if (!fs.existsSync(versionsDir)) {
|
||||
if (!silent) writeLog('扫描本地版本:未找到versions目录');
|
||||
return [];
|
||||
}
|
||||
const entries = fs.readdirSync(versionsDir, { withFileTypes: true });
|
||||
const versions = [];
|
||||
const skippedVersions = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
const versionDir = path.join(versionsDir, entry.name);
|
||||
try {
|
||||
const files = fs.readdirSync(versionDir);
|
||||
|
||||
// 检查是否只有一个 CancelDownload.txt 文件
|
||||
if (files.length === 1 && files[0] === 'CancelDownload.txt') {
|
||||
skippedVersions.push(entry.name);
|
||||
if (!silent) writeLog(`扫描本地版本:跳过空目录 "${entry.name}"(仅包含 CancelDownload.txt)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 查找所有 .json 文件
|
||||
const jsonFiles = files.filter(f => f.endsWith('.json'));
|
||||
|
||||
// 如果没有 .json 文件,跳过
|
||||
if (jsonFiles.length === 0) {
|
||||
skippedVersions.push(entry.name);
|
||||
if (!silent) writeLog(`扫描本地版本:跳过 "${entry.name}"(无 .json 文件)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 收集所有版本(包括原版和带模组加载器的版本)
|
||||
// 原版: 1.21.1 -> versionId = "1.21.1"
|
||||
// Forge: 1.21.1-forge -> versionId = "1.21.1-forge"
|
||||
// Fabric: 1.21.1-fabric -> versionId = "1.21.1-fabric"
|
||||
|
||||
const baseVersionId = entry.name; // 文件夹名
|
||||
const mcVersion = baseVersionId; // MC 版本号
|
||||
|
||||
// 总是添加原版
|
||||
if (jsonFiles.includes(`${baseVersionId}.json`)) {
|
||||
versions.push(baseVersionId);
|
||||
}
|
||||
|
||||
// 检查是否有模组加载器版本
|
||||
for (const jsonFile of jsonFiles) {
|
||||
const jsonVersionId = jsonFile.replace('.json', '');
|
||||
// 如果不是原版,且不在列表中,添加它
|
||||
if (jsonVersionId !== baseVersionId && !versions.includes(jsonVersionId)) {
|
||||
versions.push(jsonVersionId);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
writeLog(`扫描本地版本:读取目录 "${entry.name}" 失败 - ${e.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (skippedVersions.length > 0 && !silent) {
|
||||
writeLog(`扫描本地版本:跳过 ${skippedVersions.length} 个无效目录 - [${skippedVersions.join(', ')}]`);
|
||||
}
|
||||
if (!silent) writeLog(`扫描本地版本:共 ${versions.length} 个版本 - [${versions.join(', ')}]`);
|
||||
return versions;
|
||||
});
|
||||
|
||||
ipcMain.handle('get-version-manifest', async () => {
|
||||
try {
|
||||
if (!fs.existsSync(CACHE_DIR)) fs.mkdirSync(CACHE_DIR, { recursive: true });
|
||||
const cacheFile = path.join(CACHE_DIR, 'version_manifest.json');
|
||||
|
||||
let data;
|
||||
let fromCache = false;
|
||||
if (fs.existsSync(cacheFile)) {
|
||||
const stat = fs.statSync(cacheFile);
|
||||
const age = Date.now() - stat.mtimeMs;
|
||||
if (age < 10 * 60 * 1000) {
|
||||
data = fs.readFileSync(cacheFile, 'utf8');
|
||||
fromCache = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
try {
|
||||
data = await fetchText('https://piston-meta.mojang.com/mc/game/version_manifest_v2.json');
|
||||
fs.writeFileSync(cacheFile, data);
|
||||
writeLog('远程拉取版本列表:成功');
|
||||
} catch (e) {
|
||||
writeLog(`远程拉取版本列表:失败 - ${e.message}`);
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
writeLog(`读取本地缓存版本列表:成功`);
|
||||
}
|
||||
|
||||
const parsed = JSON.parse(data);
|
||||
// 返回所有类型的版本,包括正式版、快照版、旧测试版
|
||||
writeLog(`版本列表:共 ${parsed.versions.length} 个版本`);
|
||||
return parsed.versions;
|
||||
} catch (e) {
|
||||
writeLog(`版本列表解析失败:${e.message}`);
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('download-version', (event, versionId, maxConcurrent, downloadPath) => installVersion(versionId, maxConcurrent, downloadPath));
|
||||
|
||||
ipcMain.handle('download-with-modloader', (event, versionId, loaderType, loaderVersion, maxConcurrent) =>
|
||||
installVersionWithModLoader(versionId, loaderType, loaderVersion, maxConcurrent));
|
||||
|
||||
ipcMain.handle('get-version-config', (event, versionId) => readVersionConfig(versionId));
|
||||
ipcMain.handle('get-version-display-name', (event, versionId) => generateVersionDisplayName(versionId));
|
||||
ipcMain.handle('get-launch-display-name', (event, versionId) => generateLaunchDisplayName(versionId));
|
||||
|
||||
ipcMain.handle('delete-version', async (_, versionId) => {
|
||||
try {
|
||||
const versionDir = path.join(GAME_DIR, 'versions', versionId);
|
||||
|
||||
if (!fs.existsSync(versionDir)) {
|
||||
return { success: false, error: '版本不存在' };
|
||||
}
|
||||
|
||||
writeLog(`[删除版本] 开始删除版本: ${versionId}`);
|
||||
|
||||
fs.rmSync(versionDir, { recursive: true, force: true });
|
||||
|
||||
writeLog(`[删除版本] 版本 ${versionId} 删除成功`);
|
||||
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
writeLog(`[删除版本] 删除版本失败: ${e.message}`);
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-version-settings', async (_, versionId) => {
|
||||
try {
|
||||
const versionDir = path.join(GAME_DIR, 'versions', versionId);
|
||||
const gpclDir = path.join(versionDir, 'gpcl');
|
||||
const configPath = path.join(gpclDir, 'config.json');
|
||||
|
||||
if (!fs.existsSync(configPath)) {
|
||||
return { success: true, settings: null };
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(configPath, 'utf8');
|
||||
const settings = JSON.parse(content);
|
||||
|
||||
return { success: true, settings };
|
||||
} catch (e) {
|
||||
writeLog(`[版本设置] 读取版本 ${versionId} 设置失败: ${e.message}`);
|
||||
return { success: false, error: e.message, settings: null };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('save-version-settings', async (_, versionId, settings) => {
|
||||
try {
|
||||
const versionDir = path.join(GAME_DIR, 'versions', versionId);
|
||||
const gpclDir = path.join(versionDir, 'gpcl');
|
||||
|
||||
if (!fs.existsSync(gpclDir)) {
|
||||
fs.mkdirSync(gpclDir, { recursive: true });
|
||||
}
|
||||
|
||||
const configPath = path.join(gpclDir, 'config.json');
|
||||
|
||||
// 保存设置
|
||||
fs.writeFileSync(configPath, JSON.stringify(settings, null, 2), 'utf8');
|
||||
|
||||
writeLog(`[版本设置] 版本 ${versionId} 设置已保存`);
|
||||
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
writeLog(`[版本设置] 保存版本 ${versionId} 设置失败: ${e.message}`);
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-game-dir', () => {
|
||||
return GAME_DIR;
|
||||
});
|
||||
|
||||
ipcMain.handle('get-player-name', () => {
|
||||
try {
|
||||
const playerFile = path.join(app.getPath('userData'), 'gpcl', 'player.json');
|
||||
if (fs.existsSync(playerFile)) {
|
||||
const data = JSON.parse(fs.readFileSync(playerFile, 'utf8'));
|
||||
return data.name || 'GPCL_Player';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('读取玩家名失败:', e);
|
||||
}
|
||||
return 'GPCL_Player';
|
||||
});
|
||||
|
||||
ipcMain.handle('save-player-name', (_, name) => {
|
||||
try {
|
||||
const gpclDir = path.join(app.getPath('userData'), 'gpcl');
|
||||
if (!fs.existsSync(gpclDir)) fs.mkdirSync(gpclDir, { recursive: true });
|
||||
const playerFile = path.join(gpclDir, 'player.json');
|
||||
fs.writeFileSync(playerFile, JSON.stringify({ name: name || 'GPCL_Player' }));
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
console.error('保存玩家名失败:', e);
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('window-minimize', () => {
|
||||
if (mainWindow) mainWindow.minimize();
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
ipcMain.handle('window-focus', () => {
|
||||
if (mainWindow) {
|
||||
if (mainWindow.isMinimized()) {
|
||||
mainWindow.restore();
|
||||
}
|
||||
mainWindow.show();
|
||||
mainWindow.focus();
|
||||
}
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
ipcMain.handle('window-close', () => {
|
||||
if (mainWindow) mainWindow.close();
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
ipcMain.handle('select-directory', async () => {
|
||||
const result = await dialog.showOpenDialog(mainWindow, {
|
||||
properties: ['openDirectory']
|
||||
});
|
||||
if (result.canceled || result.filePaths.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return result.filePaths[0];
|
||||
});
|
||||
|
||||
// 检查指定Minecraft版本所需的Java是否已安装
|
||||
ipcMain.handle('check-java', async (_, versionOrJavaVersion) => {
|
||||
try {
|
||||
// 判断是Minecraft版本号还是Java版本号
|
||||
const isMcVersion = versionOrJavaVersion && versionOrJavaVersion.match(/^\d+\.\d+/);
|
||||
|
||||
if (isMcVersion) {
|
||||
// Minecraft版本,检查该版本需要的Java
|
||||
const versionId = versionOrJavaVersion;
|
||||
const jsonPath = path.join(GAME_DIR, 'versions', versionId, versionId + '.json');
|
||||
if (!fs.existsSync(jsonPath)) {
|
||||
return { success: false, error: '版本未下载' };
|
||||
}
|
||||
|
||||
const versionData = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
||||
const javaVersion = getJavaVersionFromVersionData(versionData);
|
||||
const result = await checkJavaInstalled(javaVersion);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
installed: result.installed,
|
||||
javaVersion: javaVersion,
|
||||
javaPath: result.javaPath
|
||||
};
|
||||
} else {
|
||||
// Java版本,直接检查该版本是否安装
|
||||
const javaVersion = versionOrJavaVersion;
|
||||
const result = await checkJavaInstalled(javaVersion);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
installed: result.installed,
|
||||
javaVersion: javaVersion,
|
||||
javaPath: result.javaPath
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
writeLog(`检查Java失败:${e.message}`);
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
});
|
||||
|
||||
// 解析 Minecraft 启动参数模板
|
||||
function parseMCArguments(template, vars) {
|
||||
let result = template;
|
||||
for (const [key, value] of Object.entries(vars)) {
|
||||
result = result.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 智能分割参数(处理引号内的空格)
|
||||
function splitArgs(str) {
|
||||
const args = [];
|
||||
let current = '';
|
||||
let inQuotes = false;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const c = str[i];
|
||||
if (c === '"') {
|
||||
inQuotes = !inQuotes;
|
||||
} else if (c === ' ' && !inQuotes) {
|
||||
if (current.length > 0) {
|
||||
args.push(current);
|
||||
current = '';
|
||||
}
|
||||
} else {
|
||||
current += c;
|
||||
}
|
||||
}
|
||||
if (current.length > 0) {
|
||||
args.push(current);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
// 检查库规则是否允许当前平台
|
||||
function checkLibraryRules(rules) {
|
||||
if (!rules || rules.length === 0) return true;
|
||||
let allowed = false;
|
||||
for (const rule of rules) {
|
||||
let match = true;
|
||||
if (rule.os) {
|
||||
const platform = os.platform();
|
||||
if (rule.os.name === 'windows' && platform !== 'win32') match = false;
|
||||
if (rule.os.name === 'linux' && platform !== 'linux') match = false;
|
||||
if (rule.os.name === 'osx' && platform !== 'darwin') match = false;
|
||||
}
|
||||
if (match) {
|
||||
allowed = (rule.action === 'allow');
|
||||
}
|
||||
}
|
||||
return allowed;
|
||||
}
|
||||
|
||||
// 构建 Minecraft 启动参数
|
||||
function buildLaunchArgs(vd, gameDir, versionId, username, windowMode, memoryMB, serverIp) {
|
||||
const cp = [];
|
||||
// 优先使用 downloads.client 的 jar 路径
|
||||
const clientJar = path.join(gameDir, 'versions', versionId, `${versionId}.jar`);
|
||||
if (fs.existsSync(clientJar)) {
|
||||
cp.push(clientJar);
|
||||
}
|
||||
// 添加库(带平台规则过滤)
|
||||
let missingLibs = 0;
|
||||
for (const lib of vd.libraries || []) {
|
||||
if (!checkLibraryRules(lib.rules)) continue;
|
||||
// 普通 artifact
|
||||
if (lib.downloads?.artifact?.path) {
|
||||
const libPath = path.join(gameDir, 'libraries', lib.downloads.artifact.path);
|
||||
if (fs.existsSync(libPath)) {
|
||||
cp.push(libPath);
|
||||
} else {
|
||||
missingLibs++;
|
||||
writeLog(`[启动] 库文件缺失: ${lib.downloads.artifact.path}`);
|
||||
}
|
||||
}
|
||||
// Natives(Windows 平台)
|
||||
if (lib.downloads?.classifiers?.['natives-windows']?.path) {
|
||||
const nativePath = path.join(gameDir, 'libraries', lib.downloads.classifiers['natives-windows'].path);
|
||||
if (fs.existsSync(nativePath)) {
|
||||
cp.push(nativePath);
|
||||
} else {
|
||||
missingLibs++;
|
||||
writeLog(`[启动] Native库缺失: ${lib.downloads.classifiers['natives-windows'].path}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (missingLibs > 0) {
|
||||
writeLog(`[启动] 警告: ${missingLibs} 个库文件缺失`);
|
||||
}
|
||||
|
||||
const nativesDir = path.join(gameDir, 'versions', versionId, 'natives');
|
||||
const assetsDir = path.join(gameDir, 'assets');
|
||||
|
||||
const jvmArgs = [
|
||||
`-Djava.library.path=${nativesDir}`,
|
||||
'-cp', cp.join(';'),
|
||||
];
|
||||
|
||||
// 添加内存参数
|
||||
if (memoryMB) {
|
||||
jvmArgs.unshift(`-Xmx${memoryMB}M`);
|
||||
writeLog(`[启动] 已设置最大内存: ${memoryMB}M`);
|
||||
} else {
|
||||
writeLog(`[启动] 未设置内存限制`);
|
||||
}
|
||||
|
||||
const vars = {
|
||||
auth_player_name: username,
|
||||
version_name: versionId,
|
||||
game_directory: gameDir,
|
||||
assets_root: assetsDir,
|
||||
assets_index_name: vd.assetIndex?.id || vd.assets || versionId,
|
||||
auth_uuid: '00000000000000000000000000000000',
|
||||
auth_access_token: 'offline',
|
||||
auth_session: 'offline',
|
||||
user_type: 'legacy',
|
||||
user_properties: '{}',
|
||||
version_type: 'GPCL'
|
||||
};
|
||||
|
||||
let gameArgs = [];
|
||||
|
||||
// 新版格式 (1.13+)
|
||||
if (vd.arguments?.game) {
|
||||
for (const arg of vd.arguments.game) {
|
||||
if (typeof arg === 'string') {
|
||||
gameArgs.push(parseMCArguments(arg, vars));
|
||||
}
|
||||
// 忽略条件参数(简化处理)
|
||||
}
|
||||
}
|
||||
// 旧版格式 (1.12.2 及以下)
|
||||
else if (vd.minecraftArguments) {
|
||||
const parsed = parseMCArguments(vd.minecraftArguments, vars);
|
||||
gameArgs = splitArgs(parsed);
|
||||
}
|
||||
// 兜底
|
||||
else {
|
||||
gameArgs = [
|
||||
'--username', username,
|
||||
'--version', versionId,
|
||||
'--gameDir', gameDir,
|
||||
'--assetsDir', assetsDir,
|
||||
'--assetIndex', vd.assetIndex?.id || vd.assets || versionId,
|
||||
'--accessToken', 'offline',
|
||||
'--userType', 'legacy'
|
||||
];
|
||||
}
|
||||
|
||||
// 根据窗口模式添加参数
|
||||
if (windowMode === 'fullscreen') {
|
||||
gameArgs.push('--fullscreen');
|
||||
writeLog(`[启动] 已添加全屏参数: --fullscreen`);
|
||||
} else if (windowMode === 'borderless') {
|
||||
// 无边框窗口模式:使用全屏但无边框的分辨率
|
||||
gameArgs.push('--fullscreen');
|
||||
gameArgs.push('--width');
|
||||
gameArgs.push('1920');
|
||||
gameArgs.push('--height');
|
||||
gameArgs.push('1080');
|
||||
writeLog(`[启动] 已添加无边框窗口参数: --fullscreen --width 1920 --height 1080`);
|
||||
} else if (windowMode === 'minimized') {
|
||||
// 最小化模式,不添加特殊参数,但会在游戏启动后自动最小化
|
||||
writeLog(`[启动] 已设置最小化模式`);
|
||||
}
|
||||
|
||||
// 如果有服务器IP,添加服务器连接参数
|
||||
if (serverIp && serverIp.trim()) {
|
||||
const ipParts = serverIp.trim().split(':');
|
||||
const serverAddress = ipParts[0];
|
||||
const serverPort = ipParts[1] || '25565';
|
||||
|
||||
gameArgs.push('--server');
|
||||
gameArgs.push(serverAddress);
|
||||
gameArgs.push('--port');
|
||||
gameArgs.push(serverPort);
|
||||
|
||||
writeLog(`[启动] 已添加服务器连接参数: --server ${serverAddress} --port ${serverPort}`);
|
||||
}
|
||||
|
||||
return { jvmArgs, gameArgs, mainClass: vd.mainClass };
|
||||
}
|
||||
|
||||
ipcMain.handle('launch-minecraft', async (_, opt) => {
|
||||
try {
|
||||
// 重置取消标志
|
||||
launchCancelFlag = false;
|
||||
|
||||
const { versionId, username, gameDir: rawGameDir, windowMode } = opt;
|
||||
// 使用传入的路径或默认的 GAME_DIR
|
||||
const gameDir = rawGameDir ? path.resolve(rawGameDir) : GAME_DIR;
|
||||
// 窗口模式:fullscreen, windowed, borderless
|
||||
const mode = windowMode || 'windowed';
|
||||
writeLog(`启动游戏:玩家 ${username},版本 ${versionId},目录: ${gameDir},窗口模式: ${mode}`);
|
||||
|
||||
// 检查是否已取消
|
||||
if (launchCancelFlag) {
|
||||
writeLog('[启动] 用户已取消启动');
|
||||
return { success: false, error: '启动已取消', cancelled: true };
|
||||
}
|
||||
|
||||
const jsonPath = path.join(gameDir, 'versions', versionId, `${versionId}.json`);
|
||||
if (!fs.existsSync(jsonPath)) {
|
||||
writeLog(`启动游戏失败:版本 ${versionId} 未下载`);
|
||||
return { success: false, error: '请先下载游戏' };
|
||||
}
|
||||
|
||||
const vd = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
||||
const javaVersion = getJavaVersionFromVersionData(vd);
|
||||
|
||||
// 检查是否已取消
|
||||
if (launchCancelFlag) {
|
||||
writeLog('[启动] 用户已取消启动');
|
||||
return { success: false, error: '启动已取消', cancelled: true };
|
||||
}
|
||||
|
||||
// 加载设置
|
||||
const settings = loadSettings();
|
||||
let memoryMB = null;
|
||||
let actualWindowMode = mode;
|
||||
let serverIp = null;
|
||||
|
||||
// 首先检查是否有版本特定设置
|
||||
try {
|
||||
const versionConfigPath = path.join(gameDir, 'versions', versionId, 'gpcl', 'config.json');
|
||||
if (fs.existsSync(versionConfigPath)) {
|
||||
const versionSettings = JSON.parse(fs.readFileSync(versionConfigPath, 'utf8'));
|
||||
|
||||
// 处理内存设置
|
||||
if (versionSettings.memory && versionSettings.memory !== 'global') {
|
||||
const memoryVal = parseFloat(versionSettings.memory);
|
||||
if (!isNaN(memoryVal)) {
|
||||
if (versionSettings.memory === '0.5') {
|
||||
memoryMB = 512; // 0.5GB = 512MB
|
||||
} else {
|
||||
memoryMB = Math.floor(memoryVal * 1024);
|
||||
}
|
||||
writeLog(`[启动] 使用版本内存设置: ${memoryMB}M`);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理窗口模式设置
|
||||
if (versionSettings.windowMode && versionSettings.windowMode !== 'global') {
|
||||
actualWindowMode = versionSettings.windowMode;
|
||||
writeLog(`[启动] 使用版本窗口模式: ${actualWindowMode}`);
|
||||
}
|
||||
|
||||
// 处理启动模式和服务器IP
|
||||
if (versionSettings.startupMode === 'join' && versionSettings.serverIp) {
|
||||
serverIp = versionSettings.serverIp;
|
||||
writeLog(`[启动] 使用版本启动模式: 加入服务器 ${serverIp}`);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
writeLog(`[启动] 读取版本设置失败: ${e.message}`);
|
||||
}
|
||||
|
||||
// 如果没有版本特定设置,使用全局设置
|
||||
if (memoryMB === null && settings.game?.memory) {
|
||||
const globalMemory = parseFloat(settings.game.memory);
|
||||
if (!isNaN(globalMemory)) {
|
||||
memoryMB = Math.floor(globalMemory * 1024);
|
||||
writeLog(`[启动] 使用全局内存设置: ${memoryMB}M`);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否已取消
|
||||
if (launchCancelFlag) {
|
||||
writeLog('[启动] 用户已取消启动');
|
||||
return { success: false, error: '启动已取消', cancelled: true };
|
||||
}
|
||||
|
||||
// 检查Java是否已安装
|
||||
let java = await ensureJavaRuntime(javaVersion);
|
||||
|
||||
// 检查是否已取消(在Java检查后)
|
||||
if (launchCancelFlag) {
|
||||
writeLog('[启动] 用户已取消启动');
|
||||
return { success: false, error: '启动已取消', cancelled: true };
|
||||
}
|
||||
|
||||
// 如果Java未安装,自动下载
|
||||
if (!java) {
|
||||
writeLog(`[Java] Java ${javaVersion} 未安装,自动开始下载`);
|
||||
|
||||
// 发送通知给渲染进程
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('java-auto-download-start', {
|
||||
javaVersion: javaVersion,
|
||||
mcVersion: versionId
|
||||
});
|
||||
}
|
||||
|
||||
// 自动下载并安装Java(期间检查取消标志)
|
||||
const installResult = await downloadAndInstallJava(javaVersion);
|
||||
|
||||
// 检查是否已取消(在Java下载后)
|
||||
if (launchCancelFlag) {
|
||||
writeLog('[启动] 用户已取消启动(Java下载完成后)');
|
||||
return { success: false, error: '启动已取消', cancelled: true };
|
||||
}
|
||||
|
||||
if (installResult.success) {
|
||||
java = installResult.javaPath;
|
||||
writeLog(`[Java] Java ${javaVersion} 安装成功: ${java}`);
|
||||
|
||||
// 发送安装完成通知
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('java-auto-download-complete', {
|
||||
javaVersion: javaVersion,
|
||||
javaPath: java
|
||||
});
|
||||
}
|
||||
} else {
|
||||
writeLog(`[Java] Java ${javaVersion} 安装失败: ${installResult.error}`);
|
||||
return {
|
||||
success: false,
|
||||
error: `Java ${javaVersion} 安装失败: ${installResult.error}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 最终检查是否已取消(在启动游戏前)
|
||||
if (launchCancelFlag) {
|
||||
writeLog('[启动] 用户已取消启动(即将启动游戏前)');
|
||||
return { success: false, error: '启动已取消', cancelled: true };
|
||||
}
|
||||
|
||||
const { jvmArgs, gameArgs, mainClass } = buildLaunchArgs(vd, gameDir, versionId, username, actualWindowMode, memoryMB, serverIp);
|
||||
const args = [...jvmArgs, mainClass, ...gameArgs];
|
||||
|
||||
writeLog(`[启动] Java: ${java}`);
|
||||
writeLog(`[启动] 主类: ${mainClass}`);
|
||||
writeLog(`[启动] 窗口模式: ${actualWindowMode}`);
|
||||
writeLog(`[启动] 参数数: ${args.length}`);
|
||||
writeLog(`[启动] JVM参数: ${JSON.stringify(jvmArgs)}`);
|
||||
writeLog(`[启动] 游戏参数: ${JSON.stringify(gameArgs.slice(0, 10))}...`);
|
||||
writeLog(`[启动] Classpath条目数: ${jvmArgs[2].split(';').length}`);
|
||||
writeLog(`[启动] Classpath总长度: ${jvmArgs[2].length} 字符`);
|
||||
|
||||
let proc;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
try {
|
||||
proc = spawn(java, args, { cwd: gameDir, detached: false, windowsHide: false });
|
||||
writeLog(`启动游戏成功:PID ${proc.pid}`);
|
||||
break;
|
||||
} catch (e) {
|
||||
if (e.code === 'EBUSY' && i < 2) {
|
||||
await new Promise(r => setTimeout(r, 1500));
|
||||
continue;
|
||||
}
|
||||
writeLog(`启动游戏失败:${e.message}`);
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
// 检测游戏窗口创建成功
|
||||
await detectGameWindow(proc.pid);
|
||||
|
||||
// 捕获输出用于调试
|
||||
let stdoutData = '';
|
||||
let stderrData = '';
|
||||
proc.stdout?.on('data', d => {
|
||||
const text = d.toString();
|
||||
stdoutData += text;
|
||||
mainWindow.webContents.send('game-log', text);
|
||||
|
||||
// 检测Minecraft窗口创建成功的标志
|
||||
if (text.includes('LWJGL Version') || text.includes('OpenGL version') || text.includes('Window')) {
|
||||
writeLog(`[启动] 检测到窗口初始化日志`);
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('game-window-created', { pid: proc.pid });
|
||||
}
|
||||
}
|
||||
});
|
||||
proc.stderr?.on('data', d => {
|
||||
const text = d.toString();
|
||||
stderrData += text;
|
||||
mainWindow.webContents.send('game-log', text);
|
||||
|
||||
// 检测Minecraft窗口创建成功的标志
|
||||
if (text.includes('LWJGL Version') || text.includes('OpenGL version') || text.includes('Window')) {
|
||||
writeLog(`[启动] 检测到窗口初始化日志`);
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('game-window-created', { pid: proc.pid });
|
||||
}
|
||||
}
|
||||
});
|
||||
proc.on('error', (err) => {
|
||||
writeLog(`[启动] 进程错误: ${err.message}`);
|
||||
});
|
||||
proc.on('exit', (code) => {
|
||||
writeLog(`[启动] 进程退出,代码: ${code}`);
|
||||
if (code !== 0 && stderrData) {
|
||||
writeLog(`[启动] 错误输出: ${stderrData.slice(0, 1000)}`);
|
||||
}
|
||||
});
|
||||
|
||||
return { success: true, pid: proc.pid };
|
||||
} catch (e) {
|
||||
writeLog(`启动游戏异常:${e.message}`);
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
});
|
||||
|
||||
// 检测游戏窗口创建成功
|
||||
async function detectGameWindow(pid) {
|
||||
writeLog(`[启动] 开始检测游戏窗口,PID: ${pid}`);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
<<<<<<< Updated upstream
|
||||
// 设置超时(最多等待5秒,比原来快3秒)
|
||||
const timeout = setTimeout(() => {
|
||||
writeLog(`[启动] 游戏窗口检测超时,继续启动流程`);
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('game-window-created', { pid: pid, timeout: true });
|
||||
}
|
||||
resolve();
|
||||
}, 5000);
|
||||
|
||||
// 使用更快速的检测方式
|
||||
let checkCount = 0;
|
||||
const maxChecks = 50; // 最多检查50次(5秒)
|
||||
let detected = false;
|
||||
|
||||
// 首先立即发送一次,不等待检测完成
|
||||
writeLog(`[启动] 快速发送创建事件(立即检测)`);
|
||||
setTimeout(() => {
|
||||
if (!detected) {
|
||||
detected = true;
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('game-window-created', { pid: pid, fast: true });
|
||||
}
|
||||
}
|
||||
}, 1500); // 1.5秒后直接认为启动成功
|
||||
|
||||
const checkInterval = setInterval(() => {
|
||||
checkCount++;
|
||||
|
||||
if (detected) {
|
||||
clearInterval(checkInterval);
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
return;
|
||||
=======
|
||||
let resolved = false;
|
||||
const done = () => {
|
||||
if (resolved) return;
|
||||
resolved = true;
|
||||
resolve();
|
||||
};
|
||||
|
||||
const cancelCheck = setInterval(() => {
|
||||
if (launchCancelFlag) {
|
||||
writeLog(`[启动] 窗口检测期间检测到取消标志,立即退出`);
|
||||
clearInterval(cancelCheck);
|
||||
try { detector.kill(); } catch {}
|
||||
done();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
writeLog(`[启动] 游戏窗口检测超时(8秒),继续启动流程`);
|
||||
clearInterval(cancelCheck);
|
||||
try { detector.kill(); } catch {}
|
||||
if (mainWindow && !launchCancelFlag) {
|
||||
mainWindow.webContents.send('game-window-created', { pid, timeout: true });
|
||||
>>>>>>> Stashed changes
|
||||
}
|
||||
done();
|
||||
}, 8000);
|
||||
|
||||
const script = `
|
||||
$deadline = (Get-Date).AddSeconds(7)
|
||||
while ((Get-Date) -lt $deadline) {
|
||||
try {
|
||||
$p = Get-Process -Id ${pid} -ErrorAction Stop
|
||||
if ($p.MainWindowHandle -ne 0) { Write-Output "FOUND"; exit 0 }
|
||||
} catch { Write-Output "GONE"; exit 1 }
|
||||
Start-Sleep -Milliseconds 150
|
||||
}
|
||||
Write-Output "TIMEOUT"; exit 2
|
||||
`;
|
||||
const detector = spawn('powershell', ['-NoProfile', '-Command', script], { windowsHide: true });
|
||||
let output = '';
|
||||
|
||||
detector.stdout.on('data', d => { output += d.toString(); });
|
||||
detector.stderr.on('data', d => { output += d.toString(); });
|
||||
|
||||
detector.on('close', (code) => {
|
||||
clearInterval(cancelCheck);
|
||||
clearTimeout(timeout);
|
||||
|
||||
if (launchCancelFlag) { done(); return; }
|
||||
|
||||
if (output.includes('FOUND')) {
|
||||
writeLog(`[启动] 检测到游戏窗口(MainWindowHandle != 0)`);
|
||||
} else if (output.includes('GONE')) {
|
||||
writeLog(`[启动] 进程已退出`);
|
||||
} else {
|
||||
writeLog(`[启动] 窗口检测结束,code=${code},output=${output.trim()}`);
|
||||
}
|
||||
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('game-window-created', { pid, windowDetected: output.includes('FOUND') });
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
detector.on('error', (err) => {
|
||||
clearInterval(cancelCheck);
|
||||
clearTimeout(timeout);
|
||||
writeLog(`[启动] 窗口检测器启动失败: ${err.message}`);
|
||||
if (mainWindow && !launchCancelFlag) {
|
||||
mainWindow.webContents.send('game-window-created', { pid, error: true });
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// IPC: 下载并安装Java运行时
|
||||
ipcMain.handle('install-java', async (_, javaVersion) => {
|
||||
try {
|
||||
writeLog(`[Java] 开始安装 Java ${javaVersion}`);
|
||||
|
||||
// 发送下载开始消息
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('java-download-started', { javaVersion });
|
||||
}
|
||||
|
||||
const result = await downloadAndInstallJava(javaVersion);
|
||||
|
||||
if (result.success) {
|
||||
writeLog(`[Java] Java ${javaVersion} 安装成功: ${result.javaPath}`);
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('java-download-completed', {
|
||||
javaVersion,
|
||||
javaPath: result.javaPath
|
||||
});
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
writeLog(`[Java] Java ${javaVersion} 安装失败: ${result.error}`);
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('java-download-failed', {
|
||||
javaVersion,
|
||||
error: result.error
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} catch (e) {
|
||||
writeLog(`[Java] 安装Java异常: ${e.message}`);
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('java-download-failed', {
|
||||
javaVersion,
|
||||
error: e.message
|
||||
});
|
||||
}
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
});
|
||||
|
||||
let currentSettings = null;
|
||||
|
||||
ipcMain.handle('get-settings', () => {
|
||||
if (!currentSettings) {
|
||||
currentSettings = loadSettings();
|
||||
}
|
||||
return currentSettings;
|
||||
});
|
||||
|
||||
ipcMain.handle('save-settings', (_, settings) => {
|
||||
currentSettings = mergeWithDefaults(settings);
|
||||
const success = saveSettings(currentSettings);
|
||||
return { success, settings: currentSettings };
|
||||
});
|
||||
|
||||
ipcMain.handle('reset-settings', () => {
|
||||
currentSettings = JSON.parse(JSON.stringify(DEFAULT_SETTINGS));
|
||||
const success = saveSettings(currentSettings);
|
||||
return { success, settings: currentSettings };
|
||||
});
|
||||
|
||||
ipcMain.handle('restart-app', () => {
|
||||
writeLog('[重启] 正在重启应用...');
|
||||
app.relaunch();
|
||||
app.exit();
|
||||
});
|
||||
|
||||
ipcMain.handle('set-developer-mode', (_, enabled) => {
|
||||
isDeveloperMode = !!enabled;
|
||||
writeLog('[开发者模式] ' + (isDeveloperMode ? '已启用' : '已禁用'));
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
let activeAnimWindow = null;
|
||||
|
||||
function cleanupAnimTempDir(dir) {
|
||||
try {
|
||||
if (fs.existsSync(dir)) {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
ipcMain.handle('play-startup-animation', async () => {
|
||||
if (activeAnimWindow && !activeAnimWindow.isDestroyed()) {
|
||||
try { activeAnimWindow.close(); } catch (e) {}
|
||||
activeAnimWindow = null;
|
||||
}
|
||||
|
||||
writeLog('[启动动画] 开始播放启动动画');
|
||||
|
||||
const iconDir = path.join(__dirname, 'static', 'icon');
|
||||
const tempAnimDir = path.join(app.getPath('temp'), 'gpcl-anim-' + Date.now());
|
||||
try { fs.mkdirSync(tempAnimDir, { recursive: true }); } catch (e) {}
|
||||
|
||||
var imageNames = ['caellab.png', 'gamets.png', 'gamets.ico', 'GPCL.png'];
|
||||
for (var _i = 0; _i < imageNames.length; _i++) {
|
||||
var src = path.join(iconDir, imageNames[_i]);
|
||||
if (fs.existsSync(src)) {
|
||||
try { fs.copyFileSync(src, path.join(tempAnimDir, imageNames[_i])); } catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
var hasCaelab = fs.existsSync(path.join(tempAnimDir, 'caellab.png'));
|
||||
var hasGamets = fs.existsSync(path.join(tempAnimDir, 'gamets.png')) || fs.existsSync(path.join(tempAnimDir, 'gamets.ico'));
|
||||
var hasGpcl = fs.existsSync(path.join(tempAnimDir, 'GPCL.png'));
|
||||
var gametsFile = fs.existsSync(path.join(tempAnimDir, 'gamets.png')) ? 'gamets.png' : 'gamets.ico';
|
||||
|
||||
writeLog('[启动动画] 资源: caellab=' + hasCaelab + ' gamets=' + hasGamets + ' gpcl=' + hasGpcl);
|
||||
|
||||
var ANIM_DURATION = 30;
|
||||
|
||||
var htmlContent = '<!DOCTYPE html>\n<html><head><meta charset="utf-8"><style>\n' +
|
||||
'*{margin:0;padding:0;box-sizing:border-box}\n' +
|
||||
'html,body{width:100%;height:100%;overflow:hidden;background:#0a0a0a}\n' +
|
||||
'canvas{position:fixed;top:0;left:0;width:100%;height:100%;z-index:1}\n' +
|
||||
'.scene{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none;z-index:10;opacity:0}\n' +
|
||||
'#welcome-txt{text-align:center;font-family:"Microsoft YaHei","Segoe UI",sans-serif}\n' +
|
||||
'#welcome-txt h1{font-size:56px;font-weight:700;color:#fff;letter-spacing:3px;text-shadow:0 0 25px rgba(91,135,49,0.6),0 0 50px rgba(91,135,49,0.2);margin-bottom:16px}\n' +
|
||||
'#welcome-txt p{font-size:20px;color:rgba(180,180,180,0.7);letter-spacing:5px}\n' +
|
||||
'.logo-row{display:flex;flex-direction:row;align-items:center;justify-content:center}\n' +
|
||||
'#logo-caellab img{max-height:130px;max-width:300px;object-fit:contain;filter:drop-shadow(0 0 18px rgba(91,135,49,0.35))}\n' +
|
||||
'#logo-both .logo-row{gap:50px}\n' +
|
||||
'#logo-both img{max-height:110px;max-width:240px;object-fit:contain}\n' +
|
||||
'#logo-both .caellab-img{filter:drop-shadow(0 0 18px rgba(91,135,49,0.35))}\n' +
|
||||
'#logo-both .gamets-img{filter:drop-shadow(0 0 14px rgba(255,255,255,0.25))}\n' +
|
||||
'#logo-gpcl img{max-height:170px;max-width:340px;object-fit:contain;filter:drop-shadow(0 0 25px rgba(91,135,49,0.4))}\n' +
|
||||
'.logo-text{text-align:center;font-family:"Microsoft YaHei","Segoe UI",sans-serif;margin-top:18px}\n' +
|
||||
'.logo-text-bottom{position:absolute;bottom:40px;left:0;width:100%;margin-top:0}\n' +
|
||||
'.logo-title{font-size:20px;color:rgba(200,200,200,0.85);letter-spacing:4px;margin-bottom:6px}\n' +
|
||||
'.footer-link{font-size:14px;color:rgba(91,135,49,0.7);text-decoration:underline;pointer-events:auto;cursor:pointer}\n' +
|
||||
'.copyright{font-size:13px;color:rgba(160,160,160,0.6);letter-spacing:1px;margin-bottom:6px}\n' +
|
||||
'.license-text{font-size:12px;color:rgba(140,140,140,0.5);margin:0}\n' +
|
||||
'.license-text .footer-link{font-size:12px}\n' +
|
||||
'.disclaimer{font-size:12px;color:rgba(120,120,120,0.45);max-width:600px;line-height:1.6;margin:40px auto 0;text-align:center}\n' +
|
||||
'#sweep-line{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:25;opacity:0}\n' +
|
||||
'#sweep-line .band{position:absolute;top:0;height:100%;width:100px;background:linear-gradient(90deg,transparent,rgba(91,135,49,0.1),rgba(127,255,0,0.12),rgba(91,135,49,0.1),transparent)}\n' +
|
||||
'#fade{position:fixed;top:0;left:0;width:100%;height:100%;background:#0a0a0a;opacity:0;pointer-events:none;z-index:30;transition:opacity 1.5s ease-in}\n' +
|
||||
'#fade.show{opacity:1}\n' +
|
||||
'#flash{position:fixed;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;pointer-events:none;z-index:28}\n' +
|
||||
'</style></head><body>\n' +
|
||||
'<canvas id="c"></canvas>\n' +
|
||||
'<div class="scene" id="welcome"><div id="welcome-txt"><h1>Minecraft\u6B22\u8FCE\u4F60\uFF01</h1><p>By GPCL And AllMinecraftPlayer</p></div></div>\n' +
|
||||
'<div class="scene" id="logo-caellab"><div class="logo-row"><img src="caellab.png" alt="CaelLab"></div><div class="logo-text"><div class="logo-title">\u865A\u821F\u5B9E\u9A8C\u5BA4</div><a href="https://www.caellab.com" target="_blank" class="footer-link">caellab.com</a></div></div>\n' +
|
||||
'<div class="scene" id="logo-both"><div class="logo-row"><img class="caellab-img" src="caellab.png" alt="CaelLab"><img class="gamets-img" src="' + gametsFile + '" alt="GameTS"></div><div class="logo-text logo-text-bottom"><div class="copyright">Copyright \u00A9 2026 Yunyun(\u4E91\u4E91) By \u865A\u821F\u5B9E\u9A8C\u5BA4(CaelLab) / CaelLabGameTS</div><p class="license-text">Licensed under <a href="https://www.caellab.com/license/bysa-code" target="_blank" class="footer-link">CaelLab BY-SA Code License</a>. Open Source on <a href="https://git.caellab.com/yunyun/GoodPlanCraftLauncher" target="_blank" class="footer-link">GitCaelLab</a>.</p></div></div>\n' +
|
||||
'<div class="scene" id="logo-gpcl"><img src="GPCL.png" alt="GPCL"><div class="logo-text logo-text-bottom"><div class="disclaimer">GoodPlanCraftLauncher \u4E0E Mojang Studios \u53CA Microsoft\u3001Xbox \u65E0\u5B98\u65B9\u5173\u8054\u3002Minecraft \u4E3A Mojang Studios \u5546\u6807\u3002</div></div></div>\n' +
|
||||
'<div id="sweep-line"><div class="band" id="sweep-band"></div></div>\n' +
|
||||
'<div id="flash"></div><div id="fade"></div>\n' +
|
||||
'<script>\n' +
|
||||
'(function(){\n' +
|
||||
'var canvas=document.getElementById("c"),ctx=canvas.getContext("2d");\n' +
|
||||
'var W,H,dpr=window.devicePixelRatio||1;\n' +
|
||||
'function resize(){W=window.innerWidth;H=window.innerHeight;canvas.width=W*dpr;canvas.height=H*dpr;ctx.setTransform(dpr,0,0,dpr,0,0)}\n' +
|
||||
'resize();window.addEventListener("resize",resize);\n' +
|
||||
'var startTime=performance.now();\n' +
|
||||
'var DUR=' + ANIM_DURATION + ';\n' +
|
||||
'function rand(a,b){return a+Math.random()*(b-a)}\n' +
|
||||
'function easeOut(t){return 1-Math.pow(1-t,3)}\n' +
|
||||
'function easeInOut(t){return t<.5?4*t*t*t:1-Math.pow(-2*t+2,3)/2}\n' +
|
||||
'function clamp(v,a,b){return v<a?a:v>b?b:v}\n' +
|
||||
'function lerp(a,b,t){return a+(b-a)*t}\n' +
|
||||
'var BS=24;\n' +
|
||||
'var MC_COLORS=["#8B6B3D","#6B6B6B","#5B8731","#3E6B1F","#6B4226","#5A5A5A","#7B5B2D","#4A4A4A"];\n' +
|
||||
'var terrCols=Math.ceil(W/BS)+1;\n' +
|
||||
'var terrH=[];\n' +
|
||||
'for(var i=0;i<terrCols;i++){terrH.push(2+~~rand(0,4))}\n' +
|
||||
'var parts=[];\n' +
|
||||
'for(var i=0;i<160;i++){\n' +
|
||||
' var a=rand(0,6.283),sp=rand(0.12,0.6);\n' +
|
||||
' parts.push({x:rand(0,W),y:rand(0,H),vx:Math.cos(a)*sp,vy:Math.sin(a)*sp,\n' +
|
||||
' sz:rand(8,18),c:MC_COLORS[~~rand(0,MC_COLORS.length)],a:0,ta:rand(.08,.25),\n' +
|
||||
' rot:rand(0,6.28),rs:rand(-.012,.012)});\n' +
|
||||
'}\n' +
|
||||
'var star={active:false,x:0,y:0,sx:0,sy:0,tx:0,ty:0,t:0,dur:1.6,trail:[]};\n' +
|
||||
'var sparkles=[];\n' +
|
||||
'function addSparkle(x,y){for(var i=0;i<6;i++){var a=rand(0,6.28),sp=rand(0.5,2);\n' +
|
||||
' sparkles.push({x:x,y:y,vx:Math.cos(a)*sp,vy:Math.sin(a)*sp,a:1,sz:rand(3,6)})}}\n' +
|
||||
'var shockwaves=[];\n' +
|
||||
'function addShock(x,y,c,mr,d){shockwaves.push({x:x,y:y,r:0,mr:mr,c:c,a:.35,d:d,t:0})}\n' +
|
||||
'var phaseShown={};\n' +
|
||||
'function showScene(id,o,d){document.title="show:"+id+":"+o+":"+d}\n' +
|
||||
'function hideScene(id,d){document.title="hide:"+id+":"+d}\n' +
|
||||
'var waitingForDismiss=false;\n' +
|
||||
'window.dismissAnimation=function(){\n' +
|
||||
' var f=document.getElementById("fade");f.style.transition="opacity 1.2s ease-in";f.classList.add("show");\n' +
|
||||
' setTimeout(function(){document.title="ended"},1300);\n' +
|
||||
'};\n' +
|
||||
'function drawBlock(x,y,sz,color,alpha){\n' +
|
||||
' ctx.save();ctx.globalAlpha=alpha;\n' +
|
||||
' ctx.fillStyle=color;ctx.fillRect(x,y,sz,sz);\n' +
|
||||
' ctx.fillStyle="rgba(255,255,255,0.09)";ctx.fillRect(x,y,sz,1);ctx.fillRect(x,y,1,sz);\n' +
|
||||
' ctx.fillStyle="rgba(0,0,0,0.18)";ctx.fillRect(x,y+sz-1,sz,1);ctx.fillRect(x+sz-1,y,1,sz);\n' +
|
||||
' ctx.restore();\n' +
|
||||
'}\n' +
|
||||
'function drawTerrain(t){\n' +
|
||||
' var a=clamp(t*.3,0,.2);\n' +
|
||||
' for(var c=0;c<terrCols;c++){\n' +
|
||||
' for(var r=0;r<terrH[c];r++){\n' +
|
||||
' var y=H-(r+1)*BS;var x=c*BS;\n' +
|
||||
' var col=r===0?"#3E6B1F":r===1?"#5B4B2D":"#3D3D3D";\n' +
|
||||
' drawBlock(x,y,BS,col,a);\n' +
|
||||
' }\n' +
|
||||
' }\n' +
|
||||
'}\n' +
|
||||
'function drawGrid(t){\n' +
|
||||
' var a=clamp(t*.3,0,.018);\n' +
|
||||
' ctx.save();ctx.strokeStyle="rgba(255,255,255,"+a+")";ctx.lineWidth=0.5;\n' +
|
||||
' for(var x=0;x<W;x+=BS){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke()}\n' +
|
||||
' for(var y=0;y<H;y+=BS){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke()}\n' +
|
||||
' ctx.restore();\n' +
|
||||
'}\n' +
|
||||
'function frame(now){\n' +
|
||||
' var t=(now-startTime)/1000;\n' +
|
||||
' ctx.clearRect(0,0,W,H);\n' +
|
||||
' ctx.fillStyle="rgba(10,10,10,"+clamp(t*.5,0,1)+")";ctx.fillRect(0,0,W,H);\n' +
|
||||
' drawGrid(t);\n' +
|
||||
' drawTerrain(t);\n' +
|
||||
' if(t>1&&t<8){\n' +
|
||||
' var gp=clamp((t-1)*.6,0,.3);var r=100+gp*120;\n' +
|
||||
' var g=ctx.createRadialGradient(W/2,H/2,0,W/2,H/2,r);\n' +
|
||||
' g.addColorStop(0,"rgba(91,135,49,"+(.06*gp)+")");g.addColorStop(1,"rgba(62,107,31,0)");\n' +
|
||||
' ctx.fillStyle=g;ctx.fillRect(0,0,W,H);\n' +
|
||||
' }\n' +
|
||||
' if(t>22&&t<28){\n' +
|
||||
' var gp2=clamp((t-22)*.5,0,.25);var r2=120+gp2*130;\n' +
|
||||
' var g2=ctx.createRadialGradient(W/2,H/2,0,W/2,H/2,r2);\n' +
|
||||
' g2.addColorStop(0,"rgba(91,135,49,"+(.06*gp2)+")");g2.addColorStop(1,"rgba(62,107,31,0)");\n' +
|
||||
' ctx.fillStyle=g2;ctx.fillRect(0,0,W,H);\n' +
|
||||
' }\n' +
|
||||
' for(var i=0;i<parts.length;i++){var p=parts[i];\n' +
|
||||
' p.a=clamp(p.a+.003,0,p.ta);p.x+=p.vx;p.y+=p.vy;p.rot+=p.rs;\n' +
|
||||
' p.vx*=.9995;p.vy*=.9995;\n' +
|
||||
' if(p.x<-30)p.x=W+30;if(p.x>W+30)p.x=-30;\n' +
|
||||
' if(p.y<-30)p.y=H+30;if(p.y>H+30)p.y=-30;\n' +
|
||||
' ctx.save();ctx.translate(p.x,p.y);ctx.rotate(p.rot);\n' +
|
||||
' drawBlock(-p.sz/2,-p.sz/2,p.sz,p.c,p.a);ctx.restore();\n' +
|
||||
' }\n' +
|
||||
' if(star.active){\n' +
|
||||
' var st=clamp((t-star.t)/star.dur,0,1);var e=easeInOut(st);\n' +
|
||||
' star.x=lerp(star.sx,star.tx,e);star.y=lerp(star.sy,star.ty,e);\n' +
|
||||
' star.trail.push({x:star.x,y:star.y,a:1});if(star.trail.length>35)star.trail.shift();\n' +
|
||||
' for(var j=0;j<star.trail.length;j++){var tr=star.trail[j];tr.a*=.93;\n' +
|
||||
' ctx.save();ctx.globalAlpha=tr.a*.5;ctx.fillStyle="#7FFF00";\n' +
|
||||
' ctx.fillRect(tr.x-2,tr.y-2,4,4);ctx.restore();}\n' +
|
||||
' ctx.save();ctx.fillStyle="#7FFF00";ctx.shadowColor="#7FFF00";ctx.shadowBlur=15;\n' +
|
||||
' ctx.fillRect(star.x-5,star.y-5,10,10);\n' +
|
||||
' ctx.shadowBlur=30;ctx.fillStyle="rgba(127,255,0,0.12)";\n' +
|
||||
' ctx.fillRect(star.x-14,star.y-14,28,28);ctx.restore();\n' +
|
||||
' if(Math.random()>.6)addSparkle(star.x+rand(-8,8),star.y+rand(-8,8));\n' +
|
||||
' if(st>=1)star.active=false;\n' +
|
||||
' }\n' +
|
||||
' for(var s=sparkles.length-1;s>=0;s--){var sp=sparkles[s];\n' +
|
||||
' sp.x+=sp.vx;sp.y+=sp.vy;sp.a*=.95;sp.vx*=.97;sp.vy*=.97;\n' +
|
||||
' if(sp.a<.01){sparkles.splice(s,1);continue}\n' +
|
||||
' ctx.save();ctx.globalAlpha=sp.a;ctx.fillStyle="#7FFF00";\n' +
|
||||
' ctx.fillRect(sp.x-sp.sz/2,sp.y-sp.sz/2,sp.sz,sp.sz);ctx.restore();\n' +
|
||||
' }\n' +
|
||||
' for(var s=shockwaves.length-1;s>=0;s--){var sw=shockwaves[s];\n' +
|
||||
' sw.t+=1/60;var pct=sw.t/sw.d;if(pct>=1){shockwaves.splice(s,1);continue}\n' +
|
||||
' sw.r=sw.mr*easeOut(pct);sw.a=.35*(1-pct);\n' +
|
||||
' ctx.save();ctx.strokeStyle=sw.c;ctx.globalAlpha=sw.a;ctx.lineWidth=2+2*(1-pct);\n' +
|
||||
' ctx.beginPath();ctx.arc(sw.x,sw.y,sw.r,0,6.28);ctx.stroke();ctx.restore();\n' +
|
||||
' }\n' +
|
||||
' if(t>1.5&&!phaseShown.w){phaseShown.w=true;showScene("welcome",1,1)}\n' +
|
||||
' if(t>7&&!phaseShown.hidew){phaseShown.hidew=true;hideScene("welcome",1)}\n' +
|
||||
' if(t>8.5&&!phaseShown.star){\n' +
|
||||
' phaseShown.star=true;\n' +
|
||||
' star.active=true;star.sx=-50;star.sy=H*.55;star.tx=W/2;star.ty=H/2;star.t=t;star.trail=[];\n' +
|
||||
' }\n' +
|
||||
' if(t>10.5&&!phaseShown.ca){phaseShown.ca=true;addShock(W/2,H/2,"rgba(91,135,49,0.3)",400,1.2);showScene("logo-caellab",1,1.2)}\n' +
|
||||
' if(t>14.5&&!phaseShown.hideca){phaseShown.hideca=true;hideScene("logo-caellab",.8)}\n' +
|
||||
' if(t>15.5&&!phaseShown.both){phaseShown.both=true;showScene("logo-both",1,1.2)}\n' +
|
||||
' if(t>20.5&&!phaseShown.hideboth){phaseShown.hideboth=true;hideScene("logo-both",1)}\n' +
|
||||
' if(t>22&&!phaseShown.gpcl){phaseShown.gpcl=true;addShock(W/2,H/2,"rgba(127,255,0,0.12)",350,1);showScene("logo-gpcl",1,1.5)}\n' +
|
||||
' if(t>26&&!phaseShown.sweep){\n' +
|
||||
' phaseShown.sweep=true;\n' +
|
||||
' var sl=document.getElementById("sweep-line");sl.style.opacity="1";\n' +
|
||||
' var band=document.getElementById("sweep-band");var swStart=performance.now();\n' +
|
||||
' function sweepFrame(swNow){var swT=(swNow-swStart)/1500;\n' +
|
||||
' if(swT>1){sl.style.opacity="0";return}\n' +
|
||||
' band.style.left=(-200+(W+400)*easeInOut(swT))+"px";\n' +
|
||||
' requestAnimationFrame(sweepFrame)}\n' +
|
||||
' requestAnimationFrame(sweepFrame);\n' +
|
||||
' }\n' +
|
||||
' if(t>27.5&&!phaseShown.launch){phaseShown.launch=true;document.title="launch"}\n' +
|
||||
' if(t>28&&!phaseShown.hgpcl){phaseShown.hgpcl=true;hideScene("logo-gpcl",1)}\n' +
|
||||
' if(t>29.5&&!phaseShown.fadeblk){phaseShown.fadeblk=true;document.getElementById("fade").classList.add("show")}\n' +
|
||||
' if(t>DUR&&!waitingForDismiss){waitingForDismiss=true;document.title="waiting"}\n' +
|
||||
' requestAnimationFrame(frame);\n' +
|
||||
'}\n' +
|
||||
'document.title="meta:"+DUR;\n' +
|
||||
'requestAnimationFrame(frame);\n' +
|
||||
'})();\n' +
|
||||
'<\/script></body></html>';
|
||||
|
||||
var tempHtmlPath = path.join(tempAnimDir, 'index.html');
|
||||
try { fs.writeFileSync(tempHtmlPath, htmlContent, 'utf8'); } catch (e) {
|
||||
writeLog('[启动动画] 写入临时文件失败: ' + e.message);
|
||||
cleanupAnimTempDir(tempAnimDir);
|
||||
return { success: false, error: '写入临时文件失败', duration: 0 };
|
||||
}
|
||||
|
||||
return new Promise(function(resolve) {
|
||||
var animWindow = new BrowserWindow({
|
||||
fullscreen: true,
|
||||
frame: false,
|
||||
skipTaskbar: true,
|
||||
backgroundColor: '#000000',
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true
|
||||
}
|
||||
});
|
||||
|
||||
activeAnimWindow = animWindow;
|
||||
|
||||
animWindow.setAlwaysOnTop(true, 'screen-saver');
|
||||
animWindow.webContents.setWindowOpenHandler(function(details) {
|
||||
shell.openExternal(details.url);
|
||||
return { action: 'deny' };
|
||||
});
|
||||
animWindow.loadFile(tempHtmlPath);
|
||||
|
||||
var resolved = false;
|
||||
|
||||
animWindow.on('page-title-updated', function(e, title) {
|
||||
e.preventDefault();
|
||||
|
||||
if (title.startsWith('show:')) {
|
||||
var parts = title.split(':');
|
||||
var sceneId = parts[1];
|
||||
var opacity = parts[2];
|
||||
var dur = parts[3];
|
||||
animWindow.webContents.executeJavaScript(
|
||||
'(function(){var el=document.getElementById("' + sceneId + '");' +
|
||||
'if(el){el.style.transition="opacity ' + dur + 's ease-out";' +
|
||||
'el.style.opacity="' + opacity + '"}})()'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (title.startsWith('hide:')) {
|
||||
var parts = title.split(':');
|
||||
var sceneId = parts[1];
|
||||
var dur = parts[2];
|
||||
animWindow.webContents.executeJavaScript(
|
||||
'(function(){var el=document.getElementById("' + sceneId + '");' +
|
||||
'if(el){el.style.transition="opacity ' + dur + 's ease-in";' +
|
||||
'el.style.opacity="0"}})()'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (title === 'launch' && !resolved) {
|
||||
resolved = true;
|
||||
writeLog('[启动动画] 动画即将结束,提前启动MC');
|
||||
resolve({ success: true, duration: ANIM_DURATION });
|
||||
return;
|
||||
}
|
||||
|
||||
if (title === 'waiting') {
|
||||
writeLog('[启动动画] 进入黑屏等待阶段');
|
||||
return;
|
||||
}
|
||||
|
||||
if (title === 'ended') {
|
||||
writeLog('[启动动画] 动画退场完毕');
|
||||
try { animWindow.close(); } catch (e) {}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
animWindow.on('closed', function() {
|
||||
if (activeAnimWindow === animWindow) activeAnimWindow = null;
|
||||
cleanupAnimTempDir(tempAnimDir);
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
resolve({ success: false, error: '动画窗口已关闭', duration: 0 });
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
writeLog('[启动动画] 动画加载超时');
|
||||
resolve({ success: false, error: '动画加载超时', duration: 0 });
|
||||
try { if (!animWindow.isDestroyed()) animWindow.close(); } catch (e) {}
|
||||
}
|
||||
}, 35000);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.handle('dismiss-startup-animation', function() {
|
||||
if (activeAnimWindow && !activeAnimWindow.isDestroyed()) {
|
||||
writeLog('[启动动画] MC窗口就绪,执行退场');
|
||||
try {
|
||||
activeAnimWindow.webContents.executeJavaScript(
|
||||
'if(typeof dismissAnimation==="function")dismissAnimation()'
|
||||
);
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('read-json-file', async (_, subPath, fileName) => {
|
||||
try {
|
||||
const dir = path.join(BASE_DIR, 'gpcl', subPath);
|
||||
const filePath = path.join(dir, fileName);
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return null;
|
||||
}
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
return JSON.parse(content);
|
||||
} catch (e) {
|
||||
writeLog(`读取文件失败: ${e.message}`);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('write-json-file', async (_, subPath, fileName, data) => {
|
||||
try {
|
||||
const dir = path.join(BASE_DIR, 'gpcl', subPath);
|
||||
const filePath = path.join(dir, fileName);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
|
||||
return true;
|
||||
} catch (e) {
|
||||
writeLog(`写入文件失败: ${e.message}`);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// 获取 Forge 版本列表
|
||||
ipcMain.handle('get-forge-versions', async (_, mcVersion) => {
|
||||
try {
|
||||
writeLog(`[Forge] 获取版本列表: ${mcVersion}`);
|
||||
const versions = await fetchForgeVersions(mcVersion);
|
||||
return { success: true, versions };
|
||||
} catch (e) {
|
||||
writeLog(`[Forge] 获取版本列表失败: ${e.message}`);
|
||||
return { success: false, error: e.message, versions: [] };
|
||||
}
|
||||
});
|
||||
|
||||
// 从 Forge Maven 获取版本列表
|
||||
async function fetchForgeVersions(mcVersion) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const https = require('https');
|
||||
const url = `https://files.minecraftforge.net/net/minecraftforge/forge/maven-metadata.json`;
|
||||
|
||||
https.get(url, { timeout: 10000 }, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const metadata = JSON.parse(data);
|
||||
const versions = [];
|
||||
|
||||
// 查找对应 MC 版本的 Forge 版本
|
||||
const versionArray = metadata[mcVersion];
|
||||
if (versionArray && Array.isArray(versionArray)) {
|
||||
versionArray.forEach((fullVersion) => {
|
||||
const parts = fullVersion.split('-');
|
||||
const forgeVersion = parts.slice(1).join('-');
|
||||
versions.push({
|
||||
id: fullVersion,
|
||||
name: `Forge ${forgeVersion}`,
|
||||
version: forgeVersion,
|
||||
mcVersion: mcVersion
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 按版本号排序(最新的在前)
|
||||
versions.sort((a, b) => compareVersions(b.version, a.version));
|
||||
|
||||
// 只返回前 10 个版本
|
||||
const topVersions = versions.slice(0, 10);
|
||||
|
||||
// 标记推荐版本(第一个)
|
||||
if (topVersions.length > 0) {
|
||||
topVersions[0].name += ' (推荐)';
|
||||
}
|
||||
|
||||
writeLog(`[Forge] 找到 ${topVersions.length} 个版本`);
|
||||
resolve(topVersions);
|
||||
} catch (e) {
|
||||
writeLog(`[Forge] 解析错误: ${e.message}`);
|
||||
reject(new Error('解析 Forge 元数据失败'));
|
||||
}
|
||||
});
|
||||
}).on('error', (e) => {
|
||||
reject(new Error(`请求 Forge 元数据失败: ${e.message}`));
|
||||
}).on('timeout', () => {
|
||||
reject(new Error('请求 Forge 元数据超时'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 版本号比较函数
|
||||
function compareVersions(v1, v2) {
|
||||
const parts1 = v1.split(/[.-]/).map(p => parseInt(p, 10) || 0);
|
||||
const parts2 = v2.split(/[.-]/).map(p => parseInt(p, 10) || 0);
|
||||
|
||||
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
||||
const a = parts1[i] || 0;
|
||||
const b = parts2[i] || 0;
|
||||
if (a > b) return 1;
|
||||
if (a < b) return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
async function installVersionWithModLoader(versionId, loaderType, loaderVersion, maxConcurrent) {
|
||||
try {
|
||||
writeLog(`[模组加载器] 开始下载: ${loaderType} ${loaderVersion} for MC ${versionId}`);
|
||||
|
||||
// 1. 先下载原版游戏
|
||||
writeLog(`[模组加载器] 下载原版 MC ${versionId}`);
|
||||
await installVersion(versionId, maxConcurrent);
|
||||
|
||||
if (shouldCancelDownload) {
|
||||
return { success: false, error: '下载已取消' };
|
||||
}
|
||||
|
||||
// 2. 根据模组加载器类型进行安装
|
||||
let finalVersionId;
|
||||
if (loaderType === 'forge') {
|
||||
finalVersionId = await installForge(versionId, loaderVersion);
|
||||
} else {
|
||||
return { success: false, error: `不支持的模组加载器: ${loaderType}` };
|
||||
}
|
||||
|
||||
writeLog(`[模组加载器] 安装完成: ${finalVersionId}`);
|
||||
|
||||
// 更新版本最后启动时间
|
||||
updateVersionLastPlayed(finalVersionId);
|
||||
|
||||
return { success: true, finalVersionId };
|
||||
|
||||
} catch (e) {
|
||||
writeLog(`[模组加载器] 安装失败: ${e.message}`);
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
// 安装 Forge:直接从 Forge Maven 下载完整的版本 JSON
|
||||
async function installForge(mcVersion, forgeVersion) {
|
||||
writeLog(`[Forge] 安装: MC ${mcVersion}, Forge ${forgeVersion}`);
|
||||
|
||||
const versionDir = path.join(GAME_DIR, 'versions', mcVersion);
|
||||
if (!fs.existsSync(versionDir)) {
|
||||
throw new Error(`原版版本 ${mcVersion} 不存在`);
|
||||
}
|
||||
|
||||
// 构造完整的 Forge 版本 ID(如 1.20.1-47.2.0)
|
||||
const fullForgeVersionId = `${mcVersion}-${forgeVersion}`;
|
||||
|
||||
// Forge 在 Maven 上提供了完整的版本 JSON(含 mainClass、libraries、inheritsFrom 等)
|
||||
const forgeJsonUrl = `https://maven.minecraftforge.net/net/minecraftforge/forge/${fullForgeVersionId}/forge-${fullForgeVersionId}.json`;
|
||||
|
||||
writeLog(`[Forge] 下载版本 JSON: ${forgeJsonUrl}`);
|
||||
let forgeJsonData;
|
||||
try {
|
||||
forgeJsonData = await fetchText(forgeJsonUrl);
|
||||
} catch (e) {
|
||||
throw new Error(`无法下载 Forge 版本 JSON: ${e.message}`);
|
||||
}
|
||||
|
||||
let forgeVersionData;
|
||||
try {
|
||||
forgeVersionData = JSON.parse(forgeJsonData);
|
||||
} catch (e) {
|
||||
const snippet = forgeJsonData.substring(0, 100);
|
||||
writeLog(`[Forge] JSON 解析失败,响应内容: ${snippet}`);
|
||||
throw new Error(`Forge 版本 JSON 解析失败,服务器可能返回了错误页面`);
|
||||
}
|
||||
|
||||
const forgeJsonId = `${mcVersion}-forge`;
|
||||
forgeVersionData.id = forgeJsonId;
|
||||
|
||||
const forgeJsonPath = path.join(versionDir, `${forgeJsonId}.json`);
|
||||
fs.writeFileSync(forgeJsonPath, JSON.stringify(forgeVersionData, null, 2));
|
||||
|
||||
const gpclDir = path.join(versionDir, 'gpcl');
|
||||
if (!fs.existsSync(gpclDir)) fs.mkdirSync(gpclDir, { recursive: true });
|
||||
|
||||
const configPath = path.join(gpclDir, 'config.ini');
|
||||
const configContent = `[Version]
|
||||
Minecraft=${mcVersion}
|
||||
Forge=${forgeVersion}
|
||||
`;
|
||||
fs.writeFileSync(configPath, configContent, 'utf8');
|
||||
|
||||
writeLog(`[Forge] 安装完成: ${forgeJsonId}`);
|
||||
return forgeJsonId;
|
||||
}
|
||||
|
||||
// 读取 config.ini 配置文件
|
||||
function readVersionConfig(versionId) {
|
||||
const versionDir = path.join(GAME_DIR, 'versions', versionId);
|
||||
const configPath = path.join(versionDir, 'gpcl', 'config.ini');
|
||||
|
||||
if (!fs.existsSync(configPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(configPath, 'utf8');
|
||||
const config = {
|
||||
Minecraft: '',
|
||||
Forge: ''
|
||||
};
|
||||
|
||||
const lines = content.split('\n');
|
||||
let inVersionSection = false;
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
|
||||
if (trimmed === '[Version]') {
|
||||
inVersionSection = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inVersionSection) continue;
|
||||
|
||||
const parts = trimmed.split('=');
|
||||
if (parts.length === 2) {
|
||||
const key = parts[0].trim();
|
||||
const value = parts[1].trim();
|
||||
if (config.hasOwnProperty(key)) {
|
||||
config[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
} catch (e) {
|
||||
writeLog(`[Config] 读取配置失败: ${e.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 生成版本显示名称
|
||||
function generateVersionDisplayName(versionId) {
|
||||
const config = readVersionConfig(versionId);
|
||||
|
||||
if (!config) {
|
||||
return versionId;
|
||||
}
|
||||
|
||||
const parts = [];
|
||||
parts.push(config.Minecraft || versionId);
|
||||
|
||||
if (config.Forge) {
|
||||
parts.push(`Forge${config.Forge}`);
|
||||
}
|
||||
|
||||
return parts.join(' ');
|
||||
}
|
||||
|
||||
// 生成启动页显示名称
|
||||
function generateLaunchDisplayName(versionId) {
|
||||
const config = readVersionConfig(versionId);
|
||||
|
||||
if (!config) {
|
||||
return versionId;
|
||||
}
|
||||
|
||||
const parts = [];
|
||||
parts.push(config.Minecraft || versionId);
|
||||
|
||||
if (config.Forge) {
|
||||
parts.push(`Forge${config.Forge}`);
|
||||
}
|
||||
|
||||
return parts.join(' ');
|
||||
}
|
||||
|
||||
// 安装 Forge
|
||||
async function installForge(mcVersion, forgeVersionId) {
|
||||
writeLog(`[Forge] 安装: ${forgeVersionId}`);
|
||||
|
||||
const versionDir = path.join(GAME_DIR, 'versions', mcVersion);
|
||||
if (!fs.existsSync(versionDir)) {
|
||||
throw new Error(`原版版本 ${mcVersion} 不存在`);
|
||||
}
|
||||
|
||||
// 创建 gpcl 目录
|
||||
const gpclDir = path.join(versionDir, 'gpcl');
|
||||
if (!fs.existsSync(gpclDir)) fs.mkdirSync(gpclDir, { recursive: true });
|
||||
|
||||
// 读取或创建 config.ini
|
||||
const configPath = path.join(gpclDir, 'config.ini');
|
||||
let config = readVersionConfig(mcVersion) || {
|
||||
Minecraft: mcVersion,
|
||||
Forge: '',
|
||||
Fabric: '',
|
||||
OptiFine: ''
|
||||
};
|
||||
config.Minecraft = mcVersion;
|
||||
config.Forge = forgeVersionId;
|
||||
|
||||
// 写入 config.ini
|
||||
const configContent = `[Version]
|
||||
Minecraft=${config.Minecraft}
|
||||
Forge=${config.Forge}
|
||||
Fabric=${config.Fabric || ''}
|
||||
OptiFine=${config.OptiFine || ''}
|
||||
`;
|
||||
fs.writeFileSync(configPath, configContent, 'utf8');
|
||||
|
||||
// 更新 version.json
|
||||
const jsonPath = path.join(versionDir, `${mcVersion}.json`);
|
||||
const versionData = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
||||
versionData.mainClass = 'net.minecraftforge.userdev.LaunchTesting';
|
||||
|
||||
versionData.libraries = versionData.libraries || [];
|
||||
const hasForgeLib = versionData.libraries.some(lib =>
|
||||
lib.name && lib.name.startsWith('net.minecraftforge:forge:')
|
||||
);
|
||||
if (!hasForgeLib) {
|
||||
versionData.libraries.push({
|
||||
name: `net.minecraftforge:forge:${forgeVersionId}`,
|
||||
downloads: {
|
||||
artifact: {
|
||||
url: `https://maven.minecraftforge.net/net/minecraftforge/forge/${forgeVersionId}/forge-${forgeVersionId}.jar`,
|
||||
path: `net/minecraftforge/forge/${forgeVersionId}/forge-${forgeVersionId}.jar`,
|
||||
sha1: '',
|
||||
size: 0
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fs.writeFileSync(jsonPath, JSON.stringify(versionData, null, 2));
|
||||
|
||||
writeLog(`[Forge] 安装完成: ${mcVersion} + Forge ${forgeVersionId}`);
|
||||
return mcVersion;
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"compact": true,
|
||||
"controlFlowFlattening": false,
|
||||
"controlFlowFlatteningThreshold": 0,
|
||||
"deadCodeInjection": false,
|
||||
"deadCodeInjectionThreshold": 0,
|
||||
"debugProtection": false,
|
||||
"debugProtectionInterval": 0,
|
||||
"disableConsoleOutput": false,
|
||||
"identifierNamesGenerator": "mangled",
|
||||
"rotateStringArray": true,
|
||||
"selfDefending": false,
|
||||
"stringArray": true,
|
||||
"stringArrayEncoding": ["rc4"],
|
||||
"stringArrayThreshold": 0.75,
|
||||
"transformObjectKeys": false,
|
||||
"unicodeEscapeSequence": false,
|
||||
"renameGlobals": false,
|
||||
"shuffleStringArray": true,
|
||||
"splitStrings": false
|
||||
}
|
||||
|
|
@ -0,0 +1,4709 @@
|
|||
{
|
||||
"name": "gpcl",
|
||||
"version": "1.4.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "gpcl",
|
||||
"version": "1.4.1",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.5.17",
|
||||
"unzip-stream": "^0.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^42.1.0",
|
||||
"electron-builder": "^26.8.1",
|
||||
"javascript-obfuscator": "^5.4.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@develar/schema-utils": {
|
||||
"version": "2.6.5",
|
||||
"resolved": "https://registry.npmmirror.com/@develar/schema-utils/-/schema-utils-2.6.5.tgz",
|
||||
"integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.0",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/asar": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/@electron/asar/-/asar-3.4.1.tgz",
|
||||
"integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^5.0.0",
|
||||
"glob": "^7.1.6",
|
||||
"minimatch": "^3.0.4"
|
||||
},
|
||||
"bin": {
|
||||
"asar": "bin/asar.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/asar/node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@electron/asar/node_modules/brace-expansion": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/asar/node_modules/minimatch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/fuses": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmmirror.com/@electron/fuses/-/fuses-1.8.0.tgz",
|
||||
"integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.1",
|
||||
"fs-extra": "^9.0.1",
|
||||
"minimist": "^1.2.5"
|
||||
},
|
||||
"bin": {
|
||||
"electron-fuses": "dist/bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/fuses/node_modules/fs-extra": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz",
|
||||
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"at-least-node": "^1.0.0",
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/get": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/@electron/get/-/get-5.0.0.tgz",
|
||||
"integrity": "sha512-pjoBpru1KdEtcExBnuHAP1cAc/5faoedw0hzJkL3o4/IJp7HNF1+fbrdxT3gMYRX2oJfvnA/WXeCTVQpYYxyJA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"env-paths": "^3.0.0",
|
||||
"graceful-fs": "^4.2.11",
|
||||
"progress": "^2.0.3",
|
||||
"semver": "^7.6.3",
|
||||
"sumchecker": "^3.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.12.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"undici": "^7.24.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/notarize": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/@electron/notarize/-/notarize-2.5.0.tgz",
|
||||
"integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"fs-extra": "^9.0.1",
|
||||
"promise-retry": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/notarize/node_modules/fs-extra": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz",
|
||||
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"at-least-node": "^1.0.0",
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/osx-sign": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/@electron/osx-sign/-/osx-sign-1.3.3.tgz",
|
||||
"integrity": "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"compare-version": "^0.1.2",
|
||||
"debug": "^4.3.4",
|
||||
"fs-extra": "^10.0.0",
|
||||
"isbinaryfile": "^4.0.8",
|
||||
"minimist": "^1.2.6",
|
||||
"plist": "^3.0.5"
|
||||
},
|
||||
"bin": {
|
||||
"electron-osx-flat": "bin/electron-osx-flat.js",
|
||||
"electron-osx-sign": "bin/electron-osx-sign.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/osx-sign/node_modules/isbinaryfile": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
|
||||
"integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/gjtorikian/"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/@electron/rebuild/-/rebuild-4.0.4.tgz",
|
||||
"integrity": "sha512-Rzc39XPdk/+/wBG8MfwAHohXflep0ITUfulb6Rgz3R0NeSB1noE+E9/M/cb8ftCAiyDD9PPhLuuWgE1GaInbKg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@malept/cross-spawn-promise": "^2.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"node-abi": "^4.2.0",
|
||||
"node-api-version": "^0.2.1",
|
||||
"node-gyp": "^12.2.0",
|
||||
"read-binary-file-arch": "^1.0.6"
|
||||
},
|
||||
"bin": {
|
||||
"electron-rebuild": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/universal": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@electron/universal/-/universal-2.0.3.tgz",
|
||||
"integrity": "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@electron/asar": "^3.3.1",
|
||||
"@malept/cross-spawn-promise": "^2.0.0",
|
||||
"debug": "^4.3.1",
|
||||
"dir-compare": "^4.2.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"minimatch": "^9.0.3",
|
||||
"plist": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/universal/node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@electron/universal/node_modules/brace-expansion": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/universal/node_modules/fs-extra": {
|
||||
"version": "11.3.5",
|
||||
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.5.tgz",
|
||||
"integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/universal/node_modules/minimatch": {
|
||||
"version": "9.0.9",
|
||||
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz",
|
||||
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/windows-sign": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/@electron/windows-sign/-/windows-sign-1.2.2.tgz",
|
||||
"integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cross-dirname": "^0.1.0",
|
||||
"debug": "^4.3.4",
|
||||
"fs-extra": "^11.1.1",
|
||||
"minimist": "^1.2.8",
|
||||
"postject": "^1.0.0-alpha.6"
|
||||
},
|
||||
"bin": {
|
||||
"electron-windows-sign": "bin/electron-windows-sign.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/windows-sign/node_modules/fs-extra": {
|
||||
"version": "11.3.5",
|
||||
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.5.tgz",
|
||||
"integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@inversifyjs/common": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmmirror.com/@inversifyjs/common/-/common-1.5.2.tgz",
|
||||
"integrity": "sha512-WlzR9xGadABS9gtgZQ+luoZ8V6qm4Ii6RQfcfC9Ho2SOlE6ZuemFo7PKJvKI0ikm8cmKbU8hw5UK6E4qovH21w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@inversifyjs/container": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmmirror.com/@inversifyjs/container/-/container-1.15.0.tgz",
|
||||
"integrity": "sha512-U2xYsPrJTz5za2TExi5lg8qOWf8TEVBpN+pQM7B8BVA2rajtbRE9A66SLRHk8c1eGXmg+0K4Hdki6tWAsSQBUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@inversifyjs/common": "1.5.2",
|
||||
"@inversifyjs/core": "9.2.0",
|
||||
"@inversifyjs/plugin": "0.2.0",
|
||||
"@inversifyjs/reflect-metadata-utils": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"reflect-metadata": "~0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@inversifyjs/core": {
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@inversifyjs/core/-/core-9.2.0.tgz",
|
||||
"integrity": "sha512-Nm7BR6KmpgshIHpVQWuEDehqRVb6GBm8LFEuhc2s4kSZWrArZ15RmXQzROLk4m+hkj4kMXgvMm5Qbopot/D6Sg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@inversifyjs/common": "1.5.2",
|
||||
"@inversifyjs/prototype-utils": "0.1.3",
|
||||
"@inversifyjs/reflect-metadata-utils": "1.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@inversifyjs/plugin": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@inversifyjs/plugin/-/plugin-0.2.0.tgz",
|
||||
"integrity": "sha512-R/JAdkTSD819pV1zi0HP54mWHyX+H2m8SxldXRgPQarS3ySV4KPyRdosWcfB8Se0JJZWZLHYiUNiS6JvMWSPjw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@inversifyjs/prototype-utils": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/@inversifyjs/prototype-utils/-/prototype-utils-0.1.3.tgz",
|
||||
"integrity": "sha512-EzRamZzNgE9Sn3QtZ8NncNa2lpPMZfspqbK6BWFguWnOpK8ymp2TUuH46ruFHZhrHKnknPd7fG22ZV7iF517TQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@inversifyjs/common": "1.5.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@inversifyjs/reflect-metadata-utils": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/@inversifyjs/reflect-metadata-utils/-/reflect-metadata-utils-1.4.1.tgz",
|
||||
"integrity": "sha512-Cp77C4d2wLaHXiUB7iH6Cxb7i1lD/YDuTIHLTDzKINqGSz0DCSoL/Dg2wVkW/6Qx03r/yQMLJ+32Agl32N2X8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"reflect-metadata": "~0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/fs-minipass": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
||||
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"minipass": "^7.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@javascript-obfuscator/escodegen": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/@javascript-obfuscator/escodegen/-/escodegen-2.4.1.tgz",
|
||||
"integrity": "sha512-YrEJJDr4cb+pIQKWzHFoDlDkQzatcrNB6OhAD6iTSwiKwzZUMVdobwbOuLpF4EiLxUj0qP28Xl1saTHYzIPCLg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@javascript-obfuscator/estraverse": "^5.3.0",
|
||||
"esprima": "^4.0.1",
|
||||
"esutils": "^2.0.2",
|
||||
"optionator": "^0.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"source-map": "~0.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@javascript-obfuscator/estraverse": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/@javascript-obfuscator/estraverse/-/estraverse-5.4.0.tgz",
|
||||
"integrity": "sha512-CZFX7UZVN9VopGbjTx4UXaXsi9ewoM1buL0kY7j1ftYdSs7p2spv9opxFjHlQ/QGTgh4UqufYqJJ0WKLml7b6w==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@malept/cross-spawn-promise": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz",
|
||||
"integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/malept"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund"
|
||||
}
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@malept/flatpak-bundler": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz",
|
||||
"integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"fs-extra": "^9.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"tmp-promise": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@malept/flatpak-bundler/node_modules/fs-extra": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz",
|
||||
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"at-least-node": "^1.0.0",
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sindresorhus/is": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/@sindresorhus/is/-/is-4.6.0.tgz",
|
||||
"integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@szmarczak/http-timer": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
|
||||
"integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"defer-to-connect": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/cacheable-request": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
|
||||
"integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/http-cache-semantics": "*",
|
||||
"@types/keyv": "^3.1.4",
|
||||
"@types/node": "*",
|
||||
"@types/responselike": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/debug": {
|
||||
"version": "4.1.13",
|
||||
"resolved": "https://registry.npmmirror.com/@types/debug/-/debug-4.1.13.tgz",
|
||||
"integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/ms": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/fs-extra": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmmirror.com/@types/fs-extra/-/fs-extra-9.0.13.tgz",
|
||||
"integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/http-cache-semantics": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
|
||||
"integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/keyv": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmmirror.com/@types/keyv/-/keyv-3.1.4.tgz",
|
||||
"integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/minimatch": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/@types/minimatch/-/minimatch-3.0.5.tgz",
|
||||
"integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ms": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/@types/ms/-/ms-2.1.0.tgz",
|
||||
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.12.4",
|
||||
"resolved": "https://registry.npmmirror.com/@types/node/-/node-24.12.4.tgz",
|
||||
"integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/plist": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/@types/plist/-/plist-3.0.5.tgz",
|
||||
"integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"xmlbuilder": ">=11.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/responselike": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@types/responselike/-/responselike-1.0.3.tgz",
|
||||
"integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/validator": {
|
||||
"version": "13.15.10",
|
||||
"resolved": "https://registry.npmmirror.com/@types/validator/-/validator-13.15.10.tgz",
|
||||
"integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/verror": {
|
||||
"version": "1.10.11",
|
||||
"resolved": "https://registry.npmmirror.com/@types/verror/-/verror-1.10.11.tgz",
|
||||
"integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@types/yauzl": {
|
||||
"version": "2.10.3",
|
||||
"resolved": "https://registry.npmmirror.com/@types/yauzl/-/yauzl-2.10.3.tgz",
|
||||
"integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@vercel/blob": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vercel/blob/-/blob-2.4.0.tgz",
|
||||
"integrity": "sha512-ncQ8CRb6XoEAYJwjOTRGpACRT6h/AeY+/33gLyeVxG5BIes27OPm1jmqreF+JHjcTmGhClTP+kBpmyLfbV0xew==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"async-retry": "^1.3.4",
|
||||
"is-buffer": "^2.0.5",
|
||||
"is-node-process": "^1.2.0",
|
||||
"throttleit": "^2.1.0",
|
||||
"undici": "^6.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vercel/blob/node_modules/undici": {
|
||||
"version": "6.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/undici/-/undici-6.25.0.tgz",
|
||||
"integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@xmldom/xmldom": {
|
||||
"version": "0.8.13",
|
||||
"resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.13.tgz",
|
||||
"integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/7zip-bin": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/7zip-bin/-/7zip-bin-5.2.0.tgz",
|
||||
"integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-4.0.0.tgz",
|
||||
"integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "^20.17.0 || >=22.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-import-attributes": {
|
||||
"version": "1.9.5",
|
||||
"resolved": "https://registry.npmmirror.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
|
||||
"integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"acorn": "^8"
|
||||
}
|
||||
},
|
||||
"node_modules/adm-zip": {
|
||||
"version": "0.5.17",
|
||||
"resolved": "https://registry.npmmirror.com/adm-zip/-/adm-zip-0.5.17.tgz",
|
||||
"integrity": "sha512-+Ut8d9LLqwEvHHJl1+PIHqoyDxFgVN847JTVM3Izi3xHDWPE4UtzzXysMZQs64DMcrJfBeS/uoEP4AD3HQHnQQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz",
|
||||
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.15.0",
|
||||
"resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.15.0.tgz",
|
||||
"integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-keywords": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"ajv": "^6.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-bin": {
|
||||
"version": "5.0.0-alpha.12",
|
||||
"resolved": "https://registry.npmmirror.com/app-builder-bin/-/app-builder-bin-5.0.0-alpha.12.tgz",
|
||||
"integrity": "sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/app-builder-lib": {
|
||||
"version": "26.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/app-builder-lib/-/app-builder-lib-26.8.1.tgz",
|
||||
"integrity": "sha512-p0Im/Dx5C4tmz8QEE1Yn4MkuPC8PrnlRneMhWJj7BBXQfNTJUshM/bp3lusdEsDbvvfJZpXWnYesgSLvwtM2Zw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@develar/schema-utils": "~2.6.5",
|
||||
"@electron/asar": "3.4.1",
|
||||
"@electron/fuses": "^1.8.0",
|
||||
"@electron/get": "^3.0.0",
|
||||
"@electron/notarize": "2.5.0",
|
||||
"@electron/osx-sign": "1.3.4",
|
||||
"@electron/rebuild": "^4.0.3",
|
||||
"@electron/universal": "2.0.3",
|
||||
"@malept/flatpak-bundler": "^0.4.0",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"async-exit-hook": "^2.0.1",
|
||||
"builder-util": "26.8.1",
|
||||
"builder-util-runtime": "9.5.1",
|
||||
"chromium-pickle-js": "^0.2.0",
|
||||
"ci-info": "4.3.1",
|
||||
"debug": "^4.3.4",
|
||||
"dotenv": "^16.4.5",
|
||||
"dotenv-expand": "^11.0.6",
|
||||
"ejs": "^3.1.8",
|
||||
"electron-publish": "26.8.1",
|
||||
"fs-extra": "^10.1.0",
|
||||
"hosted-git-info": "^4.1.0",
|
||||
"isbinaryfile": "^5.0.0",
|
||||
"jiti": "^2.4.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"json5": "^2.2.3",
|
||||
"lazy-val": "^1.0.5",
|
||||
"minimatch": "^10.0.3",
|
||||
"plist": "3.1.0",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"resedit": "^1.7.0",
|
||||
"semver": "~7.7.3",
|
||||
"tar": "^7.5.7",
|
||||
"temp-file": "^3.4.0",
|
||||
"tiny-async-pool": "1.3.0",
|
||||
"which": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"dmg-builder": "26.8.1",
|
||||
"electron-builder-squirrel-windows": "26.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/@electron/get": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/@electron/get/-/get-3.1.0.tgz",
|
||||
"integrity": "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"env-paths": "^2.2.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"got": "^11.8.5",
|
||||
"progress": "^2.0.3",
|
||||
"semver": "^6.2.0",
|
||||
"sumchecker": "^3.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"global-agent": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/@electron/get/node_modules/fs-extra": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^4.0.0",
|
||||
"universalify": "^0.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6 <7 || >=8"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/@electron/get/node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/ci-info": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-4.3.1.tgz",
|
||||
"integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/sibiraj-s"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/env-paths": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz",
|
||||
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"dev": true,
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/array-differ": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/array-differ/-/array-differ-3.0.0.tgz",
|
||||
"integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/array-union": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz",
|
||||
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/arrify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/arrify/-/arrify-2.0.1.tgz",
|
||||
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/assert": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/assert/-/assert-2.1.0.tgz",
|
||||
"integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"is-nan": "^1.3.2",
|
||||
"object-is": "^1.1.5",
|
||||
"object.assign": "^4.1.4",
|
||||
"util": "^0.12.5"
|
||||
}
|
||||
},
|
||||
"node_modules/assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/astral-regex": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/astral-regex/-/astral-regex-2.0.0.tgz",
|
||||
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmmirror.com/async/-/async-3.2.6.tgz",
|
||||
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/async-exit-hook": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz",
|
||||
"integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/async-retry": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmmirror.com/async-retry/-/async-retry-1.3.4.tgz",
|
||||
"integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"retry": "0.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/async-retry/node_modules/retry": {
|
||||
"version": "0.13.1",
|
||||
"resolved": "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz",
|
||||
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/at-least-node": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz",
|
||||
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/available-typed-arrays": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"possible-typed-array-names": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/binary": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/binary/-/binary-0.3.0.tgz",
|
||||
"integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffers": "~0.1.1",
|
||||
"chainsaw": "~0.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/boolean": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/boolean/-/boolean-3.2.0.tgz",
|
||||
"integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
|
||||
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-crc32": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
||||
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/buffers": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/buffers/-/buffers-0.1.1.tgz",
|
||||
"integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==",
|
||||
"engines": {
|
||||
"node": ">=0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/builder-util": {
|
||||
"version": "26.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/builder-util/-/builder-util-26.8.1.tgz",
|
||||
"integrity": "sha512-pm1lTYbGyc90DHgCDO7eo8Rl4EqKLciayNbZqGziqnH9jrlKe8ZANGdityLZU+pJh16dfzjAx2xQq9McuIPEtw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/debug": "^4.1.6",
|
||||
"7zip-bin": "~5.2.0",
|
||||
"app-builder-bin": "5.0.0-alpha.12",
|
||||
"builder-util-runtime": "9.5.1",
|
||||
"chalk": "^4.1.2",
|
||||
"cross-spawn": "^7.0.6",
|
||||
"debug": "^4.3.4",
|
||||
"fs-extra": "^10.1.0",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"source-map-support": "^0.5.19",
|
||||
"stat-mode": "^1.0.0",
|
||||
"temp-file": "^3.4.0",
|
||||
"tiny-async-pool": "1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/builder-util-runtime": {
|
||||
"version": "9.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/builder-util-runtime/-/builder-util-runtime-9.5.1.tgz",
|
||||
"integrity": "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"sax": "^1.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cacheable-lookup": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
|
||||
"integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cacheable-request": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/cacheable-request/-/cacheable-request-7.0.4.tgz",
|
||||
"integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clone-response": "^1.0.2",
|
||||
"get-stream": "^5.1.0",
|
||||
"http-cache-semantics": "^4.0.0",
|
||||
"keyv": "^4.0.0",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
"normalize-url": "^6.0.1",
|
||||
"responselike": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.9.tgz",
|
||||
"integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"get-intrinsic": "^1.3.0",
|
||||
"set-function-length": "^1.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"dev": true,
|
||||
"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.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz",
|
||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||
"dev": true,
|
||||
"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/chainsaw": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/chainsaw/-/chainsaw-0.1.0.tgz",
|
||||
"integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==",
|
||||
"license": "MIT/X11",
|
||||
"dependencies": {
|
||||
"traverse": ">=0.3.0 <0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/chance": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmmirror.com/chance/-/chance-1.1.13.tgz",
|
||||
"integrity": "sha512-V6lQCljcLznE7tUYUM9EOAnnKXbctE6j/rdQkYOHIWbfGQbrzTsAXNW9CdU5XCo4ArXQCj/rb6HgxPlmGJcaUg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/char-regex": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/char-regex/-/char-regex-1.0.2.tgz",
|
||||
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/charenc": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/charenc/-/charenc-0.0.2.tgz",
|
||||
"integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/chownr": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/chownr/-/chownr-3.0.0.tgz",
|
||||
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/chromium-pickle-js": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz",
|
||||
"integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ci-info": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-4.4.0.tgz",
|
||||
"integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/sibiraj-s"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/class-validator": {
|
||||
"version": "0.14.3",
|
||||
"resolved": "https://registry.npmmirror.com/class-validator/-/class-validator-0.14.3.tgz",
|
||||
"integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/validator": "^13.15.3",
|
||||
"libphonenumber-js": "^1.11.1",
|
||||
"validator": "^13.15.20"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-truncate": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-2.1.0.tgz",
|
||||
"integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"slice-ansi": "^3.0.0",
|
||||
"string-width": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/clone-response": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/clone-response/-/clone-response-1.0.3.tgz",
|
||||
"integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mimic-response": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/commander/-/commander-5.1.0.tgz",
|
||||
"integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/compare-version": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/compare-version/-/compare-version-0.1.2.tgz",
|
||||
"integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/crc": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmmirror.com/crc/-/crc-3.8.0.tgz",
|
||||
"integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"buffer": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-dirname": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/cross-dirname/-/cross-dirname-0.1.0.tgz",
|
||||
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn/node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/cross-spawn/node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/node-which"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/crypt": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/crypt/-/crypt-0.0.2.tgz",
|
||||
"integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/decompress-response": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mimic-response": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/decompress-response/node_modules/mimic-response": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/deep-is": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz",
|
||||
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/defer-to-connect": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
|
||||
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/define-data-property": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/define-properties": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz",
|
||||
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.0.1",
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
"object-keys": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-node": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz",
|
||||
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/dir-compare": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/dir-compare/-/dir-compare-4.2.0.tgz",
|
||||
"integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minimatch": "^3.0.5",
|
||||
"p-limit": "^3.1.0 "
|
||||
}
|
||||
},
|
||||
"node_modules/dir-compare/node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dir-compare/node_modules/brace-expansion": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/dir-compare/node_modules/minimatch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/dmg-builder": {
|
||||
"version": "26.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/dmg-builder/-/dmg-builder-26.8.1.tgz",
|
||||
"integrity": "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"app-builder-lib": "26.8.1",
|
||||
"builder-util": "26.8.1",
|
||||
"fs-extra": "^10.1.0",
|
||||
"iconv-lite": "^0.6.2",
|
||||
"js-yaml": "^4.1.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"dmg-license": "^1.0.11"
|
||||
}
|
||||
},
|
||||
"node_modules/dmg-license": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmmirror.com/dmg-license/-/dmg-license-1.0.11.tgz",
|
||||
"integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/plist": "^3.0.1",
|
||||
"@types/verror": "^1.10.3",
|
||||
"ajv": "^6.10.0",
|
||||
"crc": "^3.8.0",
|
||||
"iconv-corefoundation": "^1.1.7",
|
||||
"plist": "^3.0.4",
|
||||
"smart-buffer": "^4.0.2",
|
||||
"verror": "^1.10.0"
|
||||
},
|
||||
"bin": {
|
||||
"dmg-license": "bin/dmg-license.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz",
|
||||
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv-expand": {
|
||||
"version": "11.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/dotenv-expand/-/dotenv-expand-11.0.7.tgz",
|
||||
"integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"dev": true,
|
||||
"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/ejs": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmmirror.com/ejs/-/ejs-3.1.10.tgz",
|
||||
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"jake": "^10.8.5"
|
||||
},
|
||||
"bin": {
|
||||
"ejs": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron": {
|
||||
"version": "42.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/electron/-/electron-42.1.0.tgz",
|
||||
"integrity": "sha512-0szNwC/0dWtkvNce5j3ThiuL0TxBNrZN/BZhdOiGwbLreiD/+u3MGpkct4hA5Ycagb8MXjpEr5/oosi+FwuKRQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@electron/get": "^5.0.0",
|
||||
"@types/node": "^24.9.0",
|
||||
"extract-zip": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"electron": "cli.js",
|
||||
"install-electron": "install.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-builder": {
|
||||
"version": "26.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/electron-builder/-/electron-builder-26.8.1.tgz",
|
||||
"integrity": "sha512-uWhx1r74NGpCagG0ULs/P9Nqv2nsoo+7eo4fLUOB8L8MdWltq9odW/uuLXMFCDGnPafknYLZgjNX0ZIFRzOQAw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"app-builder-lib": "26.8.1",
|
||||
"builder-util": "26.8.1",
|
||||
"builder-util-runtime": "9.5.1",
|
||||
"chalk": "^4.1.2",
|
||||
"ci-info": "^4.2.0",
|
||||
"dmg-builder": "26.8.1",
|
||||
"fs-extra": "^10.1.0",
|
||||
"lazy-val": "^1.0.5",
|
||||
"simple-update-notifier": "2.0.0",
|
||||
"yargs": "^17.6.2"
|
||||
},
|
||||
"bin": {
|
||||
"electron-builder": "cli.js",
|
||||
"install-app-deps": "install-app-deps.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-builder-squirrel-windows": {
|
||||
"version": "26.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.8.1.tgz",
|
||||
"integrity": "sha512-o288fIdgPLHA76eDrFADHPoo7VyGkDCYbLV1GzndaMSAVBoZrGvM9m2IehdcVMzdAZJ2eV9bgyissQXHv5tGzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"app-builder-lib": "26.8.1",
|
||||
"builder-util": "26.8.1",
|
||||
"electron-winstaller": "5.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-publish": {
|
||||
"version": "26.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/electron-publish/-/electron-publish-26.8.1.tgz",
|
||||
"integrity": "sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/fs-extra": "^9.0.11",
|
||||
"builder-util": "26.8.1",
|
||||
"builder-util-runtime": "9.5.1",
|
||||
"chalk": "^4.1.2",
|
||||
"form-data": "^4.0.5",
|
||||
"fs-extra": "^10.1.0",
|
||||
"lazy-val": "^1.0.5",
|
||||
"mime": "^2.5.2"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-winstaller": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/electron-winstaller/-/electron-winstaller-5.4.0.tgz",
|
||||
"integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@electron/asar": "^3.2.1",
|
||||
"debug": "^4.1.1",
|
||||
"fs-extra": "^7.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"temp": "^0.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@electron/windows-sign": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-winstaller/node_modules/fs-extra": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-7.0.1.tgz",
|
||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^4.0.0",
|
||||
"universalify": "^0.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6 <7 || >=8"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-winstaller/node_modules/jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-winstaller/node_modules/universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/env-paths": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-3.0.0.tgz",
|
||||
"integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/err-code": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/err-code/-/err-code-2.0.3.tgz",
|
||||
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-error": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/es6-error/-/es6-error-4.1.1.tgz",
|
||||
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
|
||||
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-scope": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-8.4.0.tgz",
|
||||
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"esrecurse": "^4.3.0",
|
||||
"estraverse": "^5.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-visitor-keys": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
|
||||
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"bin": {
|
||||
"esparse": "bin/esparse.js",
|
||||
"esvalidate": "bin/esvalidate.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/esrecurse": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
|
||||
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"estraverse": "^5.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/estraverse": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
|
||||
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/esutils": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
|
||||
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/exponential-backoff": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/exponential-backoff/-/exponential-backoff-3.1.3.tgz",
|
||||
"integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/extract-zip": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/extract-zip/-/extract-zip-2.0.1.tgz",
|
||||
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"get-stream": "^5.1.0",
|
||||
"yauzl": "^2.10.0"
|
||||
},
|
||||
"bin": {
|
||||
"extract-zip": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.17.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@types/yauzl": "^2.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/extsprintf": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/extsprintf/-/extsprintf-1.4.1.tgz",
|
||||
"integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==",
|
||||
"dev": true,
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-levenshtein": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
||||
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fd-slicer": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz",
|
||||
"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pend": "~1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
|
||||
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"picomatch": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/filelist": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/filelist/-/filelist-1.0.6.tgz",
|
||||
"integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"minimatch": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/filelist/node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/filelist/node_modules/brace-expansion": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/filelist/node_modules/minimatch": {
|
||||
"version": "5.1.9",
|
||||
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz",
|
||||
"integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/for-each": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz",
|
||||
"integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-callable": "^1.2.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
|
||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/generator-function": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/generator-function/-/generator-function-2.0.1.tgz",
|
||||
"integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"dev": true,
|
||||
"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.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/get-stream": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz",
|
||||
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pump": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/glob/node_modules/brace-expansion": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/minimatch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/global-agent": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/global-agent/-/global-agent-3.0.0.tgz",
|
||||
"integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"boolean": "^3.0.1",
|
||||
"es6-error": "^4.1.1",
|
||||
"matcher": "^3.0.0",
|
||||
"roarr": "^2.15.3",
|
||||
"semver": "^7.3.2",
|
||||
"serialize-error": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/globalthis": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz",
|
||||
"integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"define-properties": "^1.2.1",
|
||||
"gopd": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/got": {
|
||||
"version": "11.8.6",
|
||||
"resolved": "https://registry.npmmirror.com/got/-/got-11.8.6.tgz",
|
||||
"integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sindresorhus/is": "^4.0.0",
|
||||
"@szmarczak/http-timer": "^4.0.5",
|
||||
"@types/cacheable-request": "^6.0.1",
|
||||
"@types/responselike": "^1.0.0",
|
||||
"cacheable-lookup": "^5.0.3",
|
||||
"cacheable-request": "^7.0.2",
|
||||
"decompress-response": "^6.0.0",
|
||||
"http2-wrapper": "^1.0.0-beta.5.2",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
"p-cancelable": "^2.0.0",
|
||||
"responselike": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/got?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/has-property-descriptors": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.3.tgz",
|
||||
"integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/hosted-git-info": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
|
||||
"integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/http-cache-semantics": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
|
||||
"integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/http2-wrapper": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
|
||||
"integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"quick-lru": "^5.1.1",
|
||||
"resolve-alpn": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
||||
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-corefoundation": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmmirror.com/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
|
||||
"integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"dependencies": {
|
||||
"cli-truncate": "^2.1.0",
|
||||
"node-addon-api": "^1.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^8.11.2 || >=10"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/inversify": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmmirror.com/inversify/-/inversify-7.11.0.tgz",
|
||||
"integrity": "sha512-yZDprSSr8TyVeMGI/AOV4ws6gwjX22hj9Z8/oHAVpJORY6WRFTcUzhnZtibBUHEw2U8ArvHcR+i863DplQ3Cwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@inversifyjs/common": "1.5.2",
|
||||
"@inversifyjs/container": "1.15.0",
|
||||
"@inversifyjs/core": "9.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arguments": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/is-arguments/-/is-arguments-1.2.0.tgz",
|
||||
"integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"has-tostringtag": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-buffer": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-2.0.5.tgz",
|
||||
"integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/is-callable": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz",
|
||||
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-generator-function": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.2.tgz",
|
||||
"integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.4",
|
||||
"generator-function": "^2.0.0",
|
||||
"get-proto": "^1.0.1",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"safe-regex-test": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-nan": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/is-nan/-/is-nan-1.3.2.tgz",
|
||||
"integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.0",
|
||||
"define-properties": "^1.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-node-process": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/is-node-process/-/is-node-process-1.2.0.tgz",
|
||||
"integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-regex": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz",
|
||||
"integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"gopd": "^1.2.0",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-safe-filename": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/is-safe-filename/-/is-safe-filename-0.1.1.tgz",
|
||||
"integrity": "sha512-4SrR7AdnY11LHfDKTZY1u6Ga3RuxZdl3YKWWShO5iyuG5h8QS4GD2tOb04peBJ5I7pXbR+CGBNEhTcwK+FzN3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-typed-array": {
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.15.tgz",
|
||||
"integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"which-typed-array": "^1.1.16"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/isbinaryfile": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-5.0.7.tgz",
|
||||
"integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/gjtorikian/"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmmirror.com/isexe/-/isexe-3.1.5.tgz",
|
||||
"integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/jake": {
|
||||
"version": "10.9.4",
|
||||
"resolved": "https://registry.npmmirror.com/jake/-/jake-10.9.4.tgz",
|
||||
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"async": "^3.2.6",
|
||||
"filelist": "^1.0.4",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"jake": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/javascript-obfuscator": {
|
||||
"version": "5.4.3",
|
||||
"resolved": "https://registry.npmmirror.com/javascript-obfuscator/-/javascript-obfuscator-5.4.3.tgz",
|
||||
"integrity": "sha512-G5WUgh84tJa5jtk49w+nQCxQpXIvp65xhLyaFeTAdQeYqWji+SXo89k96UvYIDqojyWZEWSDKRmaWKK6bBn/2A==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@javascript-obfuscator/escodegen": "2.4.1",
|
||||
"@javascript-obfuscator/estraverse": "5.4.0",
|
||||
"@vercel/blob": ">=0.23.0",
|
||||
"acorn": "8.15.0",
|
||||
"acorn-import-attributes": "^1.9.5",
|
||||
"assert": "2.1.0",
|
||||
"chalk": "4.1.2",
|
||||
"chance": "1.1.13",
|
||||
"class-validator": "0.14.3",
|
||||
"commander": "12.1.0",
|
||||
"env-paths": "4.0.0",
|
||||
"eslint-scope": "8.4.0",
|
||||
"eslint-visitor-keys": "4.2.1",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"inversify": "7.11.0",
|
||||
"js-string-escape": "1.0.1",
|
||||
"md5": "2.3.0",
|
||||
"multimatch": "5.0.0",
|
||||
"process": "0.11.10",
|
||||
"reflect-metadata": "0.2.2",
|
||||
"string-template": "1.0.0",
|
||||
"stringz": "2.1.0",
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
"bin": {
|
||||
"javascript-obfuscator": "bin/javascript-obfuscator"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/javascript-obfuscator/node_modules/commander": {
|
||||
"version": "12.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/commander/-/commander-12.1.0.tgz",
|
||||
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/javascript-obfuscator/node_modules/env-paths": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-4.0.0.tgz",
|
||||
"integrity": "sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-safe-filename": "^0.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/jiti": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.7.0.tgz",
|
||||
"integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jiti": "lib/jiti-cli.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/js-string-escape": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/js-string-escape/-/js-string-escape-1.0.1.tgz",
|
||||
"integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonfile": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz",
|
||||
"integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
|
||||
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"json-buffer": "3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/lazy-val": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/lazy-val/-/lazy-val-1.0.5.tgz",
|
||||
"integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/levn": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/levn/-/levn-0.3.0.tgz",
|
||||
"integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prelude-ls": "~1.1.2",
|
||||
"type-check": "~0.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/libphonenumber-js": {
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmmirror.com/libphonenumber-js/-/libphonenumber-js-1.13.3.tgz",
|
||||
"integrity": "sha512-xMkdAMqcyG7iN2WZZmGIfWbYxW4orRkny+0/AXIbwL0xll2zkDX0Vzo/BXFa6+7mh2UvJl9MbcTtHk0YXkFtBA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz",
|
||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lowercase-keys": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
|
||||
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/matcher": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/matcher/-/matcher-3.0.0.tgz",
|
||||
"integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"escape-string-regexp": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/md5": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/md5/-/md5-2.3.0.tgz",
|
||||
"integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"charenc": "0.0.2",
|
||||
"crypt": "0.0.2",
|
||||
"is-buffer": "~1.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/md5/node_modules/is-buffer": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz",
|
||||
"integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-response": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-1.0.1.tgz",
|
||||
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.2.5",
|
||||
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.5.tgz",
|
||||
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz",
|
||||
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/minizlib": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-3.1.0.tgz",
|
||||
"integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minipass": "^7.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/multimatch": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/multimatch/-/multimatch-5.0.0.tgz",
|
||||
"integrity": "sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/minimatch": "^3.0.3",
|
||||
"array-differ": "^3.0.0",
|
||||
"array-union": "^2.1.0",
|
||||
"arrify": "^2.0.1",
|
||||
"minimatch": "^3.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/multimatch/node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/multimatch/node_modules/brace-expansion": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/multimatch/node_modules/minimatch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/node-abi": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-4.31.0.tgz",
|
||||
"integrity": "sha512-Erq5w/t3syw3s4sDsUaX4QttIdBPsGKTT1DTRsCkTonGggczhlDKm/wDX3o+HPJpQ41EjXCbcmXf0tgr5YZJXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"semver": "^7.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-1.7.2.tgz",
|
||||
"integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/node-api-version": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/node-api-version/-/node-api-version-0.2.1.tgz",
|
||||
"integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"semver": "^7.3.5"
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp": {
|
||||
"version": "12.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/node-gyp/-/node-gyp-12.3.0.tgz",
|
||||
"integrity": "sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"env-paths": "^2.2.0",
|
||||
"exponential-backoff": "^3.1.1",
|
||||
"graceful-fs": "^4.2.6",
|
||||
"nopt": "^9.0.0",
|
||||
"proc-log": "^6.0.0",
|
||||
"semver": "^7.3.5",
|
||||
"tar": "^7.5.4",
|
||||
"tinyglobby": "^0.2.12",
|
||||
"undici": "^6.25.0",
|
||||
"which": "^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"node-gyp": "bin/node-gyp.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.17.0 || >=22.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp/node_modules/env-paths": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz",
|
||||
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp/node_modules/isexe": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/isexe/-/isexe-4.0.0.tgz",
|
||||
"integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp/node_modules/undici": {
|
||||
"version": "6.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/undici/-/undici-6.25.0.tgz",
|
||||
"integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.17"
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp/node_modules/which": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/which/-/which-6.0.1.tgz",
|
||||
"integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/which.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.17.0 || >=22.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nopt": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/nopt/-/nopt-9.0.0.tgz",
|
||||
"integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"abbrev": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"nopt": "bin/nopt.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.17.0 || >=22.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-url": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/normalize-url/-/normalize-url-6.1.0.tgz",
|
||||
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/object-is": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmmirror.com/object-is/-/object-is-1.1.6.tgz",
|
||||
"integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.7",
|
||||
"define-properties": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/object-keys": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz",
|
||||
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/object.assign": {
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.7.tgz",
|
||||
"integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.8",
|
||||
"call-bound": "^1.0.3",
|
||||
"define-properties": "^1.2.1",
|
||||
"es-object-atoms": "^1.0.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"object-keys": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.8.3.tgz",
|
||||
"integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"deep-is": "~0.1.3",
|
||||
"fast-levenshtein": "~2.0.6",
|
||||
"levn": "~0.3.0",
|
||||
"prelude-ls": "~1.1.2",
|
||||
"type-check": "~0.3.2",
|
||||
"word-wrap": "~1.2.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/p-cancelable": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/p-cancelable/-/p-cancelable-2.1.1.tgz",
|
||||
"integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz",
|
||||
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"yocto-queue": "^0.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pe-library": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/pe-library/-/pe-library-0.4.1.tgz",
|
||||
"integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"npm": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jet2jet"
|
||||
}
|
||||
},
|
||||
"node_modules/pend": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz",
|
||||
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/plist": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/plist/-/plist-3.1.0.tgz",
|
||||
"integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@xmldom/xmldom": "^0.8.8",
|
||||
"base64-js": "^1.5.1",
|
||||
"xmlbuilder": "^15.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/possible-typed-array-names": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
|
||||
"integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/postject": {
|
||||
"version": "1.0.0-alpha.6",
|
||||
"resolved": "https://registry.npmmirror.com/postject/-/postject-1.0.0-alpha.6.tgz",
|
||||
"integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"commander": "^9.4.0"
|
||||
},
|
||||
"bin": {
|
||||
"postject": "dist/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postject/node_modules/commander": {
|
||||
"version": "9.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/commander/-/commander-9.5.0.tgz",
|
||||
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||
"integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proc-log": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/proc-log/-/proc-log-6.1.0.tgz",
|
||||
"integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "^20.17.0 || >=22.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmmirror.com/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/progress": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz",
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/promise-retry": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/promise-retry/-/promise-retry-2.0.1.tgz",
|
||||
"integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"err-code": "^2.0.2",
|
||||
"retry": "^0.12.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/proper-lockfile": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
|
||||
"integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"retry": "^0.12.0",
|
||||
"signal-exit": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.4.tgz",
|
||||
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
|
||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/quick-lru": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-5.1.1.tgz",
|
||||
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/read-binary-file-arch": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz",
|
||||
"integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"bin": {
|
||||
"read-binary-file-arch": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect-metadata": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
|
||||
"integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resedit": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/resedit/-/resedit-1.7.2.tgz",
|
||||
"integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pe-library": "^0.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"npm": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jet2jet"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-alpn": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
|
||||
"integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/responselike": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/responselike/-/responselike-2.0.1.tgz",
|
||||
"integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lowercase-keys": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/retry": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz",
|
||||
"integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-2.6.3.tgz",
|
||||
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
|
||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/roarr": {
|
||||
"version": "2.15.4",
|
||||
"resolved": "https://registry.npmmirror.com/roarr/-/roarr-2.15.4.tgz",
|
||||
"integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"boolean": "^3.0.1",
|
||||
"detect-node": "^2.0.4",
|
||||
"globalthis": "^1.0.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"semver-compare": "^1.0.0",
|
||||
"sprintf-js": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-regex-test": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
|
||||
"integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"is-regex": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sanitize-filename": {
|
||||
"version": "1.6.4",
|
||||
"resolved": "https://registry.npmmirror.com/sanitize-filename/-/sanitize-filename-1.6.4.tgz",
|
||||
"integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==",
|
||||
"dev": true,
|
||||
"license": "WTFPL OR ISC",
|
||||
"dependencies": {
|
||||
"truncate-utf8-bytes": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.6.0.tgz",
|
||||
"integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.0.tgz",
|
||||
"integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/semver-compare": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz",
|
||||
"integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/serialize-error": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/serialize-error/-/serialize-error-7.0.1.tgz",
|
||||
"integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"type-fest": "^0.13.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/set-function-length": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.1.4",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"gopd": "^1.0.1",
|
||||
"has-property-descriptors": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/simple-update-notifier": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/slice-ansi": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-3.0.0.tgz",
|
||||
"integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"astral-regex": "^2.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/smart-buffer": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 6.0.0",
|
||||
"npm": ">= 3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.1.3.tgz",
|
||||
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/stat-mode": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/stat-mode/-/stat-mode-1.0.0.tgz",
|
||||
"integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/string-template": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/string-template/-/string-template-1.0.0.tgz",
|
||||
"integrity": "sha512-SLqR3GBUXuoPP5MmYtD7ompvXiG87QjT6lzOszyXjTM86Uu7At7vNnt2xgyTLq5o9T4IxTYFyGxcULqpsmsfdg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/stringz": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/stringz/-/stringz-2.1.0.tgz",
|
||||
"integrity": "sha512-KlywLT+MZ+v0IRepfMxRtnSvDCMc3nR1qqCs3m/qIbSOWkNZYT8XHQA31rS3TnKp0c5xjZu3M4GY/2aRKSi/6A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"char-regex": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/sumchecker": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/sumchecker/-/sumchecker-3.0.1.tgz",
|
||||
"integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "7.5.15",
|
||||
"resolved": "https://registry.npmmirror.com/tar/-/tar-7.5.15.tgz",
|
||||
"integrity": "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/fs-minipass": "^4.0.0",
|
||||
"chownr": "^3.0.0",
|
||||
"minipass": "^7.1.2",
|
||||
"minizlib": "^3.1.0",
|
||||
"yallist": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tar/node_modules/yallist": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/yallist/-/yallist-5.0.0.tgz",
|
||||
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/temp": {
|
||||
"version": "0.9.4",
|
||||
"resolved": "https://registry.npmmirror.com/temp/-/temp-0.9.4.tgz",
|
||||
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"mkdirp": "^0.5.1",
|
||||
"rimraf": "~2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/temp-file": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/temp-file/-/temp-file-3.4.0.tgz",
|
||||
"integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"async-exit-hook": "^2.0.1",
|
||||
"fs-extra": "^10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/throttleit": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/throttleit/-/throttleit-2.1.0.tgz",
|
||||
"integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/tiny-async-pool": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz",
|
||||
"integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"semver": "^5.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tiny-async-pool/node_modules/semver": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.16",
|
||||
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.16.tgz",
|
||||
"integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tmp": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.2.5.tgz",
|
||||
"integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.14"
|
||||
}
|
||||
},
|
||||
"node_modules/tmp-promise": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/tmp-promise/-/tmp-promise-3.0.3.tgz",
|
||||
"integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tmp": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/traverse": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmmirror.com/traverse/-/traverse-0.3.9.tgz",
|
||||
"integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==",
|
||||
"license": "MIT/X11",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/truncate-utf8-bytes": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
|
||||
"integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==",
|
||||
"dev": true,
|
||||
"license": "WTFPL",
|
||||
"dependencies": {
|
||||
"utf8-byte-length": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"dev": true,
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.3.2.tgz",
|
||||
"integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prelude-ls": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "0.13.1",
|
||||
"resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.13.1.tgz",
|
||||
"integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
|
||||
"dev": true,
|
||||
"license": "(MIT OR CC0-1.0)",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "7.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/undici/-/undici-7.25.0.tgz",
|
||||
"integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=20.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
|
||||
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unzip-stream": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmmirror.com/unzip-stream/-/unzip-stream-0.3.4.tgz",
|
||||
"integrity": "sha512-PyofABPVv+d7fL7GOpusx7eRT9YETY2X04PhwbSipdj6bMxVCFJrr+nm0Mxqbf9hUiTin/UsnuFWBXlDZFy0Cw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"binary": "^0.3.0",
|
||||
"mkdirp": "^0.5.1"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utf8-byte-length": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
|
||||
"integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==",
|
||||
"dev": true,
|
||||
"license": "(WTFPL OR MIT)"
|
||||
},
|
||||
"node_modules/util": {
|
||||
"version": "0.12.5",
|
||||
"resolved": "https://registry.npmmirror.com/util/-/util-0.12.5.tgz",
|
||||
"integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"is-arguments": "^1.0.4",
|
||||
"is-generator-function": "^1.0.7",
|
||||
"is-typed-array": "^1.1.3",
|
||||
"which-typed-array": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/validator": {
|
||||
"version": "13.15.35",
|
||||
"resolved": "https://registry.npmmirror.com/validator/-/validator-13.15.35.tgz",
|
||||
"integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/verror": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmmirror.com/verror/-/verror-1.10.1.tgz",
|
||||
"integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
"extsprintf": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/which/-/which-5.0.0.tgz",
|
||||
"integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/which.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which-typed-array": {
|
||||
"version": "1.1.20",
|
||||
"resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.20.tgz",
|
||||
"integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"available-typed-arrays": "^1.0.7",
|
||||
"call-bind": "^1.0.8",
|
||||
"call-bound": "^1.0.4",
|
||||
"for-each": "^0.3.5",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-tostringtag": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/xmlbuilder": {
|
||||
"version": "15.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
||||
"integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs-parser": {
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yauzl": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz",
|
||||
"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-crc32": "~0.2.3",
|
||||
"fd-slicer": "~1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"name": "gpcl",
|
||||
"version": "1.4.1",
|
||||
"description": "GoodPlanCraftLauncher",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"clean": "if (Test-Path build) { Remove-Item -Recurse -Force build }",
|
||||
"copy-files": "node scripts/copy.js",
|
||||
"copy-static": "node -e \"const p=require('./package.json');console.log(JSON.stringify({name:p.name,version:p.version,main:p.main,type:p.type}))\" > build/app/package.json",
|
||||
"obfuscate": "javascript-obfuscator build/app --config obfuscator.json --exclude node_modules",
|
||||
"build": "npm run copy-files && npm run obfuscate && npm run copy-static && electron-builder --publish=never",
|
||||
"start:dev": "electron .",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"devDependencies": {
|
||||
"electron": "^42.1.0",
|
||||
"electron-builder": "^26.8.1",
|
||||
"javascript-obfuscator": "^5.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.5.17",
|
||||
"unzip-stream": "^0.3.4"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.caellab.gamets.gpcl",
|
||||
"productName": "GoodPlanCraftLauncher",
|
||||
"icon": "gpcl-icon.ico",
|
||||
"directories": {
|
||||
"app": "build/app",
|
||||
"output": "dist"
|
||||
},
|
||||
"asar": true,
|
||||
"win": {
|
||||
"target": [
|
||||
{
|
||||
"target": "nsis",
|
||||
"arch": ["x64"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"perMachine": false,
|
||||
"installerIcon": "icon.ico",
|
||||
"uninstallerIcon": "icon.ico",
|
||||
"installerHeaderIcon": "icon.ico",
|
||||
"createDesktopShortcut": true,
|
||||
"createStartMenuShortcut": true,
|
||||
"shortcutName": "GoodPlanCraftLauncher",
|
||||
"artifactName": "GPCL-${version}-Setup.${ext}",
|
||||
"deleteAppDataOnUninstall": false
|
||||
},
|
||||
"files": [
|
||||
"**/*",
|
||||
"!.minecraft/**"
|
||||
],
|
||||
"asarUnpack": [
|
||||
"node_modules/adm-zip/**/*",
|
||||
"node_modules/unzip-stream/**/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* CaelLab BY-SA Code License
|
||||
* Copyright (c) 2026 Yunyun(云云) By 虚舟实验室(CaelLab) / CaelLabGameTS
|
||||
* Source: https://git.caellab.com/yunyun/GoodPlanCraftLauncher
|
||||
* Source: https://github.com/yunyun-3782/GoodPlanCraftLauncher
|
||||
*/
|
||||
|
||||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('gpcl', {
|
||||
// 获取游戏目录路径
|
||||
getGameDir: () => ipcRenderer.invoke('get-game-dir'),
|
||||
|
||||
// 扫描本地已安装版本
|
||||
scanVersions: (gameDir, silent) => ipcRenderer.invoke('scan-versions', gameDir, silent),
|
||||
|
||||
// 启动游戏
|
||||
launch: (options) => ipcRenderer.invoke('launch-minecraft', options),
|
||||
|
||||
// 获取远程版本列表
|
||||
getVersionManifest: () => ipcRenderer.invoke('get-version-manifest'),
|
||||
|
||||
// 下载指定版本
|
||||
downloadVersion: (versionId, maxConcurrent) => ipcRenderer.invoke('download-version', versionId, maxConcurrent),
|
||||
|
||||
// 监听游戏日志
|
||||
onGameLog: (callback) => {
|
||||
ipcRenderer.on('game-log', (event, text) => callback(text));
|
||||
},
|
||||
|
||||
// 监听游戏关闭
|
||||
onGameClosed: (callback) => {
|
||||
ipcRenderer.on('game-closed', (event, code) => callback(code));
|
||||
},
|
||||
|
||||
// 监听启动错误
|
||||
onGameError: (callback) => {
|
||||
ipcRenderer.on('game-error', (event, message) => callback(message));
|
||||
},
|
||||
|
||||
// 监听下载进度
|
||||
onDownloadProgress: (callback) => {
|
||||
ipcRenderer.on('download-progress', (event, data) => callback(data));
|
||||
},
|
||||
|
||||
// 监听关闭确认询问
|
||||
onConfirmCloseWhileDownloading: (callback) => {
|
||||
ipcRenderer.on('confirm-close-while-downloading', () => callback());
|
||||
},
|
||||
|
||||
// 读取玩家名
|
||||
getPlayerName: () => ipcRenderer.invoke('get-player-name'),
|
||||
|
||||
// 保存玩家名
|
||||
savePlayerName: (name) => ipcRenderer.invoke('save-player-name', name),
|
||||
|
||||
// 显示消息对话框
|
||||
showMessageDialog: (options) => ipcRenderer.invoke('show-message-dialog', options),
|
||||
|
||||
// 确认关闭并清理下载
|
||||
confirmCloseDownload: () => ipcRenderer.invoke('confirm-close-download'),
|
||||
|
||||
// 取消下载(只清理,不关闭窗口)
|
||||
cancelDownloadOnly: () => ipcRenderer.invoke('cancel-download-only'),
|
||||
|
||||
// 获取Java镜像源设置
|
||||
getJavaMirrorSettings: () => ipcRenderer.invoke('get-java-mirror-settings'),
|
||||
|
||||
// 获取Java版本对应的下载URL
|
||||
getJavaDownloadUrl: (javaVersion) => ipcRenderer.invoke('get-java-download-url', javaVersion),
|
||||
|
||||
// 取消关闭
|
||||
cancelClose: () => ipcRenderer.invoke('cancel-close'),
|
||||
|
||||
// 检查Java是否已安装
|
||||
checkJava: (versionId) => ipcRenderer.invoke('check-java', versionId),
|
||||
|
||||
// 显示Java安装对话框
|
||||
showJavaInstallDialog: (callback) => {
|
||||
ipcRenderer.on('show-java-install-dialog', (event, data) => callback(data));
|
||||
},
|
||||
|
||||
// 选择Java版本
|
||||
selectJavaVersion: (data) => ipcRenderer.invoke('select-java-version', data),
|
||||
|
||||
// 安装Java
|
||||
installJava: (javaVersion) => ipcRenderer.invoke('install-java', javaVersion),
|
||||
|
||||
// 卸载Java
|
||||
uninstallJava: (javaVersion) => ipcRenderer.invoke('uninstall-java', javaVersion),
|
||||
|
||||
// 监听Java安装对话框
|
||||
onJavaInstallDialog: (callback) => {
|
||||
ipcRenderer.on('show-java-install-dialog', (event, data) => callback(data));
|
||||
},
|
||||
|
||||
// 监听Java下载完成
|
||||
onJavaDownloadCompleted: (callback) => {
|
||||
ipcRenderer.on('java-download-completed', (event, data) => callback(data));
|
||||
},
|
||||
|
||||
// 监听Java下载失败
|
||||
onJavaDownloadFailed: (callback) => {
|
||||
ipcRenderer.on('java-download-failed', (event, data) => callback(data));
|
||||
},
|
||||
|
||||
// 监听游戏窗口创建成功
|
||||
onGameWindowCreated: (callback) => {
|
||||
ipcRenderer.on('game-window-created', (event, data) => callback(data));
|
||||
},
|
||||
|
||||
// 移除所有监听器(避免重复绑定)
|
||||
minimizeWindow: () => ipcRenderer.invoke('window-minimize'),
|
||||
focusWindow: () => ipcRenderer.invoke('window-focus'),
|
||||
closeWindow: () => ipcRenderer.invoke('window-close'),
|
||||
deleteVersion: (versionId) => ipcRenderer.invoke('delete-version', versionId),
|
||||
getVersionSettings: (versionId) => ipcRenderer.invoke('get-version-settings', versionId),
|
||||
saveVersionSettings: (versionId, settings) => ipcRenderer.invoke('save-version-settings', versionId, settings),
|
||||
selectDirectory: () => ipcRenderer.invoke('select-directory'),
|
||||
cancelLaunch: () => ipcRenderer.invoke('cancel-launch'),
|
||||
checkForUpdates: () => ipcRenderer.invoke('check-for-updates'),
|
||||
getAppVersion: () => ipcRenderer.invoke('get-app-version'),
|
||||
openExternal: (url) => ipcRenderer.invoke('open-external', url),
|
||||
|
||||
// 获取设置
|
||||
getSettings: () => ipcRenderer.invoke('get-settings'),
|
||||
|
||||
// 保存设置
|
||||
saveSettings: (settings) => ipcRenderer.invoke('save-settings', settings),
|
||||
|
||||
// 重置设置
|
||||
resetSettings: () => ipcRenderer.invoke('reset-settings'),
|
||||
|
||||
// 播放启动动画
|
||||
playStartupAnimation: () => ipcRenderer.invoke('play-startup-animation'),
|
||||
|
||||
// 关闭启动动画
|
||||
dismissStartupAnimation: () => ipcRenderer.invoke('dismiss-startup-animation'),
|
||||
|
||||
// 开发者模式
|
||||
setDeveloperMode: (enabled) => ipcRenderer.invoke('set-developer-mode', enabled),
|
||||
|
||||
// 重启应用
|
||||
restartApp: () => ipcRenderer.invoke('restart-app'),
|
||||
|
||||
// 文件读写
|
||||
readJsonFile: (subPath, fileName) => ipcRenderer.invoke('read-json-file', subPath, fileName),
|
||||
writeJsonFile: (subPath, fileName, data) => ipcRenderer.invoke('write-json-file', subPath, fileName, data),
|
||||
|
||||
// 模组加载器版本获取
|
||||
getForgeVersions: (mcVersion) => ipcRenderer.invoke('get-forge-versions', mcVersion),
|
||||
getFabricVersions: (mcVersion) => ipcRenderer.invoke('get-fabric-versions', mcVersion),
|
||||
getOptiFineVersions: (mcVersion) => ipcRenderer.invoke('get-optifine-versions', mcVersion),
|
||||
|
||||
// 下载带模组加载器的版本
|
||||
downloadWithModLoader: (versionId, loaderType, loaderVersion, maxConcurrent) =>
|
||||
ipcRenderer.invoke('download-with-modloader', versionId, loaderType, loaderVersion, maxConcurrent),
|
||||
|
||||
// 读取版本配置
|
||||
getVersionConfig: (versionId) => ipcRenderer.invoke('get-version-config', versionId),
|
||||
getVersionDisplayName: (versionId) => ipcRenderer.invoke('get-version-display-name', versionId),
|
||||
getLaunchDisplayName: (versionId) => ipcRenderer.invoke('get-launch-display-name', versionId),
|
||||
|
||||
removeAllListeners: () => {
|
||||
ipcRenderer.removeAllListeners('game-log');
|
||||
ipcRenderer.removeAllListeners('game-closed');
|
||||
ipcRenderer.removeAllListeners('game-error');
|
||||
ipcRenderer.removeAllListeners('download-progress');
|
||||
ipcRenderer.removeAllListeners('confirm-close-while-downloading');
|
||||
ipcRenderer.removeAllListeners('show-java-install-dialog');
|
||||
ipcRenderer.removeAllListeners('java-download-completed');
|
||||
ipcRenderer.removeAllListeners('java-download-failed');
|
||||
ipcRenderer.removeAllListeners('game-window-created');
|
||||
}
|
||||
});
|
||||
|
||||
// 屏蔽开发者工具快捷键(生产环境)
|
||||
window.addEventListener('keydown', e => {
|
||||
const key = (e.key || '').toLowerCase();
|
||||
if (key === 'f12' ||
|
||||
(e.ctrlKey && e.shiftKey && key === 'i') ||
|
||||
(e.metaKey && e.altKey && key === 'i')) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}, { capture: true });
|
||||
|
||||
window.addEventListener('contextmenu', e => {
|
||||
e.preventDefault();
|
||||
}, { capture: true });
|
||||
|
|
@ -0,0 +1,885 @@
|
|||
<!--
|
||||
CaelLab BY-SA Code License
|
||||
Copyright (c) 2026 Yunyun(云云) By 虚舟实验室(CaelLab) / CaelLabGameTS
|
||||
Source: https://git.caellab.com/yunyun/GoodPlanCraftLauncher
|
||||
Source: https://github.com/yunyun-3782/GoodPlanCraftLauncher
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>GoodPlanCraftLauncher</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-layout">
|
||||
<div class="titlebar">
|
||||
<div class="titlebar-left">
|
||||
<div class="title">GPCL</div>
|
||||
</div>
|
||||
<div class="titlebar-center">
|
||||
<nav class="menu-bar">
|
||||
<ul class="menu">
|
||||
<li id="menu-launch" class="menu-item"><span class="menu-icon">▶</span>启动</li>
|
||||
<li id="menu-download" class="menu-item"><span class="menu-icon">⬇</span>下载</li>
|
||||
<li id="menu-settings" class="menu-item"><span class="menu-icon">⚙</span>设置</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="window-controls">
|
||||
<button id="minimize-btn" class="min-btn" aria-label="最小化">—</button>
|
||||
<button id="close-btn" class="close-btn" aria-label="关闭">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main class="main-content">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
|
||||
</div>
|
||||
|
||||
<div id="launch-panel" class="launch-panel">
|
||||
<div class="launch-sidebar">
|
||||
<div class="auth-tabs">
|
||||
<button id="auth-microsoft" class="auth-tab auth-tab-disabled" disabled>
|
||||
<span class="auth-label">🔷 正版</span>
|
||||
</button>
|
||||
<button id="auth-offline" class="auth-tab auth-tab-active">
|
||||
<span class="auth-label">👤 离线</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="skin-container">
|
||||
<div class="skin-wrapper">
|
||||
<img src="skin/Alex.png" alt="Skin" class="skin-image">
|
||||
</div>
|
||||
<div class="skin-reflection"></div>
|
||||
</div>
|
||||
|
||||
<div class="username-input-group">
|
||||
<label for="username" class="username-label"></label>
|
||||
<input type="text" id="username" placeholder="输入用户名" value="GPCL_Player" class="username-input">
|
||||
</div>
|
||||
|
||||
<button id="launch-btn" class="launch-button">
|
||||
<span class="launch-text">▶ 开始游戏</span>
|
||||
</button>
|
||||
|
||||
<div id="selected-version-display" class="selected-version-display hidden">
|
||||
<span class="version-display-label">将启动:</span>
|
||||
<span id="selected-version-text" class="version-display-text">-</span>
|
||||
</div>
|
||||
|
||||
<div class="launch-footer">
|
||||
<button id="version-settings" class="footer-btn">
|
||||
<span class="footer-text">版本设置</span>
|
||||
</button>
|
||||
<button id="version-select" class="footer-btn">
|
||||
<span class="footer-text">版本选择</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="launch-content">
|
||||
<div id="version-settings-panel" class="version-settings-panel hidden">
|
||||
<div class="version-settings-header">
|
||||
<button class="version-settings-back" id="version-settings-back-btn">
|
||||
<span>←</span>
|
||||
</button>
|
||||
<h3 class="version-settings-title" id="version-settings-title">版本设置</h3>
|
||||
</div>
|
||||
|
||||
<div class="version-settings-body">
|
||||
<div class="version-settings-section">
|
||||
<h4 class="section-title">版本操作</h4>
|
||||
|
||||
<div class="setting-item">
|
||||
<div class="setting-info">
|
||||
<div class="setting-label">删除版本</div>
|
||||
<div class="setting-desc">删除此版本的全部文件</div>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="version-delete-enable">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="version-settings-section">
|
||||
<h4 class="section-title">游戏设置</h4>
|
||||
<p class="section-desc">由全局设置托管表示使用全局设置</p>
|
||||
|
||||
<div class="setting-item">
|
||||
<div class="setting-info">
|
||||
<div class="setting-label">游戏最大内存</div>
|
||||
<div class="setting-desc">设置游戏分配的最大内存(GB)</div>
|
||||
</div>
|
||||
<div class="custom-select" data-select-id="version-memory">
|
||||
<div class="custom-select-trigger">
|
||||
<span class="custom-select-value">由全局设置托管</span>
|
||||
<span class="custom-select-arrow">▾</span>
|
||||
</div>
|
||||
<div class="custom-select-dropdown">
|
||||
<div class="custom-select-option selected" data-value="global">由全局设置托管</div>
|
||||
<div class="custom-select-option" data-value="0.5">512 MB</div>
|
||||
<div class="custom-select-option" data-value="1">1 GB</div>
|
||||
<div class="custom-select-option" data-value="2">2 GB</div>
|
||||
<div class="custom-select-option" data-value="4">4 GB</div>
|
||||
<div class="custom-select-option" data-value="6">6 GB</div>
|
||||
<div class="custom-select-option" data-value="8">8 GB</div>
|
||||
<div class="custom-select-option" data-value="16">16 GB</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<div class="setting-info">
|
||||
<div class="setting-label">窗口模式</div>
|
||||
<div class="setting-desc">设置游戏启动时的窗口模式</div>
|
||||
</div>
|
||||
<div class="custom-select" data-select-id="version-window-mode">
|
||||
<div class="custom-select-trigger">
|
||||
<span class="custom-select-value">由全局设置托管</span>
|
||||
<span class="custom-select-arrow">▾</span>
|
||||
</div>
|
||||
<div class="custom-select-dropdown">
|
||||
<div class="custom-select-option selected" data-value="global">由全局设置托管</div>
|
||||
<div class="custom-select-option" data-value="fullscreen">全屏</div>
|
||||
<div class="custom-select-option" data-value="windowed">窗口</div>
|
||||
<div class="custom-select-option" data-value="borderless">无边框窗口</div>
|
||||
<div class="custom-select-option" data-value="minimized">最小化</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<div class="setting-info">
|
||||
<div class="setting-label">默认启动状态</div>
|
||||
<div class="setting-desc">设置开始游戏按钮的默认状态</div>
|
||||
</div>
|
||||
<div class="custom-select" data-select-id="version-startup-mode">
|
||||
<div class="custom-select-trigger">
|
||||
<span class="custom-select-value">主界面(常规)</span>
|
||||
<span class="custom-select-arrow">▾</span>
|
||||
</div>
|
||||
<div class="custom-select-dropdown">
|
||||
<div class="custom-select-option selected" data-value="default">主界面(常规)</div>
|
||||
<div class="custom-select-option" data-value="join">加入服务器</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-item" id="version-server-ip-container">
|
||||
<div class="setting-info">
|
||||
<div class="setting-label">服务器IP/域名</div>
|
||||
<div class="setting-desc">默认要加入的服务器地址</div>
|
||||
</div>
|
||||
<input type="text" id="version-server-ip" class="setting-input" placeholder="caellab.com:25565">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="version-select-panel" class="version-select-panel hidden">
|
||||
<div class="version-panel-header">
|
||||
<h3 class="version-panel-title">版本选择</h3>
|
||||
<div id="version-count" class="version-count">加载中...</div>
|
||||
</div>
|
||||
<div id="launch-version-list" class="version-list-container">
|
||||
<div class="version-list-loading">正在扫描本地版本...</div>
|
||||
</div>
|
||||
<div id="selected-version-info" class="selected-version-info hidden">
|
||||
<div class="selected-version-label">已选择:</div>
|
||||
<div id="selected-version-name" class="selected-version-name">-</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="status" class="status">准备就绪</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="download-page" class="download-page hidden">
|
||||
<div class="download-header">
|
||||
<h3>下载</h3>
|
||||
<button id="refresh-btn-page" class="refresh-btn hidden">刷新版本列表</button>
|
||||
</div>
|
||||
|
||||
<div id="download-panel" class="download-status-panel hidden">
|
||||
<div class="download-stats">
|
||||
<div class="chart-container">
|
||||
<canvas id="speedChart"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="progress-section">
|
||||
<div class="speed-info">
|
||||
<div class="speed-item">
|
||||
<span class="speed-label">当前</span>
|
||||
<span id="current-speed" class="speed-value">0.00 Mbps</span>
|
||||
</div>
|
||||
<div class="speed-item">
|
||||
<span class="speed-label">峰值</span>
|
||||
<span id="peak-speed" class="speed-value">0.00 Mbps</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress-bar-wrapper">
|
||||
<div class="progress-bar-bg">
|
||||
<div id="progress-fill" class="progress-bar-fill"></div>
|
||||
</div>
|
||||
<div id="progress-text" class="progress-text">0%</div>
|
||||
</div>
|
||||
|
||||
<div id="download-filename" class="download-filename">文件名</div>
|
||||
|
||||
<button id="cancel-download-btn" class="cancel-download-btn">取消下载</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="download-status" class="status-message"></div>
|
||||
|
||||
<div id="version-list-section" class="version-list-section">
|
||||
<div class="version-list-header">
|
||||
<h4 class="section-title">游戏版本</h4>
|
||||
<span id="version-count-label" class="version-count-label">加载中...</span>
|
||||
</div>
|
||||
<div id="version-list-container" class="version-list-container">
|
||||
<div class="version-list-loading">正在加载版本列表...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="version-detail-page" class="version-detail-page hidden">
|
||||
<button id="detail-back-btn" class="detail-back-btn">← 返回列表</button>
|
||||
<div class="detail-content">
|
||||
<div class="detail-header">
|
||||
<div class="detail-version-id" id="detail-version-id">-</div>
|
||||
<div class="detail-version-type" id="detail-version-type">-</div>
|
||||
</div>
|
||||
<div class="detail-info-grid">
|
||||
<div class="detail-info-item">
|
||||
<span class="detail-info-label">版本类型</span>
|
||||
<span class="detail-info-value" id="detail-type-label">-</span>
|
||||
</div>
|
||||
<div class="detail-info-item">
|
||||
<span class="detail-info-label">发布时间</span>
|
||||
<span class="detail-info-value" id="detail-release-time">-</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-options" id="mod-loader-options">
|
||||
<div class="detail-option-card" id="forge-option" data-loader="forge">
|
||||
<div class="detail-option-header">
|
||||
<div class="detail-option-left">
|
||||
<span class="detail-option-icon">⚙️</span>
|
||||
<div class="detail-option-text">
|
||||
<span class="detail-option-name">Forge</span>
|
||||
<span class="detail-option-desc">Forge 模组加载器</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-option-right">
|
||||
<span class="detail-option-badge" id="forge-badge">检测中...</span>
|
||||
<span class="detail-option-arrow">▼</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-option-content hidden" id="forge-content">
|
||||
<div class="detail-option-loading" id="forge-loading">正在检测可用版本...</div>
|
||||
<div class="detail-option-versions hidden" id="forge-versions"></div>
|
||||
<div class="detail-option-error hidden" id="forge-error">该版本暂不支持 Forge</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-option-card" id="fabric-option" data-loader="fabric">
|
||||
<div class="detail-option-header">
|
||||
<div class="detail-option-left">
|
||||
<span class="detail-option-icon">🧵</span>
|
||||
<div class="detail-option-text">
|
||||
<span class="detail-option-name">Fabric</span>
|
||||
<span class="detail-option-desc">Fabric 模组加载器</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-option-right">
|
||||
<span class="detail-option-badge" id="fabric-badge">检测中...</span>
|
||||
<span class="detail-option-arrow">▼</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-option-content hidden" id="fabric-content">
|
||||
<div class="detail-option-loading" id="fabric-loading">正在检测可用版本...</div>
|
||||
<div class="detail-option-versions hidden" id="fabric-versions"></div>
|
||||
<div class="detail-option-error hidden" id="fabric-error">该版本暂不支持 Fabric</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-option-card" id="optifine-option" data-loader="optifine">
|
||||
<div class="detail-option-header">
|
||||
<div class="detail-option-left">
|
||||
<span class="detail-option-icon">👁️</span>
|
||||
<div class="detail-option-text">
|
||||
<span class="detail-option-name">OptiFine</span>
|
||||
<span class="detail-option-desc">OptiFine 高清修复</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-option-right">
|
||||
<span class="detail-option-badge" id="optifine-badge">检测中...</span>
|
||||
<span class="detail-option-arrow">▼</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-option-content hidden" id="optifine-content">
|
||||
<div class="detail-option-loading" id="optifine-loading">正在检测可用版本...</div>
|
||||
<div class="detail-option-versions hidden" id="optifine-versions"></div>
|
||||
<div class="detail-option-error hidden" id="optifine-error">该版本暂不支持 OptiFine</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="detail-download-btn" class="detail-download-btn">下载</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="java-download-section" class="java-download-section">
|
||||
<h4 class="section-title">Java 运行时</h4>
|
||||
<p class="section-desc">下载并安装 Minecraft 所需的 Java 运行时环境</p>
|
||||
<div id="java-install-status" class="java-install-status"></div>
|
||||
<div class="java-version-selector">
|
||||
<div class="java-version-grid" id="java-version-grid">
|
||||
<div class="java-version-card" data-version="8">
|
||||
<div class="java-version-header">
|
||||
<span class="java-version-num">Java 8</span>
|
||||
<span class="java-version-badge" id="java-8-status"></span>
|
||||
</div>
|
||||
<div class="java-version-info">
|
||||
<div class="java-info-row">
|
||||
<span class="info-label">适用版本:</span>
|
||||
<span class="info-value">Minecraft 1.7.10 - 1.16.5</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<button class="java-install-btn" data-version="8">安装</button>
|
||||
</div>
|
||||
|
||||
<div class="java-version-card" data-version="17">
|
||||
<div class="java-version-header">
|
||||
<span class="java-version-num">Java 17</span>
|
||||
<span class="java-version-badge" id="java-17-status">推荐</span>
|
||||
</div>
|
||||
<div class="java-version-info">
|
||||
<div class="java-info-row">
|
||||
<span class="info-label">适用版本:</span>
|
||||
<span class="info-value">Minecraft 1.17 - 1.20.4</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<button class="java-install-btn" data-version="17">安装</button>
|
||||
</div>
|
||||
|
||||
<div class="java-version-card" data-version="21">
|
||||
<div class="java-version-header">
|
||||
<span class="java-version-num">Java 21</span>
|
||||
<span class="java-version-badge" id="java-21-status"></span>
|
||||
</div>
|
||||
<div class="java-version-info">
|
||||
<div class="java-info-row">
|
||||
<span class="info-label">适用版本:</span>
|
||||
<span class="info-value">Minecraft 1.20.5 - 1.21+</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<button class="java-install-btn" data-version="21">安装</button>
|
||||
</div>
|
||||
|
||||
<div class="java-version-card" data-version="25">
|
||||
<div class="java-version-header">
|
||||
<span class="java-version-num">Java 25</span>
|
||||
<span class="java-version-badge" id="java-25-status"></span>
|
||||
</div>
|
||||
<div class="java-version-info">
|
||||
<div class="java-info-row">
|
||||
<span class="info-label">适用版本:</span>
|
||||
<span class="info-value">Minecraft 1.21+ (最新)</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<button class="java-install-btn" data-version="25">安装</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="settings-page" class="settings-page hidden">
|
||||
<div class="settings-sidebar" id="settings-sidebar">
|
||||
<ul class="settings-sidebar-menu">
|
||||
<li id="settings-tab-game" class="settings-sidebar-item active" data-tab="game">
|
||||
<span class="sidebar-icon">🎮</span>
|
||||
<span class="sidebar-label">游戏</span>
|
||||
</li>
|
||||
<li id="settings-tab-compatibility" class="settings-sidebar-item" data-tab="compatibility">
|
||||
<span class="sidebar-icon">🔧</span>
|
||||
<span class="sidebar-label">兼容性</span>
|
||||
</li>
|
||||
<li id="settings-tab-appearance" class="settings-sidebar-item" data-tab="appearance">
|
||||
<span class="sidebar-icon">🎨</span>
|
||||
<span class="sidebar-label">外观</span>
|
||||
</li>
|
||||
<li id="settings-tab-download" class="settings-sidebar-item" data-tab="download">
|
||||
<span class="sidebar-icon">⬇</span>
|
||||
<span class="sidebar-label">下载</span>
|
||||
</li>
|
||||
<li id="settings-tab-advanced" class="settings-sidebar-item" data-tab="advanced">
|
||||
<span class="sidebar-icon">⚙️</span>
|
||||
<span class="sidebar-label">高级</span>
|
||||
</li>
|
||||
<li id="settings-tab-about" class="settings-sidebar-item" data-tab="about">
|
||||
<span class="sidebar-icon">ℹ️</span>
|
||||
<span class="sidebar-label">关于</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="settings-content">
|
||||
<!-- 游戏设置 -->
|
||||
<div id="settings-content-game" class="settings-content-panel active">
|
||||
<div class="settings-page-inner">
|
||||
<div class="settings-group">
|
||||
<h2 class="settings-group-title">游戏设置</h2>
|
||||
<p class="settings-group-desc">配置游戏运行相关参数</p>
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">游戏内存</div>
|
||||
<div class="setting-desc">设置游戏分配的最大内存(GB)</div>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<div class="custom-select" data-select-id="settings-memory">
|
||||
<div class="custom-select-trigger">
|
||||
<span class="custom-select-value">2 GB</span>
|
||||
<span class="custom-select-arrow">▾</span>
|
||||
</div>
|
||||
<div class="custom-select-dropdown">
|
||||
<div class="custom-select-option" data-value="0.5">512 MB</div>
|
||||
<div class="custom-select-option" data-value="1">1 GB</div>
|
||||
<div class="custom-select-option selected" data-value="2">2 GB</div>
|
||||
<div class="custom-select-option" data-value="4">4 GB</div>
|
||||
<div class="custom-select-option" data-value="6">6 GB</div>
|
||||
<div class="custom-select-option" data-value="8">8 GB</div>
|
||||
<div class="custom-select-option" data-value="16">16 GB</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">窗口模式</div>
|
||||
<div class="setting-desc">设置游戏启动时的窗口模式</div>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<div class="custom-select" data-select-id="settings-window-mode">
|
||||
<div class="custom-select-trigger">
|
||||
<span class="custom-select-value">窗口</span>
|
||||
<span class="custom-select-arrow">▾</span>
|
||||
</div>
|
||||
<div class="custom-select-dropdown">
|
||||
<div class="custom-select-option" data-value="fullscreen">全屏</div>
|
||||
<div class="custom-select-option selected" data-value="windowed">窗口</div>
|
||||
<div class="custom-select-option" data-value="borderless">无边框窗口</div>
|
||||
<div class="custom-select-option" data-value="minimized">最小化</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 兼容性设置 -->
|
||||
<div id="settings-content-compatibility" class="settings-content-panel hidden">
|
||||
<div class="settings-page-inner">
|
||||
<div class="settings-group">
|
||||
<h2 class="settings-group-title">兼容性设置</h2>
|
||||
<p class="settings-group-desc">解决启动器兼容性问题</p>
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">恢复默认设置</div>
|
||||
<div class="setting-desc">将所有设置恢复为默认值,此操作会重启启动器</div>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="settings-reset-default">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 外观设置 -->
|
||||
<div id="settings-content-appearance" class="settings-content-panel hidden">
|
||||
<div class="settings-page-inner">
|
||||
<div class="settings-group">
|
||||
<h2 class="settings-group-title">外观设置</h2>
|
||||
<p class="settings-group-desc">配置启动器外观相关参数</p>
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">主题模式</div>
|
||||
<div class="setting-desc">选择启动器主题</div>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<div class="custom-select" data-select-id="settings-theme">
|
||||
<div class="custom-select-trigger">
|
||||
<span class="custom-select-value">浅色模式</span>
|
||||
<span class="custom-select-arrow">▾</span>
|
||||
</div>
|
||||
<div class="custom-select-dropdown">
|
||||
<div class="custom-select-option" data-value="dark">深色模式</div>
|
||||
<div class="custom-select-option selected" data-value="light">浅色模式</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">界面缩放</div>
|
||||
<div class="setting-desc">调整界面元素大小</div>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<div class="custom-select" data-select-id="settings-scale">
|
||||
<div class="custom-select-trigger">
|
||||
<span class="custom-select-value">100% (默认)</span>
|
||||
<span class="custom-select-arrow">▾</span>
|
||||
</div>
|
||||
<div class="custom-select-dropdown">
|
||||
<div class="custom-select-option" data-value="75">75%</div>
|
||||
<div class="custom-select-option" data-value="90">90%</div>
|
||||
<div class="custom-select-option selected" data-value="100">100%</div>
|
||||
<div class="custom-select-option" data-value="110">110%</div>
|
||||
<div class="custom-select-option" data-value="125">125%</div>
|
||||
<div class="custom-select-option" data-value="150">150%</div>
|
||||
<div class="custom-select-option" data-value="180">180%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下载设置 -->
|
||||
<div id="settings-content-download" class="settings-content-panel hidden">
|
||||
<div class="settings-page-inner">
|
||||
<div class="settings-group">
|
||||
<h2 class="settings-group-title">下载设置</h2>
|
||||
<p class="settings-group-desc">配置Minecraft版本下载相关参数</p>
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">最大线程数</div>
|
||||
<div class="setting-desc">在下载时,由于小文件较多,需要使用多线程下载提高速度,此处数值越高,代表可建立的连接数越高,但越消耗性能。建议值:64-128。</div>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<button class="num-btn" id="btn-minus">−</button>
|
||||
<input type="number" class="num-display" id="num-threads" value="64" min="1" max="128">
|
||||
<button class="num-btn" id="btn-plus">+</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Java镜像源设置 -->
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">Java 镜像源</div>
|
||||
<div class="setting-desc">选择Java运行时的下载镜像源</div>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<div class="custom-select" data-select-id="java-mirror-select">
|
||||
<div class="custom-select-trigger">
|
||||
<span class="custom-select-value">清华大学镜像(推荐)</span>
|
||||
<span class="custom-select-arrow">▾</span>
|
||||
</div>
|
||||
<div class="custom-select-dropdown">
|
||||
<div class="custom-select-option selected" data-value="tsinghua">清华大学镜像(推荐)</div>
|
||||
<div class="custom-select-option" data-value="custom">自定义镜像源</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="custom-java-mirror-container" class="setting-row hidden">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">自定义镜像源地址</div>
|
||||
<div class="setting-desc">支持使用 {v} 变量代表Java版本,例如:https://example.com/java/{v}/OpenJDK{v}U-jre_x64_windows_hotspot_{v}.zip<br>或不使用变量:https://example.com/java/OpenJDK8U-jre_x64_windows_hotspot_8u402b06.zip</div>
|
||||
</div>
|
||||
<div class="setting-control" style="flex-direction: column; align-items: flex-start;">
|
||||
<input type="text" id="custom-java-mirror-url" class="setting-input" placeholder="https://example.com/java/{v}/OpenJDK{v}U-jre_x64_windows_hotspot_{v}.zip">
|
||||
<div class="setting-desc" style="margin-top: 8px;">
|
||||
<strong>支持的变量:</strong>
|
||||
<ul style="margin: 5px 0 5px 20px;">
|
||||
<li><code>{v}</code> - Java版本号(如 8, 17, 21)</li>
|
||||
<li><code>{version}</code> - 完整版本号(如 8u402b06)</li>
|
||||
</ul>
|
||||
<strong>示例:</strong>
|
||||
<div style="margin-top: 5px; padding: 8px; background: rgba(255,255,255,0.05); border-radius: 4px; font-family: monospace;">
|
||||
<div style="color: #4fc3f7;">清华大学:</div>
|
||||
<div style="color: #b8c0d0;">https://mirrors.tuna.tsinghua.edu.cn/Adoptium/{v}/jre/x64/windows/</div>
|
||||
<div style="color: #4fc3f7; margin-top: 8px;">自定义:</div>
|
||||
<div style="color: #b8c0d0;">https://cdn.example.com/java/{v}/OpenJDK{v}U-jre_x64_windows_hotspot_{v}.zip</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 高级设置 -->
|
||||
<div id="settings-content-advanced" class="settings-content-panel hidden">
|
||||
<div class="settings-page-inner">
|
||||
<div class="settings-group">
|
||||
<h2 class="settings-group-title">高级设置</h2>
|
||||
<p class="settings-group-desc">配置高级选项</p>
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">防止多次启动</div>
|
||||
<div class="setting-desc">开启后将阻止启动器重复运行</div>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="prevent-multiple-launch" checked>
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">播放启动动画</div>
|
||||
<div class="setting-desc">启动游戏时全屏播放 Minecraft 欢迎动画</div>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="play-startup-animation">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">开发者选项</div>
|
||||
<div class="setting-desc">启用后允许打开 DevTools、右键菜单及刷新页面</div>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="developer-mode">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">自动清除日志</div>
|
||||
<div class="setting-desc">开启后将在启动时自动清理过期的日志文件</div>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="auto-clear-logs" checked>
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="log-retention-container" class="setting-row setting-row-indent">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">保留日志时间</div>
|
||||
<div class="setting-desc">设置日志文件的保留时长</div>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<input type="number" id="log-retention-value" class="num-display" value="7" min="1">
|
||||
<div class="custom-select small" data-select-id="log-retention-unit">
|
||||
<div class="custom-select-trigger">
|
||||
<span class="custom-select-value">天</span>
|
||||
<span class="custom-select-arrow">▾</span>
|
||||
</div>
|
||||
<div class="custom-select-dropdown">
|
||||
<div class="custom-select-option" data-value="hour">时</div>
|
||||
<div class="custom-select-option selected" data-value="day">天</div>
|
||||
<div class="custom-select-option" data-value="month">月</div>
|
||||
<div class="custom-select-option" data-value="year">年</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 关于页面 -->
|
||||
<div id="settings-content-about" class="settings-content-panel hidden">
|
||||
<div class="settings-page-inner">
|
||||
<div class="settings-group">
|
||||
<h2 class="settings-group-title">关于</h2>
|
||||
<p class="settings-group-desc">关于 Good Plan Craft Launcher</p>
|
||||
|
||||
<div class="setting-card about-card">
|
||||
<div class="about-header">
|
||||
<img src="icon.ico" alt="图标" class="about-icon" onerror="this.style.display='none'">
|
||||
<div class="about-info">
|
||||
<div class="about-title">Good Plan Craft Launcher</div>
|
||||
<div class="about-version" id="about-version">版本: 1.1.0</div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="check-update-btn" class="check-update-btn">检查更新</button>
|
||||
<div id="update-status" class="update-status"></div>
|
||||
<button id="go-download-btn" class="go-download-btn hidden">立即前往下载</button>
|
||||
</div>
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-left">
|
||||
<div class="setting-label">自动检查更新</div>
|
||||
<div class="setting-desc">启动时自动检查是否有新版本可用</div>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="auto-check-update" checked>
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toast-container" id="toast-container"></div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div id="dialog-overlay" class="dialog-overlay hidden">
|
||||
<div class="dialog-box">
|
||||
<div class="dialog-header">
|
||||
<span id="dialog-icon" class="dialog-icon">ℹ️</span>
|
||||
<h3 id="dialog-title" class="dialog-title">提示</h3>
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<p id="dialog-message" class="dialog-message">消息内容</p>
|
||||
</div>
|
||||
<div class="dialog-footer" id="dialog-footer-single">
|
||||
<button id="dialog-btn-ok" class="dialog-btn dialog-btn-primary">确定</button>
|
||||
</div>
|
||||
<div class="dialog-footer hidden" id="dialog-footer-confirm">
|
||||
<button id="dialog-btn-cancel" class="dialog-btn dialog-btn-secondary">否</button>
|
||||
<button id="dialog-btn-yes" class="dialog-btn dialog-btn-primary">是</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="float-notification" class="float-notification hidden">
|
||||
<div class="float-notification-icon">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" />
|
||||
<circle cx="12" cy="13" r="3" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="float-notification-content">
|
||||
<div class="float-notification-title" id="float-notification-title"></div>
|
||||
<div class="float-notification-text" id="float-notification-text"></div>
|
||||
</div>
|
||||
<div class="float-notification-progress">
|
||||
<div class="float-progress-bar">
|
||||
<div class="float-progress-fill" id="float-progress-fill"></div>
|
||||
</div>
|
||||
<span class="float-progress-text" id="float-progress-text">0%</span>
|
||||
</div>
|
||||
<button class="float-notification-close" id="float-notification-close">×</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="launch-animation-overlay" class="launch-animation-overlay hidden">
|
||||
<div class="launch-animation-content">
|
||||
<div class="pickaxe-container">
|
||||
<div class="pickaxe" id="pickaxe">
|
||||
<svg viewBox="0 0 64 64" width="80" height="80">
|
||||
<rect x="22" y="28" width="4" height="32" fill="#8D6E63"/>
|
||||
<rect x="21" y="28" width="2" height="32" fill="#A1887F"/>
|
||||
<rect x="27" y="28" width="1" height="32" fill="#6D4C41"/>
|
||||
<rect x="20" y="24" width="10" height="4" fill="#006064"/>
|
||||
<rect x="18" y="18" width="10" height="6" fill="#00838F"/>
|
||||
<rect x="16" y="12" width="6" height="6" fill="#0097A7"/>
|
||||
<polygon points="24,20 54,10 54,25 24,30" fill="#4DD0E1"/>
|
||||
<polygon points="24,30 54,25 54,35 24,38" fill="#26C6DA"/>
|
||||
<polygon points="24,20 54,10 39,5" fill="#80DEEA"/>
|
||||
<polygon points="54,10 59,15 54,25" fill="#00ACC1"/>
|
||||
<polygon points="24,20 24,30 19,25" fill="#00BCD4"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="block-container">
|
||||
<div class="block" id="block">
|
||||
<svg viewBox="0 0 64 64" width="60" height="60">
|
||||
<rect x="2" y="2" width="60" height="60" fill="#3E3E3E" stroke="#2a2a2a" stroke-width="2"/>
|
||||
<rect x="6" y="6" width="12" height="12" fill="#4a4a4a"/>
|
||||
<rect x="22" y="8" width="10" height="8" fill="#353535"/>
|
||||
<rect x="40" y="4" width="14" height="10" fill="#484848"/>
|
||||
<rect x="4" y="24" width="16" height="14" fill="#424242"/>
|
||||
<rect x="26" y="20" width="12" height="12" fill="#383838"/>
|
||||
<rect x="44" y="18" width="10" height="14" fill="#454545"/>
|
||||
<rect x="6" y="42" width="14" height="10" fill="#3a3a3a"/>
|
||||
<rect x="24" y="38" width="18" height="12" fill="#404040"/>
|
||||
<rect x="46" y="36" width="12" height="16" fill="#464646"/>
|
||||
<rect x="8" y="54" width="10" height="8" fill="#393939"/>
|
||||
<rect x="24" y="52" width="14" height="10" fill="#434343"/>
|
||||
<rect x="42" y="54" width="16" height="8" fill="#474747"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="particles" id="particles"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="launch-status">
|
||||
<div id="launch-status-text" class="status-text">正在启动游戏...</div>
|
||||
<div id="launch-progress-bar" class="progress-bar-container">
|
||||
<div id="launch-progress-fill" class="progress-bar-fill"></div>
|
||||
</div>
|
||||
<div id="launch-version-info" class="version-info"></div>
|
||||
</div>
|
||||
|
||||
<button id="launch-cancel-btn" class="launch-cancel-btn">取消启动</button>
|
||||
|
||||
<div class="launch-tip-card">
|
||||
<div class="launch-tip-header">
|
||||
<span class="launch-tip-icon">💡</span>
|
||||
<span class="launch-tip-label">你知道吗?</span>
|
||||
</div>
|
||||
<div id="launch-tip-text" class="launch-tip-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 1.5 MiB |
|
|
@ -0,0 +1,3270 @@
|
|||
/*
|
||||
* CaelLab BY-SA Code License
|
||||
* Copyright (c) 2026 Yunyun(云云) By 虚舟实验室(CaelLab) / CaelLabGameTS
|
||||
* Source: https://git.caellab.com/yunyun/GoodPlanCraftLauncher
|
||||
* Source: https://github.com/yunyun-3782/GoodPlanCraftLauncher
|
||||
*/
|
||||
|
||||
let GAME_DIR = null;
|
||||
|
||||
async function initGameDir() {
|
||||
if (!GAME_DIR) {
|
||||
GAME_DIR = await window.gpcl.getGameDir();
|
||||
}
|
||||
return GAME_DIR;
|
||||
}
|
||||
|
||||
|
||||
const usernameInput = document.getElementById('username');
|
||||
const versionSelect = document.getElementById('version');
|
||||
const launchBtn = document.getElementById('launch-btn');
|
||||
const statusDiv = document.getElementById('status');
|
||||
|
||||
// Download page elements
|
||||
const cancelDownloadBtn = document.getElementById('cancel-download-btn');
|
||||
const downloadBtn = document.getElementById('download-btn-page');
|
||||
const versionListContainer = document.getElementById('version-list-container');
|
||||
const versionListSection = document.getElementById('version-list-section');
|
||||
const versionDetailPage = document.getElementById('version-detail-page');
|
||||
const detailBackBtn = document.getElementById('detail-back-btn');
|
||||
const detailDownloadBtn = document.getElementById('detail-download-btn');
|
||||
const versionCountLabel = document.getElementById('version-count-label');
|
||||
|
||||
const menuDownload = document.getElementById('menu-download');
|
||||
const menuLaunch = document.getElementById('menu-launch');
|
||||
const menuSettings = document.getElementById('menu-settings');
|
||||
const downloadPage = document.getElementById('download-page');
|
||||
const launchPanel = document.getElementById('launch-panel');
|
||||
const mainContent = document.querySelector('.main-content .container');
|
||||
|
||||
const minimizeBtn = document.getElementById('minimize-btn');
|
||||
const closeBtn = document.getElementById('close-btn');
|
||||
|
||||
// Toast notification system
|
||||
const toastContainer = document.getElementById('toast-container');
|
||||
const toastHistory = new Map();
|
||||
const TOAST_DEDUP_MS = 3000;
|
||||
|
||||
const dialogOverlay = document.getElementById('dialog-overlay');
|
||||
const dialogIcon = document.getElementById('dialog-icon');
|
||||
const dialogTitle = document.getElementById('dialog-title');
|
||||
const dialogMessage = document.getElementById('dialog-message');
|
||||
const dialogFooterSingle = document.getElementById('dialog-footer-single');
|
||||
const dialogFooterConfirm = document.getElementById('dialog-footer-confirm');
|
||||
const dialogBtnOk = document.getElementById('dialog-btn-ok');
|
||||
const dialogBtnYes = document.getElementById('dialog-btn-yes');
|
||||
const dialogBtnCancel = document.getElementById('dialog-btn-cancel');
|
||||
|
||||
let dialogResolve = null;
|
||||
|
||||
function showDialog(options) {
|
||||
return new Promise((resolve) => {
|
||||
dialogResolve = resolve;
|
||||
|
||||
const icons = {
|
||||
info: 'ℹ️',
|
||||
warning: '⚠️',
|
||||
error: '❌',
|
||||
question: '❓'
|
||||
};
|
||||
|
||||
dialogIcon.textContent = icons[options.type] || icons.info;
|
||||
dialogTitle.textContent = options.title || '提示';
|
||||
dialogMessage.textContent = options.message || '';
|
||||
|
||||
if (options.type === 'confirm' || options.buttons) {
|
||||
dialogFooterSingle.classList.add('hidden');
|
||||
dialogFooterConfirm.classList.remove('hidden');
|
||||
} else {
|
||||
dialogFooterSingle.classList.remove('hidden');
|
||||
dialogFooterConfirm.classList.add('hidden');
|
||||
}
|
||||
|
||||
dialogOverlay.classList.remove('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
function hideDialog() {
|
||||
dialogOverlay.classList.add('hidden');
|
||||
dialogResolve = null;
|
||||
}
|
||||
|
||||
if (dialogBtnOk) {
|
||||
dialogBtnOk.addEventListener('click', () => {
|
||||
if (dialogResolve) dialogResolve(true);
|
||||
hideDialog();
|
||||
});
|
||||
}
|
||||
|
||||
if (dialogBtnYes) {
|
||||
dialogBtnYes.addEventListener('click', () => {
|
||||
if (dialogResolve) dialogResolve(true);
|
||||
hideDialog();
|
||||
});
|
||||
}
|
||||
|
||||
if (dialogBtnCancel) {
|
||||
dialogBtnCancel.addEventListener('click', () => {
|
||||
if (dialogResolve) dialogResolve(false);
|
||||
hideDialog();
|
||||
});
|
||||
}
|
||||
|
||||
// 监听下载时关闭窗口的询问
|
||||
if (window.gpcl && window.gpcl.onConfirmCloseWhileDownloading) {
|
||||
window.gpcl.onConfirmCloseWhileDownloading(async () => {
|
||||
const result = await showDialog({
|
||||
type: 'confirm',
|
||||
title: '确认关闭',
|
||||
message: '当前正在下载中,关闭会停止下载并清理已下载的文件。确定要关闭吗?'
|
||||
});
|
||||
|
||||
if (result) {
|
||||
// 用户点击"是",确认关闭并清理
|
||||
if (window.gpcl && window.gpcl.confirmCloseDownload) {
|
||||
await window.gpcl.confirmCloseDownload();
|
||||
}
|
||||
} else {
|
||||
// 用户点击"否",取消关闭
|
||||
if (window.gpcl && window.gpcl.cancelClose) {
|
||||
await window.gpcl.cancelClose();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showToast(title, message, type = 'info', key = null) {
|
||||
if (!toastContainer) return;
|
||||
if (key) {
|
||||
const last = toastHistory.get(key);
|
||||
if (last && Date.now() - last < TOAST_DEDUP_MS) return;
|
||||
toastHistory.set(key, Date.now());
|
||||
}
|
||||
const icons = { success: '✅', error: '❌', warning: '⚠️', info: 'ℹ️' };
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast toast-${type}`;
|
||||
toast.innerHTML = `
|
||||
<span class="toast-icon">${icons[type] || icons.info}</span>
|
||||
<div style="flex:1">
|
||||
<div class="toast-title">${title}</div>
|
||||
${message ? `<div class="toast-message">${message}</div>` : ''}
|
||||
</div>
|
||||
<button class="toast-close" onclick="this.parentElement.remove()">×</button>
|
||||
`;
|
||||
toastContainer.appendChild(toast);
|
||||
setTimeout(() => {
|
||||
toast.classList.add('toast-out');
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
// Chart elements
|
||||
let chartLoaded = false;
|
||||
|
||||
function loadChartJs() {
|
||||
return new Promise((resolve) => {
|
||||
if (chartLoaded || window.Chart) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const script = document.createElement('script');
|
||||
script.src = 'js/chart.umd.js';
|
||||
script.onload = () => {
|
||||
chartLoaded = true;
|
||||
resolve();
|
||||
};
|
||||
script.onerror = () => resolve();
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
let speedChart = null;
|
||||
let speedData = [];
|
||||
let peakSpeed = 0;
|
||||
const downloadPanel = document.getElementById('download-panel');
|
||||
|
||||
let currentPage = 'launch';
|
||||
|
||||
function getMaxConcurrentFromSettings() {
|
||||
return DEFAULT_MAX_THREADS;
|
||||
}
|
||||
|
||||
function showPage(name) {
|
||||
currentPage = name;
|
||||
|
||||
const settingsPage = document.getElementById('settings-page');
|
||||
const settingsSidebar = document.getElementById('settings-sidebar');
|
||||
|
||||
if (name === 'download') {
|
||||
if (downloadPage) downloadPage.classList.remove('hidden');
|
||||
if (launchPanel) launchPanel.classList.add('hidden');
|
||||
if (settingsPage) settingsPage.classList.add('hidden');
|
||||
} else if (name === 'launch') {
|
||||
if (downloadPage) downloadPage.classList.add('hidden');
|
||||
if (launchPanel) launchPanel.classList.remove('hidden');
|
||||
if (settingsPage) settingsPage.classList.add('hidden');
|
||||
} else if (name === 'settings') {
|
||||
if (downloadPage) downloadPage.classList.add('hidden');
|
||||
if (launchPanel) launchPanel.classList.add('hidden');
|
||||
if (settingsPage) settingsPage.classList.remove('hidden');
|
||||
// 默认选中第一个子页面(游戏)
|
||||
switchSettingsTab('game');
|
||||
}
|
||||
|
||||
if (menuDownload) menuDownload.classList.toggle('active', name === 'download');
|
||||
if (menuLaunch) menuLaunch.classList.toggle('active', name === 'launch');
|
||||
if (menuSettings) menuSettings.classList.toggle('active', name === 'settings');
|
||||
}
|
||||
|
||||
function switchSettingsTab(tabName) {
|
||||
const sidebarItems = document.querySelectorAll('.settings-sidebar-item');
|
||||
const contentPanels = document.querySelectorAll('.settings-content-panel');
|
||||
|
||||
sidebarItems.forEach(item => {
|
||||
item.classList.toggle('active', item.dataset.tab === tabName);
|
||||
});
|
||||
|
||||
contentPanels.forEach(panel => {
|
||||
panel.classList.toggle('active', panel.id === `settings-content-${tabName}`);
|
||||
panel.classList.toggle('hidden', panel.id !== `settings-content-${tabName}`);
|
||||
});
|
||||
}
|
||||
|
||||
// 更新菜单选中状态(确保菜单始终与当前页面同步)
|
||||
function updateMenuActive(pageName) {
|
||||
if (menuDownload) menuDownload.classList.toggle('active', pageName === 'download');
|
||||
if (menuLaunch) menuLaunch.classList.toggle('active', pageName === 'launch');
|
||||
if (menuSettings) menuSettings.classList.toggle('active', pageName === 'settings');
|
||||
}
|
||||
|
||||
if (menuDownload) menuDownload.addEventListener('click', () => showPage('download'));
|
||||
if (menuLaunch) menuLaunch.addEventListener('click', () => showPage('launch'));
|
||||
if (menuSettings) menuSettings.addEventListener('click', () => showPage('settings'));
|
||||
if (minimizeBtn) minimizeBtn.addEventListener('click', () => { if (window.gpcl && window.gpcl.minimizeWindow) window.gpcl.minimizeWindow(); });
|
||||
if (closeBtn) closeBtn.addEventListener('click', () => {
|
||||
if (window.gpcl && typeof window.gpcl.closeWindow === 'function') {
|
||||
window.gpcl.closeWindow();
|
||||
} else {
|
||||
// 备选方案:尝试直接关闭窗口
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
|
||||
const customSelectHandlers = new Map();
|
||||
|
||||
function initCustomSelect(selectId, onChangeCallback) {
|
||||
const selectContainer = document.querySelector(`.custom-select[data-select-id="${selectId}"]`);
|
||||
if (!selectContainer) return;
|
||||
|
||||
const trigger = selectContainer.querySelector('.custom-select-trigger');
|
||||
const dropdown = selectContainer.querySelector('.custom-select-dropdown');
|
||||
const valueSpan = selectContainer.querySelector('.custom-select-value');
|
||||
const options = selectContainer.querySelectorAll('.custom-select-option');
|
||||
|
||||
// 保存初始状态
|
||||
let currentValue = null;
|
||||
options.forEach(option => {
|
||||
if (option.classList.contains('selected')) {
|
||||
currentValue = option.dataset.value;
|
||||
}
|
||||
});
|
||||
|
||||
// 点击触发器打开/关闭下拉框
|
||||
trigger.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const isOpen = dropdown.classList.contains('open');
|
||||
closeAllCustomSelects();
|
||||
if (!isOpen) {
|
||||
dropdown.classList.add('open');
|
||||
trigger.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
// 点击选项
|
||||
options.forEach(option => {
|
||||
option.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
if (option.classList.contains('disabled')) return;
|
||||
|
||||
const value = option.dataset.value;
|
||||
const label = option.textContent;
|
||||
|
||||
// 更新选中状态
|
||||
options.forEach(opt => opt.classList.remove('selected'));
|
||||
option.classList.add('selected');
|
||||
valueSpan.textContent = label;
|
||||
currentValue = value;
|
||||
|
||||
// 关闭下拉框
|
||||
closeAllCustomSelects();
|
||||
|
||||
// 调用回调
|
||||
if (onChangeCallback) {
|
||||
onChangeCallback(value);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 保存处理函数以便后续使用
|
||||
customSelectHandlers.set(selectId, {
|
||||
setValue: (value, label) => {
|
||||
options.forEach(opt => opt.classList.remove('selected'));
|
||||
const targetOpt = selectContainer.querySelector(`.custom-select-option[data-value="${value}"]`);
|
||||
if (targetOpt) {
|
||||
targetOpt.classList.add('selected');
|
||||
valueSpan.textContent = targetOpt.textContent;
|
||||
currentValue = value;
|
||||
} else if (label) {
|
||||
valueSpan.textContent = label;
|
||||
currentValue = value;
|
||||
}
|
||||
},
|
||||
getValue: () => currentValue
|
||||
});
|
||||
}
|
||||
|
||||
function closeAllCustomSelects() {
|
||||
document.querySelectorAll('.custom-select-dropdown').forEach(d => d.classList.remove('open'));
|
||||
document.querySelectorAll('.custom-select-trigger').forEach(t => t.classList.remove('active'));
|
||||
}
|
||||
|
||||
// 点击页面其他地方关闭下拉框
|
||||
document.addEventListener('click', closeAllCustomSelects);
|
||||
|
||||
// 提供全局访问方法
|
||||
function setCustomSelectValue(selectId, value, label) {
|
||||
const handler = customSelectHandlers.get(selectId);
|
||||
if (handler) {
|
||||
handler.setValue(value, label);
|
||||
}
|
||||
}
|
||||
|
||||
function getCustomSelectValue(selectId) {
|
||||
const handler = customSelectHandlers.get(selectId);
|
||||
return handler ? handler.getValue() : null;
|
||||
}
|
||||
|
||||
const DEFAULT_MAX_THREADS = 64;
|
||||
const MAX_THREADS_LIMIT = 128;
|
||||
|
||||
// Java版本 - 官方runtime目录名映射(用于显示提示)
|
||||
const JAVA_RUNTIME_NAME_MAP = {
|
||||
"8": "jre-legacy",
|
||||
"16": "java-runtime-beta",
|
||||
"17": "java-runtime-gamma",
|
||||
"21": "java-runtime-delta",
|
||||
"25": "java-runtime-epsilon"
|
||||
};
|
||||
|
||||
// Java版本说明(改进版,更准确的推荐)
|
||||
const JAVA_VERSION_DESC = {
|
||||
"8": { mcVersions: "1.7.10 - 1.16.5", desc: "经典版本兼容" },
|
||||
"17": { mcVersions: "1.17 - 1.20.4", desc: "最稳定,推荐使用" },
|
||||
"21": { mcVersions: "1.20.5 - 1.21+", desc: "最新LTS版本" },
|
||||
"25": { mcVersions: "1.21+", desc: "最新尝鲜版" }
|
||||
};
|
||||
|
||||
function getJavaRuntimeName(javaVersion) {
|
||||
return JAVA_RUNTIME_NAME_MAP[javaVersion] || `jre${javaVersion}`;
|
||||
}
|
||||
|
||||
// 初始化Java版本状态检测
|
||||
async function initJavaVersionStatus() {
|
||||
const javaVersions = ["8", "17", "21", "25"];
|
||||
|
||||
for (const ver of javaVersions) {
|
||||
try {
|
||||
const result = await window.gpcl.checkJava(ver);
|
||||
const card = document.querySelector(`.java-version-card[data-version="${ver}"]`);
|
||||
const badge = document.getElementById(`java-${ver}-status`);
|
||||
const btn = card?.querySelector('.java-install-btn');
|
||||
|
||||
if (result.installed) {
|
||||
if (card) card.classList.add('installed');
|
||||
if (badge) {
|
||||
badge.textContent = '已安装';
|
||||
badge.className = 'java-version-badge installed';
|
||||
}
|
||||
if (btn) {
|
||||
btn.textContent = '卸载';
|
||||
btn.classList.add('installed');
|
||||
btn.disabled = false;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`检查Java ${ver} 状态失败`, e);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新Java安装状态提示
|
||||
updateJavaInstallStatus();
|
||||
}
|
||||
|
||||
// 更新Java安装状态提示
|
||||
function updateJavaInstallStatus() {
|
||||
const statusEl = document.getElementById('java-install-status');
|
||||
if (!statusEl) return;
|
||||
|
||||
const javaVersions = ["8", "17", "21", "25"];
|
||||
const installed = [];
|
||||
const notInstalled = [];
|
||||
|
||||
javaVersions.forEach(ver => {
|
||||
const card = document.querySelector(`.java-version-card[data-version="${ver}"]`);
|
||||
if (card?.classList.contains('installed')) {
|
||||
installed.push(ver);
|
||||
} else {
|
||||
notInstalled.push(ver);
|
||||
}
|
||||
});
|
||||
|
||||
if (installed.length === javaVersions.length) {
|
||||
statusEl.textContent = '✅ 所有Java版本均已安装,可以运行任何Minecraft版本';
|
||||
statusEl.style.background = 'rgba(76, 175, 80, 0.15)';
|
||||
} else {
|
||||
statusEl.textContent = `已安装 Java ${installed.join(', ') || '无'} | 未安装 Java ${notInstalled.join(', ')}`;
|
||||
statusEl.style.background = 'rgba(79, 195, 247, 0.08)';
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定Java安装按钮事件
|
||||
function bindJavaInstallButtons() {
|
||||
const buttons = document.querySelectorAll('.java-install-btn');
|
||||
|
||||
buttons.forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const version = btn.dataset.version;
|
||||
if (btn.classList.contains('installed')) {
|
||||
await uninstallJavaWithPanel(version);
|
||||
} else {
|
||||
await installJavaWithPanel(version);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 使用通用面板安装Java
|
||||
async function installJavaWithPanel(javaVersion) {
|
||||
const filenameEl = document.getElementById('download-filename');
|
||||
const downloadStatusEl = document.getElementById('download-status');
|
||||
const downloadPanel = document.getElementById('download-panel');
|
||||
const progressFill = document.getElementById('progress-fill');
|
||||
const progressText = document.getElementById('progress-text');
|
||||
const cancelBtn = document.getElementById('cancel-download-btn');
|
||||
|
||||
// 滚动到下载页面顶部(用户主动操作)
|
||||
const downloadPage = document.getElementById('download-page');
|
||||
if (downloadPage) {
|
||||
downloadPage.scrollTop = 0;
|
||||
}
|
||||
|
||||
// 显示通用下载面板
|
||||
if (downloadPanel) {
|
||||
downloadPanel.classList.remove('hidden');
|
||||
downloadPanel.classList.add('download-panel-container');
|
||||
}
|
||||
if (filenameEl) filenameEl.textContent = `正在安装: Java ${javaVersion}`;
|
||||
if (downloadStatusEl) downloadStatusEl.textContent = `准备下载 Java ${javaVersion}...`;
|
||||
if (progressFill) progressFill.style.width = '0%';
|
||||
if (progressText) progressText.textContent = '0%';
|
||||
if (cancelBtn) cancelBtn.disabled = false;
|
||||
|
||||
// 禁用Java安装按钮
|
||||
document.querySelectorAll('.java-install-btn').forEach(btn => {
|
||||
btn.disabled = true;
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await window.gpcl.installJava(javaVersion);
|
||||
|
||||
if (result.success) {
|
||||
if (downloadStatusEl) downloadStatusEl.textContent = `✅ Java ${javaVersion} 安装成功!`;
|
||||
if (progressFill) progressFill.style.width = '100%';
|
||||
if (progressText) progressText.textContent = '100%';
|
||||
|
||||
showToast('安装成功', `Java ${javaVersion} 已安装完成`, 'success', 'java-install-success');
|
||||
|
||||
// 更新Java卡片状态
|
||||
const card = document.querySelector(`.java-version-card[data-version="${javaVersion}"]`);
|
||||
const badge = document.getElementById(`java-${javaVersion}-status`);
|
||||
const btn = card?.querySelector('.java-install-btn');
|
||||
|
||||
if (card) card.classList.add('installed');
|
||||
if (badge) {
|
||||
badge.textContent = '已安装';
|
||||
badge.className = 'java-version-badge installed';
|
||||
}
|
||||
if (btn) {
|
||||
btn.textContent = '卸载';
|
||||
btn.classList.add('installed');
|
||||
btn.disabled = false;
|
||||
}
|
||||
|
||||
updateJavaInstallStatus();
|
||||
|
||||
setTimeout(() => {
|
||||
if (downloadPanel) {
|
||||
downloadPanel.classList.add('hidden');
|
||||
downloadPanel.classList.remove('download-panel-container');
|
||||
}
|
||||
}, 2000);
|
||||
} else {
|
||||
throw new Error(result.error || '安装失败');
|
||||
}
|
||||
} catch (err) {
|
||||
if (downloadStatusEl) downloadStatusEl.textContent = `❌ 安装失败: ${err.message || err}`;
|
||||
showToast('安装失败', err.message || String(err), 'error', 'java-install-failed');
|
||||
}
|
||||
|
||||
// 重新启用Java安装按钮
|
||||
document.querySelectorAll('.java-install-btn').forEach(btn => {
|
||||
if (!btn.classList.contains('installed')) {
|
||||
btn.disabled = false;
|
||||
}
|
||||
});
|
||||
if (cancelBtn) cancelBtn.disabled = true;
|
||||
}
|
||||
|
||||
// 卸载Java运行时(使用弹窗确认)
|
||||
async function uninstallJavaWithPanel(javaVersion) {
|
||||
const confirmed = await showDialog({
|
||||
type: 'confirm',
|
||||
title: '确认卸载',
|
||||
message: `确定要卸载 Java ${javaVersion} 吗?`
|
||||
});
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
showToast('正在卸载', `正在卸载 Java ${javaVersion}...`, 'info', 'java-uninstalling');
|
||||
|
||||
try {
|
||||
const result = await window.gpcl.uninstallJava(javaVersion);
|
||||
|
||||
if (result.success) {
|
||||
showToast('卸载成功', `Java ${javaVersion} 已卸载`, 'success', 'java-uninstall-success');
|
||||
|
||||
// 更新Java卡片状态
|
||||
const card = document.querySelector(`.java-version-card[data-version="${javaVersion}"]`);
|
||||
const badge = document.getElementById(`java-${javaVersion}-status`);
|
||||
const btn = card?.querySelector('.java-install-btn');
|
||||
|
||||
if (card) card.classList.remove('installed');
|
||||
if (badge) {
|
||||
badge.textContent = '';
|
||||
badge.className = 'java-version-badge';
|
||||
}
|
||||
if (btn) {
|
||||
btn.textContent = '安装';
|
||||
btn.classList.remove('installed');
|
||||
btn.disabled = false;
|
||||
}
|
||||
|
||||
updateJavaInstallStatus();
|
||||
} else {
|
||||
throw new Error(result.error || '卸载失败');
|
||||
}
|
||||
} catch (err) {
|
||||
showToast('卸载失败', err.message || String(err), 'error', 'java-uninstall-failed');
|
||||
}
|
||||
}
|
||||
|
||||
// 改进的Java版本推荐(基于Minecraft版本JSON)
|
||||
function getRecommendedJavaVersionFromMC(mcVersion) {
|
||||
// 解析Minecraft版本号
|
||||
const parts = mcVersion.split('.');
|
||||
let major = parseInt(parts[0], 10);
|
||||
let minor = parseInt(parts[1], 10);
|
||||
let patch = parts.length > 2 ? parseInt(parts[2], 10) || 0 : 0;
|
||||
|
||||
// 处理无效 major
|
||||
if (isNaN(major)) major = 1;
|
||||
|
||||
// Minecraft版本对应Java版本(Mojang官方推荐)
|
||||
// 1.7-1.16: Java 8
|
||||
// 1.17-1.20.4: Java 17
|
||||
// 1.20.5-1.21.4: Java 21
|
||||
// 1.22+ / 21+: Java 21
|
||||
// 26+: Java 21
|
||||
|
||||
// 处理像 "26" 这种只有 major 没有 minor 的情况
|
||||
if (isNaN(minor)) {
|
||||
// 26 及以上使用 Java 25
|
||||
if (major >= 26) {
|
||||
return { version: '25', reason: `Minecraft ${mcVersion} 推荐使用 Java 25` };
|
||||
}
|
||||
// 21-25 使用 Java 21
|
||||
if (major >= 21) {
|
||||
return { version: '21', reason: `Minecraft ${mcVersion} 推荐使用 Java 21` };
|
||||
}
|
||||
return { version: '17', reason: '使用推荐的稳定版' };
|
||||
}
|
||||
|
||||
// 处理 1.x.x 的旧版本
|
||||
if (major === 1) {
|
||||
if (minor <= 16) {
|
||||
return { version: '8', reason: `Minecraft ${mcVersion} 官方推荐 Java 8` };
|
||||
} else if (minor <= 20 || (minor === 20 && patch <= 4)) {
|
||||
return { version: '17', reason: `Minecraft ${mcVersion} 官方推荐 Java 17` };
|
||||
} else if (minor === 20 && patch >= 5) {
|
||||
return { version: '21', reason: `Minecraft ${mcVersion} 推荐使用 Java 21` };
|
||||
} else if (minor >= 21) {
|
||||
return { version: '21', reason: `Minecraft ${mcVersion} 推荐使用 Java 21` };
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 2.x.x 和更大的版本(21, 24, 26, ...)
|
||||
if (major >= 2) {
|
||||
// 26 及以上使用 Java 25
|
||||
if (major >= 26) {
|
||||
return { version: '25', reason: `Minecraft ${mcVersion} 推荐使用 Java 25` };
|
||||
}
|
||||
return { version: '21', reason: `Minecraft ${mcVersion} 推荐使用 Java 21` };
|
||||
}
|
||||
|
||||
// 默认返回最稳定的版本
|
||||
return { version: '17', reason: '使用推荐的稳定版' };
|
||||
}
|
||||
|
||||
// 监听Java下载进度
|
||||
if (window.gpcl && window.gpcl.onDownloadProgress) {
|
||||
const originalCallback = window.gpcl.onDownloadProgress;
|
||||
window.gpcl.onDownloadProgress((data) => {
|
||||
// 更新通用下载面板的进度
|
||||
const progressFill = document.getElementById('progress-fill');
|
||||
const progressText = document.getElementById('progress-text');
|
||||
const filenameEl = document.getElementById('download-filename');
|
||||
const downloadStatusEl = document.getElementById('download-status');
|
||||
|
||||
if (progressFill && progressText) {
|
||||
const percent = data.percent || 0;
|
||||
progressFill.style.width = percent + '%';
|
||||
progressText.textContent = percent.toFixed(1) + '%';
|
||||
}
|
||||
if (data.label && filenameEl) {
|
||||
filenameEl.textContent = data.label;
|
||||
}
|
||||
if (data.label && downloadStatusEl) {
|
||||
downloadStatusEl.textContent = `正在下载: ${data.label}`;
|
||||
}
|
||||
|
||||
// 更新漂浮通知的进度
|
||||
const floatProgressFill = document.getElementById('float-progress-fill');
|
||||
const floatProgressText = document.getElementById('float-progress-text');
|
||||
const floatNotification = document.getElementById('float-notification');
|
||||
|
||||
if (floatProgressFill && floatProgressText && !floatNotification.classList.contains('hidden')) {
|
||||
const percent = data.percent || 0;
|
||||
floatProgressFill.style.width = percent + '%';
|
||||
floatProgressText.textContent = percent.toFixed(1) + '%';
|
||||
}
|
||||
|
||||
// 调用原始回调
|
||||
if (typeof originalCallback === 'function') {
|
||||
originalCallback(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 漂浮通知控制函数
|
||||
function showFloatNotification(title, text) {
|
||||
const notification = document.getElementById('float-notification');
|
||||
const titleEl = document.getElementById('float-notification-title');
|
||||
const textEl = document.getElementById('float-notification-text');
|
||||
|
||||
if (notification && titleEl && textEl) {
|
||||
titleEl.textContent = title;
|
||||
textEl.textContent = text;
|
||||
|
||||
// 重置进度
|
||||
const progressFill = document.getElementById('float-progress-fill');
|
||||
const progressText = document.getElementById('float-progress-text');
|
||||
if (progressFill) progressFill.style.width = '0%';
|
||||
if (progressText) progressText.textContent = '0%';
|
||||
|
||||
notification.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function hideFloatNotification() {
|
||||
const notification = document.getElementById('float-notification');
|
||||
if (notification) {
|
||||
notification.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// 监听自动下载开始事件(通过游戏日志检测)
|
||||
if (window.gpcl && window.gpcl.onGameLog) {
|
||||
window.gpcl.onGameLog((text) => {
|
||||
if (text.includes('Java') && text.includes('未安装') && text.includes('自动开始下载')) {
|
||||
const match = text.match(/Java\s+(\d+)/);
|
||||
if (match) {
|
||||
showFloatNotification(
|
||||
`正在下载 Java ${match[1]}`,
|
||||
`检测到您没有 Java ${match[1]},正在自动下载`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定漂浮通知关闭按钮
|
||||
const floatCloseBtn = document.getElementById('float-notification-close');
|
||||
if (floatCloseBtn) {
|
||||
floatCloseBtn.addEventListener('click', hideFloatNotification);
|
||||
}
|
||||
|
||||
// 监听Java下载完成
|
||||
if (window.gpcl && window.gpcl.onJavaDownloadCompleted) {
|
||||
window.gpcl.onJavaDownloadCompleted((data) => {
|
||||
const { javaVersion } = data;
|
||||
const downloadStatusEl = document.getElementById('download-status');
|
||||
if (downloadStatusEl) {
|
||||
downloadStatusEl.textContent = '✅ 下载完成,正在解压安装...';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 监听Java下载失败
|
||||
if (window.gpcl && window.gpcl.onJavaDownloadFailed) {
|
||||
window.gpcl.onJavaDownloadFailed((data) => {
|
||||
const { error } = data;
|
||||
const downloadStatusEl = document.getElementById('download-status');
|
||||
if (downloadStatusEl) {
|
||||
downloadStatusEl.textContent = `❌ 下载失败: ${error}`;
|
||||
}
|
||||
showToast('下载失败', error, 'error', 'java-download-failed');
|
||||
});
|
||||
}
|
||||
|
||||
async function loadSettingFromStorage() {
|
||||
try {
|
||||
const settings = await loadSettings();
|
||||
return settings.download?.maxConcurrent || DEFAULT_MAX_THREADS;
|
||||
} catch (e) {
|
||||
return DEFAULT_MAX_THREADS;
|
||||
}
|
||||
}
|
||||
async function saveSettingToStorage(maxConcurrent) {
|
||||
try {
|
||||
const settings = await loadSettings();
|
||||
settings.download.maxConcurrent = maxConcurrent;
|
||||
await gpcl.saveSettings(settings);
|
||||
return true;
|
||||
} catch (e) { return false; }
|
||||
}
|
||||
const settingsNumInput = document.getElementById('num-threads');
|
||||
const settingsBtnMinus = document.getElementById('btn-minus');
|
||||
const settingsBtnPlus = document.getElementById('btn-plus');
|
||||
const settingsBtnSave = document.getElementById('btn-save');
|
||||
if (settingsNumInput) {
|
||||
(async () => {
|
||||
settingsNumInput.value = await loadSettingFromStorage();
|
||||
})();
|
||||
function clampSettingsInput() {
|
||||
let v = parseInt(settingsNumInput.value, 10);
|
||||
if (isNaN(v) || v < 1) v = 1;
|
||||
if (v > MAX_THREADS_LIMIT) v = MAX_THREADS_LIMIT;
|
||||
settingsNumInput.value = v;
|
||||
}
|
||||
settingsNumInput.addEventListener('change', clampSettingsInput);
|
||||
settingsNumInput.addEventListener('blur', clampSettingsInput);
|
||||
if (settingsBtnMinus) {
|
||||
settingsBtnMinus.addEventListener('click', () => {
|
||||
let v = parseInt(settingsNumInput.value, 10) || 1;
|
||||
if (v > 1) { settingsNumInput.value = v - 1; clampSettingsInput(); }
|
||||
});
|
||||
}
|
||||
if (settingsBtnPlus) {
|
||||
settingsBtnPlus.addEventListener('click', () => {
|
||||
let v = parseInt(settingsNumInput.value, 10) || 1;
|
||||
if (v < MAX_THREADS_LIMIT) { settingsNumInput.value = v + 1; clampSettingsInput(); }
|
||||
});
|
||||
}
|
||||
if (settingsBtnSave) {
|
||||
settingsBtnSave.addEventListener('click', async () => {
|
||||
clampSettingsInput();
|
||||
await saveSettingToStorage(parseInt(settingsNumInput.value, 10));
|
||||
const orig = settingsBtnSave.textContent;
|
||||
settingsBtnSave.textContent = '已保存!';
|
||||
setTimeout(() => { settingsBtnSave.textContent = orig; }, 1500);
|
||||
});
|
||||
}
|
||||
|
||||
async function reloadAllSettingsToUI() {
|
||||
const settings = await loadSettings();
|
||||
|
||||
const settingsMemory = document.getElementById('settings-memory');
|
||||
if (settingsMemory) {
|
||||
settingsMemory.value = settings.game?.memory || '4';
|
||||
}
|
||||
|
||||
const settingsWindowMode = document.getElementById('settings-window-mode');
|
||||
if (settingsWindowMode) {
|
||||
settingsWindowMode.value = settings.game?.windowMode || 'windowed';
|
||||
}
|
||||
|
||||
const settingsTheme = document.getElementById('settings-theme');
|
||||
if (settingsTheme) {
|
||||
settingsTheme.value = settings.appearance?.theme || 'dark';
|
||||
applyTheme(settings.appearance?.theme || 'dark');
|
||||
}
|
||||
|
||||
const settingsScale = document.getElementById('settings-scale');
|
||||
if (settingsScale) {
|
||||
settingsScale.value = settings.appearance?.scale || '100';
|
||||
applyScale(settings.appearance?.scale || '100');
|
||||
}
|
||||
|
||||
const settingsNumInput = document.getElementById('num-threads');
|
||||
if (settingsNumInput) {
|
||||
settingsNumInput.value = settings.download?.maxConcurrent || 64;
|
||||
}
|
||||
|
||||
const javaMirrorSelect = document.getElementById('java-mirror-select');
|
||||
const customMirrorContainer = document.getElementById('custom-java-mirror-container');
|
||||
const customMirrorUrl = document.getElementById('custom-java-mirror-url');
|
||||
if (javaMirrorSelect) {
|
||||
javaMirrorSelect.value = settings.download?.javaMirror || 'tsinghua';
|
||||
if (customMirrorContainer) {
|
||||
customMirrorContainer.classList.toggle('hidden', javaMirrorSelect.value !== 'custom');
|
||||
}
|
||||
if (customMirrorUrl) {
|
||||
customMirrorUrl.value = settings.download?.customJavaMirror || '';
|
||||
}
|
||||
}
|
||||
|
||||
const autoCheckUpdate = document.getElementById('auto-check-update');
|
||||
if (autoCheckUpdate) {
|
||||
autoCheckUpdate.checked = settings.advanced?.autoCheckUpdate !== false;
|
||||
}
|
||||
|
||||
const preventMultipleLaunch = document.getElementById('prevent-multiple-launch');
|
||||
if (preventMultipleLaunch) {
|
||||
preventMultipleLaunch.checked = settings.advanced?.preventMultipleLaunch !== false;
|
||||
}
|
||||
|
||||
const autoClearLogs = document.getElementById('auto-clear-logs');
|
||||
const logRetentionContainer = document.getElementById('log-retention-container');
|
||||
const logRetentionValue = document.getElementById('log-retention-value');
|
||||
const logRetentionUnit = document.getElementById('log-retention-unit');
|
||||
|
||||
if (autoClearLogs) {
|
||||
autoClearLogs.checked = settings.advanced?.autoClearLogs !== false;
|
||||
}
|
||||
|
||||
if (logRetentionContainer) {
|
||||
logRetentionContainer.classList.toggle('hidden', settings.advanced?.autoClearLogs === false);
|
||||
}
|
||||
|
||||
if (logRetentionValue) {
|
||||
logRetentionValue.value = settings.advanced?.logRetentionValue || 7;
|
||||
}
|
||||
|
||||
if (logRetentionUnit) {
|
||||
logRetentionUnit.value = settings.advanced?.logRetentionUnit || 'day';
|
||||
}
|
||||
|
||||
const playStartupAnimationEl = document.getElementById('play-startup-animation');
|
||||
if (playStartupAnimationEl) {
|
||||
playStartupAnimationEl.checked = settings.advanced?.playStartupAnimation === true;
|
||||
}
|
||||
|
||||
const developerModeEl = document.getElementById('developer-mode');
|
||||
if (developerModeEl) {
|
||||
developerModeEl.checked = settings.advanced?.developerMode === true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function initChart() {
|
||||
const ctx = document.getElementById('speedChart');
|
||||
if (!ctx) return;
|
||||
|
||||
const chartCtx = ctx.getContext('2d');
|
||||
|
||||
const gradient = chartCtx.createLinearGradient(0, 0, 0, 180);
|
||||
gradient.addColorStop(0, 'rgba(79, 195, 247, 0.3)');
|
||||
gradient.addColorStop(1, 'rgba(79, 195, 247, 0)');
|
||||
|
||||
speedChart = new Chart(chartCtx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: Array(40).fill(''),
|
||||
datasets: [{
|
||||
label: '下载速度',
|
||||
data: Array(40).fill(0),
|
||||
borderColor: '#4fc3f7',
|
||||
backgroundColor: gradient,
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBackgroundColor: '#4fc3f7'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: {
|
||||
intersect: false,
|
||||
mode: 'index'
|
||||
},
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
||||
titleColor: '#fff',
|
||||
bodyColor: '#4fc3f7',
|
||||
borderColor: 'rgba(79, 195, 247, 0.3)',
|
||||
borderWidth: 1,
|
||||
padding: 10,
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
return `速度: ${context.parsed.y.toFixed(2)} Mbps`;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: { display: false },
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
suggestedMax: 10,
|
||||
grid: { color: 'rgba(255, 255, 255, 0.05)' },
|
||||
ticks: { display: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateChart(speed) {
|
||||
if (!speedChart) return;
|
||||
|
||||
speedData.push(speed);
|
||||
if (speedData.length > 40) speedData.shift();
|
||||
|
||||
speedChart.data.datasets[0].data = speedData;
|
||||
|
||||
const maxSpeed = Math.max(...speedData, 1);
|
||||
speedChart.options.scales.y.suggestedMax = maxSpeed * 1.5;
|
||||
|
||||
speedChart.update('none');
|
||||
}
|
||||
|
||||
function resetChart() {
|
||||
speedData = Array(40).fill(0);
|
||||
peakSpeed = 0;
|
||||
|
||||
if (speedChart) {
|
||||
speedChart.data.datasets[0].data = speedData;
|
||||
speedChart.update();
|
||||
}
|
||||
|
||||
const currentSpeedEl = document.getElementById('current-speed');
|
||||
const peakSpeedEl = document.getElementById('peak-speed');
|
||||
const progressFillEl = document.getElementById('progress-fill');
|
||||
const progressTextEl = document.getElementById('progress-text');
|
||||
|
||||
if (currentSpeedEl) currentSpeedEl.textContent = '0.00 Mbps';
|
||||
if (peakSpeedEl) peakSpeedEl.textContent = '0.00 Mbps';
|
||||
if (progressFillEl) progressFillEl.style.width = '0%';
|
||||
if (progressTextEl) progressTextEl.textContent = '0%';
|
||||
}
|
||||
|
||||
// 注意:下载进度监听在 startDownload 函数中单独处理,避免全局监听器冲突
|
||||
|
||||
// addLog removed - using toast notifications instead
|
||||
|
||||
function setStatus(text, type = 'info') {
|
||||
statusDiv.textContent = text;
|
||||
statusDiv.style.color =
|
||||
type === 'error' ? '#ef5350' :
|
||||
type === 'success' ? '#66bb6a' :
|
||||
'#e0e0e0';
|
||||
}
|
||||
|
||||
async function loadVersions(silent = false) {
|
||||
try {
|
||||
let versions = [];
|
||||
|
||||
// 尝试调用主进程API
|
||||
if (window.gpcl && typeof window.gpcl.scanVersions === 'function') {
|
||||
try {
|
||||
versions = await window.gpcl.scanVersions(null, silent);
|
||||
} catch (e) {
|
||||
console.error('调用scanVersions失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新启动按钮状态(无论 versionSelect 是否存在都要更新)
|
||||
if (!versions || versions.length === 0) {
|
||||
if (!silent) setStatus('没有本地版本,请先下载');
|
||||
// 切换按钮为前往下载状态
|
||||
updateLaunchButtonState(false);
|
||||
} else {
|
||||
if (!silent) setStatus(`发现 ${versions.length} 个已安装版本`);
|
||||
// 切换按钮为开始游戏状态
|
||||
updateLaunchButtonState(true);
|
||||
}
|
||||
|
||||
// 检查 versionSelect 是否存在
|
||||
if (!versionSelect) {
|
||||
if (!silent) console.warn('versionSelect 元素不存在,跳过版本列表渲染');
|
||||
return;
|
||||
}
|
||||
|
||||
versionSelect.innerHTML = '<option value="">请选择版本</option>';
|
||||
|
||||
if (!versions || versions.length === 0) {
|
||||
const opt = document.createElement('option');
|
||||
opt.disabled = true;
|
||||
opt.textContent = '未发现已安装的版本';
|
||||
versionSelect.appendChild(opt);
|
||||
return;
|
||||
}
|
||||
|
||||
versions.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
|
||||
|
||||
for (const v of versions) {
|
||||
let displayName = `Minecraft ${v}`;
|
||||
if (window.gpcl && typeof window.gpcl.getVersionDisplayName === 'function') {
|
||||
try {
|
||||
displayName = await window.gpcl.getVersionDisplayName(v);
|
||||
} catch (e) {
|
||||
console.warn(`获取版本显示名称失败: ${v}`, e);
|
||||
}
|
||||
}
|
||||
const opt = document.createElement('option');
|
||||
opt.value = v;
|
||||
opt.textContent = displayName;
|
||||
versionSelect.appendChild(opt);
|
||||
}
|
||||
} catch (err) {
|
||||
if (!silent) {
|
||||
setStatus('扫描本地版本失败', 'error');
|
||||
showToast('扫描失败', err.message || String(err), 'error');
|
||||
}
|
||||
// 出错时也更新按钮为无游戏状态
|
||||
updateLaunchButtonState(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新启动按钮状态
|
||||
function updateLaunchButtonState(hasGame) {
|
||||
const launchBtn = document.getElementById('launch-btn');
|
||||
const launchText = launchBtn?.querySelector('.launch-text');
|
||||
|
||||
if (launchBtn && launchText) {
|
||||
if (hasGame) {
|
||||
launchText.textContent = '▶ 开始游戏';
|
||||
launchBtn.dataset.mode = 'launch';
|
||||
// 移除红色样式
|
||||
launchBtn.classList.remove('no-game');
|
||||
} else {
|
||||
launchText.textContent = '⏬ 前往下载';
|
||||
launchBtn.dataset.mode = 'download';
|
||||
// 添加红色样式
|
||||
launchBtn.classList.add('no-game');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 版本时间记录相关功能
|
||||
const VERSION_HISTORY_FILE = 'gpcl_version_history.json';
|
||||
const VERSION_HISTORY_PATH = 'users';
|
||||
|
||||
// 获取版本历史记录
|
||||
async function getVersionHistory() {
|
||||
try {
|
||||
if (window.gpcl && typeof window.gpcl.readJsonFile === 'function') {
|
||||
const history = await window.gpcl.readJsonFile(VERSION_HISTORY_PATH, VERSION_HISTORY_FILE);
|
||||
return history || {};
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('从文件读取版本历史失败:', e);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// 保存版本历史记录
|
||||
async function saveVersionHistory(history) {
|
||||
if (window.gpcl && typeof window.gpcl.writeJsonFile === 'function') {
|
||||
try {
|
||||
await window.gpcl.writeJsonFile(VERSION_HISTORY_PATH, VERSION_HISTORY_FILE, history);
|
||||
} catch (e) {
|
||||
console.error('写入版本历史到文件失败:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 记录版本启动时间
|
||||
async function recordVersionLaunch(versionId) {
|
||||
try {
|
||||
const history = await getVersionHistory();
|
||||
if (!history[versionId]) {
|
||||
history[versionId] = {};
|
||||
}
|
||||
history[versionId].lastLaunch = Date.now();
|
||||
await saveVersionHistory(history);
|
||||
} catch (e) {
|
||||
console.error('记录版本启动时间失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 记录版本下载成功时间
|
||||
async function recordVersionDownload(versionId) {
|
||||
try {
|
||||
const history = await getVersionHistory();
|
||||
if (!history[versionId]) {
|
||||
history[versionId] = {};
|
||||
}
|
||||
history[versionId].downloadTime = Date.now();
|
||||
history[versionId].lastLaunch = Date.now(); // 下载成功也作为最后启动时间
|
||||
await saveVersionHistory(history);
|
||||
} catch (e) {
|
||||
console.error('记录版本下载时间失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染版本选择列表
|
||||
async function renderVersionSelectList() {
|
||||
const container = document.getElementById('launch-version-list');
|
||||
const countEl = document.getElementById('version-count');
|
||||
|
||||
if (!container) return;
|
||||
|
||||
try {
|
||||
let versions = [];
|
||||
|
||||
// 尝试调用主进程API
|
||||
if (window.gpcl && typeof window.gpcl.scanVersions === 'function') {
|
||||
try {
|
||||
versions = await window.gpcl.scanVersions(null, true);
|
||||
} catch (e) {
|
||||
console.error('调用scanVersions失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
const history = await getVersionHistory();
|
||||
|
||||
if (!versions || versions.length === 0) {
|
||||
container.innerHTML = '<div class="version-list-empty">暂无已安装的版本</div>';
|
||||
countEl.textContent = '0 个版本';
|
||||
return;
|
||||
}
|
||||
|
||||
// 按最后启动时间排序,最近启动的排在前面
|
||||
versions.sort((a, b) => {
|
||||
const timeA = history[a]?.lastLaunch || history[a]?.downloadTime || 0;
|
||||
const timeB = history[b]?.lastLaunch || history[b]?.downloadTime || 0;
|
||||
return timeB - timeA;
|
||||
});
|
||||
|
||||
container.innerHTML = '';
|
||||
|
||||
versions.forEach((versionId, index) => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'version-item';
|
||||
item.dataset.versionId = versionId;
|
||||
|
||||
// 获取版本类型图标
|
||||
const icon = index === 0 && (history[versionId]?.lastLaunch || history[versionId]?.downloadTime)
|
||||
? '⭐' : '📦';
|
||||
|
||||
// 格式化时间
|
||||
const lastTime = history[versionId]?.lastLaunch || history[versionId]?.downloadTime;
|
||||
let timeText = '';
|
||||
if (lastTime) {
|
||||
const date = new Date(lastTime);
|
||||
const now = new Date();
|
||||
const diff = now.getTime() - date.getTime();
|
||||
|
||||
if (diff < 60000) {
|
||||
timeText = '刚刚';
|
||||
} else if (diff < 3600000) {
|
||||
timeText = `${Math.floor(diff / 60000)} 分钟前`;
|
||||
} else if (diff < 86400000) {
|
||||
timeText = `${Math.floor(diff / 3600000)} 小时前`;
|
||||
} else {
|
||||
timeText = `${date.getMonth() + 1}/${date.getDate()}`;
|
||||
}
|
||||
}
|
||||
|
||||
item.innerHTML = `
|
||||
<span class="version-item-icon">${icon}</span>
|
||||
<span class="version-item-id">${versionId}</span>
|
||||
<span class="version-item-time">${timeText}</span>
|
||||
`;
|
||||
|
||||
item.addEventListener('click', () => {
|
||||
selectVersion(versionId);
|
||||
});
|
||||
|
||||
container.appendChild(item);
|
||||
});
|
||||
|
||||
countEl.textContent = `${versions.length} 个版本`;
|
||||
|
||||
// 如果没有选中的版本,自动选中第一个
|
||||
if (!selectedVersionId && versions.length > 0) {
|
||||
selectVersion(versions[0]);
|
||||
}
|
||||
} catch (err) {
|
||||
container.innerHTML = '<div class="version-list-empty">加载版本列表失败</div>';
|
||||
countEl.textContent = '加载失败';
|
||||
}
|
||||
}
|
||||
|
||||
// 选择版本
|
||||
function selectVersion(versionId) {
|
||||
selectedVersionId = versionId;
|
||||
|
||||
// 更新UI
|
||||
const items = document.querySelectorAll('.version-item');
|
||||
items.forEach(item => {
|
||||
item.classList.toggle('selected', item.dataset.versionId === versionId);
|
||||
});
|
||||
|
||||
const infoEl = document.getElementById('selected-version-info');
|
||||
const nameEl = document.getElementById('selected-version-name');
|
||||
|
||||
if (infoEl && nameEl) {
|
||||
nameEl.textContent = `Minecraft ${versionId}`;
|
||||
infoEl.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// 更新启动按钮下方的小字显示
|
||||
const displayEl = document.getElementById('selected-version-display');
|
||||
const textEl = document.getElementById('selected-version-text');
|
||||
if (displayEl && textEl) {
|
||||
textEl.textContent = versionId;
|
||||
displayEl.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// 更新启动按钮状态
|
||||
updateLaunchButtonState(true);
|
||||
}
|
||||
|
||||
// 检查版本是否已安装
|
||||
async function isVersionInstalled(versionId) {
|
||||
try {
|
||||
const versions = await window.gpcl.scanVersions(null, true);
|
||||
return versions.includes(versionId);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let allVersions = [];
|
||||
let selectedVersionId = null;
|
||||
|
||||
// 模组加载器相关状态
|
||||
let modLoaderState = {
|
||||
forge: { available: false, versions: [], selected: null, expanded: false },
|
||||
fabric: { available: false, versions: [], selected: null, expanded: false },
|
||||
optifine: { available: false, versions: [], selected: null, expanded: false }
|
||||
};
|
||||
|
||||
// 模组加载器兼容性规则
|
||||
const MOD_LOADER_COMPATIBILITY = {
|
||||
// Forge 和 Fabric 互斥
|
||||
forge: ['fabric'],
|
||||
fabric: ['forge'],
|
||||
// OptiFine 与 Forge/Fabric 都互斥
|
||||
optifine: ['forge', 'fabric']
|
||||
};
|
||||
|
||||
// 检测 Forge 可用性
|
||||
async function checkForgeAvailability(mcVersion) {
|
||||
try {
|
||||
// Forge 支持 1.1 及以上版本
|
||||
const parts = mcVersion.split('.');
|
||||
const major = parseInt(parts[0], 10) || 1;
|
||||
const minor = parseInt(parts[1], 10) || 0;
|
||||
|
||||
// 1.1 以下不支持
|
||||
if (major === 1 && minor < 1) {
|
||||
return { available: false, versions: [] };
|
||||
}
|
||||
|
||||
// 从主进程获取 Forge 版本列表
|
||||
const result = await window.gpcl.getForgeVersions(mcVersion);
|
||||
if (result.success) {
|
||||
return { available: result.versions.length > 0, versions: result.versions };
|
||||
}
|
||||
return { available: false, versions: [] };
|
||||
} catch (e) {
|
||||
console.error('检测 Forge 可用性失败:', e);
|
||||
return { available: false, versions: [] };
|
||||
}
|
||||
}
|
||||
|
||||
// 检测所有模组加载器的可用性
|
||||
async function checkAllModLoaders(mcVersion) {
|
||||
// 重置状态
|
||||
modLoaderState = {
|
||||
forge: { available: false, versions: [], selected: null, expanded: false }
|
||||
};
|
||||
|
||||
// 检测 Forge
|
||||
const forgeResult = await checkForgeAvailability(mcVersion);
|
||||
modLoaderState.forge = { ...modLoaderState.forge, ...forgeResult };
|
||||
|
||||
// 更新 UI
|
||||
updateModLoaderUI();
|
||||
}
|
||||
|
||||
// 更新模组加载器 UI
|
||||
function updateModLoaderUI() {
|
||||
const loaders = ['forge'];
|
||||
|
||||
loaders.forEach(loader => {
|
||||
const state = modLoaderState[loader];
|
||||
const card = document.getElementById(`${loader}-option`);
|
||||
const badge = document.getElementById(`${loader}-badge`);
|
||||
const content = document.getElementById(`${loader}-content`);
|
||||
const loading = document.getElementById(`${loader}-loading`);
|
||||
const versionsContainer = document.getElementById(`${loader}-versions`);
|
||||
const error = document.getElementById(`${loader}-error`);
|
||||
|
||||
if (!card || !badge) return;
|
||||
|
||||
// 更新徽章状态
|
||||
if (state.available) {
|
||||
badge.textContent = '可用';
|
||||
badge.className = 'detail-option-badge available';
|
||||
card.classList.remove('not-available');
|
||||
} else {
|
||||
badge.textContent = '不可用';
|
||||
badge.className = 'detail-option-badge unavailable';
|
||||
card.classList.add('not-available');
|
||||
}
|
||||
|
||||
// 更新内容区域(仅在已展开时显示)
|
||||
if (state.expanded && state.available) {
|
||||
content.classList.remove('hidden');
|
||||
loading.classList.add('hidden');
|
||||
error.classList.add('hidden');
|
||||
versionsContainer.classList.remove('hidden');
|
||||
|
||||
// 渲染版本列表
|
||||
renderModLoaderVersionList(loader, state.versions);
|
||||
} else if (state.expanded && !state.available) {
|
||||
content.classList.remove('hidden');
|
||||
loading.classList.add('hidden');
|
||||
versionsContainer.classList.add('hidden');
|
||||
error.classList.remove('hidden');
|
||||
} else {
|
||||
content.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染模组加载器版本列表
|
||||
function renderModLoaderVersionList(loader, versions) {
|
||||
const container = document.getElementById(`${loader}-versions`);
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = '';
|
||||
|
||||
versions.forEach((version, index) => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'detail-option-version-item';
|
||||
item.dataset.versionId = version.id;
|
||||
item.dataset.version = version.version;
|
||||
|
||||
// 如果是已选中的版本,添加选中样式
|
||||
if (modLoaderState[loader].selected === version.version) {
|
||||
item.classList.add('selected');
|
||||
}
|
||||
|
||||
item.innerHTML = `
|
||||
<span class="detail-option-version-name">${version.name}</span>
|
||||
<span class="detail-option-version-check">✓</span>
|
||||
`;
|
||||
|
||||
item.addEventListener('click', () => selectModLoaderVersion(loader, version.version, version.id));
|
||||
|
||||
container.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
// 选择模组加载器版本
|
||||
function selectModLoaderVersion(loader, version, versionId) {
|
||||
const state = modLoaderState[loader];
|
||||
|
||||
// 如果点击的是已选中的版本,则取消选择
|
||||
if (state.selected === version) {
|
||||
state.selected = null;
|
||||
} else {
|
||||
state.selected = version;
|
||||
}
|
||||
|
||||
// 更新版本列表 UI
|
||||
const container = document.getElementById(`${loader}-versions`);
|
||||
if (container) {
|
||||
const items = container.querySelectorAll('.detail-option-version-item');
|
||||
items.forEach(item => {
|
||||
item.classList.toggle('selected', item.dataset.version === state.selected);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 更新互斥 UI
|
||||
function updateIncompatibilityUI() {
|
||||
// 目前只有 Forge,无需处理互斥
|
||||
}
|
||||
|
||||
// 获取加载器显示名称
|
||||
function getLoaderDisplayName(loader) {
|
||||
const names = {
|
||||
forge: 'Forge'
|
||||
};
|
||||
return names[loader] || loader;
|
||||
}
|
||||
|
||||
// 切换模组加载器展开/折叠
|
||||
function toggleModLoader(loader) {
|
||||
const state = modLoaderState[loader];
|
||||
const card = document.getElementById(`${loader}-option`);
|
||||
const content = document.getElementById(`${loader}-content`);
|
||||
const arrow = card?.querySelector('.detail-option-arrow');
|
||||
|
||||
if (!content) return;
|
||||
|
||||
// 如果不可用,不允许展开
|
||||
if (!state.available) return;
|
||||
|
||||
state.expanded = !state.expanded;
|
||||
|
||||
if (state.expanded) {
|
||||
if (arrow) arrow.classList.add('expanded');
|
||||
} else {
|
||||
if (arrow) arrow.classList.remove('expanded');
|
||||
}
|
||||
|
||||
// 重新更新整个 UI
|
||||
updateModLoaderUI();
|
||||
}
|
||||
|
||||
// 绑定模组加载器事件
|
||||
function bindModLoaderEvents() {
|
||||
const loaders = ['forge'];
|
||||
|
||||
loaders.forEach(loader => {
|
||||
const card = document.getElementById(`${loader}-option`);
|
||||
if (!card) return;
|
||||
|
||||
const header = card.querySelector('.detail-option-header');
|
||||
if (header) {
|
||||
header.addEventListener('click', () => toggleModLoader(loader));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 重置模组加载器状态
|
||||
function resetModLoaderState() {
|
||||
modLoaderState = {
|
||||
forge: { available: false, versions: [], selected: null, expanded: false }
|
||||
};
|
||||
|
||||
// 重置 UI
|
||||
const loaders = ['forge'];
|
||||
loaders.forEach(loader => {
|
||||
const card = document.getElementById(`${loader}-option`);
|
||||
const badge = document.getElementById(`${loader}-badge`);
|
||||
const content = document.getElementById(`${loader}-content`);
|
||||
const loading = document.getElementById(`${loader}-loading`);
|
||||
const versions = document.getElementById(`${loader}-versions`);
|
||||
const error = document.getElementById(`${loader}-error`);
|
||||
const arrow = card?.querySelector('.detail-option-arrow');
|
||||
|
||||
if (card) {
|
||||
card.classList.remove('not-available', 'incompatible');
|
||||
}
|
||||
if (badge) {
|
||||
badge.textContent = '检测中...';
|
||||
badge.className = 'detail-option-badge';
|
||||
}
|
||||
if (content) content.classList.add('hidden');
|
||||
if (loading) loading.classList.remove('hidden');
|
||||
if (versions) versions.classList.add('hidden');
|
||||
if (error) error.classList.add('hidden');
|
||||
if (arrow) arrow.classList.remove('expanded');
|
||||
});
|
||||
}
|
||||
|
||||
function getCategoryLabel(type) {
|
||||
const map = { release: '正式版', snapshot: '快照版', old_beta: '老版本 Beta', old_alpha: '老版本 Alpha' };
|
||||
return map[type] || type;
|
||||
}
|
||||
|
||||
function getCategoryIcon(type) {
|
||||
const map = { release: '🟢', snapshot: '🟡', old_beta: '🟠', old_alpha: '🔴' };
|
||||
return map[type] || '⚪';
|
||||
}
|
||||
|
||||
function formatDate(dateStr) {
|
||||
if (!dateStr) return '';
|
||||
const d = new Date(dateStr);
|
||||
return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
|
||||
}
|
||||
|
||||
function renderVersionList(versionMap) {
|
||||
if (!versionListContainer) return;
|
||||
versionListContainer.innerHTML = '';
|
||||
|
||||
const categoryOrder = ['release', 'snapshot', 'old_beta', 'old_alpha'];
|
||||
let totalCount = 0;
|
||||
|
||||
categoryOrder.forEach(type => {
|
||||
const items = versionMap[type];
|
||||
if (!items || items.length === 0) return;
|
||||
totalCount += items.length;
|
||||
|
||||
const categoryEl = document.createElement('div');
|
||||
categoryEl.className = 'version-category';
|
||||
categoryEl.dataset.category = type;
|
||||
|
||||
const isRelease = type === 'release';
|
||||
const header = document.createElement('div');
|
||||
header.className = 'version-category-header';
|
||||
header.innerHTML = `
|
||||
<span class="version-category-arrow ${isRelease ? '' : 'collapsed'}">▼</span>
|
||||
<span class="version-category-icon">${getCategoryIcon(type)}</span>
|
||||
<span class="version-category-name">${getCategoryLabel(type)}</span>
|
||||
<span class="version-category-count">${items.length} 个版本</span>
|
||||
`;
|
||||
|
||||
const body = document.createElement('div');
|
||||
body.className = `version-category-items ${isRelease ? '' : 'collapsed'}`;
|
||||
|
||||
items.forEach(v => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'version-item';
|
||||
item.dataset.versionId = v.id;
|
||||
|
||||
const displayName = v.displayName || v.id;
|
||||
item.innerHTML = `
|
||||
<span class="version-item-id">${displayName}</span>
|
||||
<span class="version-item-time">${formatDate(v.releaseTime)}</span>
|
||||
`;
|
||||
item.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
showVersionDetail(v);
|
||||
});
|
||||
body.appendChild(item);
|
||||
});
|
||||
|
||||
header.addEventListener('click', () => {
|
||||
const arrow = header.querySelector('.version-category-arrow');
|
||||
const isCollapsed = body.classList.toggle('collapsed');
|
||||
arrow.classList.toggle('collapsed', isCollapsed);
|
||||
});
|
||||
|
||||
categoryEl.appendChild(header);
|
||||
categoryEl.appendChild(body);
|
||||
versionListContainer.appendChild(categoryEl);
|
||||
});
|
||||
|
||||
if (versionCountLabel) {
|
||||
versionCountLabel.textContent = `共 ${totalCount} 个版本`;
|
||||
}
|
||||
}
|
||||
|
||||
function showVersionDetail(version) {
|
||||
selectedVersionId = version.id;
|
||||
if (versionListSection) versionListSection.classList.add('hidden');
|
||||
if (versionDetailPage) versionDetailPage.classList.remove('hidden');
|
||||
|
||||
// 重置下载按钮状态,确保它是可用的
|
||||
if (detailDownloadBtn) detailDownloadBtn.disabled = false;
|
||||
|
||||
const idEl = document.getElementById('detail-version-id');
|
||||
const typeEl = document.getElementById('detail-version-type');
|
||||
const typeLabelEl = document.getElementById('detail-type-label');
|
||||
const timeEl = document.getElementById('detail-release-time');
|
||||
|
||||
if (idEl) idEl.textContent = `Minecraft ${version.id}`;
|
||||
if (typeEl) typeEl.textContent = getCategoryLabel(version.type);
|
||||
if (typeLabelEl) typeLabelEl.textContent = `${getCategoryIcon(version.type)} ${getCategoryLabel(version.type)}`;
|
||||
if (timeEl) timeEl.textContent = formatDate(version.releaseTime) || '未知';
|
||||
|
||||
// 重置并检测模组加载器可用性
|
||||
resetModLoaderState();
|
||||
checkAllModLoaders(version.id);
|
||||
}
|
||||
|
||||
function hideVersionDetail() {
|
||||
selectedVersionId = null;
|
||||
if (versionDetailPage) versionDetailPage.classList.add('hidden');
|
||||
if (versionListSection) versionListSection.classList.remove('hidden');
|
||||
}
|
||||
|
||||
async function loadRemoteVersions() {
|
||||
const refreshBtn = document.getElementById('refresh-btn-page');
|
||||
try {
|
||||
if (refreshBtn) refreshBtn.classList.add('hidden');
|
||||
if (versionListContainer) {
|
||||
versionListContainer.innerHTML = '<div class="version-list-loading">正在加载版本列表...</div>';
|
||||
}
|
||||
|
||||
let versions = [];
|
||||
|
||||
// 尝试调用主进程API
|
||||
if (window.gpcl && typeof window.gpcl.getVersionManifest === 'function') {
|
||||
try {
|
||||
versions = await window.gpcl.getVersionManifest();
|
||||
} catch (e) {
|
||||
console.error('调用getVersionManifest失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试从文件缓存获取
|
||||
if (!versions || versions.length === 0) {
|
||||
try {
|
||||
if (window.gpcl && typeof window.gpcl.readJsonFile === 'function') {
|
||||
const cached = await window.gpcl.readJsonFile('cache', 'remote_versions.json');
|
||||
if (cached) {
|
||||
versions = cached;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('从文件缓存获取远程版本列表失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果还是没有数据,显示错误或示例数据
|
||||
if (!versions || versions.length === 0) {
|
||||
setStatus('无法获取远程版本列表', 'error');
|
||||
if (versionListContainer) {
|
||||
versionListContainer.innerHTML = '<div class="version-list-error">无法获取版本列表<br><button class="refresh-btn" style="margin-top:12px;" onclick="loadRemoteVersions()">重试</button></div>';
|
||||
}
|
||||
if (refreshBtn) refreshBtn.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
// Group versions by type
|
||||
const versionMap = {};
|
||||
versions.forEach(v => {
|
||||
if (!versionMap[v.type]) versionMap[v.type] = [];
|
||||
versionMap[v.type].push(v);
|
||||
});
|
||||
|
||||
// 保存到文件缓存
|
||||
try {
|
||||
if (window.gpcl && typeof window.gpcl.writeJsonFile === 'function') {
|
||||
await window.gpcl.writeJsonFile('cache', 'remote_versions.json', versions);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('保存远程版本列表到缓存失败:', e);
|
||||
}
|
||||
|
||||
allVersions = versions;
|
||||
renderVersionList(versionMap);
|
||||
setStatus('远程版本列表已加载');
|
||||
} catch (err) {
|
||||
setStatus('无法获取远程版本列表,请检查网络', 'error');
|
||||
showToast('版本列表加载失败', err.message || '请检查网络连接', 'error', 'remote-versions-error');
|
||||
if (versionListContainer) {
|
||||
versionListContainer.innerHTML = '<div class="version-list-error">加载失败,请检查网络连接<br><button class="refresh-btn" style="margin-top:12px;" onclick="loadRemoteVersions()">重试</button></div>';
|
||||
}
|
||||
if (refreshBtn) refreshBtn.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
async function launchGame() {
|
||||
// 检查按钮模式
|
||||
const launchBtn = document.getElementById('launch-btn');
|
||||
const mode = launchBtn?.dataset.mode || 'launch';
|
||||
|
||||
if (mode === 'download') {
|
||||
// 下载模式:跳转到下载页面
|
||||
showPage('download');
|
||||
setStatus('请选择要下载的游戏版本');
|
||||
return;
|
||||
}
|
||||
|
||||
// 启动模式:正常启动游戏
|
||||
setStatus('[动画] launchGame 函数被调用了');
|
||||
const username = usernameInput.value.trim() || 'GPCL_Player';
|
||||
|
||||
// 使用右侧面板选择的版本
|
||||
const versionId = selectedVersionId;
|
||||
|
||||
if (!versionId) {
|
||||
setStatus('请先选择已安装的版本', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录版本启动时间(不管是否成功都会记录)
|
||||
await recordVersionLaunch(versionId);
|
||||
|
||||
if (window.gpcl && window.gpcl.savePlayerName) {
|
||||
await window.gpcl.savePlayerName(username);
|
||||
}
|
||||
|
||||
launchBtn.disabled = true;
|
||||
|
||||
// 显示启动动画层
|
||||
showLaunchAnimation(versionId);
|
||||
|
||||
try {
|
||||
// 尝试启动游戏(主进程会自动处理Java下载)
|
||||
const gameDir = await initGameDir();
|
||||
setStatus('[动画] 开始启动游戏');
|
||||
|
||||
// 获取窗口模式设置
|
||||
const settings = await loadSettings();
|
||||
let windowMode = settings.game?.windowMode || 'windowed';
|
||||
let welcomeAnimPlaying = false;
|
||||
|
||||
if (settings.advanced?.playStartupAnimation && window.gpcl && window.gpcl.playStartupAnimation) {
|
||||
setStatus('[启动动画] 正在播放欢迎动画...');
|
||||
welcomeAnimPlaying = true;
|
||||
windowMode = 'fullscreen';
|
||||
|
||||
settings.advanced.playStartupAnimation = false;
|
||||
await gpcl.saveSettings(settings);
|
||||
const playStartupEl = document.getElementById('play-startup-animation');
|
||||
if (playStartupEl) playStartupEl.checked = false;
|
||||
|
||||
await window.gpcl.playStartupAnimation();
|
||||
setStatus('[启动动画] 动画播放完毕,正在启动游戏...');
|
||||
}
|
||||
|
||||
// 确保游戏窗口创建事件监听器已设置
|
||||
setupGameWindowListener();
|
||||
|
||||
const result = await window.gpcl.launch({
|
||||
versionId,
|
||||
username,
|
||||
gameDir,
|
||||
windowMode
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
setStatus('[动画] 游戏启动成功,窗口已创建');
|
||||
|
||||
if (welcomeAnimPlaying && window.gpcl && window.gpcl.dismissStartupAnimation) {
|
||||
window.gpcl.dismissStartupAnimation();
|
||||
}
|
||||
|
||||
// 显示成功动画后隐藏
|
||||
showLaunchSuccess();
|
||||
|
||||
setTimeout(() => {
|
||||
hideLaunchAnimation();
|
||||
}, 800);
|
||||
|
||||
setStatus(`游戏已启动!PID: ${result.pid}`, 'success');
|
||||
showToast('游戏已启动', `Minecraft ${versionId} (${username})`, 'success', 'game-started');
|
||||
} else if (result.cancelled) {
|
||||
// 用户取消了启动
|
||||
if (welcomeAnimPlaying && window.gpcl && window.gpcl.dismissStartupAnimation) {
|
||||
window.gpcl.dismissStartupAnimation();
|
||||
}
|
||||
hideLaunchAnimation();
|
||||
setStatus('启动已取消', 'info');
|
||||
showToast('启动已取消', '用户取消了游戏启动', 'info', 'launch-cancelled');
|
||||
} else {
|
||||
if (welcomeAnimPlaying && window.gpcl && window.gpcl.dismissStartupAnimation) {
|
||||
window.gpcl.dismissStartupAnimation();
|
||||
}
|
||||
hideLaunchAnimation();
|
||||
setStatus(result.error, 'error');
|
||||
showToast('启动失败', result.error, 'error', 'game-launch-failed');
|
||||
}
|
||||
} catch (err) {
|
||||
if (welcomeAnimPlaying && window.gpcl && window.gpcl.dismissStartupAnimation) {
|
||||
window.gpcl.dismissStartupAnimation();
|
||||
}
|
||||
hideLaunchAnimation();
|
||||
setStatus('启动出错', 'error');
|
||||
showToast('启动出错', err.message, 'error', 'launch-error');
|
||||
} finally {
|
||||
launchBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示启动动画
|
||||
async function showLaunchAnimation(versionId) {
|
||||
try {
|
||||
const overlay = document.getElementById('launch-animation-overlay');
|
||||
const statusText = document.getElementById('launch-status-text');
|
||||
const versionInfo = document.getElementById('launch-version-info');
|
||||
const progressFill = document.getElementById('launch-progress-fill');
|
||||
|
||||
if (overlay) {
|
||||
overlay.classList.remove('hidden', 'fade-out', 'launch-success');
|
||||
overlay.style.display = 'flex';
|
||||
void overlay.offsetHeight;
|
||||
overlay.classList.add('show');
|
||||
setStatus('[动画] 启动动画层已显示');
|
||||
} else {
|
||||
setStatus('[动画] 未找到启动动画层元素', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (statusText) {
|
||||
statusText.textContent = '正在启动游戏...';
|
||||
}
|
||||
|
||||
// 获取启动显示名称(不包含 OptiFine)
|
||||
let displayName = versionId;
|
||||
if (window.gpcl && typeof window.gpcl.getLaunchDisplayName === 'function') {
|
||||
try {
|
||||
displayName = await window.gpcl.getLaunchDisplayName(versionId);
|
||||
} catch (e) {
|
||||
console.warn(`获取启动显示名称失败: ${versionId}`, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (versionInfo) {
|
||||
versionInfo.textContent = `将启动: ${displayName}`;
|
||||
}
|
||||
|
||||
if (progressFill) {
|
||||
progressFill.style.width = '0%';
|
||||
}
|
||||
|
||||
simulateLaunchProgress();
|
||||
showRandomTip();
|
||||
} catch (err) {
|
||||
setStatus('[动画] 显示启动动画失败: ' + err.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 小知识列表
|
||||
const launchTips = [
|
||||
'Minecraft 最初由 Markus "Notch" Persson 于 2009 年独立开发,最初版本仅用 6 天完成。',
|
||||
'Minecraft 的苦力怕(Creeper)是 Notch 在尝试制作猪模型时,因搞错长宽比而意外诞生的。',
|
||||
'末影人(Enderman)会随机搬运一些方块,这是参考了现实中的都市传说 Slender Man。',
|
||||
'Minecraft 中最大的自然结构是下界要塞(Nether Fortress),可绵延数百个区块。',
|
||||
'Minecraft 中钻石矿石只在 Y 坐标 16 以下生成,最集中的区域是 Y=5 到 Y=12 之间。',
|
||||
'Minecraft 的附魔台周围的图书馆架数量会影响附魔等级,最高可达到 30 级。',
|
||||
'红石(Redstone)是 Minecraft 中的"电力"系统,可以构建从简单门到计算机的各种电路。',
|
||||
'Minecraft 中潮涌核心(Conduit)可以为水下玩家提供无限呼吸和夜视效果。',
|
||||
'Minecraft 的下界合金(Netherite)装备需要先制作品铁装备再锻造升级。',
|
||||
'Minecraft 中的嗅探兽(Sniffer)是 2022 年 Minecraft Live 由玩家投票选出的生物。',
|
||||
'Minecraft 中沼泽小屋(Swamp Hut)必定会生成女巫,是获取红石粉的稳定来源。',
|
||||
'Minecraft 中你可以用线制作羊毛,但不能用羊毛反合成线。',
|
||||
'Minecraft 的创造模式最初是为开发调试而加入的,后来才成为正式游戏模式。',
|
||||
'Minecraft 中装备了冰霜行者(Frost Walker)附魔的靴子可以在水上行走。',
|
||||
'Minecraft 中每个区块(Chunk)是 16×16 方块大小,游戏世界是通过区块流式加载的。',
|
||||
'Minecraft 中鞘翅(Elytra)可以在末地船中找到,配合烟花火箭可以实现飞行。',
|
||||
'Minecraft 中下界对应主世界的比例为 1:8,在下界走 1 格相当于主世界 8 格。',
|
||||
'Minecraft 中掠夺者前哨站(Pillager Outpost)会生成灾厄旗帜,周围的灾厄村民不会攻击。',
|
||||
'Minecraft 中蜜蜂(Bee)在采蜜后会帮助作物加速生长。',
|
||||
'Minecraft 中史莱姆(Slime)只在特定区块生成,可以使用种子计算器定位史莱姆区块。',
|
||||
'Minecraft 的唱片机播放的是 C418 创作的音乐,13 和 cat 两张唱片可在要塞的箱子中找到。',
|
||||
'Minecraft 中深海监守者(Warden)是盲人,依靠声音和振动感知玩家。',
|
||||
'Minecraft 的紫颂果(Chorus Fruit)食用后会随机传送,但会失去一些饥饿值。',
|
||||
'Minecraft 中袭击(Raid)是不祥之兆效果进入村庄时触发的多波战斗事件。',
|
||||
'Minecraft 中下界传送门(Nether Portal)的大小最小为 4×5,最大为 23×23。',
|
||||
'Minecraft 中海龟壳帽(Turtle Shell)可以让玩家在水下呼吸更长时间。',
|
||||
'Minecraft 中甜浆果(Sweet Berries)可以在针叶林生物群系中找到,也可种植。',
|
||||
'Minecraft 中你已经可以用铜(Copper)制作避雷针、望远镜和装饰方块。',
|
||||
'Minecraft 中营火(Campfire)可以烹饪食物,而且不会像熔炉那样消耗燃料。',
|
||||
'Minecraft 中幽匿块(Sculk)会在深暗之域生成,挖掉之前最好先准备好。',
|
||||
'GPCL 是云云一个人开发的启动器,并没有什么所谓的开发团队。',
|
||||
];
|
||||
|
||||
// 显示随机小知识
|
||||
function showRandomTip() {
|
||||
const tipText = document.getElementById('launch-tip-text');
|
||||
if (!tipText) return;
|
||||
|
||||
const randomIndex = Math.floor(Math.random() * launchTips.length);
|
||||
tipText.textContent = launchTips[randomIndex];
|
||||
}
|
||||
|
||||
// 模拟启动进度
|
||||
function simulateLaunchProgress() {
|
||||
const progressFill = document.getElementById('launch-progress-fill');
|
||||
const statusText = document.getElementById('launch-status-text');
|
||||
|
||||
let progress = 0;
|
||||
const statusMessages = [
|
||||
'正在检查游戏文件...',
|
||||
'正在准备Java运行时...',
|
||||
'正在初始化游戏环境...',
|
||||
'正在启动游戏窗口...'
|
||||
];
|
||||
|
||||
const interval = setInterval(() => {
|
||||
// 随机增加进度
|
||||
progress += Math.random() * 15 + 5;
|
||||
|
||||
if (progress >= 90) {
|
||||
progress = 90;
|
||||
}
|
||||
|
||||
if (progressFill) {
|
||||
progressFill.style.width = `${progress}%`;
|
||||
}
|
||||
|
||||
// 更新状态文字
|
||||
const messageIndex = Math.floor(progress / 25);
|
||||
if (statusText && messageIndex < statusMessages.length) {
|
||||
statusText.textContent = statusMessages[messageIndex];
|
||||
}
|
||||
}, 200);
|
||||
|
||||
// 保存interval ID以便清除
|
||||
window.launchProgressInterval = interval;
|
||||
}
|
||||
|
||||
// 游戏窗口创建监听器设置
|
||||
let gameWindowResolve = null;
|
||||
let gameWindowTimeout = null;
|
||||
|
||||
function setupGameWindowListener() {
|
||||
// 移除旧的监听器避免重复
|
||||
if (window.gpcl && window.gpcl.removeAllListeners) {
|
||||
window.gpcl.removeAllListeners();
|
||||
}
|
||||
|
||||
// 重新注册关闭确认监听器
|
||||
if (window.gpcl && window.gpcl.onConfirmCloseWhileDownloading) {
|
||||
window.gpcl.onConfirmCloseWhileDownloading(async () => {
|
||||
const result = await showDialog({
|
||||
type: 'confirm',
|
||||
title: '确认关闭',
|
||||
message: '当前正在下载中,关闭会停止下载并清理已下载的文件。确定要关闭吗?如有不完整的"libraries"需自行清理。'
|
||||
});
|
||||
|
||||
if (result) {
|
||||
if (window.gpcl && window.gpcl.confirmCloseDownload) {
|
||||
await window.gpcl.confirmCloseDownload();
|
||||
}
|
||||
} else {
|
||||
if (window.gpcl && window.gpcl.cancelClose) {
|
||||
await window.gpcl.cancelClose();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 注册游戏窗口创建监听器
|
||||
if (window.gpcl && window.gpcl.onGameWindowCreated) {
|
||||
window.gpcl.onGameWindowCreated((data) => {
|
||||
setStatus('[动画] 收到游戏窗口创建事件');
|
||||
if (gameWindowResolve) {
|
||||
clearTimeout(gameWindowTimeout);
|
||||
gameWindowResolve();
|
||||
gameWindowResolve = null;
|
||||
gameWindowTimeout = null;
|
||||
}
|
||||
});
|
||||
setStatus('[动画] 游戏窗口监听器已设置');
|
||||
}
|
||||
}
|
||||
|
||||
// 等待游戏窗口创建成功
|
||||
function waitForGameWindow() {
|
||||
return new Promise((resolve) => {
|
||||
gameWindowResolve = resolve;
|
||||
|
||||
gameWindowTimeout = setTimeout(() => {
|
||||
setStatus('[动画] 窗口检测超时');
|
||||
gameWindowResolve = null;
|
||||
gameWindowTimeout = null;
|
||||
resolve();
|
||||
}, 10000); // 10秒超时
|
||||
});
|
||||
}
|
||||
|
||||
// 显示成功动画
|
||||
function showLaunchSuccess() {
|
||||
try {
|
||||
const overlay = document.getElementById('launch-animation-overlay');
|
||||
const pickaxe = document.getElementById('pickaxe');
|
||||
const block = document.getElementById('block');
|
||||
|
||||
if (overlay) {
|
||||
overlay.classList.add('launch-success');
|
||||
setStatus('[动画] 显示成功动画');
|
||||
}
|
||||
|
||||
if (block) {
|
||||
block.classList.add('breaking');
|
||||
}
|
||||
|
||||
// 创建粒子效果
|
||||
createParticles();
|
||||
} catch (err) {
|
||||
setStatus('[动画] 显示成功动画失败: ' + err.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 创建粒子效果
|
||||
function createParticles() {
|
||||
const particlesContainer = document.getElementById('particles');
|
||||
if (!particlesContainer) return;
|
||||
|
||||
// 清除现有粒子
|
||||
particlesContainer.innerHTML = '';
|
||||
|
||||
// 创建多个粒子
|
||||
for (let i = 0; i < 12; i++) {
|
||||
const particle = document.createElement('div');
|
||||
particle.className = 'particle';
|
||||
|
||||
// 随机方向
|
||||
const angle = (Math.PI * 2 * i) / 12;
|
||||
const distance = 50 + Math.random() * 50;
|
||||
particle.style.setProperty('--dx', `${Math.cos(angle) * distance}px`);
|
||||
particle.style.setProperty('--dy', `${Math.sin(angle) * distance}px`);
|
||||
|
||||
// 随机大小和延迟
|
||||
particle.style.width = `${4 + Math.random() * 8}px`;
|
||||
particle.style.height = particle.style.width;
|
||||
particle.style.animationDelay = `${Math.random() * 0.2}s`;
|
||||
|
||||
particlesContainer.appendChild(particle);
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏启动动画
|
||||
function hideLaunchAnimation() {
|
||||
try {
|
||||
const overlay = document.getElementById('launch-animation-overlay');
|
||||
|
||||
if (overlay) {
|
||||
overlay.classList.remove('show');
|
||||
overlay.classList.add('fade-out');
|
||||
|
||||
setTimeout(() => {
|
||||
overlay.style.display = 'none';
|
||||
overlay.classList.remove('fade-out', 'launch-success');
|
||||
overlay.classList.add('hidden');
|
||||
setStatus('[动画] 启动动画层已隐藏');
|
||||
}, 400);
|
||||
}
|
||||
|
||||
// 清除进度interval
|
||||
if (window.launchProgressInterval) {
|
||||
clearInterval(window.launchProgressInterval);
|
||||
window.launchProgressInterval = null;
|
||||
}
|
||||
} catch (err) {
|
||||
setStatus('[动画] 隐藏启动动画失败: ' + err.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function startDownload(versionId) {
|
||||
if (!versionId) { setStatus('请先选择要下载的版本', 'error'); return; }
|
||||
|
||||
// 检查版本是否已安装
|
||||
const installed = await isVersionInstalled(versionId);
|
||||
|
||||
// 检查是否选择了模组加载器
|
||||
let selectedModLoader = null;
|
||||
let selectedModLoaderVersion = null;
|
||||
let modLoaderName = '';
|
||||
|
||||
for (const [loader, state] of Object.entries(modLoaderState)) {
|
||||
if (state.selected) {
|
||||
selectedModLoader = loader;
|
||||
selectedModLoaderVersion = state.selected;
|
||||
if (loader === 'forge') modLoaderName = 'Forge';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (detailDownloadBtn) detailDownloadBtn.disabled = true;
|
||||
await loadChartJs();
|
||||
initChart();
|
||||
resetChart();
|
||||
|
||||
if (downloadPanel) downloadPanel.classList.remove('hidden');
|
||||
// 从详情页回到列表页,显示下载监控
|
||||
hideVersionDetail();
|
||||
showPage('download');
|
||||
|
||||
// 滚动到下载页面顶部
|
||||
const scrollContainer = document.querySelector('.main-content');
|
||||
if (scrollContainer) {
|
||||
scrollContainer.scrollTop = 0;
|
||||
}
|
||||
|
||||
const downloadStatusEl = document.getElementById('download-status');
|
||||
const filenameEl = document.getElementById('download-filename');
|
||||
const cancelBtn = document.getElementById('cancel-download-btn');
|
||||
|
||||
// 重置按钮状态
|
||||
if (cancelBtn) {
|
||||
cancelBtn.textContent = '取消下载';
|
||||
cancelBtn.classList.remove('complete');
|
||||
cancelBtn.disabled = false;
|
||||
}
|
||||
|
||||
let displayName = `Minecraft ${versionId}`;
|
||||
if (selectedModLoader) {
|
||||
displayName = `${modLoaderName} ${versionId}`;
|
||||
}
|
||||
|
||||
if (downloadStatusEl) downloadStatusEl.textContent = `开始下载 ${displayName} ...`;
|
||||
if (filenameEl) filenameEl.textContent = `正在下载: ${displayName}`;
|
||||
|
||||
window.gpcl.removeAllListeners();
|
||||
|
||||
// 重新注册关闭确认监听器(不会被removeAllListeners移除)
|
||||
window.gpcl.onConfirmCloseWhileDownloading(async () => {
|
||||
const result = await showDialog({
|
||||
type: 'confirm',
|
||||
title: '确认关闭',
|
||||
message: '当前正在下载中,关闭会停止下载并清理已下载的文件。确定要关闭吗?如有不完整的"libraries"需自行清理。'
|
||||
});
|
||||
|
||||
if (result) {
|
||||
if (window.gpcl && window.gpcl.confirmCloseDownload) {
|
||||
await window.gpcl.confirmCloseDownload();
|
||||
}
|
||||
} else {
|
||||
if (window.gpcl && window.gpcl.cancelClose) {
|
||||
await window.gpcl.cancelClose();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let lastTime = Date.now();
|
||||
let lastBytes = 0;
|
||||
let lastLabel = '';
|
||||
|
||||
window.gpcl.onDownloadProgress((data) => {
|
||||
const currentTime = Date.now();
|
||||
const timeDiff = (currentTime - lastTime) / 1000;
|
||||
|
||||
const currentSpeedEl = document.getElementById('current-speed');
|
||||
const peakSpeedEl = document.getElementById('peak-speed');
|
||||
const progressFillEl = document.getElementById('progress-fill');
|
||||
const progressTextEl = document.getElementById('progress-text');
|
||||
|
||||
if (data.label && data.label !== lastLabel) {
|
||||
lastLabel = data.label;
|
||||
lastBytes = 0;
|
||||
lastTime = currentTime;
|
||||
}
|
||||
|
||||
if (timeDiff >= 0.5 && data.bytesDownloaded !== undefined) {
|
||||
const bytesDiff = data.bytesDownloaded - lastBytes;
|
||||
if (bytesDiff < 0) {
|
||||
lastBytes = data.bytesDownloaded;
|
||||
lastTime = currentTime;
|
||||
return;
|
||||
}
|
||||
const speedBps = Math.max(0, (bytesDiff * 8) / timeDiff / 1000000);
|
||||
|
||||
if (currentSpeedEl) currentSpeedEl.textContent = speedBps.toFixed(2) + ' Mbps';
|
||||
|
||||
if (speedBps > peakSpeed) {
|
||||
peakSpeed = speedBps;
|
||||
if (peakSpeedEl) peakSpeedEl.textContent = peakSpeed.toFixed(2) + ' Mbps';
|
||||
}
|
||||
|
||||
updateChart(speedBps);
|
||||
|
||||
lastTime = currentTime;
|
||||
lastBytes = data.bytesDownloaded;
|
||||
}
|
||||
|
||||
const percent = data.percent || 0;
|
||||
if (progressFillEl) progressFillEl.style.width = percent + '%';
|
||||
if (progressTextEl) progressTextEl.textContent = percent.toFixed(1) + '%';
|
||||
|
||||
if (data.label && filenameEl) {
|
||||
filenameEl.textContent = data.label;
|
||||
}
|
||||
|
||||
if (downloadStatusEl) downloadStatusEl.textContent = `正在下载 ${versionId}: ${percent.toFixed(1)}%`;
|
||||
|
||||
if (menuDownload) menuDownload.classList.add('active');
|
||||
if (data.percent >= 100 && menuDownload) {
|
||||
menuDownload.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
const maxConcurrent = getMaxConcurrentFromSettings();
|
||||
let result;
|
||||
|
||||
if (selectedModLoader) {
|
||||
// 下载带模组加载器的版本
|
||||
result = await window.gpcl.downloadWithModLoader(
|
||||
versionId,
|
||||
selectedModLoader,
|
||||
selectedModLoaderVersion,
|
||||
maxConcurrent
|
||||
);
|
||||
} else {
|
||||
// 下载原版
|
||||
result = await window.gpcl.downloadVersion(versionId, maxConcurrent);
|
||||
}
|
||||
|
||||
if (result.success) {
|
||||
// 记录版本下载时间
|
||||
await recordVersionDownload(result.finalVersionId || versionId);
|
||||
|
||||
const finalDisplayName = selectedModLoader
|
||||
? `${modLoaderName} ${versionId}`
|
||||
: `Minecraft ${versionId}`;
|
||||
|
||||
setStatus('下载完成', 'success');
|
||||
if (downloadStatusEl) downloadStatusEl.textContent = `下载完成: ${finalDisplayName}`;
|
||||
const progressFillEl = document.getElementById('progress-fill');
|
||||
const progressTextEl = document.getElementById('progress-text');
|
||||
if (progressFillEl) progressFillEl.style.width = '100%';
|
||||
if (progressTextEl) progressTextEl.textContent = '100%';
|
||||
showToast('下载完成', `${finalDisplayName} 已准备就绪`, 'success', 'download-complete');
|
||||
await loadVersions();
|
||||
// 刷新版本选择列表
|
||||
renderVersionSelectList();
|
||||
// 修改按钮状态为下载完成(绿色)
|
||||
if (cancelBtn) {
|
||||
cancelBtn.textContent = '下载完成';
|
||||
cancelBtn.classList.add('complete');
|
||||
cancelBtn.disabled = true;
|
||||
}
|
||||
} else {
|
||||
setStatus(`下载失败: ${result.error}`, 'error');
|
||||
if (downloadStatusEl) downloadStatusEl.textContent = `下载失败: ${result.error}`;
|
||||
}
|
||||
} catch (err) {
|
||||
setStatus('下载出错', 'error');
|
||||
if (downloadStatusEl) downloadStatusEl.textContent = `下载出错: ${err.message || err}`;
|
||||
showToast('下载出错', err.message || String(err), 'error', 'download-error');
|
||||
}
|
||||
|
||||
if (detailDownloadBtn) detailDownloadBtn.disabled = false;
|
||||
}
|
||||
|
||||
// 详情页面事件
|
||||
if (detailBackBtn) {
|
||||
detailBackBtn.addEventListener('click', hideVersionDetail);
|
||||
}
|
||||
|
||||
if (detailDownloadBtn) {
|
||||
detailDownloadBtn.addEventListener('click', () => {
|
||||
if (selectedVersionId) {
|
||||
startDownload(selectedVersionId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.gpcl.onGameClosed((code) => {
|
||||
setStatus('游戏已退出');
|
||||
showToast('游戏已退出', `退出码: ${code}`, 'info', 'game-closed');
|
||||
launchBtn.disabled = false;
|
||||
});
|
||||
|
||||
window.gpcl.onGameError((message) => {
|
||||
setStatus(`启动出错: ${message}`, 'error');
|
||||
showToast('游戏错误', message, 'error', 'game-error');
|
||||
launchBtn.disabled = false;
|
||||
});
|
||||
|
||||
launchBtn.addEventListener('click', launchGame);
|
||||
|
||||
(async function init() {
|
||||
setStatus('正在加载...');
|
||||
initTheme();
|
||||
initScale();
|
||||
if (menuLaunch) menuLaunch.classList.add('active');
|
||||
|
||||
if (usernameInput && window.gpcl && window.gpcl.getPlayerName) {
|
||||
const savedName = await window.gpcl.getPlayerName();
|
||||
if (savedName) usernameInput.value = savedName;
|
||||
}
|
||||
|
||||
await loadVersions();
|
||||
await loadRemoteVersions();
|
||||
|
||||
// 初始化版本选择列表并自动选中最近启动的版本
|
||||
await renderVersionSelectList();
|
||||
|
||||
// 初始化Java版本状态
|
||||
await initJavaVersionStatus();
|
||||
bindJavaInstallButtons();
|
||||
|
||||
// 绑定模组加载器事件
|
||||
bindModLoaderEvents();
|
||||
|
||||
setInterval(async () => {
|
||||
// 刷新游戏版本(使用静默模式,不写日志)
|
||||
if (currentPage === 'launch') {
|
||||
await loadVersions(true);
|
||||
await renderVersionSelectList();
|
||||
}
|
||||
|
||||
// 刷新Java版本状态(在所有页面都刷新)
|
||||
if (currentPage === 'download') {
|
||||
await refreshJavaStatus();
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
// 刷新Java版本状态的函数
|
||||
async function refreshJavaStatus() {
|
||||
const javaVersions = ["8", "17", "21", "25"];
|
||||
|
||||
for (const ver of javaVersions) {
|
||||
try {
|
||||
const result = await window.gpcl.checkJava(ver);
|
||||
const card = document.querySelector(`.java-version-card[data-version="${ver}"]`);
|
||||
const badge = document.getElementById(`java-${ver}-status`);
|
||||
const btn = card?.querySelector('.java-install-btn');
|
||||
|
||||
if (result.installed) {
|
||||
if (!card?.classList.contains('installed')) {
|
||||
if (card) card.classList.add('installed');
|
||||
if (badge) {
|
||||
badge.textContent = '已安装';
|
||||
badge.className = 'java-version-badge installed';
|
||||
}
|
||||
if (btn) {
|
||||
btn.textContent = '卸载';
|
||||
btn.classList.add('installed');
|
||||
btn.disabled = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (card?.classList.contains('installed')) {
|
||||
card.classList.remove('installed');
|
||||
if (badge) {
|
||||
badge.textContent = '';
|
||||
badge.className = 'java-version-badge';
|
||||
}
|
||||
if (btn) {
|
||||
btn.textContent = '安装';
|
||||
btn.classList.remove('installed');
|
||||
btn.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`检查Java ${ver} 状态失败`, e);
|
||||
}
|
||||
}
|
||||
|
||||
updateJavaInstallStatus();
|
||||
}
|
||||
|
||||
// bind refresh button
|
||||
const refreshBtn = document.getElementById('refresh-btn-page');
|
||||
if (refreshBtn) refreshBtn.addEventListener('click', async () => {
|
||||
setStatus('重试加载远程版本...');
|
||||
await loadRemoteVersions();
|
||||
// 同时刷新Java状态
|
||||
await refreshJavaStatus();
|
||||
setStatus('准备就绪');
|
||||
});
|
||||
|
||||
// bind cancel download button
|
||||
if (cancelDownloadBtn) cancelDownloadBtn.addEventListener('click', async () => {
|
||||
const result = await showDialog({
|
||||
type: 'confirm',
|
||||
title: '确认取消',
|
||||
message: '确定要取消当前下载吗?已下载的文件将被清理。'
|
||||
});
|
||||
|
||||
if (result) {
|
||||
if (window.gpcl && window.gpcl.cancelDownloadOnly) {
|
||||
await window.gpcl.cancelDownloadOnly();
|
||||
if (downloadPanel) downloadPanel.classList.add('hidden');
|
||||
if (detailDownloadBtn) detailDownloadBtn.disabled = false;
|
||||
showToast('已取消', '下载已取消,文件已清理', 'info');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setStatus('准备就绪');
|
||||
|
||||
// 启动器加载完成后自动前置窗口
|
||||
if (window.gpcl && window.gpcl.focusWindow) {
|
||||
setTimeout(() => {
|
||||
window.gpcl.focusWindow();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// 绑定启动取消按钮
|
||||
const launchCancelBtn = document.getElementById('launch-cancel-btn');
|
||||
if (launchCancelBtn) {
|
||||
launchCancelBtn.addEventListener('click', () => {
|
||||
setStatus('[动画] 用户取消启动');
|
||||
if (window.gpcl && window.gpcl.cancelLaunch) {
|
||||
window.gpcl.cancelLaunch();
|
||||
}
|
||||
hideLaunchAnimation();
|
||||
});
|
||||
}
|
||||
|
||||
// 静默检查更新
|
||||
checkForUpdates(true);
|
||||
|
||||
// 绑定检查更新按钮
|
||||
const checkUpdateBtn = document.getElementById('check-update-btn');
|
||||
if (checkUpdateBtn) {
|
||||
checkUpdateBtn.addEventListener('click', async () => {
|
||||
checkUpdateBtn.disabled = true;
|
||||
checkUpdateBtn.textContent = '检查中...';
|
||||
|
||||
await checkForUpdates(false);
|
||||
|
||||
const updateStatus = document.getElementById('update-status');
|
||||
const goDownloadBtn = document.getElementById('go-download-btn');
|
||||
|
||||
if (updateAvailable) {
|
||||
const newest = allNewerVersions[allNewerVersions.length - 1];
|
||||
checkUpdateBtn.textContent = `发现 ${allNewerVersions.length} 个新版本`;
|
||||
checkUpdateBtn.classList.add('new-version');
|
||||
|
||||
if (updateStatus) {
|
||||
updateStatus.innerHTML = `
|
||||
<div class="update-log-title">发现 ${allNewerVersions.length} 个新版本!</div>
|
||||
${renderUpdateVersionCards(allNewerVersions)}
|
||||
`;
|
||||
updateStatus.classList.add('show', 'has-update');
|
||||
}
|
||||
|
||||
if (goDownloadBtn) {
|
||||
goDownloadBtn.classList.remove('hidden');
|
||||
}
|
||||
} else {
|
||||
checkUpdateBtn.textContent = '已是最新版本';
|
||||
checkUpdateBtn.classList.remove('new-version');
|
||||
|
||||
if (updateStatus) {
|
||||
updateStatus.innerHTML = '当前已是最新版本';
|
||||
updateStatus.classList.add('show');
|
||||
updateStatus.classList.remove('has-update');
|
||||
}
|
||||
|
||||
if (goDownloadBtn) {
|
||||
goDownloadBtn.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
checkUpdateBtn.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定立即前往下载按钮
|
||||
const goDownloadBtn = document.getElementById('go-download-btn');
|
||||
|
||||
// 绑定自动检查更新开关
|
||||
const autoCheckUpdate = document.getElementById('auto-check-update');
|
||||
|
||||
// 绑定防止多次启动开关
|
||||
const preventMultipleLaunch = document.getElementById('prevent-multiple-launch');
|
||||
|
||||
// 绑定自动清除日志开关
|
||||
const autoClearLogs = document.getElementById('auto-clear-logs');
|
||||
const logRetentionContainer = document.getElementById('log-retention-container');
|
||||
const logRetentionValue = document.getElementById('log-retention-value');
|
||||
const logRetentionUnit = document.getElementById('log-retention-unit');
|
||||
|
||||
// 初始化版本显示
|
||||
const aboutVersion = document.getElementById('about-version');
|
||||
|
||||
const customMirrorContainer = document.getElementById('custom-java-mirror-container');
|
||||
const customMirrorUrl = document.getElementById('custom-java-mirror-url');
|
||||
|
||||
// 游戏内存
|
||||
initCustomSelect('settings-memory', async (value) => {
|
||||
const settings = await loadSettings();
|
||||
settings.game.memory = value;
|
||||
await gpcl.saveSettings(settings);
|
||||
});
|
||||
|
||||
// 窗口模式
|
||||
initCustomSelect('settings-window-mode', async (value) => {
|
||||
const settings = await loadSettings();
|
||||
if (!settings.game) settings.game = {};
|
||||
settings.game.windowMode = value;
|
||||
await gpcl.saveSettings(settings);
|
||||
});
|
||||
|
||||
// 主题模式
|
||||
initCustomSelect('settings-theme', async (value) => {
|
||||
const settings = await loadSettings();
|
||||
if (!settings.appearance) settings.appearance = {};
|
||||
settings.appearance.theme = value;
|
||||
await gpcl.saveSettings(settings);
|
||||
applyTheme(value);
|
||||
});
|
||||
|
||||
// 界面缩放
|
||||
initCustomSelect('settings-scale', async (value) => {
|
||||
const settings = await loadSettings();
|
||||
if (!settings.appearance) settings.appearance = {};
|
||||
settings.appearance.scale = value;
|
||||
await gpcl.saveSettings(settings);
|
||||
applyScale(value);
|
||||
});
|
||||
|
||||
// Java镜像源
|
||||
initCustomSelect('java-mirror-select', async (value) => {
|
||||
if (customMirrorContainer) {
|
||||
customMirrorContainer.classList.toggle('hidden', value !== 'custom');
|
||||
}
|
||||
await saveJavaMirrorSettings(value, customMirrorUrl?.value || '');
|
||||
});
|
||||
|
||||
// 日志保留时间单位
|
||||
initCustomSelect('log-retention-unit', async (unit) => {
|
||||
// 根据单位限制当前值
|
||||
const maxValues = {
|
||||
hour: 23,
|
||||
day: 30,
|
||||
month: 11,
|
||||
year: 10
|
||||
};
|
||||
|
||||
if (logRetentionValue) {
|
||||
let value = parseInt(logRetentionValue.value, 10);
|
||||
if (isNaN(value) || value < 1) value = 1;
|
||||
if (value > maxValues[unit]) {
|
||||
value = maxValues[unit];
|
||||
logRetentionValue.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
const settings = await loadSettings();
|
||||
settings.advanced.logRetentionUnit = unit;
|
||||
if (logRetentionValue) {
|
||||
settings.advanced.logRetentionValue = parseInt(logRetentionValue.value, 10);
|
||||
}
|
||||
await gpcl.saveSettings(settings);
|
||||
});
|
||||
|
||||
// 绑定立即前往下载按钮
|
||||
if (goDownloadBtn) {
|
||||
goDownloadBtn.addEventListener('click', () => {
|
||||
if (window.gpcl && window.gpcl.openExternal) {
|
||||
window.gpcl.openExternal('https://gamets.caellab.com/gpcl/#download');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定自动检查更新开关
|
||||
if (autoCheckUpdate) {
|
||||
// 从设置中加载状态
|
||||
(async () => {
|
||||
const settings = await loadSettings();
|
||||
autoCheckUpdate.checked = settings.advanced?.autoCheckUpdate !== false;
|
||||
})();
|
||||
|
||||
autoCheckUpdate.addEventListener('change', async () => {
|
||||
const settings = await loadSettings();
|
||||
settings.advanced.autoCheckUpdate = autoCheckUpdate.checked;
|
||||
await gpcl.saveSettings(settings);
|
||||
updateSettingsBadge();
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定防止多次启动开关
|
||||
if (preventMultipleLaunch) {
|
||||
(async () => {
|
||||
const settings = await loadSettings();
|
||||
preventMultipleLaunch.checked = settings.advanced?.preventMultipleLaunch !== false;
|
||||
})();
|
||||
|
||||
preventMultipleLaunch.addEventListener('change', async () => {
|
||||
const settings = await loadSettings();
|
||||
settings.advanced.preventMultipleLaunch = preventMultipleLaunch.checked;
|
||||
await gpcl.saveSettings(settings);
|
||||
});
|
||||
}
|
||||
|
||||
const playStartupAnimation = document.getElementById('play-startup-animation');
|
||||
if (playStartupAnimation) {
|
||||
(async () => {
|
||||
const settings = await loadSettings();
|
||||
playStartupAnimation.checked = settings.advanced?.playStartupAnimation === true;
|
||||
})();
|
||||
|
||||
playStartupAnimation.addEventListener('change', async () => {
|
||||
const settings = await loadSettings();
|
||||
settings.advanced.playStartupAnimation = playStartupAnimation.checked;
|
||||
await gpcl.saveSettings(settings);
|
||||
});
|
||||
}
|
||||
|
||||
const developerMode = document.getElementById('developer-mode');
|
||||
if (developerMode) {
|
||||
(async () => {
|
||||
const settings = await loadSettings();
|
||||
developerMode.checked = settings.advanced?.developerMode === true;
|
||||
})();
|
||||
|
||||
developerMode.addEventListener('change', async () => {
|
||||
const settings = await loadSettings();
|
||||
settings.advanced.developerMode = developerMode.checked;
|
||||
await gpcl.saveSettings(settings);
|
||||
if (window.gpcl && window.gpcl.setDeveloperMode) {
|
||||
await window.gpcl.setDeveloperMode(developerMode.checked);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定自动清除日志开关
|
||||
if (autoClearLogs) {
|
||||
(async () => {
|
||||
const settings = await loadSettings();
|
||||
autoClearLogs.checked = settings.advanced?.autoClearLogs !== false;
|
||||
})();
|
||||
|
||||
autoClearLogs.addEventListener('change', async () => {
|
||||
const settings = await loadSettings();
|
||||
settings.advanced.autoClearLogs = autoClearLogs.checked;
|
||||
await gpcl.saveSettings(settings);
|
||||
|
||||
if (logRetentionContainer) {
|
||||
logRetentionContainer.classList.toggle('hidden', !autoClearLogs.checked);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定保留日志时间输入
|
||||
if (logRetentionValue) {
|
||||
(async () => {
|
||||
const settings = await loadSettings();
|
||||
logRetentionValue.value = settings.advanced?.logRetentionValue || 7;
|
||||
})();
|
||||
|
||||
logRetentionValue.addEventListener('change', async () => {
|
||||
let value = parseInt(logRetentionValue.value, 10);
|
||||
const unit = getCustomSelectValue('log-retention-unit') || 'day';
|
||||
|
||||
// 根据单位限制最大值
|
||||
const maxValues = {
|
||||
hour: 23,
|
||||
day: 30,
|
||||
month: 11,
|
||||
year: 10
|
||||
};
|
||||
|
||||
if (isNaN(value) || value < 1) {
|
||||
value = 1;
|
||||
} else if (value > maxValues[unit]) {
|
||||
value = maxValues[unit];
|
||||
}
|
||||
|
||||
logRetentionValue.value = value;
|
||||
|
||||
const settings = await loadSettings();
|
||||
settings.advanced.logRetentionValue = value;
|
||||
await gpcl.saveSettings(settings);
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化版本显示
|
||||
if (aboutVersion) {
|
||||
const version = await getCurrentVersion();
|
||||
aboutVersion.textContent = `版本: ${version}`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 绑定Java镜像URL输入
|
||||
if (customMirrorUrl) {
|
||||
customMirrorUrl.addEventListener('input', async () => {
|
||||
await saveJavaMirrorSettings(getCustomSelectValue('java-mirror-select') || 'tsinghua', customMirrorUrl.value);
|
||||
});
|
||||
}
|
||||
|
||||
// 加载所有设置到界面
|
||||
(async () => {
|
||||
const settings = await loadSettings();
|
||||
|
||||
// 游戏内存
|
||||
let memoryValue = '2'; // 默认2GB
|
||||
if (settings.game?.memory) {
|
||||
const storedMemory = String(settings.game.memory);
|
||||
if (storedMemory.includes('096')) {
|
||||
// 旧格式,转换为GB
|
||||
const mb = parseInt(storedMemory);
|
||||
if (!isNaN(mb)) {
|
||||
memoryValue = String(mb / 1024);
|
||||
}
|
||||
} else {
|
||||
memoryValue = storedMemory;
|
||||
}
|
||||
}
|
||||
setCustomSelectValue('settings-memory', memoryValue);
|
||||
|
||||
// 窗口模式
|
||||
setCustomSelectValue('settings-window-mode', settings.game?.windowMode || 'windowed');
|
||||
|
||||
// 主题
|
||||
const theme = settings.appearance?.theme || 'dark';
|
||||
setCustomSelectValue('settings-theme', theme);
|
||||
applyTheme(theme);
|
||||
|
||||
// 缩放
|
||||
const scale = settings.appearance?.scale || '100';
|
||||
setCustomSelectValue('settings-scale', scale);
|
||||
applyScale(scale);
|
||||
|
||||
// Java镜像
|
||||
const javaMirror = settings.download?.javaMirror || 'tsinghua';
|
||||
setCustomSelectValue('java-mirror-select', javaMirror);
|
||||
if (customMirrorContainer) {
|
||||
customMirrorContainer.classList.toggle('hidden', javaMirror !== 'custom');
|
||||
}
|
||||
if (customMirrorUrl && settings.download?.customJavaMirror) {
|
||||
customMirrorUrl.value = settings.download.customJavaMirror;
|
||||
}
|
||||
|
||||
// 日志保留时间单位
|
||||
setCustomSelectValue('log-retention-unit', settings.advanced?.logRetentionUnit || 'day');
|
||||
})();
|
||||
|
||||
// 绑定兼容性设置
|
||||
(async () => {
|
||||
const resetDefaultToggle = document.getElementById('settings-reset-default');
|
||||
|
||||
if (resetDefaultToggle) {
|
||||
resetDefaultToggle.checked = false; // 默认关闭
|
||||
|
||||
resetDefaultToggle.addEventListener('change', async function() {
|
||||
if (this.checked) {
|
||||
// 显示确认对话框
|
||||
const confirmed = await showDialog({
|
||||
type: 'confirm',
|
||||
title: '恢复默认设置',
|
||||
message: '确定要将所有设置恢复为默认值吗?\n\n此操作会重启启动器。'
|
||||
});
|
||||
|
||||
if (confirmed) {
|
||||
// 恢复默认设置
|
||||
if (window.gpcl && window.gpcl.resetSettings) {
|
||||
const result = await window.gpcl.resetSettings();
|
||||
if (result.success) {
|
||||
// 重启应用
|
||||
if (window.gpcl && window.gpcl.restartApp) {
|
||||
await window.gpcl.restartApp();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 用户取消,重置开关状态
|
||||
this.checked = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
// 绑定设置侧边栏点击事件
|
||||
const settingsSidebarItems = document.querySelectorAll('.settings-sidebar-item');
|
||||
settingsSidebarItems.forEach(item => {
|
||||
item.addEventListener('click', () => {
|
||||
const tabName = item.dataset.tab;
|
||||
if (tabName) {
|
||||
switchSettingsTab(tabName);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 绑定版本设置按钮 - 先声明所有需要的变量
|
||||
let currentVersionForSettings = null;
|
||||
|
||||
const versionSettingsBtn = document.getElementById('version-settings');
|
||||
const versionSettingsPanel = document.getElementById('version-settings-panel');
|
||||
const versionSettingsBackBtn = document.getElementById('version-settings-back-btn');
|
||||
const versionSettingsTitle = document.getElementById('version-settings-title');
|
||||
const versionDeleteEnable = document.getElementById('version-delete-enable');
|
||||
const versionServerIpContainer = document.getElementById('version-server-ip-container');
|
||||
const versionServerIpInput = document.getElementById('version-server-ip');
|
||||
const versionSelectPanel = document.getElementById('version-select-panel');
|
||||
const statusElement = document.getElementById('status');
|
||||
|
||||
async function getVersionSettings(versionId) {
|
||||
try {
|
||||
if (window.gpcl && window.gpcl.getVersionSettings) {
|
||||
const result = await window.gpcl.getVersionSettings(versionId);
|
||||
if (result.success && result.settings) {
|
||||
return result.settings;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取版本设置失败:', e);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
async function saveVersionSettings(versionId, settings) {
|
||||
try {
|
||||
if (window.gpcl && window.gpcl.saveVersionSettings) {
|
||||
const result = await window.gpcl.saveVersionSettings(versionId, settings);
|
||||
return result.success;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('保存版本设置失败:', e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 更新服务器IP输入框的显示状态
|
||||
function updateServerIpVisibility() {
|
||||
const startupMode = getCustomSelectValue('version-startup-mode');
|
||||
if (startupMode === 'join') {
|
||||
versionServerIpContainer.classList.add('visible');
|
||||
} else {
|
||||
versionServerIpContainer.classList.remove('visible');
|
||||
}
|
||||
}
|
||||
|
||||
async function loadVersionSettingsToUI(versionId) {
|
||||
const settings = await getVersionSettings(versionId);
|
||||
|
||||
versionDeleteEnable.checked = false;
|
||||
setCustomSelectValue('version-memory', settings.memory || 'global');
|
||||
setCustomSelectValue('version-window-mode', settings.windowMode || 'global');
|
||||
setCustomSelectValue('version-startup-mode', settings.startupMode || 'default');
|
||||
versionServerIpInput.value = settings.serverIp || '';
|
||||
|
||||
updateServerIpVisibility();
|
||||
}
|
||||
|
||||
let saveTimeout = null;
|
||||
function saveCurrentVersionSettings() {
|
||||
if (!currentVersionForSettings) return;
|
||||
|
||||
const settings = {
|
||||
memory: getCustomSelectValue('version-memory') || 'global',
|
||||
windowMode: getCustomSelectValue('version-window-mode') || 'global',
|
||||
startupMode: getCustomSelectValue('version-startup-mode') || 'default',
|
||||
serverIp: versionServerIpInput.value || ''
|
||||
};
|
||||
|
||||
if (saveTimeout) clearTimeout(saveTimeout);
|
||||
saveTimeout = setTimeout(async () => {
|
||||
const success = await saveVersionSettings(currentVersionForSettings, settings);
|
||||
if (success) {
|
||||
showToast('保存成功', `版本 ${currentVersionForSettings} 的设置已保存`, 'success');
|
||||
} else {
|
||||
showToast('保存失败', '无法保存版本设置', 'error');
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
|
||||
async function showVersionSettingsPanel(versionId) {
|
||||
if (!versionSettingsPanel) return;
|
||||
|
||||
currentVersionForSettings = versionId;
|
||||
versionSettingsTitle.textContent = `版本设置 - ${versionId}`;
|
||||
|
||||
versionSelectPanel.classList.add('hidden');
|
||||
if (statusElement) statusElement.style.display = 'none';
|
||||
|
||||
versionSettingsPanel.classList.remove('hidden');
|
||||
versionSettingsPanel.classList.remove('slide-out');
|
||||
|
||||
await loadVersionSettingsToUI(versionId);
|
||||
}
|
||||
|
||||
async function hideVersionSettingsPanel() {
|
||||
if (!versionSettingsPanel) return;
|
||||
|
||||
// 立即保存当前设置(取消延迟保存)
|
||||
if (saveTimeout) clearTimeout(saveTimeout);
|
||||
if (currentVersionForSettings) {
|
||||
const settings = {
|
||||
memory: getCustomSelectValue('version-memory') || 'global',
|
||||
windowMode: getCustomSelectValue('version-window-mode') || 'global',
|
||||
startupMode: getCustomSelectValue('version-startup-mode') || 'default',
|
||||
serverIp: versionServerIpInput.value || ''
|
||||
};
|
||||
await saveVersionSettings(currentVersionForSettings, settings);
|
||||
}
|
||||
|
||||
versionSettingsPanel.classList.add('slide-out');
|
||||
|
||||
setTimeout(() => {
|
||||
versionSettingsPanel.classList.add('hidden');
|
||||
versionSettingsPanel.classList.remove('slide-out');
|
||||
if (statusElement) statusElement.style.display = '';
|
||||
currentVersionForSettings = null;
|
||||
}, 300);
|
||||
}
|
||||
|
||||
if (versionSettingsBtn) {
|
||||
versionSettingsBtn.addEventListener('click', async () => {
|
||||
if (selectedVersionId) {
|
||||
// 如果版本选择面板是打开的,先关闭它
|
||||
if (!versionSelectPanel.classList.contains('hidden')) {
|
||||
versionSelectPanel.classList.add('hidden');
|
||||
}
|
||||
await showVersionSettingsPanel(selectedVersionId);
|
||||
} else {
|
||||
showToast('未选择版本', '请先选择一个版本', 'warning');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (versionSettingsBackBtn) {
|
||||
versionSettingsBackBtn.addEventListener('click', async () => {
|
||||
await hideVersionSettingsPanel();
|
||||
});
|
||||
}
|
||||
|
||||
if (versionDeleteEnable) {
|
||||
versionDeleteEnable.addEventListener('change', async function() {
|
||||
if (this.checked) {
|
||||
const confirmed = await showDialog({
|
||||
type: 'confirm',
|
||||
title: '确认删除',
|
||||
message: `确定要删除版本 "${currentVersionForSettings}" 吗?此操作不可恢复!`
|
||||
});
|
||||
|
||||
if (confirmed) {
|
||||
if (window.gpcl && window.gpcl.deleteVersion) {
|
||||
try {
|
||||
const result = await window.gpcl.deleteVersion(currentVersionForSettings);
|
||||
if (result.success) {
|
||||
showToast('删除成功', `版本 ${currentVersionForSettings} 已删除`, 'success');
|
||||
|
||||
await hideVersionSettingsPanel();
|
||||
loadVersions(true);
|
||||
renderVersionSelectList();
|
||||
} else {
|
||||
showToast('删除失败', result.error || '无法删除版本', 'error');
|
||||
this.checked = false;
|
||||
}
|
||||
} catch (e) {
|
||||
showToast('删除失败', e.message, 'error');
|
||||
this.checked = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.checked = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化版本设置下拉框
|
||||
initCustomSelect('version-memory', () => {
|
||||
saveCurrentVersionSettings();
|
||||
});
|
||||
|
||||
initCustomSelect('version-window-mode', () => {
|
||||
saveCurrentVersionSettings();
|
||||
});
|
||||
|
||||
initCustomSelect('version-startup-mode', () => {
|
||||
updateServerIpVisibility();
|
||||
saveCurrentVersionSettings();
|
||||
});
|
||||
|
||||
// 版本服务器IP输入框
|
||||
if (versionServerIpInput) {
|
||||
versionServerIpInput.addEventListener('input', () => {
|
||||
saveCurrentVersionSettings();
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定版本选择按钮
|
||||
const versionSelectBtn = document.getElementById('version-select');
|
||||
const versionPanel = document.getElementById('version-select-panel');
|
||||
if (versionSelectBtn && versionPanel) {
|
||||
versionSelectBtn.addEventListener('click', () => {
|
||||
// 如果版本设置面板是打开的,就不动
|
||||
if (!versionSettingsPanel.classList.contains('hidden')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 切换版本面板显示状态
|
||||
if (versionPanel.classList.contains('hidden')) {
|
||||
versionPanel.classList.remove('hidden');
|
||||
renderVersionSelectList();
|
||||
} else {
|
||||
versionPanel.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定正版登录按钮(显示提示)
|
||||
const authMicrosoftBtn = document.getElementById('auth-microsoft');
|
||||
if (authMicrosoftBtn) {
|
||||
authMicrosoftBtn.addEventListener('click', () => {
|
||||
showToast('正版登录', '正版登录功能尚未对接微软账号系统', 'info');
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
// 版本更新检查
|
||||
let latestVersion = null;
|
||||
let latestVersionLog = null;
|
||||
let updateAvailable = false;
|
||||
let allNewerVersions = [];
|
||||
|
||||
async function getCurrentVersion() {
|
||||
try {
|
||||
if (window.gpcl && window.gpcl.getAppVersion) {
|
||||
return await window.gpcl.getAppVersion();
|
||||
}
|
||||
} catch (e) {}
|
||||
return '1.0.0';
|
||||
}
|
||||
|
||||
function compareVersions(current, latest) {
|
||||
const currentParts = current.split('.').map(Number);
|
||||
const latestParts = latest.split('.').map(Number);
|
||||
|
||||
for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
|
||||
const c = currentParts[i] || 0;
|
||||
const l = latestParts[i] || 0;
|
||||
if (l > c) return 1;
|
||||
if (c > l) return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function parseLauncherVersions(launcherData) {
|
||||
const versions = [];
|
||||
let current = null;
|
||||
|
||||
for (const item of launcherData) {
|
||||
if (item.name === 'version' && item.key) {
|
||||
if (current) versions.push(current);
|
||||
current = { version: item.key, title: '', log: '' };
|
||||
} else if (current) {
|
||||
if (item.name === 'versionlog' && item.key) {
|
||||
current.log = item.key;
|
||||
} else if (item.name === 'versiontitle' && item.key) {
|
||||
current.title = item.key;
|
||||
} else if (!item.key && item.name && item.name !== 'versionlog' && item.name !== 'versiontitle') {
|
||||
current.title = item.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (current) versions.push(current);
|
||||
return versions;
|
||||
}
|
||||
|
||||
function renderUpdateVersionCards(newerVersions) {
|
||||
if (!newerVersions || newerVersions.length === 0) return '';
|
||||
|
||||
const cards = newerVersions.map((v, index) => {
|
||||
const isNewest = index === newerVersions.length - 1;
|
||||
const titleHtml = v.title
|
||||
? `<div class="update-card-title">${v.title}</div>`
|
||||
: '';
|
||||
const logHtml = v.log
|
||||
? `<div class="update-card-log">${v.log}</div>`
|
||||
: '';
|
||||
return `
|
||||
<div class="update-version-card ${isNewest ? 'newest' : ''}">
|
||||
<div class="update-card-header">
|
||||
<span class="update-card-version">${v.version}</span>
|
||||
${isNewest ? '<span class="update-card-badge">最新</span>' : ''}
|
||||
</div>
|
||||
${titleHtml}
|
||||
${logHtml}
|
||||
</div>
|
||||
`;
|
||||
}).reverse().join('');
|
||||
|
||||
return `<div class="update-versions-list">${cards}</div>`;
|
||||
}
|
||||
|
||||
async function checkForUpdates(silent = false) {
|
||||
setStatus('[版本检查] 开始检查更新...');
|
||||
|
||||
try {
|
||||
const result = await window.gpcl.checkForUpdates();
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || '检查更新失败');
|
||||
}
|
||||
|
||||
const data = result.data;
|
||||
const launcherData = data.Launcher || [];
|
||||
|
||||
const parsedVersions = parseLauncherVersions(launcherData);
|
||||
|
||||
if (parsedVersions.length === 0) throw new Error('无法获取版本信息');
|
||||
|
||||
latestVersion = parsedVersions[0].version;
|
||||
latestVersionLog = parsedVersions[0].log;
|
||||
|
||||
const currentVersion = await getCurrentVersion();
|
||||
setStatus(`[版本检查] 当前版本: ${currentVersion}, 最新版本: ${latestVersion}`);
|
||||
|
||||
allNewerVersions = parsedVersions
|
||||
.filter(v => compareVersions(currentVersion, v.version) > 0)
|
||||
.sort((a, b) => compareVersions(a.version, b.version));
|
||||
|
||||
updateAvailable = allNewerVersions.length > 0;
|
||||
|
||||
setStatus(`[版本检查] 有更新: ${updateAvailable}, 新版本数: ${allNewerVersions.length}`);
|
||||
|
||||
if (!silent) {
|
||||
if (updateAvailable) {
|
||||
setStatus(`发现 ${allNewerVersions.length} 个新版本`, 'warning');
|
||||
} else {
|
||||
setStatus('当前已是最新版本', 'success');
|
||||
}
|
||||
}
|
||||
|
||||
if (updateAvailable && silent) {
|
||||
const newest = allNewerVersions[allNewerVersions.length - 1];
|
||||
const titlePart = newest.title ? ` "${newest.title}"` : '';
|
||||
showToast('发现新版本', `GPCL ${newest.version}${titlePart} 已发布`, 'warning', 'update-available');
|
||||
}
|
||||
|
||||
await updateSettingsBadge();
|
||||
|
||||
} catch (e) {
|
||||
setStatus(`[版本检查] 错误: ${e.message}`, 'error');
|
||||
console.error('[版本检查] 详细错误:', e);
|
||||
if (!silent) {
|
||||
setStatus('检查更新失败: ' + e.message, 'error');
|
||||
}
|
||||
updateAvailable = false;
|
||||
allNewerVersions = [];
|
||||
await updateSettingsBadge();
|
||||
}
|
||||
}
|
||||
|
||||
async function updateSettingsBadge() {
|
||||
const settingsMenu = document.getElementById('menu-settings');
|
||||
const checkUpdateBtn = document.getElementById('check-update-btn');
|
||||
if (!settingsMenu) return;
|
||||
|
||||
// 移除旧的红点
|
||||
const oldBadge = settingsMenu.querySelector('.update-badge');
|
||||
if (oldBadge) oldBadge.remove();
|
||||
|
||||
// 获取设置
|
||||
const settings = await loadSettings();
|
||||
|
||||
// 只有在有新版本且自动检查更新开启时才显示红点
|
||||
if (updateAvailable && settings.advanced?.autoCheckUpdate !== false) {
|
||||
const badge = document.createElement('span');
|
||||
badge.className = 'update-badge';
|
||||
badge.textContent = '';
|
||||
badge.style.cssText = 'position:absolute;top:2px;right:2px;width:8px;height:8px;background:#f44336;border-radius:50%;';
|
||||
settingsMenu.style.position = 'relative';
|
||||
settingsMenu.appendChild(badge);
|
||||
|
||||
// 检查更新按钮也标红
|
||||
if (checkUpdateBtn) {
|
||||
checkUpdateBtn.classList.add('new-version');
|
||||
checkUpdateBtn.textContent = `发现 ${allNewerVersions.length} 个新版本`;
|
||||
}
|
||||
} else {
|
||||
// 移除检查更新按钮的红标(如果当前没有更新)
|
||||
if (checkUpdateBtn && !updateAvailable) {
|
||||
checkUpdateBtn.classList.remove('new-version');
|
||||
checkUpdateBtn.textContent = '检查更新';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSettings() {
|
||||
try {
|
||||
return await gpcl.getSettings();
|
||||
} catch (e) {
|
||||
console.error('加载设置失败:', e);
|
||||
return {
|
||||
game: {
|
||||
memory: '2',
|
||||
playerName: 'GPCL_Player',
|
||||
javaPath: '',
|
||||
jvmArgs: '',
|
||||
windowMode: 'windowed'
|
||||
},
|
||||
appearance: {
|
||||
theme: 'light',
|
||||
scale: '100'
|
||||
},
|
||||
download: {
|
||||
maxConcurrent: 64,
|
||||
javaMirror: 'tsinghua',
|
||||
customJavaMirror: ''
|
||||
},
|
||||
advanced: {
|
||||
autoCheckUpdate: true,
|
||||
preventMultipleLaunch: true,
|
||||
autoClearLogs: true,
|
||||
logRetentionValue: 7,
|
||||
logRetentionUnit: 'day',
|
||||
playStartupAnimation: true,
|
||||
developerMode: false
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function applyTheme(theme) {
|
||||
if (theme === 'light') {
|
||||
document.body.classList.add('light-mode');
|
||||
} else {
|
||||
document.body.classList.remove('light-mode');
|
||||
}
|
||||
}
|
||||
|
||||
async function saveThemeSetting(theme) {
|
||||
const settings = await loadSettings();
|
||||
settings.appearance.theme = theme;
|
||||
await gpcl.saveSettings(settings);
|
||||
}
|
||||
|
||||
async function initTheme() {
|
||||
const settings = await loadSettings();
|
||||
const theme = settings.appearance?.theme || 'dark';
|
||||
applyTheme(theme);
|
||||
|
||||
const themeSelect = document.getElementById('settings-theme');
|
||||
if (themeSelect) {
|
||||
themeSelect.value = theme;
|
||||
themeSelect.addEventListener('change', async () => {
|
||||
const newTheme = themeSelect.value;
|
||||
applyTheme(newTheme);
|
||||
await saveThemeSetting(newTheme);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function applyScale(scale) {
|
||||
const factor = parseInt(scale, 10) / 100;
|
||||
document.body.style.zoom = factor;
|
||||
}
|
||||
|
||||
async function saveScaleSetting(scale) {
|
||||
const settings = await loadSettings();
|
||||
settings.appearance.scale = scale;
|
||||
await gpcl.saveSettings(settings);
|
||||
}
|
||||
|
||||
async function initScale() {
|
||||
const settings = await loadSettings();
|
||||
const scale = settings.appearance?.scale || '100';
|
||||
applyScale(scale);
|
||||
|
||||
const scaleSelect = document.getElementById('settings-scale');
|
||||
if (scaleSelect) {
|
||||
scaleSelect.value = scale;
|
||||
scaleSelect.addEventListener('change', async () => {
|
||||
const newScale = scaleSelect.value;
|
||||
applyScale(newScale);
|
||||
await saveScaleSetting(newScale);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function saveJavaMirrorSettings(mirror, customUrl) {
|
||||
const settings = await loadSettings();
|
||||
settings.download.javaMirror = mirror;
|
||||
settings.download.customJavaMirror = customUrl;
|
||||
await gpcl.saveSettings(settings);
|
||||
}
|
||||
|
After Width: | Height: | Size: 483 B |
|
|
@ -0,0 +1,187 @@
|
|||
<!--
|
||||
CaelLab BY-SA Code License
|
||||
Copyright (c) 2026 Yunyun(云云) By 虚舟实验室(CaelLab) / CaelLabGameTS
|
||||
Source: https://git.caellab.com/yunyun/GoodPlanCraftLauncher
|
||||
Source: https://github.com/yunyun-3782/GoodPlanCraftLauncher
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>GPCL - Loading</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', 'Segoe UI', sans-serif;
|
||||
background: linear-gradient(135deg, #0d0d15 0%, #1a1a2e 50%, #0d0d15 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.splash-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 30px;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 24px;
|
||||
background: linear-gradient(135deg, rgba(79, 195, 247, 0.2), rgba(41, 182, 246, 0.1));
|
||||
padding: 12px;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 30px rgba(79, 195, 247, 0.3);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 60px rgba(79, 195, 247, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.brand-name {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #a0aec0;
|
||||
letter-spacing: 2px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.brand-name .highlight {
|
||||
color: #4fc3f7;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.version-info {
|
||||
font-size: 12px;
|
||||
color: #5f6d85;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 11px;
|
||||
color: #5f6d85;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.particle-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.particle {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
background: rgba(79, 195, 247, 0.5);
|
||||
border-radius: 50%;
|
||||
animation: float 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0) translateX(0);
|
||||
opacity: 0;
|
||||
}
|
||||
10% {
|
||||
opacity: 1;
|
||||
}
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-100vh) translateX(20px);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.fade-out {
|
||||
animation: fadeOut 0.5s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="particle-container" id="particles"></div>
|
||||
|
||||
<div class="splash-container" id="splash-content">
|
||||
<div class="logo">
|
||||
<img src="logo.png" alt="GPCL Logo">
|
||||
</div>
|
||||
<div class="brand-name">
|
||||
<span class="highlight">G</span>ood <span class="highlight">P</span>lan <span class="highlight">C</span>raft <span class="highlight">L</span>auncher
|
||||
</div>
|
||||
<div class="version-info" id="version-info">v1.1.0</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function createParticles() {
|
||||
const container = document.getElementById('particles');
|
||||
for (let i = 0; i < 25; i++) {
|
||||
const particle = document.createElement('div');
|
||||
particle.className = 'particle';
|
||||
particle.style.left = Math.random() * 100 + '%';
|
||||
particle.style.bottom = '-10px';
|
||||
particle.style.animationDelay = Math.random() * 6 + 's';
|
||||
particle.style.animationDuration = (4 + Math.random() * 4) + 's';
|
||||
container.appendChild(particle);
|
||||
}
|
||||
}
|
||||
|
||||
createParticles();
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
if (window.gpcl && window.gpcl.getAppVersion) {
|
||||
window.gpcl.getAppVersion().then(version => {
|
||||
document.getElementById('version-info').textContent = 'v' + version;
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
const splashContent = document.getElementById('splash-content');
|
||||
splashContent.classList.add('fade-out');
|
||||
setTimeout(() => {
|
||||
window.location.href = 'index.html';
|
||||
}, 500);
|
||||
}, 2000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,2964 @@
|
|||
/*
|
||||
* CaelLab BY-SA Code License
|
||||
* Copyright (c) 2026 Yunyun(云云) By 虚舟实验室(CaelLab) / CaelLabGameTS
|
||||
* Source: https://git.caellab.com/yunyun/GoodPlanCraftLauncher
|
||||
* Source: https://github.com/yunyun-3782/GoodPlanCraftLauncher
|
||||
*/
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg-primary: #1a1a2e;
|
||||
--bg-secondary: #16213e;
|
||||
--bg-tertiary: rgba(15, 15, 25, 0.95);
|
||||
--bg-card: rgba(255, 255, 255, 0.05);
|
||||
--bg-card-hover: rgba(255, 255, 255, 0.08);
|
||||
--bg-input: rgba(255, 255, 255, 0.08);
|
||||
--bg-input-hover: rgba(255, 255, 255, 0.12);
|
||||
--bg-dark: rgba(0, 0, 0, 0.2);
|
||||
--bg-sidebar: rgba(0, 0, 0, 0.2);
|
||||
|
||||
--text-primary: #e0e0e0;
|
||||
--text-secondary: #b0bec5;
|
||||
--text-tertiary: #90a4ae;
|
||||
--text-muted: #78909c;
|
||||
--text-dim: #5f6d85;
|
||||
--text-label: #dde4f0;
|
||||
--text-title: #e8edf5;
|
||||
|
||||
--accent-primary: #4fc3f7;
|
||||
--accent-secondary: #29b6f6;
|
||||
--accent-tertiary: #03a9f4;
|
||||
--accent-glow: rgba(79, 195, 247, 0.3);
|
||||
|
||||
--border-light: rgba(255, 255, 255, 0.1);
|
||||
--border-medium: rgba(255, 255, 255, 0.15);
|
||||
--border-accent: rgba(79, 195, 247, 0.2);
|
||||
|
||||
--success: #66bb6a;
|
||||
--error: #ef5350;
|
||||
--warning: #ffa726;
|
||||
|
||||
--scrollbar-thumb: rgba(79, 195, 247, 0.6);
|
||||
--scrollbar-thumb-hover: rgba(79, 195, 247, 0.8);
|
||||
--scrollbar-thumb-active: rgba(41, 182, 246, 0.9);
|
||||
}
|
||||
|
||||
body.light-mode {
|
||||
--bg-primary: #f0f8ff;
|
||||
--bg-secondary: #e6f2ff;
|
||||
--bg-tertiary: #ffffff;
|
||||
--bg-card: rgba(255, 255, 255, 0.9);
|
||||
--bg-card-hover: rgba(200, 230, 255, 0.5);
|
||||
--bg-input: rgba(240, 248, 255, 0.8);
|
||||
--bg-input-hover: rgba(200, 230, 255, 0.6);
|
||||
--bg-dark: rgba(0, 100, 180, 0.08);
|
||||
--bg-sidebar: rgba(240, 248, 255, 0.95);
|
||||
|
||||
--text-primary: #1a3a5c;
|
||||
--text-secondary: #2d4a6f;
|
||||
--text-tertiary: #4a6a8a;
|
||||
--text-muted: #6a8aa6;
|
||||
--text-dim: #8aa0c0;
|
||||
--text-label: #0a2040;
|
||||
--text-title: #001530;
|
||||
|
||||
--accent-primary: #0099ff;
|
||||
--accent-secondary: #0077cc;
|
||||
--accent-tertiary: #0055aa;
|
||||
--accent-glow: rgba(0, 153, 255, 0.2);
|
||||
|
||||
--border-light: rgba(0, 100, 180, 0.1);
|
||||
--border-medium: rgba(0, 100, 180, 0.2);
|
||||
--border-accent: rgba(0, 153, 255, 0.3);
|
||||
|
||||
--scrollbar-thumb: rgba(0, 153, 255, 0.5);
|
||||
--scrollbar-thumb-hover: rgba(0, 153, 255, 0.7);
|
||||
--scrollbar-thumb-active: rgba(0, 119, 204, 0.8);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button {
|
||||
display: none !important;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar-thumb);
|
||||
border-radius: 4px;
|
||||
min-height: 24px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--scrollbar-thumb-hover);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
background: var(--scrollbar-thumb-active);
|
||||
}
|
||||
|
||||
.main-content {
|
||||
scrollbar-color: var(--scrollbar-thumb) transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.version-list-container {
|
||||
scrollbar-color: var(--scrollbar-thumb) transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.settings-page {
|
||||
scrollbar-color: var(--scrollbar-thumb) transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', 'Segoe UI', sans-serif;
|
||||
background: linear-gradient(135deg, var(--bg-primary) 0%, var(--bg-secondary) 100%);
|
||||
color: var(--text-primary);
|
||||
min-height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 48px;
|
||||
color: var(--accent-primary);
|
||||
text-shadow: 0 0 20px var(--accent-glow);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: var(--text-muted);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.launch-panel {
|
||||
background: var(--bg-card);
|
||||
border-radius: 12px;
|
||||
padding: 0;
|
||||
margin-bottom: 20px;
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--border-light);
|
||||
display: flex;
|
||||
min-height: 480px;
|
||||
overflow: hidden; /* 禁用外层滚动条 */
|
||||
}
|
||||
|
||||
.launch-sidebar {
|
||||
width: 33.33%;
|
||||
background: linear-gradient(180deg, rgba(79, 195, 247, 0.08) 0%, rgba(79, 195, 247, 0.02) 100%);
|
||||
border-right: 1px solid var(--border-light);
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.launch-content {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.version-select-panel {
|
||||
background: var(--bg-card);
|
||||
border-radius: 10px;
|
||||
padding: 16px;
|
||||
border: 1px solid var(--border-light);
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.version-settings-panel {
|
||||
background: var(--bg-card);
|
||||
border-radius: 10px;
|
||||
padding: 16px;
|
||||
border: 1px solid var(--border-light);
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 300px;
|
||||
animation: slideInRight 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(100%);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOutRight {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
.version-settings-panel.slide-out {
|
||||
animation: slideOutRight 0.3s ease-in;
|
||||
}
|
||||
|
||||
.version-settings-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.version-settings-back {
|
||||
background: none;
|
||||
border: 1px solid var(--border-light);
|
||||
color: var(--text-secondary);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.version-settings-back:hover {
|
||||
background: var(--bg-card-hover);
|
||||
color: var(--accent-primary);
|
||||
border-color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.version-settings-title {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-title);
|
||||
}
|
||||
|
||||
.version-settings-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.version-settings-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.version-settings-section .section-title {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.version-settings-section .section-desc {
|
||||
margin: -4px 0 12px 0;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
background: var(--bg-dark);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.setting-item:hover {
|
||||
background: var(--bg-card-hover);
|
||||
}
|
||||
|
||||
.setting-info {
|
||||
flex: 1;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.setting-info .setting-label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.setting-info .setting-desc {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* 自定义下拉框容器 */
|
||||
.custom-select {
|
||||
position: relative;
|
||||
min-width: 140px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.custom-select-trigger {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-light);
|
||||
color: var(--text-primary);
|
||||
padding: 8px 14px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
transition: all 0.2s;
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.custom-select-trigger:hover {
|
||||
border-color: var(--accent-primary);
|
||||
box-shadow: 0 0 0 2px rgba(79, 195, 247, 0.1);
|
||||
}
|
||||
|
||||
.custom-select-trigger.active {
|
||||
border-color: var(--accent-primary);
|
||||
box-shadow: 0 0 0 2px rgba(79, 195, 247, 0.2);
|
||||
}
|
||||
|
||||
.custom-select-arrow {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: transform 0.2s;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.custom-select-trigger.active .custom-select-arrow {
|
||||
transform: rotate(180deg);
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.custom-select-dropdown {
|
||||
position: absolute;
|
||||
top: calc(100% + 8px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-light);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(-10px);
|
||||
transition: all 0.2s;
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
body.light-theme .custom-select-dropdown {
|
||||
box-shadow: 0 8px 24px rgba(0, 100, 180, 0.15);
|
||||
}
|
||||
|
||||
.custom-select-dropdown.open {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.custom-select-option {
|
||||
padding: 10px 14px;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.custom-select-option:hover {
|
||||
background: var(--bg-card-hover);
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.custom-select-option.selected {
|
||||
background: rgba(79, 195, 247, 0.1);
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.custom-select-option.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.custom-select-option.disabled:hover {
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* 小尺寸下拉框 */
|
||||
.custom-select.small {
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.custom-select.small .custom-select-trigger {
|
||||
padding: 6px 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.custom-select.small .custom-select-option {
|
||||
padding: 8px 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 保持旧的setting-select样式用于兼容 */
|
||||
.setting-select {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-light);
|
||||
color: var(--text-primary);
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.setting-select:hover {
|
||||
border-color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.setting-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent-primary);
|
||||
box-shadow: 0 0 0 2px rgba(79, 195, 247, 0.2);
|
||||
}
|
||||
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.toggle-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.toggle-switch .slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--bg-dark);
|
||||
border: 1px solid var(--border-light);
|
||||
transition: 0.3s;
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
.toggle-switch .slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
background: var(--text-muted);
|
||||
transition: 0.3s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.toggle-switch input:checked + .slider {
|
||||
background: var(--accent-primary);
|
||||
border-color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.toggle-switch input:checked + .slider:before {
|
||||
transform: translateX(20px);
|
||||
background: white;
|
||||
}
|
||||
|
||||
.version-panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.version-panel-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--text-title);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.version-count {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.version-list-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.version-list-loading {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: var(--text-muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.version-list-empty {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: var(--text-muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.version-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 12px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.version-item:hover {
|
||||
background: var(--bg-card-hover);
|
||||
}
|
||||
|
||||
.version-item.selected {
|
||||
background: rgba(79, 195, 247, 0.15);
|
||||
border-left: 3px solid var(--accent-primary);
|
||||
}
|
||||
|
||||
.version-item-icon {
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.version-item-id {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--text-label);
|
||||
}
|
||||
|
||||
.version-item-time {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.selected-version-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px;
|
||||
background: var(--bg-card-hover);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.selected-version-label {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.selected-version-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.auth-tabs {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.auth-tab {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
padding: 10px 8px;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border-medium);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s;
|
||||
}
|
||||
|
||||
.auth-tab:hover:not(:disabled) {
|
||||
background: var(--bg-input-hover);
|
||||
border-color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.auth-tab.auth-tab-active {
|
||||
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
|
||||
border-color: var(--accent-primary);
|
||||
color: var(--bg-primary);
|
||||
}
|
||||
|
||||
.auth-tab.auth-tab-disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.auth-label {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.auth-badge {
|
||||
font-size: 10px;
|
||||
padding: 2px 6px;
|
||||
background: rgba(255, 87, 87, 0.2);
|
||||
color: var(--error);
|
||||
border-radius: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.auth-tab-active .auth-badge {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.skin-container {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
margin-bottom: 8px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.skin-wrapper {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.skin-wrapper:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.skin-image {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
object-fit: contain;
|
||||
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.4));
|
||||
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.skin-wrapper:hover .skin-image {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.skin-reflection {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60px;
|
||||
height: 12px;
|
||||
background: linear-gradient(180deg, rgba(79, 195, 247, 0.2) 0%, transparent 100%);
|
||||
border-radius: 50%;
|
||||
filter: blur(6px);
|
||||
}
|
||||
|
||||
.username-input-group {
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.username-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: var(--text-tertiary);
|
||||
margin-bottom: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.username-input {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border-medium);
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.username-input:focus {
|
||||
border-color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.launch-button {
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
|
||||
color: var(--bg-primary);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.launch-button:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 20px var(--accent-glow);
|
||||
}
|
||||
|
||||
.launch-button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.launch-button:disabled {
|
||||
background: var(--text-muted);
|
||||
cursor: not-allowed;
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.launch-button.no-game {
|
||||
background: linear-gradient(135deg, #f44336, #e53935);
|
||||
animation: pulse-red 2s infinite;
|
||||
}
|
||||
|
||||
.launch-button.no-game:hover:not(:disabled) {
|
||||
background: linear-gradient(135deg, #e53935, #d32f2f);
|
||||
box-shadow: 0 5px 20px rgba(244, 67, 54, 0.3);
|
||||
}
|
||||
|
||||
.launch-text {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.selected-version-display {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--text-tertiary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.selected-version-display.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.version-display-label {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.version-display-text {
|
||||
color: var(--accent-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 设置输入框 */
|
||||
.setting-input {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-light);
|
||||
color: var(--text-primary);
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
transition: all 0.2s;
|
||||
min-width: 180px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.setting-input:focus {
|
||||
border-color: var(--accent-primary);
|
||||
box-shadow: 0 0 0 2px rgba(79, 195, 247, 0.15);
|
||||
}
|
||||
|
||||
.setting-input::placeholder {
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
/* 服务器IP输入框默认隐藏 */
|
||||
#version-server-ip-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#version-server-ip-container.visible {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.launch-footer {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.footer-btn {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px 8px;
|
||||
background: transparent;
|
||||
border: 1px solid var(--border-light);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.footer-btn:hover {
|
||||
background: var(--bg-card-hover);
|
||||
border-color: var(--border-accent);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.download-panel {
|
||||
background: var(--bg-card);
|
||||
border-radius: 12px;
|
||||
padding: 25px;
|
||||
margin-bottom: 20px;
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.download-panel h3 {
|
||||
margin-bottom: 15px;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.input-group input,
|
||||
.input-group select {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border-medium);
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 15px;
|
||||
outline: none;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.input-group input:focus,
|
||||
.input-group select:focus {
|
||||
border-color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.launch-button {
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
background: var(--accent-primary);
|
||||
color: var(--bg-primary);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.launch-button:hover {
|
||||
background: var(--accent-secondary);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 20px var(--accent-glow);
|
||||
}
|
||||
|
||||
.launch-button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.launch-button:disabled {
|
||||
background: var(--text-muted);
|
||||
cursor: not-allowed;
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-top: 15px;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
background: var(--bg-card);
|
||||
display: none !important;
|
||||
height: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
margin-bottom: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.progress-bar-bg {
|
||||
background: var(--bg-input);
|
||||
border-radius: 4px;
|
||||
height: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-bar-fill {
|
||||
background: var(--accent-primary);
|
||||
height: 100%;
|
||||
width: 0%;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
select option {
|
||||
background-color: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
select {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.titlebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 120;
|
||||
-webkit-app-region: drag;
|
||||
height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 16px;
|
||||
background: var(--bg-tertiary);
|
||||
backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
}
|
||||
.titlebar .title {
|
||||
color: var(--accent-primary);
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.titlebar-left {
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
.titlebar-center {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
.window-controls { display: flex; gap: 6px; margin-left: auto; }
|
||||
.window-controls button {
|
||||
-webkit-app-region: no-drag;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--text-primary);
|
||||
width: 40px;
|
||||
height: 28px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.window-controls button.min-btn:hover { background: var(--bg-card-hover); }
|
||||
.window-controls button.close-btn:hover { background: rgba(255,80,80,0.9); }
|
||||
|
||||
.menu-bar {
|
||||
-webkit-app-region: no-drag;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
.menu {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.menu-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
color: var(--text-secondary);
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
-webkit-app-region: no-drag;
|
||||
transition: all 0.25s;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-light);
|
||||
}
|
||||
.menu-item:hover {
|
||||
background: var(--bg-card-hover);
|
||||
color: var(--text-primary);
|
||||
border-color: var(--border-accent);
|
||||
}
|
||||
.menu-item.active {
|
||||
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
|
||||
color: var(--bg-primary);
|
||||
border-color: var(--accent-primary);
|
||||
box-shadow: 0 4px 12px var(--accent-glow);
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
margin-top: 52px;
|
||||
padding: 24px;
|
||||
overflow-y: auto;
|
||||
max-height: calc(100vh - 52px);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* ==================== 下载页面样式 ==================== */
|
||||
.download-page {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.download-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.download-header h3 {
|
||||
color: var(--accent-primary);
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.download-status-panel {
|
||||
background: var(--bg-card);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
border: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.download-stats {
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
flex: 1;
|
||||
background: var(--bg-dark);
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
#speedChart {
|
||||
width: 100% !important;
|
||||
height: 180px !important;
|
||||
}
|
||||
|
||||
.progress-section {
|
||||
width: 320px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.speed-info {
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.speed-item {
|
||||
flex: 1;
|
||||
background: var(--bg-dark);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.speed-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.speed-value {
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.progress-bar-wrapper {
|
||||
background: var(--bg-dark);
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.progress-bar-bg {
|
||||
background: var(--bg-input);
|
||||
border-radius: 6px;
|
||||
height: 12px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.progress-bar-fill {
|
||||
background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
|
||||
height: 100%;
|
||||
width: 0%;
|
||||
transition: width 0.3s ease;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 0 10px var(--accent-glow);
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
text-align: center;
|
||||
margin-top: 8px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.download-filename {
|
||||
background: var(--bg-dark);
|
||||
border-radius: 8px;
|
||||
padding: 12px 15px;
|
||||
font-size: 13px;
|
||||
color: var(--text-tertiary);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
background: var(--bg-card-hover);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-accent);
|
||||
border-radius:6px;
|
||||
cursor:pointer;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
.refresh-btn:hover { background: var(--bg-input-hover); }
|
||||
|
||||
.cancel-download-btn {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background: rgba(255, 87, 87, 0.15);
|
||||
color: var(--error);
|
||||
border: 1px solid rgba(255, 87, 87, 0.3);
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.cancel-download-btn:hover {
|
||||
background: rgba(255, 87, 87, 0.25);
|
||||
border-color: rgba(255, 87, 87, 0.5);
|
||||
}
|
||||
|
||||
.cancel-download-btn.complete {
|
||||
background: rgba(102, 187, 106, 0.15);
|
||||
color: #66bb6a;
|
||||
border: 1px solid rgba(102, 187, 106, 0.3);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.cancel-download-btn.complete:hover {
|
||||
background: rgba(102, 187, 106, 0.15);
|
||||
border-color: rgba(102, 187, 106, 0.3);
|
||||
}
|
||||
|
||||
/* ==================== 版本列表样式 ==================== */
|
||||
.version-list-section {
|
||||
background: var(--bg-card);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
border: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.version-list-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.version-list-header .section-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: var(--accent-primary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.version-count-label {
|
||||
font-size: 13px;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.version-list-loading,
|
||||
.version-list-error {
|
||||
text-align: center;
|
||||
padding: 24px;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.version-category {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.version-category-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 14px;
|
||||
background: var(--bg-card);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: background 0.2s;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.version-category-header:hover {
|
||||
background: var(--bg-card-hover);
|
||||
}
|
||||
|
||||
.version-category-arrow {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
transition: transform 0.25s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.version-category-arrow.collapsed {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.version-category-icon {
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.version-category-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.version-category-count {
|
||||
font-size: 12px;
|
||||
color: var(--text-dim);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.version-category-items {
|
||||
overflow-y: auto;
|
||||
max-height: 5000px;
|
||||
transition: max-height 0.3s ease-out;
|
||||
}
|
||||
|
||||
.version-category-items.collapsed {
|
||||
max-height: 0;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.version-items {
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.version-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 14px 8px 28px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.version-item:hover {
|
||||
background: var(--bg-card-hover);
|
||||
}
|
||||
|
||||
.version-item.active {
|
||||
background: var(--bg-card-hover);
|
||||
}
|
||||
|
||||
.version-item-id {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--text-label);
|
||||
}
|
||||
|
||||
.version-item-time {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 版本详情页面 */
|
||||
.version-detail-page {
|
||||
background: var(--bg-card);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
border: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.detail-back-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
padding: 4px 0;
|
||||
margin-bottom: 16px;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.detail-back-btn:hover {
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.detail-version-id {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: var(--text-title);
|
||||
}
|
||||
|
||||
.detail-version-type {
|
||||
font-size: 12px;
|
||||
color: var(--text-tertiary);
|
||||
background: var(--bg-card);
|
||||
padding: 2px 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.detail-info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.detail-info-item {
|
||||
background: var(--bg-dark);
|
||||
border-radius: 8px;
|
||||
padding: 12px 14px;
|
||||
}
|
||||
|
||||
.detail-info-label {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.detail-info-value {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.detail-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.detail-option-card {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-light);
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.detail-option-card:hover {
|
||||
border-color: var(--border-accent);
|
||||
}
|
||||
|
||||
.detail-option-card.disabled-option {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.detail-option-card.not-available {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.detail-option-card.not-available .detail-option-header {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.detail-option-card.incompatible {
|
||||
opacity: 0.7;
|
||||
border-color: rgba(255, 87, 87, 0.3);
|
||||
}
|
||||
|
||||
.detail-option-card.incompatible .detail-option-badge {
|
||||
background: rgba(255, 87, 87, 0.15);
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.detail-option-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 14px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.detail-option-header:hover {
|
||||
background: var(--bg-card-hover);
|
||||
}
|
||||
|
||||
.detail-option-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.detail-option-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.detail-option-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.detail-option-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.detail-option-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.detail-option-desc {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.detail-option-badge {
|
||||
font-size: 11px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 4px;
|
||||
background: var(--bg-input);
|
||||
color: var(--text-muted);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.detail-option-badge.available {
|
||||
background: rgba(102, 187, 106, 0.15);
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.detail-option-badge.unavailable {
|
||||
background: rgba(255, 87, 87, 0.15);
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.detail-option-badge.coming-soon {
|
||||
background: rgba(255, 193, 7, 0.15);
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
.detail-option-arrow {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.detail-option-arrow.expanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.detail-option-content {
|
||||
padding: 0 14px 14px 14px;
|
||||
border-top: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.detail-option-loading {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.detail-option-versions {
|
||||
padding: 10px 0;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.detail-option-version-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
margin-bottom: 6px;
|
||||
background: var(--bg-dark);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.detail-option-version-item:hover {
|
||||
background: var(--bg-card-hover);
|
||||
}
|
||||
|
||||
.detail-option-version-item.selected {
|
||||
background: rgba(79, 195, 247, 0.15);
|
||||
border: 1px solid var(--accent-primary);
|
||||
}
|
||||
|
||||
.detail-option-version-name {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.detail-option-version-check {
|
||||
font-size: 14px;
|
||||
color: var(--accent-primary);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.detail-option-version-item.selected .detail-option-version-check {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.detail-option-error {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: var(--error);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.detail-option-incompatible-msg {
|
||||
padding: 10px 12px;
|
||||
background: rgba(255, 87, 87, 0.1);
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
color: var(--error);
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.detail-download-btn {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
|
||||
color: var(--bg-primary);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.detail-download-btn:hover {
|
||||
background: linear-gradient(135deg, var(--accent-secondary), var(--accent-tertiary));
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 20px var(--accent-glow);
|
||||
}
|
||||
|
||||
.detail-download-btn:disabled {
|
||||
background: var(--text-muted);
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* ==================== Java下载区域样式 ==================== */
|
||||
.java-download-section {
|
||||
background: var(--bg-card);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid var(--border-light);
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.java-download-section .section-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: var(--accent-primary);
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.java-download-section .section-desc {
|
||||
font-size: 13px;
|
||||
color: var(--text-tertiary);
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
|
||||
.java-install-status {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
background: var(--bg-card-hover);
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.java-version-selector {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.java-version-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.java-version-card {
|
||||
background: var(--bg-card);
|
||||
border: 2px solid var(--border-light);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.java-version-card:hover {
|
||||
border-color: var(--accent-primary);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px var(--accent-glow);
|
||||
}
|
||||
|
||||
.java-version-card.installed {
|
||||
border-color: var(--success);
|
||||
background: var(--bg-card);
|
||||
}
|
||||
|
||||
.java-version-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.java-version-num {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: var(--text-title);
|
||||
}
|
||||
|
||||
.java-version-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 10px;
|
||||
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
|
||||
color: var(--bg-primary);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
border-radius: 12px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.java-version-info {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.java-info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.java-info-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 12px;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.java-install-btn {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
|
||||
color: var(--bg-primary);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.java-install-btn:hover:not(:disabled) {
|
||||
background: linear-gradient(135deg, var(--accent-secondary), var(--accent-tertiary));
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px var(--accent-glow);
|
||||
}
|
||||
|
||||
.java-install-btn:disabled {
|
||||
background: var(--text-muted);
|
||||
color: var(--text-secondary);
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.java-install-btn.installed {
|
||||
background: rgba(255, 87, 87, 0.15);
|
||||
color: var(--error);
|
||||
border: 1px solid rgba(255, 87, 87, 0.3);
|
||||
}
|
||||
|
||||
.java-install-btn.installed:hover:not(:disabled) {
|
||||
background: rgba(255, 87, 87, 0.25);
|
||||
border-color: rgba(255, 87, 87, 0.5);
|
||||
box-shadow: 0 4px 12px rgba(255, 87, 87, 0.2);
|
||||
}
|
||||
|
||||
/* ==================== 设置页面样式 ==================== */
|
||||
.settings-page {
|
||||
padding: 0;
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
min-height: calc(100vh - 100px);
|
||||
height: calc(100vh - 100px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.settings-sidebar {
|
||||
width: 180px;
|
||||
background: var(--bg-sidebar);
|
||||
border-right: 1px solid var(--border-light);
|
||||
padding: 20px 0;
|
||||
flex-shrink: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.settings-sidebar-menu {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.settings-sidebar-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 20px;
|
||||
cursor: pointer;
|
||||
color: var(--text-tertiary);
|
||||
transition: all 0.2s;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.settings-sidebar-item:hover {
|
||||
background: var(--bg-card-hover);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.settings-sidebar-item.active {
|
||||
background: var(--bg-card-hover);
|
||||
color: var(--accent-primary);
|
||||
border-left-color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
flex: 1;
|
||||
padding: 28px 36px;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: calc(100vh - 100px);
|
||||
}
|
||||
|
||||
.settings-content-panel {
|
||||
display: none;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.settings-content-panel.active {
|
||||
display: block;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.settings-page-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 28px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.settings-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.settings-group-title {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
color: var(--text-title);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.settings-group-desc {
|
||||
font-size: 13px;
|
||||
color: var(--text-dim);
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.setting-card {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-light);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.setting-card:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.setting-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.setting-row-indent {
|
||||
padding-left: 30px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.setting-left {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
max-width: 60%;
|
||||
}
|
||||
|
||||
.setting-label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--text-label);
|
||||
}
|
||||
|
||||
.setting-desc {
|
||||
font-size: 12px;
|
||||
color: var(--text-dim);
|
||||
margin-top: 4px;
|
||||
line-height: 1.55;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.setting-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.setting-control > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.num-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border-light);
|
||||
background: var(--bg-input);
|
||||
color: var(--text-secondary);
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.num-btn:hover {
|
||||
border-color: var(--accent-primary);
|
||||
color: var(--accent-primary);
|
||||
background: var(--bg-card-hover);
|
||||
}
|
||||
|
||||
.num-btn:active { transform: scale(0.94); }
|
||||
|
||||
.num-display {
|
||||
width: 56px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border-light);
|
||||
background: var(--bg-input);
|
||||
color: var(--text-title);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.num-display:focus {
|
||||
border-color: var(--accent-primary);
|
||||
box-shadow: 0 0 0 2px var(--accent-glow);
|
||||
}
|
||||
|
||||
.setting-select {
|
||||
padding: 8px 12px;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border-medium);
|
||||
border-radius: 6px;
|
||||
color: var(--text-title);
|
||||
font-size: 14px;
|
||||
min-width: 180px;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.setting-select:hover {
|
||||
border-color: var(--accent-primary);
|
||||
background: var(--bg-input-hover);
|
||||
}
|
||||
|
||||
.setting-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent-primary);
|
||||
box-shadow: 0 0 0 2px var(--accent-glow);
|
||||
}
|
||||
|
||||
.setting-select.small {
|
||||
min-width: 80px;
|
||||
padding: 8px 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.setting-input {
|
||||
padding: 8px 12px;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border-medium);
|
||||
border-radius: 6px;
|
||||
color: var(--text-title);
|
||||
font-size: 14px;
|
||||
min-width: 200px;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.setting-input:hover {
|
||||
border-color: var(--accent-primary);
|
||||
background: var(--bg-input-hover);
|
||||
}
|
||||
|
||||
.setting-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent-primary);
|
||||
box-shadow: 0 0 0 2px var(--accent-glow);
|
||||
}
|
||||
|
||||
.setting-input::placeholder {
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.setting-input code {
|
||||
background: var(--bg-card-hover);
|
||||
color: var(--accent-primary);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 48px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--text-muted);
|
||||
transition: 0.3s;
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
background-color: white;
|
||||
transition: 0.3s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: var(--accent-primary);
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
transform: translateX(24px);
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
box-shadow: 0 0 10px var(--accent-glow);
|
||||
}
|
||||
|
||||
.settings-page-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid var(--border-light);
|
||||
margin-top: auto;
|
||||
flex-shrink: 0;
|
||||
background: var(--bg-primary);
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 10px 24px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
transition: all 0.2s;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.btn-reset {
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
border: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.btn-reset:hover {
|
||||
color: var(--text-secondary);
|
||||
border-color: var(--border-medium);
|
||||
background: var(--bg-card);
|
||||
}
|
||||
|
||||
.btn-save {
|
||||
background: var(--accent-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-save:hover {
|
||||
background: var(--accent-secondary);
|
||||
box-shadow: 0 2px 12px var(--accent-glow);
|
||||
}
|
||||
|
||||
.btn-save:active { transform: scale(0.97); }
|
||||
|
||||
.btn-browse {
|
||||
padding: 8px 16px;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-light);
|
||||
border-radius: 6px;
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.btn-browse:hover {
|
||||
border-color: var(--accent-primary);
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.about-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.about-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.about-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 12px;
|
||||
background: var(--bg-card-hover);
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.about-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.about-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: var(--text-title);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.about-version {
|
||||
font-size: 14px;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.check-update-btn {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
color: var(--bg-primary);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.check-update-btn:hover {
|
||||
background: linear-gradient(135deg, var(--accent-secondary), var(--accent-tertiary));
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px var(--accent-glow);
|
||||
}
|
||||
|
||||
.check-update-btn.new-version {
|
||||
background: linear-gradient(135deg, #f44336, #e53935);
|
||||
animation: pulse-red 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-red {
|
||||
0%, 100% { box-shadow: 0 0 0 0 rgba(244, 67, 54, 0.4); }
|
||||
50% { box-shadow: 0 0 0 8px rgba(244, 67, 54, 0); }
|
||||
}
|
||||
|
||||
.update-status {
|
||||
margin-top: 12px;
|
||||
padding: 12px;
|
||||
background: var(--bg-card);
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.update-status.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.update-status.has-update {
|
||||
background: rgba(244, 67, 54, 0.1);
|
||||
border: 1px solid rgba(244, 67, 54, 0.2);
|
||||
}
|
||||
|
||||
.update-log-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #f44336;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.update-log-content {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.update-log-label {
|
||||
font-size: 12px;
|
||||
color: var(--text-tertiary);
|
||||
margin-bottom: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.update-log-text {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.8;
|
||||
padding: 8px 10px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 6px;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
body.light-mode .update-log-text {
|
||||
background: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.update-versions-list {
|
||||
max-height: 280px;
|
||||
overflow-y: auto;
|
||||
margin-top: 10px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.update-versions-list::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.update-versions-list::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.update-versions-list::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.update-versions-list::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
body.light-mode .update-versions-list::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
body.light-mode .update-versions-list::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.update-version-card {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
border-radius: 8px;
|
||||
padding: 12px 14px;
|
||||
margin-bottom: 8px;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.update-version-card:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.update-version-card.newest {
|
||||
border-color: rgba(244, 67, 54, 0.3);
|
||||
background: rgba(244, 67, 54, 0.06);
|
||||
}
|
||||
|
||||
body.light-mode .update-version-card {
|
||||
background: rgba(0, 0, 0, 0.03);
|
||||
border-color: rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
body.light-mode .update-version-card.newest {
|
||||
border-color: rgba(244, 67, 54, 0.3);
|
||||
background: rgba(244, 67, 54, 0.04);
|
||||
}
|
||||
|
||||
.update-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.update-card-version {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.update-card-badge {
|
||||
font-size: 11px;
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
background: rgba(244, 67, 54, 0.2);
|
||||
color: #f44336;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.update-card-title {
|
||||
font-size: 13px;
|
||||
color: #4fc3f7;
|
||||
font-weight: 500;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.update-card-log {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.7;
|
||||
padding: 8px 10px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 6px;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
body.light-mode .update-card-log {
|
||||
background: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.go-download-btn {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: rgba(244, 67, 54, 0.15);
|
||||
color: #f44336;
|
||||
border: 1px solid rgba(244, 67, 54, 0.3);
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.go-download-btn:hover {
|
||||
background: rgba(244, 67, 54, 0.25);
|
||||
border-color: rgba(244, 67, 54, 0.5);
|
||||
}
|
||||
|
||||
.status-message {
|
||||
margin-top: 15px;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
background: var(--bg-card);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
top: 70px;
|
||||
right: 20px;
|
||||
z-index: 200;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.toast {
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-light);
|
||||
border-radius: 10px;
|
||||
padding: 12px 16px;
|
||||
min-width: 260px;
|
||||
max-width: 360px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.4);
|
||||
pointer-events: auto;
|
||||
animation: toastIn 0.3s ease;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.toast.toast-success { border-left: 3px solid var(--success); }
|
||||
.toast.toast-error { border-left: 3px solid var(--error); }
|
||||
.toast.toast-warning { border-left: 3px solid var(--warning); }
|
||||
.toast.toast-info { border-left: 3px solid var(--accent-primary); }
|
||||
|
||||
.toast-icon {
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
flex-shrink: 0;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.toast-success .toast-icon { color: var(--success); }
|
||||
.toast-error .toast-icon { color: var(--error); }
|
||||
.toast-warning .toast-icon { color: var(--warning); }
|
||||
.toast-info .toast-icon { color: var(--accent-primary); }
|
||||
|
||||
.toast-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-title);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.toast-message {
|
||||
font-size: 12px;
|
||||
color: var(--text-tertiary);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.toast-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-dim);
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
padding: 0;
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
line-height: 1;
|
||||
transition: color 0.15s;
|
||||
}
|
||||
|
||||
.toast-close:hover { color: var(--text-tertiary); }
|
||||
|
||||
@keyframes toastIn {
|
||||
from { transform: translateX(100%); opacity: 0; }
|
||||
to { transform: translateX(0); opacity: 1; }
|
||||
}
|
||||
|
||||
/* ==================== 对话框 ==================== */
|
||||
.dialog-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.dialog-box {
|
||||
background: linear-gradient(180deg, var(--bg-primary) 0%, var(--bg-secondary) 100%);
|
||||
border: 2px solid var(--border-medium);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 0 1px var(--bg-dark), 0 8px 32px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
||||
min-width: 400px;
|
||||
max-width: 500px;
|
||||
animation: dialogSlideIn 0.2s ease;
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px 20px 12px;
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.dialog-icon {
|
||||
font-size: 20px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--text-title);
|
||||
margin: 0;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.dialog-body {
|
||||
padding: 20px;
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
.dialog-message {
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
padding: 12px 20px 16px;
|
||||
border-top: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.dialog-btn {
|
||||
padding: 8px 20px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
border: 1px solid;
|
||||
transition: all 0.15s;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.dialog-btn-primary {
|
||||
background: linear-gradient(180deg, #5a8f3e 0%, #4a7a32 100%);
|
||||
border-color: #3d6a2a;
|
||||
color: #fff;
|
||||
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.dialog-btn-primary:hover {
|
||||
background: linear-gradient(180deg, #6a9f4e 0%, #5a8a42 100%);
|
||||
border-color: #4d7a3a;
|
||||
}
|
||||
|
||||
.dialog-btn-primary:active {
|
||||
background: linear-gradient(180deg, #4a7f32 0%, #3a6a22 100%);
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.dialog-btn-secondary {
|
||||
background: linear-gradient(180deg, var(--bg-card-hover) 0%, var(--bg-card) 100%);
|
||||
border-color: var(--border-medium);
|
||||
color: var(--text-secondary);
|
||||
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.dialog-btn-secondary:hover {
|
||||
background: linear-gradient(180deg, var(--bg-input-hover) 0%, var(--bg-card-hover) 100%);
|
||||
border-color: var(--border-medium);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.dialog-btn-secondary:active {
|
||||
background: linear-gradient(180deg, var(--bg-card) 0%, var(--bg-dark) 100%);
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
@keyframes dialogSlideIn {
|
||||
from { transform: scale(0.9); opacity: 0; }
|
||||
to { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
/* ==================== 漂浮下载通知 ==================== */
|
||||
.float-notification {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 320px;
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-light);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
||||
z-index: 150;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.float-notification-icon {
|
||||
flex-shrink: 0;
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.float-notification-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.float-notification-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-title);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.float-notification-text {
|
||||
font-size: 12px;
|
||||
color: var(--text-tertiary);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.float-notification-progress {
|
||||
flex-shrink: 0;
|
||||
width: 100px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.float-progress-bar {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
background: var(--bg-input);
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.float-progress-fill {
|
||||
height: 100%;
|
||||
background: var(--accent-primary);
|
||||
width: 0%;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.float-progress-text {
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.float-notification-close {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-dim);
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.float-notification-close:hover {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* ==================== 启动动画 ==================== */
|
||||
.launch-animation-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(10, 10, 20, 0.95);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
body.light-theme .launch-animation-overlay {
|
||||
background: rgba(240, 248, 255, 0.95);
|
||||
}
|
||||
|
||||
.launch-animation-content {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.pickaxe-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 80px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.pickaxe {
|
||||
transform-origin: right center;
|
||||
animation: pickaxeSwing 0.8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.block-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.block {
|
||||
animation: blockShake 0.4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.particles {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes pickaxeSwing {
|
||||
0%, 100% { transform: rotate(0deg); }
|
||||
25% { transform: rotate(-20deg); }
|
||||
50% { transform: rotate(0deg); }
|
||||
75% { transform: rotate(20deg); }
|
||||
}
|
||||
|
||||
@keyframes blockShake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
25% { transform: translateX(-2px); }
|
||||
75% { transform: translateX(2px); }
|
||||
}
|
||||
|
||||
.launch-status {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 18px;
|
||||
color: #e8edf5;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
body.light-theme .status-text {
|
||||
color: #1a3a5c;
|
||||
}
|
||||
|
||||
.progress-bar-container {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
height: 8px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body.light-theme .progress-bar-container {
|
||||
background: rgba(0, 100, 180, 0.1);
|
||||
}
|
||||
|
||||
.progress-bar-container .progress-bar-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #4fc3f7, #29b6f6);
|
||||
width: 0%;
|
||||
transition: width 0.3s ease;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
body.light-theme .progress-bar-container .progress-bar-fill {
|
||||
background: linear-gradient(90deg, #0099ff, #0077cc);
|
||||
}
|
||||
|
||||
.version-info {
|
||||
font-size: 13px;
|
||||
color: #78909c;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
body.light-theme .version-info {
|
||||
color: #6a8aa6;
|
||||
}
|
||||
|
||||
.launch-cancel-btn {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: #b0bec5;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
padding: 10px 24px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.launch-cancel-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
border-color: rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
body.light-theme .launch-cancel-btn {
|
||||
background: rgba(0, 100, 180, 0.08);
|
||||
color: #2d4a6f;
|
||||
border: 1px solid rgba(0, 100, 180, 0.2);
|
||||
}
|
||||
|
||||
body.light-theme .launch-cancel-btn:hover {
|
||||
background: rgba(0, 100, 180, 0.12);
|
||||
border-color: rgba(0, 100, 180, 0.3);
|
||||
}
|
||||
|
||||
/* ==================== 启动界面的你知道吗?卡片 ==================== */
|
||||
.launch-tip-card {
|
||||
position: absolute;
|
||||
bottom: 24px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 10px;
|
||||
padding: 14px 20px;
|
||||
max-width: 480px;
|
||||
width: calc(100% - 80px);
|
||||
backdrop-filter: blur(4px);
|
||||
animation: tipFadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
.launch-tip-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.launch-tip-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.launch-tip-label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #90caf9;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.launch-tip-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: #b0bec5;
|
||||
}
|
||||
|
||||
@keyframes tipFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-50%) translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
#floating-download-wrapper {
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
z-index: 60;
|
||||
}
|
||||
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 201 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* CaelLab BY-SA Code License
|
||||
* Copyright (c) 2026 Yunyun(云云) By 虚舟实验室(CaelLab) / CaelLabGameTS
|
||||
* Source: https://git.caellab.com/yunyun/GoodPlanCraftLauncher
|
||||
* Source: https://github.com/yunyun-3782/GoodPlanCraftLauncher
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const pkg = require('../package.json');
|
||||
|
||||
const output = {
|
||||
name: pkg.name,
|
||||
version: pkg.version,
|
||||
main: pkg.main,
|
||||
type: pkg.type
|
||||
};
|
||||
|
||||
const destPath = path.resolve('build/app/package.json');
|
||||
fs.writeFileSync(destPath, JSON.stringify(output, null, 2));
|
||||
|
||||
console.log('✅ copy-static 完成');
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* CaelLab BY-SA Code License
|
||||
* Copyright (c) 2026 Yunyun(云云) By 虚舟实验室(CaelLab) / CaelLabGameTS
|
||||
* Source: https://git.caellab.com/yunyun/GoodPlanCraftLauncher
|
||||
* Source: https://github.com/yunyun-3782/GoodPlanCraftLauncher
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const dest = path.resolve('build/app');
|
||||
|
||||
if (fs.existsSync(dest)) {
|
||||
fs.rmSync(dest, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.join(dest, 'renderer'), { recursive: true });
|
||||
|
||||
const files = [
|
||||
'main.js',
|
||||
'preload.js',
|
||||
'gpcl-icon.ico',
|
||||
'package.json'
|
||||
];
|
||||
|
||||
files.forEach(file => {
|
||||
const src = path.resolve(file);
|
||||
const target = path.join(dest, file);
|
||||
if (fs.existsSync(src)) {
|
||||
fs.copyFileSync(src, target);
|
||||
}
|
||||
});
|
||||
|
||||
const rendererSrc = path.resolve('renderer');
|
||||
const rendererDest = path.join(dest, 'renderer');
|
||||
if (fs.existsSync(rendererSrc)) {
|
||||
fs.cpSync(rendererSrc, rendererDest, { recursive: true });
|
||||
}
|
||||
|
||||
const iconSrc = path.resolve('static', 'icon');
|
||||
const iconDest = path.join(dest, 'static', 'icon');
|
||||
if (fs.existsSync(iconSrc)) {
|
||||
fs.mkdirSync(path.join(dest, 'static'), { recursive: true });
|
||||
fs.cpSync(iconSrc, iconDest, { recursive: true });
|
||||
}
|
||||
|
||||
console.log('✅ copy-files 完成');
|
||||
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 308 KiB |
|
After Width: | Height: | Size: 233 KiB |
|
After Width: | Height: | Size: 1.5 MiB |