初始化
This commit is contained in:
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
BIN
assets/icon.ico
Normal file
BIN
assets/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
268
index.html
Normal file
268
index.html
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>YOLO 更新检查</title>
|
||||||
|
<meta http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'self'; style-src 'unsafe-inline' 'self'; script-src 'self' 'unsafe-inline'; img-src 'self' data:;">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #0b1220;
|
||||||
|
--card: #0f172a;
|
||||||
|
--text: #e5e7eb;
|
||||||
|
--muted: #9ca3af;
|
||||||
|
--accent: #60a5fa;
|
||||||
|
--accent-2: #34d399;
|
||||||
|
--bar-bg: #1f2937;
|
||||||
|
--bar-fg: linear-gradient(90deg, var(--accent), var(--accent-2));
|
||||||
|
--ok: #34d399;
|
||||||
|
--error: #ef4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
--bg: #f8fafc;
|
||||||
|
--card: #ffffff;
|
||||||
|
--text: #0f172a;
|
||||||
|
--muted: #64748b;
|
||||||
|
--bar-bg: #e5e7eb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
|
||||||
|
background: radial-gradient(1200px 600px at 20% -10%, rgba(96, 165, 250, .25), transparent 50%),
|
||||||
|
radial-gradient(1200px 600px at 120% 120%, rgba(52, 211, 153, .25), transparent 50%),
|
||||||
|
var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
width: 560px;
|
||||||
|
max-width: calc(100% - 40px);
|
||||||
|
background: linear-gradient(180deg, rgba(255, 255, 255, .04), rgba(255, 255, 255, 0)), var(--card);
|
||||||
|
border: 1px solid rgba(255, 255, 255, .08);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 28px;
|
||||||
|
box-shadow: 0 10px 40px rgba(0, 0, 0, .25);
|
||||||
|
backdrop-filter: blur(6px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: linear-gradient(135deg, var(--accent), var(--accent-2));
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
color: white;
|
||||||
|
font-weight: 900;
|
||||||
|
text-shadow: 0 1px 2px rgba(0, 0, 0, .3);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 18px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub {
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 13px;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
margin: 20px 0 16px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
background: rgba(255, 255, 255, .04);
|
||||||
|
border: 1px dashed rgba(255, 255, 255, .18);
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-wrap {
|
||||||
|
background: var(--bar-bg);
|
||||||
|
border-radius: 999px;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 14px;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, .25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
height: 100%;
|
||||||
|
width: 0%;
|
||||||
|
background: var(--bar-fg);
|
||||||
|
transition: width .25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 14px;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
margin-top: 18px;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
appearance: none;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: linear-gradient(90deg, var(--accent), var(--accent-2));
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
box-shadow: 0 6px 18px rgba(56, 189, 248, .35);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.ghost {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid rgba(255, 255, 255, .18);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ok {
|
||||||
|
color: var(--ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
.err {
|
||||||
|
color: var(--error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade {
|
||||||
|
animation: fade .3s ease both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade {
|
||||||
|
from {
|
||||||
|
opacity: .0;
|
||||||
|
transform: translateY(6px)
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="card fade">
|
||||||
|
<div class="header">
|
||||||
|
<div class="logo">PY</div>
|
||||||
|
<div>
|
||||||
|
<h1>YOLO 启动 / 更新检查</h1>
|
||||||
|
<div class="sub">为你自动检查可用更新并在后台下载</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="status" class="status">正在初始化…</div>
|
||||||
|
|
||||||
|
<div class="progress-wrap" aria-label="下载进度">
|
||||||
|
<div id="bar" class="progress-bar"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div>速度:<span id="speed">0</span> MB/s</div>
|
||||||
|
<div>已下:<span id="transferred">0</span>/<span id="total">0</span> MB</div>
|
||||||
|
<div>进度:<span id="percent">0</span>%</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button id="btn-install" style="display:none;">立即重启安装</button>
|
||||||
|
<button id="btn-hide" class="ghost">最小化到托盘</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const $ = (id) => document.getElementById(id);
|
||||||
|
const statusEl = $('status');
|
||||||
|
const bar = $('bar');
|
||||||
|
const sp = $('speed'), tf = $('transferred'), tt = $('total'), pc = $('percent');
|
||||||
|
const btnInstall = $('btn-install');
|
||||||
|
const btnHide = $('btn-hide');
|
||||||
|
|
||||||
|
// 订阅主进程事件
|
||||||
|
window.electronAPI.onUpdateStatus(({ status, version, notes, currentVersion, message }) => {
|
||||||
|
switch (status) {
|
||||||
|
case 'checking':
|
||||||
|
statusEl.textContent = '正在检查更新…';
|
||||||
|
break;
|
||||||
|
case 'available':
|
||||||
|
statusEl.innerHTML = `检测到新版本 <b>v${version}</b>,正在下载…`;
|
||||||
|
break;
|
||||||
|
case 'none':
|
||||||
|
statusEl.innerHTML = `已是最新版本 <span class="ok">(v${currentVersion})</span>。3 秒后自动最小化。`;
|
||||||
|
bar.style.width = '100%'; pc.textContent = '100';
|
||||||
|
setTimeout(() => window.close(), 3000);
|
||||||
|
break;
|
||||||
|
case 'downloaded':
|
||||||
|
statusEl.innerHTML = `下载完成!可立即安装更新 <b>v${version}</b>。`;
|
||||||
|
bar.style.width = '100%'; pc.textContent = '100';
|
||||||
|
btnInstall.style.display = '';
|
||||||
|
// 也可自动倒计时安装:
|
||||||
|
// setTimeout(() => btnInstall.click(), 3000);
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
statusEl.innerHTML = `更新失败:<span class="err">${message || '未知错误'}</span>`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
statusEl.textContent = '状态:' + status;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.electronAPI.onUpdateProgress(({ percent, transferred, total, speed }) => {
|
||||||
|
bar.style.width = Math.max(0, Math.min(100, percent || 0)) + '%';
|
||||||
|
sp.textContent = speed ?? 0;
|
||||||
|
tf.textContent = transferred ?? 0;
|
||||||
|
tt.textContent = total ?? 0;
|
||||||
|
pc.textContent = (percent ?? 0).toFixed(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 交互
|
||||||
|
btnInstall.addEventListener('click', () => {
|
||||||
|
statusEl.textContent = '即将重启并安装更新…';
|
||||||
|
window.electronAPI.installNow();
|
||||||
|
});
|
||||||
|
|
||||||
|
btnHide.addEventListener('click', () => {
|
||||||
|
window.close(); // 在 main.js 里拦截 close -> hide()
|
||||||
|
});
|
||||||
|
|
||||||
|
// 首次加载立即请求检查
|
||||||
|
window.electronAPI.checkForUpdates();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
228
main.js
Normal file
228
main.js
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
// main.js
|
||||||
|
const { app, Tray, Menu, dialog, BrowserWindow, ipcMain, nativeTheme } = require('electron');
|
||||||
|
const { autoUpdater } = require('electron-updater');
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const { spawn, spawnSync, exec } = require('child_process');
|
||||||
|
const log = require('electron-log');
|
||||||
|
|
||||||
|
Object.assign(console, log.functions);
|
||||||
|
|
||||||
|
const ALLOW_UNSIGNED = process.env.YOLO_ALLOW_UNSIGNED === '1';
|
||||||
|
let installTriggered = false; // NEW: 防止重复触发安装
|
||||||
|
|
||||||
|
// ===== 全局状态 =====
|
||||||
|
let tray = null;
|
||||||
|
let managedChild = null;
|
||||||
|
let quitting = false;
|
||||||
|
let updateWin = null;
|
||||||
|
let pythonStarted = false; // NEW: 已启动 tkpage?
|
||||||
|
let updaterKicked = false; // NEW: 是否已触发检查
|
||||||
|
|
||||||
|
// ===== tkpage 路径 =====
|
||||||
|
function resolveExePath() {
|
||||||
|
const devExe = path.resolve(__dirname, 'resources', 'python', 'tkpage.exe');
|
||||||
|
const prodExe = path.join(process.resourcesPath, 'python', 'tkpage.exe');
|
||||||
|
return app.isPackaged ? prodExe : devExe;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 启动 tkpage(只会启动一次) =====
|
||||||
|
function launchPythonIfNeeded() {
|
||||||
|
if (pythonStarted) return;
|
||||||
|
const exe = resolveExePath();
|
||||||
|
const cwd = path.dirname(exe);
|
||||||
|
|
||||||
|
console.info('[tkpage exe]', exe);
|
||||||
|
if (!fs.existsSync(exe)) {
|
||||||
|
dialog.showErrorBox('启动失败', `未找到可执行文件:\n${exe}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pythonStarted = true;
|
||||||
|
managedChild = spawn(exe, [], {
|
||||||
|
cwd,
|
||||||
|
windowsHide: false,
|
||||||
|
detached: false,
|
||||||
|
stdio: 'ignore',
|
||||||
|
});
|
||||||
|
|
||||||
|
managedChild.on('exit', (code, signal) => {
|
||||||
|
console.info(`[tkpage] exit code=${code} signal=${signal}`);
|
||||||
|
if (!quitting) { quitting = true; app.quit(); }
|
||||||
|
});
|
||||||
|
managedChild.on('close', (code, signal) => {
|
||||||
|
console.info(`[tkpage] close code=${code} signal=${signal}`);
|
||||||
|
if (!quitting) { quitting = true; app.quit(); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 结束 tkpage =====
|
||||||
|
function killPythonGUI(killSelf = true) {
|
||||||
|
if (managedChild && managedChild.pid) {
|
||||||
|
try { process.kill(managedChild.pid); } catch (e) {
|
||||||
|
console.warn('[kill] managed kill error:', e?.message || e);
|
||||||
|
} finally { managedChild = null; }
|
||||||
|
}
|
||||||
|
try { spawnSync('taskkill', ['/f', '/t', '/im', 'tkpage.exe'], { windowsHide: true }); } catch { }
|
||||||
|
|
||||||
|
if (killSelf) {
|
||||||
|
try { exec('taskkill /IM YOLO助手.exe /F'); } catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 托盘 =====
|
||||||
|
function createTray() {
|
||||||
|
const iconPath = path.join(__dirname, 'assets', 'icon.ico');
|
||||||
|
tray = new Tray(iconPath);
|
||||||
|
const menu = Menu.buildFromTemplate([
|
||||||
|
{ label: '检查更新', click: () => startUpdateCheck(/*force*/true) },
|
||||||
|
{ type: 'separator' },
|
||||||
|
{ label: '显示更新窗口', click: () => { if (updateWin) { updateWin.show(); updateWin.focus(); } else { createUpdateWindow(); } } },
|
||||||
|
{ type: 'separator' },
|
||||||
|
{ label: '重启 Python GUI', click: () => { killPythonGUI(); pythonStarted = false; launchPythonIfNeeded(); } },
|
||||||
|
{ type: 'separator' },
|
||||||
|
{ label: '退出', click: () => { quitting = true; killPythonGUI(); app.quit(); } },
|
||||||
|
]);
|
||||||
|
tray.setToolTip('YOLO');
|
||||||
|
tray.setContextMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 更新窗口 =====
|
||||||
|
function createUpdateWindow() {
|
||||||
|
if (updateWin && !updateWin.isDestroyed()) { updateWin.show(); updateWin.focus(); return; }
|
||||||
|
updateWin = new BrowserWindow({
|
||||||
|
width: 640, height: 420, resizable: false, fullscreenable: false, maximizable: false,
|
||||||
|
autoHideMenuBar: true, show: true, center: true,
|
||||||
|
backgroundColor: nativeTheme.shouldUseDarkColors ? '#0b1220' : '#ffffff',
|
||||||
|
icon: path.join(__dirname, 'assets', 'icon.ico'),
|
||||||
|
webPreferences: {
|
||||||
|
preload: path.join(__dirname, 'preload.js'),
|
||||||
|
contextIsolation: true, nodeIntegration: false, sandbox: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
updateWin.on('close', (e) => { e.preventDefault(); updateWin.hide(); });
|
||||||
|
updateWin.loadFile(path.join(__dirname, 'index.html')).catch(err => console.error('[updateWin] load error', err));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Updater 事件桥接到渲染进程,并控制 tkpage 启动时机 =====
|
||||||
|
function wireUpdaterIpc() {
|
||||||
|
if (ALLOW_UNSIGNED) {
|
||||||
|
console.warn('[updater] verifyUpdateCodeSignature disabled (YOLO_ALLOW_UNSIGNED=1)');
|
||||||
|
autoUpdater.verifyUpdateCodeSignature = async () => null;
|
||||||
|
}
|
||||||
|
autoUpdater.autoDownload = true;
|
||||||
|
autoUpdater.autoInstallOnAppQuit = false;
|
||||||
|
|
||||||
|
const send = (ch, data) => { if (updateWin && !updateWin.isDestroyed()) updateWin.webContents.send(ch, data); };
|
||||||
|
|
||||||
|
autoUpdater.on('checking-for-update', () => {
|
||||||
|
console.info('[updater] Checking');
|
||||||
|
tray?.setToolTip('YOLO - 正在检查更新…');
|
||||||
|
send('update:status', { status: 'checking' });
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('update-available', (info) => {
|
||||||
|
console.info('[updater] Available', info?.version);
|
||||||
|
tray?.setToolTip(`YOLO - 检测到新版本 v${info?.version},开始下载…`);
|
||||||
|
send('update:status', { status: 'available', version: info?.version, notes: info?.releaseNotes || '' });
|
||||||
|
// 此时不启动 tkpage,等待下载完成
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('update-not-available', (info) => {
|
||||||
|
console.info('[updater] None');
|
||||||
|
tray?.setToolTip('YOLO - 已是最新版本');
|
||||||
|
send('update:status', { status: 'none', currentVersion: app.getVersion(), info });
|
||||||
|
// ✅ 没有更新,立刻启动 tkpage
|
||||||
|
launchPythonIfNeeded();
|
||||||
|
// 可隐藏更新窗口(让应用留在托盘+tkpage前台)
|
||||||
|
setTimeout(() => updateWin?.hide(), 1200);
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('download-progress', (p) => {
|
||||||
|
const percent = Math.max(0, Math.min(100, p?.percent || 0));
|
||||||
|
const transferred = Math.floor((p?.transferred || 0) / 1024 / 1024);
|
||||||
|
const total = Math.floor((p?.total || 0) / 1024 / 1024);
|
||||||
|
const speed = Math.floor((p?.bytesPerSecond || 0) / 1024 / 1024);
|
||||||
|
tray?.setToolTip(`YOLO - 正在下载更新:${percent.toFixed(1)}%`);
|
||||||
|
send('update:progress', { percent, transferred, total, speed });
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('update-downloaded', (info) => {
|
||||||
|
console.info('[updater] Downloaded', info?.version);
|
||||||
|
tray?.setToolTip('YOLO - 更新已下载,等待安装…');
|
||||||
|
// 仅通知渲染进程显示“立即安装”按钮;不自动安装
|
||||||
|
send('update:status', { status: 'downloaded', version: info?.version });
|
||||||
|
// 可选择把更新窗口确保前置
|
||||||
|
if (updateWin && !updateWin.isDestroyed()) { updateWin.show(); updateWin.focus(); }
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('error', (err) => {
|
||||||
|
const msg = err?.message || String(err);
|
||||||
|
console.error('[updater] error', msg);
|
||||||
|
tray?.setToolTip('YOLO - 更新失败');
|
||||||
|
dialog.showErrorBox('自动更新失败', msg);
|
||||||
|
send('update:status', { status: 'error', message: msg });
|
||||||
|
// ⚠️ 出错:不阻断主流程,直接启动 tkpage
|
||||||
|
launchPythonIfNeeded();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 渲染侧手动触发
|
||||||
|
ipcMain.on('updater:check', () => startUpdateCheck(/*force*/true));
|
||||||
|
ipcMain.on('updater:install-now', () => {
|
||||||
|
if (installTriggered) return;
|
||||||
|
installTriggered = true;
|
||||||
|
|
||||||
|
console.info('[updater] quitAndInstall requested by renderer');
|
||||||
|
tray?.setToolTip('YOLO - 正在安装更新…');
|
||||||
|
|
||||||
|
quitting = true;
|
||||||
|
try { killPythonGUI(false); } catch (e) {
|
||||||
|
console.warn('[updater] killPythonGUI error:', e?.message || e);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
autoUpdater.quitAndInstall(false, true);
|
||||||
|
} catch (e) {
|
||||||
|
installTriggered = false;
|
||||||
|
console.error('[updater] quitAndInstall error:', e?.message || e);
|
||||||
|
dialog.showErrorBox('安装失败', String(e?.message || e));
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 触发更新检查(仅触发一次,托盘点“检查更新”可强制再触发) =====
|
||||||
|
function startUpdateCheck(force = false) {
|
||||||
|
if (updaterKicked && !force) return;
|
||||||
|
updaterKicked = true;
|
||||||
|
// 显示更新窗口(可选:你也可以只在有更新时显示)
|
||||||
|
if (!updateWin || updateWin.isDestroyed()) createUpdateWindow();
|
||||||
|
else { updateWin.show(); updateWin.focus(); }
|
||||||
|
autoUpdater.checkForUpdates().catch(() => { /* 错误会在 on('error') 里处理 */ });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 单例 & 生命周期 =====
|
||||||
|
process.on('unhandledRejection', (r) => console.error('[unhandledRejection]', r));
|
||||||
|
process.on('uncaughtException', (e) => console.error('[uncaughtException]', e));
|
||||||
|
|
||||||
|
const gotLock = app.requestSingleInstanceLock();
|
||||||
|
if (!gotLock) {
|
||||||
|
app.quit();
|
||||||
|
} else {
|
||||||
|
app.on('second-instance', () => { if (updateWin) { updateWin.show(); updateWin.focus(); } });
|
||||||
|
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
app.setAppUserModelId('com.yolozs.newPage');
|
||||||
|
}
|
||||||
|
createTray();
|
||||||
|
createUpdateWindow();
|
||||||
|
wireUpdaterIpc();
|
||||||
|
// ✅ 启动时:只检查更新,不启动 tkpage。无更新 → 启动;有更新 → 下载并重启安装。
|
||||||
|
startUpdateCheck();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on('before-quit', () => { quitting = true; killPythonGUI(true); });
|
||||||
|
app.on('window-all-closed', () => { /* 后台常驻 */ });
|
||||||
|
}
|
||||||
6840
package-lock.json
generated
Normal file
6840
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
49
package.json
Normal file
49
package.json
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "YOLO",
|
||||||
|
"productName": "YOLO助手",
|
||||||
|
"version": "4.7.0",
|
||||||
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "electron .",
|
||||||
|
"build": "electron-builder"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"electron-log": "^5",
|
||||||
|
"electron-updater": "^6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"electron": "^31",
|
||||||
|
"electron-builder": "^24"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"appId": "com.yolozs.newPage",
|
||||||
|
"productName": "YOLO助手",
|
||||||
|
"asar": true,
|
||||||
|
"icon": "icon.ico",
|
||||||
|
"win": {
|
||||||
|
"target": "nsis"
|
||||||
|
},
|
||||||
|
"nsis": {
|
||||||
|
"oneClick": false,
|
||||||
|
"allowElevation": true,
|
||||||
|
"allowToChangeInstallationDirectory": true,
|
||||||
|
"perMachine": false,
|
||||||
|
"deleteAppDataOnUninstall": false
|
||||||
|
},
|
||||||
|
"publish": [
|
||||||
|
{
|
||||||
|
"provider": "generic",
|
||||||
|
"url": "http://tkNewPage.yolozs.com/updata/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"extraResources": [
|
||||||
|
{
|
||||||
|
"from": "resources/python",
|
||||||
|
"to": "python",
|
||||||
|
"filter": [
|
||||||
|
"**/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
13
preload.js
Normal file
13
preload.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// preload.js
|
||||||
|
const { contextBridge, ipcRenderer } = require('electron');
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
|
// 主动触发检查
|
||||||
|
checkForUpdates: () => ipcRenderer.send('updater:check'),
|
||||||
|
// 安装更新
|
||||||
|
installNow: () => ipcRenderer.send('updater:install-now'),
|
||||||
|
// 订阅状态
|
||||||
|
onUpdateStatus: (cb) => ipcRenderer.on('update:status', (_e, payload) => cb(payload)),
|
||||||
|
// 订阅进度
|
||||||
|
onUpdateProgress: (cb) => ipcRenderer.on('update:progress', (_e, payload) => cb(payload)),
|
||||||
|
});
|
||||||
BIN
resources/python/PIL/_imaging.pyd
Normal file
BIN
resources/python/PIL/_imaging.pyd
Normal file
Binary file not shown.
BIN
resources/python/PIL/_imagingcms.pyd
Normal file
BIN
resources/python/PIL/_imagingcms.pyd
Normal file
Binary file not shown.
BIN
resources/python/PIL/_imagingmath.pyd
Normal file
BIN
resources/python/PIL/_imagingmath.pyd
Normal file
Binary file not shown.
BIN
resources/python/PIL/_imagingtk.pyd
Normal file
BIN
resources/python/PIL/_imagingtk.pyd
Normal file
Binary file not shown.
BIN
resources/python/PIL/_webp.pyd
Normal file
BIN
resources/python/PIL/_webp.pyd
Normal file
Binary file not shown.
BIN
resources/python/PySide6/QtCore.pyd
Normal file
BIN
resources/python/PySide6/QtCore.pyd
Normal file
Binary file not shown.
BIN
resources/python/PySide6/QtGui.pyd
Normal file
BIN
resources/python/PySide6/QtGui.pyd
Normal file
Binary file not shown.
BIN
resources/python/PySide6/QtNetwork.pyd
Normal file
BIN
resources/python/PySide6/QtNetwork.pyd
Normal file
Binary file not shown.
BIN
resources/python/PySide6/QtPrintSupport.pyd
Normal file
BIN
resources/python/PySide6/QtPrintSupport.pyd
Normal file
Binary file not shown.
BIN
resources/python/PySide6/QtSvg.pyd
Normal file
BIN
resources/python/PySide6/QtSvg.pyd
Normal file
Binary file not shown.
BIN
resources/python/PySide6/QtWebChannel.pyd
Normal file
BIN
resources/python/PySide6/QtWebChannel.pyd
Normal file
Binary file not shown.
BIN
resources/python/PySide6/QtWebEngineCore.pyd
Normal file
BIN
resources/python/PySide6/QtWebEngineCore.pyd
Normal file
Binary file not shown.
BIN
resources/python/PySide6/QtWebEngineWidgets.pyd
Normal file
BIN
resources/python/PySide6/QtWebEngineWidgets.pyd
Normal file
Binary file not shown.
BIN
resources/python/PySide6/QtWidgets.pyd
Normal file
BIN
resources/python/PySide6/QtWidgets.pyd
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/iconengines/qsvgicon.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/iconengines/qsvgicon.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/imageformats/qgif.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/imageformats/qgif.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/imageformats/qicns.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/imageformats/qicns.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/imageformats/qico.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/imageformats/qico.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/imageformats/qjpeg.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/imageformats/qjpeg.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/imageformats/qpdf.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/imageformats/qpdf.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/imageformats/qsvg.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/imageformats/qsvg.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/imageformats/qtga.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/imageformats/qtga.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/imageformats/qtiff.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/imageformats/qtiff.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/imageformats/qwbmp.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/imageformats/qwbmp.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/imageformats/qwebp.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/imageformats/qwebp.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/platforms/qdirect2d.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/platforms/qdirect2d.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/platforms/qminimal.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/platforms/qminimal.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/platforms/qoffscreen.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/platforms/qoffscreen.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/platforms/qwindows.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/platforms/qwindows.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/tls/qcertonlybackend.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/tls/qcertonlybackend.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/tls/qopensslbackend.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/tls/qopensslbackend.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/qt-plugins/tls/qschannelbackend.dll
Normal file
BIN
resources/python/PySide6/qt-plugins/tls/qschannelbackend.dll
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_ar.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_ar.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_bg.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_bg.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_cs.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_cs.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_da.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_da.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_de.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_de.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_en.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_en.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_es.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_es.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_fr.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_fr.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_hr.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_hr.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_hu.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_hu.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_ja.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_ja.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_ka.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_ka.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_ko.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_ko.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_nl.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_nl.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_pl.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_pl.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_pt_BR.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_pt_BR.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_ru.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_ru.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_sk.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_sk.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_sl.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_sl.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_sv.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_sv.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_tr.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_tr.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_uk.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_uk.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_zh_CN.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_zh_CN.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/assistant_zh_TW.qm
Normal file
BIN
resources/python/PySide6/translations/assistant_zh_TW.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_ar.qm
Normal file
BIN
resources/python/PySide6/translations/designer_ar.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_bg.qm
Normal file
BIN
resources/python/PySide6/translations/designer_bg.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_cs.qm
Normal file
BIN
resources/python/PySide6/translations/designer_cs.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_da.qm
Normal file
BIN
resources/python/PySide6/translations/designer_da.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_de.qm
Normal file
BIN
resources/python/PySide6/translations/designer_de.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_en.qm
Normal file
BIN
resources/python/PySide6/translations/designer_en.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_es.qm
Normal file
BIN
resources/python/PySide6/translations/designer_es.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_fr.qm
Normal file
BIN
resources/python/PySide6/translations/designer_fr.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_hr.qm
Normal file
BIN
resources/python/PySide6/translations/designer_hr.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_hu.qm
Normal file
BIN
resources/python/PySide6/translations/designer_hu.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_ja.qm
Normal file
BIN
resources/python/PySide6/translations/designer_ja.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_ka.qm
Normal file
BIN
resources/python/PySide6/translations/designer_ka.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_ko.qm
Normal file
BIN
resources/python/PySide6/translations/designer_ko.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_nl.qm
Normal file
BIN
resources/python/PySide6/translations/designer_nl.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_pl.qm
Normal file
BIN
resources/python/PySide6/translations/designer_pl.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_ru.qm
Normal file
BIN
resources/python/PySide6/translations/designer_ru.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_sk.qm
Normal file
BIN
resources/python/PySide6/translations/designer_sk.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_sl.qm
Normal file
BIN
resources/python/PySide6/translations/designer_sl.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_sv.qm
Normal file
BIN
resources/python/PySide6/translations/designer_sv.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_tr.qm
Normal file
BIN
resources/python/PySide6/translations/designer_tr.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_uk.qm
Normal file
BIN
resources/python/PySide6/translations/designer_uk.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_zh_CN.qm
Normal file
BIN
resources/python/PySide6/translations/designer_zh_CN.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/designer_zh_TW.qm
Normal file
BIN
resources/python/PySide6/translations/designer_zh_TW.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/linguist_ar.qm
Normal file
BIN
resources/python/PySide6/translations/linguist_ar.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/linguist_bg.qm
Normal file
BIN
resources/python/PySide6/translations/linguist_bg.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/linguist_cs.qm
Normal file
BIN
resources/python/PySide6/translations/linguist_cs.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/linguist_da.qm
Normal file
BIN
resources/python/PySide6/translations/linguist_da.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/linguist_de.qm
Normal file
BIN
resources/python/PySide6/translations/linguist_de.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/linguist_en.qm
Normal file
BIN
resources/python/PySide6/translations/linguist_en.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/linguist_es.qm
Normal file
BIN
resources/python/PySide6/translations/linguist_es.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/linguist_fr.qm
Normal file
BIN
resources/python/PySide6/translations/linguist_fr.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/linguist_hr.qm
Normal file
BIN
resources/python/PySide6/translations/linguist_hr.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/linguist_hu.qm
Normal file
BIN
resources/python/PySide6/translations/linguist_hu.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/linguist_it.qm
Normal file
BIN
resources/python/PySide6/translations/linguist_it.qm
Normal file
Binary file not shown.
BIN
resources/python/PySide6/translations/linguist_ja.qm
Normal file
BIN
resources/python/PySide6/translations/linguist_ja.qm
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user