Skip to content

Commit a839181

Browse files
committed
改动在 N.E.K.O/plugin/plugins/galgame_plugin/static/main.js:833:
- 移除了 rebindCardButton 里的 cloneNode/replaceChild。 - rebindCardButton 现在只绑定一次 click listener,后续刷新只更新保存的 handler。 - 新增 syncActionButtons,action HTML 相同则不写 DOM;按钮结构相同时原地同步 textContent、disabled、属性,保留原按钮节点和键盘焦点。 - 替换了依赖卡片和主诊断 actions 的直接 actions.innerHTML = ... 写入。 未跳过其他项:这次只有这一条 finding。 验证: node --check plugin/plugins/galgame_plugin/static/main.js 通过 git diff --check 通过
1 parent cb03bf2 commit a839181

1 file changed

Lines changed: 91 additions & 18 deletions

File tree

  • plugin/plugins/galgame_plugin/static

plugin/plugins/galgame_plugin/static/main.js

Lines changed: 91 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -830,22 +830,95 @@ function configureUseButton(id, { active = false, disabled = false, text = '', t
830830
button.title = title || '';
831831
}
832832

833+
function setActionButtonDisabled(button, disabled) {
834+
const nextDisabled = Boolean(disabled);
835+
if (button.disabled !== nextDisabled) {
836+
button.disabled = nextDisabled;
837+
}
838+
button.__galgameActionDesiredDisabled = nextDisabled;
839+
}
840+
841+
function syncActionButtonElement(current, next) {
842+
const nextDisabled = next.hasAttribute('disabled');
843+
const attributeNames = new Set([
844+
...Array.from(current.attributes, (attr) => attr.name),
845+
...Array.from(next.attributes, (attr) => attr.name),
846+
]);
847+
attributeNames.forEach((name) => {
848+
if (name === 'disabled') {
849+
return;
850+
}
851+
const nextValue = next.getAttribute(name);
852+
if (nextValue == null) {
853+
current.removeAttribute(name);
854+
} else if (current.getAttribute(name) !== nextValue) {
855+
current.setAttribute(name, nextValue);
856+
}
857+
});
858+
if (current.textContent !== next.textContent) {
859+
current.textContent = next.textContent;
860+
}
861+
setActionButtonDisabled(current, nextDisabled);
862+
}
863+
864+
function canPatchActionButtons(currentChildren, nextChildren) {
865+
return currentChildren.length === nextChildren.length
866+
&& nextChildren.every((next, index) => {
867+
const current = currentChildren[index];
868+
return current
869+
&& current.tagName === next.tagName
870+
&& (current.id || '') === (next.id || '')
871+
&& (current.getAttribute('data-primary-action') || '') === (next.getAttribute('data-primary-action') || '');
872+
});
873+
}
874+
875+
function syncActionButtons(actions, html) {
876+
const nextHtml = html || '';
877+
if (!actions || actions.dataset.renderedHtml === nextHtml) {
878+
return;
879+
}
880+
const template = document.createElement('template');
881+
template.innerHTML = nextHtml.trim();
882+
const currentChildren = Array.from(actions.children);
883+
const nextChildren = Array.from(template.content.children);
884+
if (!canPatchActionButtons(currentChildren, nextChildren)) {
885+
actions.innerHTML = nextHtml;
886+
actions.dataset.renderedHtml = nextHtml;
887+
Array.from(actions.children).forEach((button) => {
888+
if (button instanceof HTMLButtonElement) {
889+
setActionButtonDisabled(button, button.disabled);
890+
}
891+
});
892+
return;
893+
}
894+
currentChildren.forEach((current, index) => {
895+
syncActionButtonElement(current, nextChildren[index]);
896+
});
897+
actions.dataset.renderedHtml = nextHtml;
898+
}
899+
833900
function rebindCardButton(id, handler) {
834901
const button = document.getElementById(id);
835902
if (!button) {
836903
return;
837904
}
838-
const clone = button.cloneNode(true);
839-
button.parentNode.replaceChild(clone, button);
840-
clone.addEventListener('click', async () => {
841-
if (clone.disabled) {
905+
button.__galgameCardClickHandler = handler;
906+
if (button.__galgameCardClickBound) {
907+
return;
908+
}
909+
button.__galgameCardClickBound = true;
910+
button.addEventListener('click', async () => {
911+
if (button.disabled) {
842912
return;
843913
}
844-
clone.disabled = true;
914+
button.disabled = true;
845915
try {
846-
await handler();
916+
const currentHandler = button.__galgameCardClickHandler;
917+
if (typeof currentHandler === 'function') {
918+
await currentHandler();
919+
}
847920
} finally {
848-
clone.disabled = false;
921+
button.disabled = Boolean(button.__galgameActionDesiredDisabled);
849922
}
850923
});
851924
}
@@ -1474,11 +1547,11 @@ function renderPrimaryDiagnosis(status = {}) {
14741547
kicker.textContent = uiT('ui.diag.kicker', '运行诊断');
14751548
title.textContent = diagnosis.title;
14761549
body.textContent = diagnosis.body;
1477-
actions.innerHTML = (diagnosis.actions || []).map((action, index) => `
1550+
syncActionButtons(actions, (diagnosis.actions || []).map((action, index) => `
14781551
<button class="${index === 0 ? 'primary' : 'secondary'}" data-primary-action="${escapeHtml(action.id)}">
14791552
${escapeHtml(primaryActionLabel(action.id, action.label))}
14801553
</button>
1481-
`).join('');
1554+
`).join(''));
14821555
}
14831556

14841557
function buildFirstRunStepsLegacy(status = {}) {
@@ -3355,7 +3428,7 @@ function renderInstallTaskState(kind) {
33553428
card.style.display = '';
33563429
if (button) {
33573430
button.hidden = false;
3358-
button.disabled = false;
3431+
setActionButtonDisabled(button, false);
33593432
}
33603433
statusText.textContent = uiTf('ui.install.task.waiting', '等待 {label} 安装任务', { label });
33613434
percentText.textContent = '0%';
@@ -3396,15 +3469,15 @@ function renderInstallTaskState(kind) {
33963469
if (button) {
33973470
const terminalCompleted = !rapidocrModelsStillMissing;
33983471
button.hidden = terminalCompleted;
3399-
button.disabled = terminalCompleted;
3472+
setActionButtonDisabled(button, terminalCompleted);
34003473
if (rapidocrModelsStillMissing) {
34013474
button.textContent = getInstallConfig(kind).retryText;
34023475
}
34033476
}
34043477
} else if (state.status === 'failed') {
34053478
if (button) {
34063479
button.hidden = false;
3407-
button.disabled = false;
3480+
setActionButtonDisabled(button, false);
34083481
button.textContent = getInstallConfig(kind).retryText;
34093482
}
34103483
}
@@ -3465,7 +3538,7 @@ function renderPluginUnavailable(error) {
34653538
if (chip) chip.textContent = pluginNotStarted;
34663539
if (desc) desc.textContent = uiT('ui.install.plugin_unavailable_body', '当前无法读取插件运行状态。请先启动或重载 galgame_plugin,启动完成后这里会显示安装和运行时状态。');
34673540
if (meta) meta.textContent = `${PROMPT_LABELS[kind]} · ${message}`;
3468-
if (actions) actions.innerHTML = '';
3541+
if (actions) syncActionButtons(actions, '');
34693542
if (kind === 'rapidocr') {
34703543
const card = document.getElementById('rapidocrInstallCard');
34713544
if (card) {
@@ -4960,7 +5033,7 @@ function renderRapidOcr(status) {
49605033
chip.textContent = chipText;
49615034
desc.textContent = descText;
49625035
meta.textContent = metaText;
4963-
actions.innerHTML = buttons.join('');
5036+
syncActionButtons(actions, buttons.join(''));
49645037
renderInstallTaskState('rapidocr_models');
49655038
rebindCardButton('rapidocrUseBtn', () => setOcrBackendSelection({ backendSelection: 'rapidocr' }));
49665039
rebindCardButton('ocrBackendAutoBtn', () => setOcrBackendSelection({ backendSelection: 'auto' }));
@@ -4992,14 +5065,14 @@ function renderDxcam(status) {
49925065
? selectedCaptureBackend === 'mss' || selectedCaptureBackend === 'imagegrab'
49935066
: selectedCaptureBackend === value
49945067
);
4995-
actions.innerHTML = [
5068+
syncActionButtons(actions, [
49965069
`<button id="smartCaptureUseBtn" class="secondary" ${captureActive('smart') ? 'disabled' : ''}>${escapeHtml(captureActive('smart') ? uiT('ui.install.smart.using', '正在使用 Smart') : uiT('ui.install.smart.use', '使用 Smart'))}</button>`,
49975070
`<button id="dxcamUseBtn" class="secondary" ${(!installed || captureActive('dxcam')) ? 'disabled' : ''}>${escapeHtml(captureActive('dxcam') ? uiT('ui.install.dxcam.using', '正在使用 DXcam') : uiT('ui.install.dxcam.use', '使用 DXcam'))}</button>`,
49985071
`<button id="captureBackendAutoBtn" class="ghost" ${captureActive('auto') ? 'disabled' : ''}>${escapeHtml(captureActive('auto') ? uiT('ui.install.capture_auto.using', '截图自动选择中') : uiT('ui.install.capture_auto', '截图自动'))}</button>`,
49995072
`<button id="mssUseBtn" class="ghost" ${captureActive('mss') ? 'disabled' : ''}>${escapeHtml(captureActive('mss') ? uiT('ui.install.mss.using', '正在使用 MSS') : uiT('ui.install.mss.use', '使用 MSS'))}</button>`,
50005073
`<button id="pyautoguiUseBtn" class="ghost" ${captureActive('pyautogui') ? 'disabled' : ''}>${escapeHtml(captureActive('pyautogui') ? uiT('ui.install.pyautogui.using', '正在使用 PyAutoGUI') : uiT('ui.install.pyautogui.use', '使用 PyAutoGUI'))}</button>`,
50015074
`<button id="printwindowUseBtn" class="ghost" ${captureActive('printwindow') ? 'disabled' : ''}>${escapeHtml(captureActive('printwindow') ? uiT('ui.install.printwindow.using', '正在使用 PrintWindow') : uiT('ui.install.printwindow.use', '使用 PrintWindow'))}</button>`,
5002-
].join('');
5075+
].join(''));
50035076

50045077
if (!dxcam.install_supported) {
50055078
card.className = 'install-card neutral';
@@ -5094,7 +5167,7 @@ function renderTesseract(status) {
50945167
chip.textContent = chipText;
50955168
desc.textContent = descText;
50965169
meta.textContent = metaText;
5097-
actions.innerHTML = buttons.join('');
5170+
syncActionButtons(actions, buttons.join(''));
50985171
if (installed) {
50995172
const nodes = getInstallNodes('tesseract');
51005173
if (nodes.card) nodes.card.hidden = true;
@@ -5160,7 +5233,7 @@ function renderTextractor(status) {
51605233
chip.textContent = chipText;
51615234
desc.textContent = descText;
51625235
meta.textContent = metaText;
5163-
actions.innerHTML = installButtonHtml('textractor', installable, installed);
5236+
syncActionButtons(actions, installButtonHtml('textractor', installable, installed));
51645237
if (installed) {
51655238
const nodes = getInstallNodes('textractor');
51665239
if (nodes.card) nodes.card.hidden = true;

0 commit comments

Comments
 (0)