dsh-life-pack 0.3.15 → 0.3.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +186 -38
- package/dist/client.js.map +1 -1
- package/dist/config-panel-view.d.ts.map +1 -1
- package/dist/config-panel-view.js +8 -5
- package/dist/config-panel-view.js.map +1 -1
- package/dist/update-client.d.ts +10 -2
- package/dist/update-client.d.ts.map +1 -1
- package/dist/update-client.js +24 -2
- package/dist/update-client.js.map +1 -1
- package/dist/update-contract.d.ts +6 -0
- package/dist/update-contract.d.ts.map +1 -1
- package/dist/update-contract.js +6 -0
- package/dist/update-contract.js.map +1 -1
- package/dist/update-queue.d.ts +29 -0
- package/dist/update-queue.d.ts.map +1 -1
- package/dist/update-queue.js +44 -0
- package/dist/update-queue.js.map +1 -1
- package/dist/update-view.d.ts +8 -0
- package/dist/update-view.d.ts.map +1 -1
- package/dist/update-view.js +10 -0
- package/dist/update-view.js.map +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -193,6 +193,16 @@ window.__ModuleLoader__.load({
|
|
|
193
193
|
* 一处定义、两侧引用:宿主半 `manager-version.ts` 读失败时回它,面板半 `update-client.ts`
|
|
194
194
|
* 归一化时也认它——面板侧不许 import 宿主半(会把 node 内建带进浏览器束)。 */
|
|
195
195
|
const VERSION_UNKNOWN = "unknown";
|
|
196
|
+
/** 挂载取数失败的退避重试表(票 #982,更新目标表用)。
|
|
197
|
+
*
|
|
198
|
+
* 一处定义:与版本读取重试同形(`client.ts` 的退避三次)——挂载时首次传输失败
|
|
199
|
+
* (宿主路由尚未就绪)会使错误态常驻整个会话;单次直返是当时的缺口。
|
|
200
|
+
* 次数有界、调用方负责卸载清理(见 `update-panel.ts` 的取数),不是无界轮询。 */
|
|
201
|
+
const LOAD_RETRY_MS = [
|
|
202
|
+
1e3,
|
|
203
|
+
3e3,
|
|
204
|
+
8e3
|
|
205
|
+
];
|
|
196
206
|
/** 装不了 / 装不成的人话原因表:八种 `blockedReason` + 过程错误码(更新包 README 第 8、12 节)。
|
|
197
207
|
*
|
|
198
208
|
* 每条都要三段齐全(票 #740 的文案判据):**事实**(哪里对不上)→ **后果**(这家现在能不能装)
|
|
@@ -282,8 +292,27 @@ window.__ModuleLoader__.load({
|
|
|
282
292
|
const error = result.error;
|
|
283
293
|
return failure(typeof error?.code === "string" ? error.code : "internal", error && typeof error.details === "object" && error.details !== null ? error.details : {});
|
|
284
294
|
}
|
|
285
|
-
/** 取七个更新目标的表(宿主转交更新包的三个电话名与版本行,面板不写死任何电话名)。
|
|
286
|
-
|
|
295
|
+
/** 取七个更新目标的表(宿主转交更新包的三个电话名与版本行,面板不写死任何电话名)。
|
|
296
|
+
*
|
|
297
|
+
* 重试参数(票 #982):缺省单次(与旧行为一致);给 `delays` 即按退避重试——
|
|
298
|
+
* 挂载时首次传输失败不再直接进入错误态。`sleep`/`isAlive` 可注入(测试与卸载清理用),
|
|
299
|
+
* 缺省走本模块同形的等待与恒真。首次成功即返回,否则返回最后一次失败。 */
|
|
300
|
+
async function loadTargets(call, retry) {
|
|
301
|
+
const delays = retry?.delays ?? [];
|
|
302
|
+
const sleepFn = retry?.sleep ?? sleep;
|
|
303
|
+
const alive = retry?.isAlive ?? (() => true);
|
|
304
|
+
let last = await loadTargetsOnce(call);
|
|
305
|
+
for (let i = 0; i < delays.length && !last.ok; i += 1) {
|
|
306
|
+
if (!alive()) return last;
|
|
307
|
+
const wait = delays[i];
|
|
308
|
+
if (wait > 0) await sleepFn(wait);
|
|
309
|
+
if (!alive()) return last;
|
|
310
|
+
last = await loadTargetsOnce(call);
|
|
311
|
+
}
|
|
312
|
+
return last;
|
|
313
|
+
}
|
|
314
|
+
/** 取七个更新目标的表:单次(重试循环的每一步都是它)。 */
|
|
315
|
+
async function loadTargetsOnce(call) {
|
|
287
316
|
const out = await callManager(call, MANAGER_ACTIONS.targets, {});
|
|
288
317
|
if (!out.ok) return out;
|
|
289
318
|
return {
|
|
@@ -517,6 +546,15 @@ window.__ModuleLoader__.load({
|
|
|
517
546
|
else lines.push("技能包随插件(不单独查更新)");
|
|
518
547
|
return lines;
|
|
519
548
|
}
|
|
549
|
+
/** 取数失败的日志细节(票 #982):传输层返回的原文优先,否则返回原因码——短文本,不带用户数据。
|
|
550
|
+
*
|
|
551
|
+
* 住这里(不是取数钩子旁边):本文件是面板纯函数与单测共用的那一份,判据可单测;
|
|
552
|
+
* 参数只取失败体的形状(`code`+`details`),不 import 取数模块。 */
|
|
553
|
+
function loadFailureDetail(failure) {
|
|
554
|
+
const raw = failure.details?.detail;
|
|
555
|
+
if (typeof raw === "string" && raw.length > 0) return failure.code + " detail=" + raw.slice(0, 300);
|
|
556
|
+
return failure.code;
|
|
557
|
+
}
|
|
520
558
|
//#endregion
|
|
521
559
|
//#region src/update-queue.ts
|
|
522
560
|
/** 任一行 installing 即忙:题头检查更新禁用、行按钮全局互斥、checkAll/act 入口直接返回的同一判据。 */
|
|
@@ -524,6 +562,40 @@ window.__ModuleLoader__.load({
|
|
|
524
562
|
for (const key of Object.keys(rows)) if (rows[key]?.phase === "installing") return true;
|
|
525
563
|
return false;
|
|
526
564
|
}
|
|
565
|
+
/** 批量入口点不得:一键串行中、任一行安装中、检查轮未收尾,三者任一即拦。
|
|
566
|
+
*
|
|
567
|
+
* 面板 `checkAll`/`updateAll`/`act` 共用同一判据:双轮同时写同一行即错(#980:
|
|
568
|
+
* 原先缺 `checking`,检查未收尾即点一键会另起一轮覆盖行状态)。
|
|
569
|
+
* 人话仍沿用 `update-busy` 原句,本函数只回答拦不拦。 */
|
|
570
|
+
function shouldBlockBatchStart(state) {
|
|
571
|
+
return state.hasInstallingRow || state.updatingAll || state.checking;
|
|
572
|
+
}
|
|
573
|
+
/** 重取目标表时保不保留旧表:新表为空且屏上有进度(任一行非空闲或一键串行中)
|
|
574
|
+
* 则保留旧表——否则可见行计算短暂为空,整块闪掉如重载(#980)。
|
|
575
|
+
* 首挂全空闲与正常非空表不受影响,照常替换。 */
|
|
576
|
+
function shouldKeepTargetsOnReload(loadedCount, rows, updatingAll) {
|
|
577
|
+
if (loadedCount > 0) return false;
|
|
578
|
+
if (updatingAll) return true;
|
|
579
|
+
for (const key of Object.keys(rows)) if (rows[key]?.phase !== "idle") return true;
|
|
580
|
+
return false;
|
|
581
|
+
}
|
|
582
|
+
/** 重取目标表后的下一张表:空表保旧(见 `shouldKeepTargetsOnReload`);非空表照常换,
|
|
583
|
+
* 但批量进行中(串行中或有安装中)时在新表里缺失、屏上又有进度的旧目标会被追加到表尾——
|
|
584
|
+
* 键乱序或短暂缺键时不断进度、不闪掉整块(#980 验收“顺序变化时不把整块置空”)。
|
|
585
|
+
* 空闲时不追加:正常下架由新表说了算。 */
|
|
586
|
+
function mergeTargetsOnReload(previous, loaded, rows, updatingAll) {
|
|
587
|
+
if (shouldKeepTargetsOnReload(loaded.length, rows, updatingAll)) return previous;
|
|
588
|
+
if (!updatingAll && !hasInstallingRow(rows)) return loaded;
|
|
589
|
+
const keys = new Set(loaded.map((target) => target.key));
|
|
590
|
+
const kept = previous.filter((target) => {
|
|
591
|
+
const row = rows[target.key];
|
|
592
|
+
return row !== void 0 && !keys.has(target.key) && row.phase !== "idle";
|
|
593
|
+
});
|
|
594
|
+
return kept.length > 0 ? [...loaded, ...kept] : loaded;
|
|
595
|
+
}
|
|
596
|
+
function resolveDialogOpen(event) {
|
|
597
|
+
return event === "show";
|
|
598
|
+
}
|
|
527
599
|
/** 按目标顺序逐家查→装→收尾:一家收尾才起下一家;失败记 failed 继续;待重启跳过安装。
|
|
528
600
|
*
|
|
529
601
|
* 跳过安装只有一种:快照事实已是待重启(`restartPendingOf`)——由横幅点名,不在这里装第二遍。
|
|
@@ -920,18 +992,66 @@ window.__ModuleLoader__.load({
|
|
|
920
992
|
react.useEffect(() => () => {
|
|
921
993
|
alive.current = false;
|
|
922
994
|
}, []);
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
995
|
+
const rowsRef = react.useRef(rows);
|
|
996
|
+
rowsRef.current = rows;
|
|
997
|
+
const targetsRef = react.useRef(targets);
|
|
998
|
+
targetsRef.current = targets;
|
|
999
|
+
const pollMsRef = react.useRef(pollMs);
|
|
1000
|
+
pollMsRef.current = pollMs;
|
|
1001
|
+
const checkingRef = react.useRef(false);
|
|
1002
|
+
checkingRef.current = checking;
|
|
1003
|
+
const updatingAllRef = react.useRef(false);
|
|
1004
|
+
const retryTimers = react.useRef([]);
|
|
1005
|
+
react.useEffect(() => () => {
|
|
1006
|
+
for (const id of retryTimers.current) clearTimeout(id);
|
|
1007
|
+
retryTimers.current = [];
|
|
1008
|
+
}, []);
|
|
1009
|
+
/** 重取七个目标的表(装机读数变了就重取:缺席卡的态是拿这张表算的,缓存住它会继续说旧话)。
|
|
1010
|
+
*
|
|
1011
|
+
* 进度在屏上时不断进度(#980):空表保旧表,非空表把批量中缺失的旧目标追加到表尾——
|
|
1012
|
+
* 否则可见行计算短暂为空或缺键,整块闪掉如重载。
|
|
1013
|
+
*
|
|
1014
|
+
* 首帧失败重试(票 #982):挂载时首次传输失败(宿主路由尚未就绪)不再直接进入错误态,
|
|
1015
|
+
* 只在挂载那一次按退避表重试;装后重取走单次(此前刚成功调用过宿主,不必等)。
|
|
1016
|
+
* 重试用尽仍失败才显示红色错误行,并把传输层细节打到 `[dsh-life-pack]` 前缀的日志里——
|
|
1017
|
+
* 面板指引用户去日志里找的那一行,此前在这里从未输出过(浏览器侧只能打到控制台)。
|
|
1018
|
+
*
|
|
1019
|
+
* 已超线,需要根据规则进行重构:本文件改前约 747 行(本包告警线 350 行),本次 #982 增加约 60 行
|
|
1020
|
+
* (重试等待、卸载清理、失败日志)。暂不拆分——拆分面板文件(取数、行渲染、浮层、缺席卡)是独立重构,
|
|
1021
|
+
* 与本次修复无关;下轮单独立项。 */
|
|
1022
|
+
const reload = react.useCallback(async (withRetry) => {
|
|
1023
|
+
let tries = 0;
|
|
1024
|
+
const sleep = (ms) => new Promise((resolve) => {
|
|
1025
|
+
tries += 1;
|
|
1026
|
+
if (tries >= LOAD_RETRY_MS.length + 1) {
|
|
1027
|
+
resolve();
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
const timer = setTimeout(() => {
|
|
1031
|
+
clearTimeout(timer);
|
|
1032
|
+
resolve();
|
|
1033
|
+
}, ms);
|
|
1034
|
+
retryTimers.current.push(timer);
|
|
1035
|
+
});
|
|
1036
|
+
const loaded = await loadTargets(getCall(), {
|
|
1037
|
+
delays: withRetry ? LOAD_RETRY_MS : [],
|
|
1038
|
+
sleep,
|
|
1039
|
+
isAlive: () => alive.current
|
|
1040
|
+
});
|
|
1041
|
+
for (const id of retryTimers.current) clearTimeout(id);
|
|
1042
|
+
retryTimers.current = [];
|
|
926
1043
|
if (!alive.current) return;
|
|
927
1044
|
if (loaded.ok) {
|
|
928
|
-
setTargets(loaded.value.targets);
|
|
1045
|
+
setTargets(mergeTargetsOnReload(targetsRef.current, loaded.value.targets, rowsRef.current, updatingAllRef.current));
|
|
929
1046
|
setPollMs(loaded.value.pollMs);
|
|
930
1047
|
setLoadError(null);
|
|
931
|
-
} else
|
|
1048
|
+
} else {
|
|
1049
|
+
setLoadError(loaded.message);
|
|
1050
|
+
console.warn("[dsh-life-pack] update targets load failed: " + loadFailureDetail(loaded));
|
|
1051
|
+
}
|
|
932
1052
|
}, [getCall]);
|
|
933
1053
|
react.useEffect(() => {
|
|
934
|
-
reload();
|
|
1054
|
+
reload(true);
|
|
935
1055
|
}, [reload]);
|
|
936
1056
|
const patch = react.useCallback((key, next) => {
|
|
937
1057
|
setRows((previous) => ({
|
|
@@ -939,13 +1059,11 @@ window.__ModuleLoader__.load({
|
|
|
939
1059
|
[key]: next
|
|
940
1060
|
}));
|
|
941
1061
|
}, []);
|
|
942
|
-
const
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
pollMsRef.current = pollMs;
|
|
948
|
-
const updatingAllRef = react.useRef(false);
|
|
1062
|
+
const batchBlocked = react.useCallback(() => shouldBlockBatchStart({
|
|
1063
|
+
hasInstallingRow: hasInstallingRow(rowsRef.current),
|
|
1064
|
+
updatingAll: updatingAllRef.current,
|
|
1065
|
+
checking: checkingRef.current
|
|
1066
|
+
}), []);
|
|
949
1067
|
return {
|
|
950
1068
|
targets,
|
|
951
1069
|
rows,
|
|
@@ -954,11 +1072,12 @@ window.__ModuleLoader__.load({
|
|
|
954
1072
|
checking,
|
|
955
1073
|
updatingAll,
|
|
956
1074
|
open,
|
|
957
|
-
close: () => setOpen(
|
|
1075
|
+
close: () => setOpen(resolveDialogOpen("hide")),
|
|
1076
|
+
showProgress: () => setOpen(resolveDialogOpen("show")),
|
|
958
1077
|
checkAll: react.useCallback(() => {
|
|
959
|
-
if (
|
|
1078
|
+
if (batchBlocked()) return;
|
|
960
1079
|
setChecking(true);
|
|
961
|
-
setOpen(
|
|
1080
|
+
setOpen(resolveDialogOpen("show"));
|
|
962
1081
|
const list = targetsRef.current;
|
|
963
1082
|
const snapshot = rowsRef.current;
|
|
964
1083
|
for (const target of list) patch(target.key, {
|
|
@@ -980,14 +1099,18 @@ window.__ModuleLoader__.load({
|
|
|
980
1099
|
})).finally(() => {
|
|
981
1100
|
if (alive.current) setChecking(false);
|
|
982
1101
|
});
|
|
983
|
-
}, [
|
|
1102
|
+
}, [
|
|
1103
|
+
getCall,
|
|
1104
|
+
patch,
|
|
1105
|
+
batchBlocked
|
|
1106
|
+
]),
|
|
984
1107
|
updateAll: react.useCallback(() => {
|
|
985
|
-
if (
|
|
1108
|
+
if (batchBlocked()) return;
|
|
986
1109
|
const list = targetsRef.current;
|
|
987
1110
|
if (list.length === 0) return;
|
|
988
1111
|
updatingAllRef.current = true;
|
|
989
1112
|
setUpdatingAll(true);
|
|
990
|
-
setOpen(
|
|
1113
|
+
setOpen(resolveDialogOpen("show"));
|
|
991
1114
|
(async () => {
|
|
992
1115
|
try {
|
|
993
1116
|
await runSerialUpdateAll(list, {
|
|
@@ -998,7 +1121,7 @@ window.__ModuleLoader__.load({
|
|
|
998
1121
|
patch,
|
|
999
1122
|
getRow: (key) => rowsRef.current[key]
|
|
1000
1123
|
});
|
|
1001
|
-
await reload();
|
|
1124
|
+
await reload(false);
|
|
1002
1125
|
} finally {
|
|
1003
1126
|
updatingAllRef.current = false;
|
|
1004
1127
|
if (alive.current) setUpdatingAll(false);
|
|
@@ -1007,10 +1130,11 @@ window.__ModuleLoader__.load({
|
|
|
1007
1130
|
}, [
|
|
1008
1131
|
getCall,
|
|
1009
1132
|
patch,
|
|
1010
|
-
reload
|
|
1133
|
+
reload,
|
|
1134
|
+
batchBlocked
|
|
1011
1135
|
]),
|
|
1012
1136
|
act: react.useCallback((target, action) => {
|
|
1013
|
-
if (
|
|
1137
|
+
if (batchBlocked()) return;
|
|
1014
1138
|
const current = rowsRef.current[target.key] ?? IDLE$1;
|
|
1015
1139
|
if (action === "recheck") {
|
|
1016
1140
|
patch(target.key, {
|
|
@@ -1069,7 +1193,7 @@ window.__ModuleLoader__.load({
|
|
|
1069
1193
|
outcome: null,
|
|
1070
1194
|
failure: refreshed
|
|
1071
1195
|
});
|
|
1072
|
-
await reload();
|
|
1196
|
+
await reload(false);
|
|
1073
1197
|
return;
|
|
1074
1198
|
}
|
|
1075
1199
|
patch(target.key, {
|
|
@@ -1084,21 +1208,27 @@ window.__ModuleLoader__.load({
|
|
|
1084
1208
|
}, [
|
|
1085
1209
|
getCall,
|
|
1086
1210
|
patch,
|
|
1087
|
-
reload
|
|
1211
|
+
reload,
|
|
1212
|
+
batchBlocked
|
|
1088
1213
|
])
|
|
1089
1214
|
};
|
|
1090
1215
|
}
|
|
1091
1216
|
/** 标题右侧两件:「检查更新」一次查七家 + 「全部更新」按顺序串行装(吃 `useUpdateRows` 的表)。
|
|
1092
1217
|
*
|
|
1093
|
-
* 禁用形状(#925 第 2
|
|
1218
|
+
* 禁用形状(#925 第 2 条 + #980 检查中):任一行 installing、一键串行中或检查轮未收尾时两件都点不得;人话沿用 `update-busy` 原句
|
|
1094
1219
|
* (挂在 title 上,按钮文本仍是动作名)。题头不汇总排队数:串行进度只在各行自己显示。
|
|
1095
1220
|
*/
|
|
1096
1221
|
function CheckUpdateButton(props) {
|
|
1097
1222
|
const { checking, updatingAll, checkAll, updateAll, targets, rows } = props.face;
|
|
1098
|
-
const
|
|
1099
|
-
|
|
1100
|
-
|
|
1223
|
+
const blocked = shouldBlockBatchStart({
|
|
1224
|
+
hasInstallingRow: hasInstallingRow(rows),
|
|
1225
|
+
updatingAll,
|
|
1226
|
+
checking
|
|
1227
|
+
});
|
|
1228
|
+
const checkDisabled = blocked || targets.length === 0;
|
|
1229
|
+
const allDisabled = blocked || targets.length === 0;
|
|
1101
1230
|
const busyText = reasonText("update-busy");
|
|
1231
|
+
const installingNow = hasInstallingRow(rows) || updatingAll;
|
|
1102
1232
|
return react.createElement("span", { style: PANEL_STYLE.actions }, react.createElement("button", {
|
|
1103
1233
|
type: "button",
|
|
1104
1234
|
style: checkDisabled ? {
|
|
@@ -1108,7 +1238,7 @@ window.__ModuleLoader__.load({
|
|
|
1108
1238
|
} : PANEL_STYLE.btn,
|
|
1109
1239
|
disabled: checkDisabled,
|
|
1110
1240
|
onClick: checkAll,
|
|
1111
|
-
title:
|
|
1241
|
+
title: blocked ? busyText : "检查总管与六家插件的版本"
|
|
1112
1242
|
}, checking ? "检查中…" : "检查更新"), react.createElement("button", {
|
|
1113
1243
|
type: "button",
|
|
1114
1244
|
style: allDisabled ? {
|
|
@@ -1118,8 +1248,8 @@ window.__ModuleLoader__.load({
|
|
|
1118
1248
|
} : PANEL_STYLE.btn,
|
|
1119
1249
|
disabled: allDisabled,
|
|
1120
1250
|
onClick: updateAll,
|
|
1121
|
-
title:
|
|
1122
|
-
},
|
|
1251
|
+
title: blocked ? busyText : "按顺序逐家装上更新(一家收尾才起下一家)"
|
|
1252
|
+
}, installingNow ? "更新中…" : "全部更新"));
|
|
1123
1253
|
}
|
|
1124
1254
|
/** 待重启横幅:一行总账,只点名哪几家要重启(理由与做法写在各家卡片上,见 `restartBannerText`)。 */
|
|
1125
1255
|
function RestartBanners(props) {
|
|
@@ -1142,7 +1272,7 @@ window.__ModuleLoader__.load({
|
|
|
1142
1272
|
* 手工命令只在「装不了」或「装失败」的行显示:正常用户用不到,摊在每行上既吵又误导。
|
|
1143
1273
|
*/
|
|
1144
1274
|
function UpdateResults(props) {
|
|
1145
|
-
const { targets, rows, loadError, checking, updatingAll, open, close } = props.face;
|
|
1275
|
+
const { targets, rows, loadError, checking, updatingAll, open, close, showProgress } = props.face;
|
|
1146
1276
|
const dialogRef = react.useRef(null);
|
|
1147
1277
|
react.useEffect(() => {
|
|
1148
1278
|
if (open) dialogRef.current?.focus();
|
|
@@ -1151,7 +1281,12 @@ window.__ModuleLoader__.load({
|
|
|
1151
1281
|
if (loadError) return react.createElement("div", { style: PANEL_STYLE.reason }, "更新能力没接上:" + loadError);
|
|
1152
1282
|
if (targets.length === 0) return null;
|
|
1153
1283
|
if (shown.length === 0 && !open) return null;
|
|
1154
|
-
const locked =
|
|
1284
|
+
const locked = shouldBlockBatchStart({
|
|
1285
|
+
hasInstallingRow: hasInstallingRow(rows),
|
|
1286
|
+
updatingAll,
|
|
1287
|
+
checking
|
|
1288
|
+
});
|
|
1289
|
+
const installingNow = hasInstallingRow(rows) || updatingAll;
|
|
1155
1290
|
const body = shown.map((target) => {
|
|
1156
1291
|
const row = rows[target.key];
|
|
1157
1292
|
const snapshot = row.outcome?.snapshot ?? null;
|
|
@@ -1207,7 +1342,16 @@ window.__ModuleLoader__.load({
|
|
|
1207
1342
|
style: PANEL_STYLE.skill
|
|
1208
1343
|
}, line)), showManual ? react.createElement("div", { style: PANEL_STYLE.skill }, "在终端里执行这条命令可以手动完成这一步:") : null, showManual ? react.createElement("code", { style: PANEL_STYLE.cmd }, manual) : null);
|
|
1209
1344
|
});
|
|
1210
|
-
|
|
1345
|
+
const progressStrip = !open && (locked || shown.length > 0) ? react.createElement("div", { style: PANEL_STYLE.banner }, installingNow ? "有更新正在安装:关掉对话框不会中断。" : checking ? "正在检查更新,稍候。" : "上次检查的结果还在,直接打开看。", react.createElement("button", {
|
|
1346
|
+
type: "button",
|
|
1347
|
+
style: {
|
|
1348
|
+
...PANEL_STYLE.btn,
|
|
1349
|
+
marginLeft: 8
|
|
1350
|
+
},
|
|
1351
|
+
onClick: () => showProgress()
|
|
1352
|
+
}, "查看进度")) : null;
|
|
1353
|
+
const dialogNote = installingNow ? "安装在后台继续:关掉这块不会中断它。点「查看进度」可随时重新打开。" : checking ? "正在检查更新:关掉这块不会中断它,查完点「查看进度」看结果。" : "安装在宿主后台继续:关掉这块不会中断它。再点「检查更新」看最新进度。";
|
|
1354
|
+
return react.createElement("div", null, react.createElement(RestartBanners, { face: props.face }), progressStrip, open ? react.createElement("div", {
|
|
1211
1355
|
style: PANEL_STYLE.overlay,
|
|
1212
1356
|
role: "presentation",
|
|
1213
1357
|
onClick: () => close()
|
|
@@ -1227,7 +1371,7 @@ window.__ModuleLoader__.load({
|
|
|
1227
1371
|
onClick: () => close(),
|
|
1228
1372
|
title: "关闭",
|
|
1229
1373
|
"aria-label": "关闭"
|
|
1230
|
-
}, "×")), react.createElement("div", { style: PANEL_STYLE.dialogBody }, body, react.createElement("div", { style: PANEL_STYLE.dialogNote },
|
|
1374
|
+
}, "×")), react.createElement("div", { style: PANEL_STYLE.dialogBody }, body, react.createElement("div", { style: PANEL_STYLE.dialogNote }, dialogNote)))) : null);
|
|
1231
1375
|
}
|
|
1232
1376
|
/** 缺席卡:**按两处独立事实分三态**,每态只给能改变状态的动作。
|
|
1233
1377
|
*
|
|
@@ -1245,7 +1389,11 @@ window.__ModuleLoader__.load({
|
|
|
1245
1389
|
const target = props.target;
|
|
1246
1390
|
const row = target ? props.face.rows[target.key] : null;
|
|
1247
1391
|
const busy = row?.phase === "installing";
|
|
1248
|
-
const locked =
|
|
1392
|
+
const locked = shouldBlockBatchStart({
|
|
1393
|
+
hasInstallingRow: hasInstallingRow(props.face.rows),
|
|
1394
|
+
updatingAll: props.face.updatingAll,
|
|
1395
|
+
checking: props.face.checking
|
|
1396
|
+
});
|
|
1249
1397
|
const manual = (target ? manualForDisplay(target, row?.failure?.manual ?? row?.outcome?.manual ?? null) : null) ?? props.fallbackCommand;
|
|
1250
1398
|
const installed = target ? target.installedVersion ?? target.runningVersion : null;
|
|
1251
1399
|
const state = slotStateOf(target, props.inLedger);
|