From 650d9c03987e2e2a51378a2cab2ce976af54fa98 Mon Sep 17 00:00:00 2001 From: tianmo Date: Mon, 21 Sep 2026 10:30:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=B9=E7=AA=97=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineThumbnailGenerateServer/js/plugin.js | 2 +- SpineThumbnailGenerateServer/progress.html | 28 +++++++++++++++---- .../tests/progress-window.test.js | 17 +++++++++++ 3 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 SpineThumbnailGenerateServer/tests/progress-window.test.js diff --git a/SpineThumbnailGenerateServer/js/plugin.js b/SpineThumbnailGenerateServer/js/plugin.js index 4ea80a6..669522c 100644 --- a/SpineThumbnailGenerateServer/js/plugin.js +++ b/SpineThumbnailGenerateServer/js/plugin.js @@ -2,7 +2,7 @@ // 每次改动递增。Eagle 里的 service 插件是启动时加载并常驻的,改完代码必须重新加载插件 // 才会生效;状态栏会显示这个版本号,方便确认加载的到底是不是最新代码。 -const VERSION = 'v61'; +const VERSION = 'v65'; const path = require('path'); const os = require('os'); diff --git a/SpineThumbnailGenerateServer/progress.html b/SpineThumbnailGenerateServer/progress.html index 0ee9fc3..deb47c5 100644 --- a/SpineThumbnailGenerateServer/progress.html +++ b/SpineThumbnailGenerateServer/progress.html @@ -64,6 +64,7 @@ let doneCount = 0; let lastHeight = 0; let placedAt = null; // 上次由我们设置的位置,用来判断用户是不是手动拖过 + let allowClose = false; /** Eagle 的 userData 目录;eagle.app.userDataPath 注入得比较晚,所以留一个兜底。 */ function userDataPath() { @@ -105,7 +106,8 @@ /** 窗口高度跟着内容走。 */ function fitHeight() { - const height = Math.ceil(document.body.scrollHeight); + // setSize 设置的是包含系统标题栏的外部高度,内容高度要补回窗口边框占用。 + const height = Math.ceil(document.body.scrollHeight + Math.max(0, window.outerHeight - window.innerHeight)); if (Math.abs(height - lastHeight) < 2) return; lastHeight = height; eagle.window.setSize(WIDTH, height) @@ -127,6 +129,22 @@ } catch (error) { /* 写不进去就等于没按 */ } } + function requestStop() { + if (paused) return; + paused = true; + writeControl('pause'); + syncButtons(); + resumeEl.focus(); + } + + // 系统关闭按钮与页面里的「停止」走同一条暂停确认流程。 + window.addEventListener('beforeunload', (event) => { + if (allowClose || finished) return; + event.preventDefault(); + event.returnValue = false; + requestStop(); + }); + function render(state) { const total = Math.max(1, state.total || 0); const done = state.done || 0; @@ -149,14 +167,11 @@ actionEl.addEventListener('click', () => { if (finished) { + allowClose = true; window.close(); return; } - // 先让宿主暂停(它会在当前素材做完后停在原地),再就地问要不要继续。 - paused = true; - writeControl('pause'); - syncButtons(); - resumeEl.focus(); // 默认选中「继续」 + requestStop(); }); resumeEl.addEventListener('click', () => { @@ -168,6 +183,7 @@ document.getElementById('cancel').addEventListener('click', () => { // 同步写控制文件再关窗:窗口没了宿主也能读到 cancel,把剩余队列丢掉。 writeControl('cancel'); + allowClose = true; window.close(); }); diff --git a/SpineThumbnailGenerateServer/tests/progress-window.test.js b/SpineThumbnailGenerateServer/tests/progress-window.test.js new file mode 100644 index 0000000..10e4b7e --- /dev/null +++ b/SpineThumbnailGenerateServer/tests/progress-window.test.js @@ -0,0 +1,17 @@ +'use strict'; + +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); + +const root = path.join(__dirname, '..'); +const html = fs.readFileSync(path.join(root, 'progress.html'), 'utf8'); +const windowSource = fs.readFileSync(path.join(root, 'js', 'progress-window.js'), 'utf8'); + +assert.match(windowSource, /minimizable: true/); +assert.match(html, /addEventListener\('beforeunload'/); +assert.match(html, /function requestStop\(\)[\s\S]*writeControl\('pause'\)[\s\S]*resumeEl\.focus\(\)/); +assert.match(html, /if \(allowClose \|\| finished\) return;[\s\S]*event\.returnValue = false;[\s\S]*requestStop\(\)/); +assert.equal((html.match(/requestStop\(\);/g) || []).length, 2); +assert.equal((html.match(/allowClose = true;[\s\S]{0,80}window\.close\(\)/g) || []).length, 2); +console.log('PASS: 系统关闭与停止按钮共用暂停确认流程');