dsh-life-pack 0.3.12 → 0.3.14
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 +291 -60
- package/dist/client.js.map +1 -1
- package/dist/config-panel-api.d.ts +5 -2
- package/dist/config-panel-api.d.ts.map +1 -1
- package/dist/config-panel-api.js +4 -2
- package/dist/config-panel-api.js.map +1 -1
- package/dist/config-panel-contract.d.ts +13 -2
- package/dist/config-panel-contract.d.ts.map +1 -1
- package/dist/config-panel-contract.js +5 -2
- package/dist/config-panel-contract.js.map +1 -1
- package/dist/config-panel-value.d.ts +0 -12
- package/dist/config-panel-value.d.ts.map +1 -1
- package/dist/config-panel-value.js +6 -16
- package/dist/config-panel-value.js.map +1 -1
- package/dist/config-panel-view.d.ts +230 -13
- package/dist/config-panel-view.d.ts.map +1 -1
- package/dist/config-panel-view.js +598 -99
- package/dist/config-panel-view.js.map +1 -1
- package/dist/config-panel.d.ts +7 -0
- package/dist/config-panel.d.ts.map +1 -1
- package/dist/config-panel.js +21 -0
- package/dist/config-panel.js.map +1 -1
- package/dist/directory-browser-parts.d.ts.map +1 -1
- package/dist/directory-browser-parts.js +1 -1
- package/dist/directory-browser-parts.js.map +1 -1
- package/dist/health-contract.d.ts +7 -0
- package/dist/health-contract.d.ts.map +1 -1
- package/dist/health-contract.js.map +1 -1
- package/dist/health-view.js +1 -1
- package/dist/health-view.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/manager-version.d.ts +11 -0
- package/dist/manager-version.d.ts.map +1 -1
- package/dist/manager-version.js +39 -6
- package/dist/manager-version.js.map +1 -1
- package/dist/nav.js +1 -1
- package/dist/nav.js.map +1 -1
- package/dist/update-queue.d.ts +42 -0
- package/dist/update-queue.d.ts.map +1 -0
- package/dist/update-queue.js +91 -0
- package/dist/update-queue.js.map +1 -0
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -100,7 +100,7 @@ window.__ModuleLoader__.load({
|
|
|
100
100
|
const PANEL_LINKS = [{
|
|
101
101
|
key: "star",
|
|
102
102
|
glyph: "⭐",
|
|
103
|
-
tip: "
|
|
103
|
+
tip: "你的🌟是我夜空中最亮的星。",
|
|
104
104
|
url: "https://github.com/FeatherHunter/ilife"
|
|
105
105
|
}, {
|
|
106
106
|
key: "feedback",
|
|
@@ -518,6 +518,112 @@ window.__ModuleLoader__.load({
|
|
|
518
518
|
return lines;
|
|
519
519
|
}
|
|
520
520
|
//#endregion
|
|
521
|
+
//#region src/update-queue.ts
|
|
522
|
+
/** 任一行 installing 即忙:题头检查更新禁用、行按钮全局互斥、checkAll/act 入口直接返回的同一判据。 */
|
|
523
|
+
function hasInstallingRow(rows) {
|
|
524
|
+
for (const key of Object.keys(rows)) if (rows[key]?.phase === "installing") return true;
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
/** 按目标顺序逐家查→装→收尾:一家收尾才起下一家;失败记 failed 继续;待重启跳过安装。
|
|
528
|
+
*
|
|
529
|
+
* 跳过安装只有一种:快照事实已是待重启(`restartPendingOf`)——由横幅点名,不在这里装第二遍。
|
|
530
|
+
* 不可装(已是最新/拦下且无安装动作)记 ready 收尾;缺席且无最新版本号记 ready 收尾(等下一次检查)。
|
|
531
|
+
* 本函数不汇总排队数、不碰他行(`patch` 只收本家 key)。
|
|
532
|
+
*/
|
|
533
|
+
async function runSerialUpdateAll(targets, deps) {
|
|
534
|
+
for (const target of targets) {
|
|
535
|
+
const current = deps.getRow(target.key);
|
|
536
|
+
deps.patch(target.key, {
|
|
537
|
+
phase: "checking",
|
|
538
|
+
outcome: current?.outcome ?? null,
|
|
539
|
+
failure: null
|
|
540
|
+
});
|
|
541
|
+
const checked = await deps.check(target);
|
|
542
|
+
if (!checked.ok) {
|
|
543
|
+
deps.patch(target.key, {
|
|
544
|
+
phase: "failed",
|
|
545
|
+
outcome: null,
|
|
546
|
+
failure: checked
|
|
547
|
+
});
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
if (restartPendingOf(checked.value.snapshot)) {
|
|
551
|
+
deps.patch(target.key, {
|
|
552
|
+
phase: "ready",
|
|
553
|
+
outcome: checked.value,
|
|
554
|
+
failure: null
|
|
555
|
+
});
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
const verdict = verdictOf(target, checked.value.snapshot);
|
|
559
|
+
if (!(verdict.action === "update" || verdict.action === "install" || verdict.action === "retry")) {
|
|
560
|
+
deps.patch(target.key, {
|
|
561
|
+
phase: "ready",
|
|
562
|
+
outcome: checked.value,
|
|
563
|
+
failure: null
|
|
564
|
+
});
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
deps.patch(target.key, {
|
|
568
|
+
phase: "installing",
|
|
569
|
+
outcome: checked.value,
|
|
570
|
+
failure: null
|
|
571
|
+
});
|
|
572
|
+
if (isAbsent(target)) {
|
|
573
|
+
const version = checked.value.snapshot.latestVersion;
|
|
574
|
+
if (!version) {
|
|
575
|
+
deps.patch(target.key, {
|
|
576
|
+
phase: "ready",
|
|
577
|
+
outcome: checked.value,
|
|
578
|
+
failure: null
|
|
579
|
+
});
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
const done = await deps.installAbsent(target, version);
|
|
583
|
+
if (done.ok) {
|
|
584
|
+
const refreshed = await deps.checkStatus(target);
|
|
585
|
+
deps.patch(target.key, refreshed.ok ? {
|
|
586
|
+
phase: "ready",
|
|
587
|
+
outcome: refreshed.value,
|
|
588
|
+
failure: null
|
|
589
|
+
} : {
|
|
590
|
+
phase: "failed",
|
|
591
|
+
outcome: null,
|
|
592
|
+
failure: refreshed
|
|
593
|
+
});
|
|
594
|
+
} else deps.patch(target.key, {
|
|
595
|
+
phase: "failed",
|
|
596
|
+
outcome: checked.value,
|
|
597
|
+
failure: {
|
|
598
|
+
...done,
|
|
599
|
+
manual: done.manual ?? checked.value.manual ?? null
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
const done = await deps.installPresent(target);
|
|
605
|
+
if (done.ok) {
|
|
606
|
+
const refreshed = await deps.checkStatus(target);
|
|
607
|
+
deps.patch(target.key, refreshed.ok ? {
|
|
608
|
+
phase: "ready",
|
|
609
|
+
outcome: refreshed.value,
|
|
610
|
+
failure: null
|
|
611
|
+
} : {
|
|
612
|
+
phase: "failed",
|
|
613
|
+
outcome: null,
|
|
614
|
+
failure: refreshed
|
|
615
|
+
});
|
|
616
|
+
} else deps.patch(target.key, {
|
|
617
|
+
phase: "failed",
|
|
618
|
+
outcome: checked.value,
|
|
619
|
+
failure: {
|
|
620
|
+
...done,
|
|
621
|
+
manual: done.manual ?? checked.value.manual ?? null
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
//#endregion
|
|
521
627
|
//#region src/update-panel.ts
|
|
522
628
|
/** 爱生活面板的更新界面:检查更新按钮、七家结果行、待重启横幅、缺席卡的「装上」。
|
|
523
629
|
*
|
|
@@ -562,7 +668,7 @@ window.__ModuleLoader__.load({
|
|
|
562
668
|
marginBottom: 10
|
|
563
669
|
},
|
|
564
670
|
tab: {
|
|
565
|
-
border: "1px solid var(--dsw-alias-border, rgba(128,128,128,.35))",
|
|
671
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35))",
|
|
566
672
|
background: "transparent",
|
|
567
673
|
color: "var(--dsw-alias-label-primary, inherit)",
|
|
568
674
|
borderRadius: 999,
|
|
@@ -573,13 +679,13 @@ window.__ModuleLoader__.load({
|
|
|
573
679
|
tabActive: {
|
|
574
680
|
background: "var(--dsw-alias-brand-primary, #0a84ff)",
|
|
575
681
|
borderColor: "transparent",
|
|
576
|
-
color: "#
|
|
682
|
+
color: "var(--dsw-alias-label-primary-foreground, #0f1115)",
|
|
577
683
|
fontWeight: 700
|
|
578
684
|
},
|
|
579
685
|
reco: {
|
|
580
686
|
padding: "12px 14px",
|
|
581
687
|
borderRadius: 10,
|
|
582
|
-
border: "1px dashed var(--dsw-alias-border, rgba(128,128,128,.45))",
|
|
688
|
+
border: "1px dashed var(--dsw-alias-border-l1, rgba(128,128,128,.45))",
|
|
583
689
|
color: "var(--dsw-alias-label-secondary, #9a9a9a)",
|
|
584
690
|
fontSize: 13,
|
|
585
691
|
lineHeight: 1.7
|
|
@@ -596,7 +702,7 @@ window.__ModuleLoader__.load({
|
|
|
596
702
|
userSelect: "all"
|
|
597
703
|
},
|
|
598
704
|
btn: {
|
|
599
|
-
border: "1px solid var(--dsw-alias-border, rgba(128,128,128,.35))",
|
|
705
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35))",
|
|
600
706
|
background: "transparent",
|
|
601
707
|
color: "var(--dsw-alias-label-primary, inherit)",
|
|
602
708
|
borderRadius: 8,
|
|
@@ -608,7 +714,7 @@ window.__ModuleLoader__.load({
|
|
|
608
714
|
padding: "10px 12px",
|
|
609
715
|
marginBottom: 10,
|
|
610
716
|
borderRadius: 8,
|
|
611
|
-
border: "1px solid var(--dsw-alias-state-
|
|
717
|
+
border: "1px solid var(--dsw-alias-state-warn-primary, #d8a300)",
|
|
612
718
|
background: "var(--dsw-alias-bg-base, rgba(216,163,0,.12))",
|
|
613
719
|
color: "var(--dsw-alias-label-primary, inherit)",
|
|
614
720
|
fontSize: 13,
|
|
@@ -618,7 +724,7 @@ window.__ModuleLoader__.load({
|
|
|
618
724
|
padding: "10px 12px",
|
|
619
725
|
marginBottom: 8,
|
|
620
726
|
borderRadius: 10,
|
|
621
|
-
border: "1px solid var(--dsw-alias-border, rgba(128,128,128,.3))",
|
|
727
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.3))",
|
|
622
728
|
borderLeftWidth: 3,
|
|
623
729
|
fontSize: 13,
|
|
624
730
|
lineHeight: 1.7
|
|
@@ -668,7 +774,7 @@ window.__ModuleLoader__.load({
|
|
|
668
774
|
btnPrimary: {
|
|
669
775
|
border: "1px solid transparent",
|
|
670
776
|
background: "var(--dsw-alias-brand-primary, #0a84ff)",
|
|
671
|
-
color: "#
|
|
777
|
+
color: "var(--dsw-alias-label-primary-foreground, #0f1115)",
|
|
672
778
|
borderRadius: 8,
|
|
673
779
|
padding: "5px 14px",
|
|
674
780
|
fontSize: 13,
|
|
@@ -701,8 +807,8 @@ window.__ModuleLoader__.load({
|
|
|
701
807
|
flexDirection: "column",
|
|
702
808
|
overflow: "hidden",
|
|
703
809
|
borderRadius: 12,
|
|
704
|
-
border: "1px solid var(--dsw-alias-border, rgba(128,128,128,.4))",
|
|
705
|
-
background: "var(--dsw-alias-bg-
|
|
810
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.4))",
|
|
811
|
+
background: "var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-base, #222))",
|
|
706
812
|
color: "var(--dsw-alias-label-primary, inherit)",
|
|
707
813
|
boxShadow: "0 12px 32px rgba(0,0,0,.35)"
|
|
708
814
|
},
|
|
@@ -713,7 +819,7 @@ window.__ModuleLoader__.load({
|
|
|
713
819
|
gap: 8,
|
|
714
820
|
flex: "0 0 auto",
|
|
715
821
|
padding: "12px 14px",
|
|
716
|
-
borderBottom: "1px solid var(--dsw-alias-border, rgba(128,128,128,.3))"
|
|
822
|
+
borderBottom: "1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.3))"
|
|
717
823
|
},
|
|
718
824
|
dialogTitle: {
|
|
719
825
|
fontSize: 14,
|
|
@@ -727,7 +833,7 @@ window.__ModuleLoader__.load({
|
|
|
727
833
|
padding: "12px 14px"
|
|
728
834
|
},
|
|
729
835
|
close: {
|
|
730
|
-
border: "1px solid var(--dsw-alias-border, rgba(128,128,128,.35))",
|
|
836
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35))",
|
|
731
837
|
background: "transparent",
|
|
732
838
|
color: "var(--dsw-alias-label-primary, inherit)",
|
|
733
839
|
borderRadius: 8,
|
|
@@ -751,7 +857,7 @@ window.__ModuleLoader__.load({
|
|
|
751
857
|
},
|
|
752
858
|
blocked: {
|
|
753
859
|
mark: "!",
|
|
754
|
-
color: "var(--dsw-alias-state-
|
|
860
|
+
color: "var(--dsw-alias-state-warn-primary, #d8a300)",
|
|
755
861
|
tint: "rgba(216,163,0,.10)"
|
|
756
862
|
},
|
|
757
863
|
failed: {
|
|
@@ -794,13 +900,21 @@ window.__ModuleLoader__.load({
|
|
|
794
900
|
outcome: null,
|
|
795
901
|
failure: null
|
|
796
902
|
};
|
|
797
|
-
/** 面板的更新状态源:挂载时取七个目标的表,之后按需查/装(组件与按钮共用一个 face)。
|
|
903
|
+
/** 面板的更新状态源:挂载时取七个目标的表,之后按需查/装(组件与按钮共用一个 face)。
|
|
904
|
+
*
|
|
905
|
+
* 并发形状(#925 拍板四条,用户已确认):
|
|
906
|
+
* - 一键全部更新按目标顺序串行(查→装→轮询到收尾,一家收尾才起下一家;失败记 failed 继续);
|
|
907
|
+
* - 任一行 installing 时 checkAll 入口直接返回、题头禁用;
|
|
908
|
+
* - act 入口有忙直接返回(有忙拒 update-busy 且不改 rows,含同家第二点);
|
|
909
|
+
* - 每行各自显示(patch 只写本家 key),题头不汇总排队数。
|
|
910
|
+
*/
|
|
798
911
|
function useUpdateRows(getCall) {
|
|
799
912
|
const [targets, setTargets] = react.useState([]);
|
|
800
913
|
const [rows, setRows] = react.useState({});
|
|
801
914
|
const [pollMs, setPollMs] = react.useState(1e3);
|
|
802
915
|
const [loadError, setLoadError] = react.useState(null);
|
|
803
916
|
const [checking, setChecking] = react.useState(false);
|
|
917
|
+
const [updatingAll, setUpdatingAll] = react.useState(false);
|
|
804
918
|
const [open, setOpen] = react.useState(false);
|
|
805
919
|
const alive = react.useRef(true);
|
|
806
920
|
react.useEffect(() => () => {
|
|
@@ -825,21 +939,31 @@ window.__ModuleLoader__.load({
|
|
|
825
939
|
[key]: next
|
|
826
940
|
}));
|
|
827
941
|
}, []);
|
|
942
|
+
const rowsRef = react.useRef(rows);
|
|
943
|
+
rowsRef.current = rows;
|
|
944
|
+
const targetsRef = react.useRef(targets);
|
|
945
|
+
targetsRef.current = targets;
|
|
946
|
+
const pollMsRef = react.useRef(pollMs);
|
|
947
|
+
pollMsRef.current = pollMs;
|
|
948
|
+
const updatingAllRef = react.useRef(false);
|
|
828
949
|
return {
|
|
829
950
|
targets,
|
|
830
951
|
rows,
|
|
831
952
|
pollMs,
|
|
832
953
|
loadError,
|
|
833
954
|
checking,
|
|
955
|
+
updatingAll,
|
|
834
956
|
open,
|
|
835
957
|
close: () => setOpen(false),
|
|
836
958
|
checkAll: react.useCallback(() => {
|
|
959
|
+
if (hasInstallingRow(rowsRef.current) || updatingAllRef.current) return;
|
|
837
960
|
setChecking(true);
|
|
838
961
|
setOpen(true);
|
|
839
|
-
const list =
|
|
962
|
+
const list = targetsRef.current;
|
|
963
|
+
const snapshot = rowsRef.current;
|
|
840
964
|
for (const target of list) patch(target.key, {
|
|
841
965
|
phase: "checking",
|
|
842
|
-
outcome:
|
|
966
|
+
outcome: snapshot[target.key]?.outcome ?? null,
|
|
843
967
|
failure: null
|
|
844
968
|
});
|
|
845
969
|
Promise.all(list.map(async (target) => {
|
|
@@ -853,15 +977,41 @@ window.__ModuleLoader__.load({
|
|
|
853
977
|
outcome: null,
|
|
854
978
|
failure: result
|
|
855
979
|
});
|
|
856
|
-
})).finally(() =>
|
|
980
|
+
})).finally(() => {
|
|
981
|
+
if (alive.current) setChecking(false);
|
|
982
|
+
});
|
|
983
|
+
}, [getCall, patch]),
|
|
984
|
+
updateAll: react.useCallback(() => {
|
|
985
|
+
if (hasInstallingRow(rowsRef.current) || updatingAllRef.current) return;
|
|
986
|
+
const list = targetsRef.current;
|
|
987
|
+
if (list.length === 0) return;
|
|
988
|
+
updatingAllRef.current = true;
|
|
989
|
+
setUpdatingAll(true);
|
|
990
|
+
setOpen(true);
|
|
991
|
+
(async () => {
|
|
992
|
+
try {
|
|
993
|
+
await runSerialUpdateAll(list, {
|
|
994
|
+
check: (target) => checkTarget(getCall(), target),
|
|
995
|
+
checkStatus: (target) => checkTarget(getCall(), target, target.phones?.status),
|
|
996
|
+
installAbsent: (target, version) => installAbsent(getCall(), target, version),
|
|
997
|
+
installPresent: (target) => updateInstalled(getCall(), target, pollMsRef.current),
|
|
998
|
+
patch,
|
|
999
|
+
getRow: (key) => rowsRef.current[key]
|
|
1000
|
+
});
|
|
1001
|
+
await reload();
|
|
1002
|
+
} finally {
|
|
1003
|
+
updatingAllRef.current = false;
|
|
1004
|
+
if (alive.current) setUpdatingAll(false);
|
|
1005
|
+
}
|
|
1006
|
+
})();
|
|
857
1007
|
}, [
|
|
858
1008
|
getCall,
|
|
859
1009
|
patch,
|
|
860
|
-
|
|
861
|
-
targets
|
|
1010
|
+
reload
|
|
862
1011
|
]),
|
|
863
1012
|
act: react.useCallback((target, action) => {
|
|
864
|
-
|
|
1013
|
+
if (hasInstallingRow(rowsRef.current) || updatingAllRef.current) return;
|
|
1014
|
+
const current = rowsRef.current[target.key] ?? IDLE$1;
|
|
865
1015
|
if (action === "recheck") {
|
|
866
1016
|
patch(target.key, {
|
|
867
1017
|
phase: "checking",
|
|
@@ -907,7 +1057,7 @@ window.__ModuleLoader__.load({
|
|
|
907
1057
|
});
|
|
908
1058
|
version = checked.value.snapshot.latestVersion;
|
|
909
1059
|
}
|
|
910
|
-
const result = absent ? await installAbsent(getCall(), target, version ?? "") : await updateInstalled(getCall(), target,
|
|
1060
|
+
const result = absent ? await installAbsent(getCall(), target, version ?? "") : await updateInstalled(getCall(), target, pollMsRef.current);
|
|
911
1061
|
if (result.ok) {
|
|
912
1062
|
const refreshed = await checkTarget(getCall(), target, target.phones?.status);
|
|
913
1063
|
patch(target.key, refreshed.ok ? {
|
|
@@ -934,27 +1084,42 @@ window.__ModuleLoader__.load({
|
|
|
934
1084
|
}, [
|
|
935
1085
|
getCall,
|
|
936
1086
|
patch,
|
|
937
|
-
|
|
938
|
-
reload,
|
|
939
|
-
rows
|
|
1087
|
+
reload
|
|
940
1088
|
])
|
|
941
1089
|
};
|
|
942
1090
|
}
|
|
943
|
-
/**
|
|
1091
|
+
/** 标题右侧两件:「检查更新」一次查七家 + 「全部更新」按顺序串行装(吃 `useUpdateRows` 的表)。
|
|
1092
|
+
*
|
|
1093
|
+
* 禁用形状(#925 第 2 条):任一行 installing 时两件都点不得;人话沿用 `update-busy` 原句
|
|
1094
|
+
* (挂在 title 上,按钮文本仍是动作名)。题头不汇总排队数:串行进度只在各行自己显示。
|
|
1095
|
+
*/
|
|
944
1096
|
function CheckUpdateButton(props) {
|
|
945
|
-
const { checking, checkAll, targets } = props.face;
|
|
946
|
-
const
|
|
947
|
-
|
|
1097
|
+
const { checking, updatingAll, checkAll, updateAll, targets, rows } = props.face;
|
|
1098
|
+
const busy = hasInstallingRow(rows) || updatingAll;
|
|
1099
|
+
const checkDisabled = checking || updatingAll || busy || targets.length === 0;
|
|
1100
|
+
const allDisabled = checking || updatingAll || busy || targets.length === 0;
|
|
1101
|
+
const busyText = reasonText("update-busy");
|
|
1102
|
+
return react.createElement("span", { style: PANEL_STYLE.actions }, react.createElement("button", {
|
|
948
1103
|
type: "button",
|
|
949
|
-
style:
|
|
1104
|
+
style: checkDisabled ? {
|
|
950
1105
|
...PANEL_STYLE.btn,
|
|
951
1106
|
opacity: .55,
|
|
952
1107
|
cursor: "default"
|
|
953
1108
|
} : PANEL_STYLE.btn,
|
|
954
|
-
disabled,
|
|
1109
|
+
disabled: checkDisabled,
|
|
955
1110
|
onClick: checkAll,
|
|
956
|
-
title: "检查总管与六家插件的版本"
|
|
957
|
-
}, checking ? "检查中…" : "检查更新")
|
|
1111
|
+
title: busy ? busyText : "检查总管与六家插件的版本"
|
|
1112
|
+
}, checking ? "检查中…" : "检查更新"), react.createElement("button", {
|
|
1113
|
+
type: "button",
|
|
1114
|
+
style: allDisabled ? {
|
|
1115
|
+
...PANEL_STYLE.btn,
|
|
1116
|
+
opacity: .55,
|
|
1117
|
+
cursor: "default"
|
|
1118
|
+
} : PANEL_STYLE.btn,
|
|
1119
|
+
disabled: allDisabled,
|
|
1120
|
+
onClick: updateAll,
|
|
1121
|
+
title: busy ? busyText : "按顺序逐家装上更新(一家收尾才起下一家)"
|
|
1122
|
+
}, updatingAll ? "更新中…" : "全部更新"));
|
|
958
1123
|
}
|
|
959
1124
|
/** 待重启横幅:一行总账,只点名哪几家要重启(理由与做法写在各家卡片上,见 `restartBannerText`)。 */
|
|
960
1125
|
function RestartBanners(props) {
|
|
@@ -977,7 +1142,7 @@ window.__ModuleLoader__.load({
|
|
|
977
1142
|
* 手工命令只在「装不了」或「装失败」的行显示:正常用户用不到,摊在每行上既吵又误导。
|
|
978
1143
|
*/
|
|
979
1144
|
function UpdateResults(props) {
|
|
980
|
-
const { targets, rows, loadError, checking, open, close } = props.face;
|
|
1145
|
+
const { targets, rows, loadError, checking, updatingAll, open, close } = props.face;
|
|
981
1146
|
const dialogRef = react.useRef(null);
|
|
982
1147
|
react.useEffect(() => {
|
|
983
1148
|
if (open) dialogRef.current?.focus();
|
|
@@ -986,6 +1151,7 @@ window.__ModuleLoader__.load({
|
|
|
986
1151
|
if (loadError) return react.createElement("div", { style: PANEL_STYLE.reason }, "更新能力没接上:" + loadError);
|
|
987
1152
|
if (targets.length === 0) return null;
|
|
988
1153
|
if (shown.length === 0 && !open) return null;
|
|
1154
|
+
const locked = hasInstallingRow(rows) || updatingAll;
|
|
989
1155
|
const body = shown.map((target) => {
|
|
990
1156
|
const row = rows[target.key];
|
|
991
1157
|
const snapshot = row.outcome?.snapshot ?? null;
|
|
@@ -1026,12 +1192,13 @@ window.__ModuleLoader__.load({
|
|
|
1026
1192
|
color: "var(--dsw-alias-label-secondary, #9a9a9a)"
|
|
1027
1193
|
} }, busy ? "正在装,请稍候…" : verdict?.text ?? (checking ? "检查中…" : "")), buttonAction ? react.createElement("button", {
|
|
1028
1194
|
type: "button",
|
|
1029
|
-
style:
|
|
1195
|
+
style: locked ? {
|
|
1030
1196
|
...PANEL_STYLE.btnPrimary,
|
|
1031
1197
|
opacity: .55,
|
|
1032
1198
|
cursor: "default"
|
|
1033
1199
|
} : PANEL_STYLE.btnPrimary,
|
|
1034
|
-
disabled:
|
|
1200
|
+
disabled: locked,
|
|
1201
|
+
title: locked ? reasonText("update-busy") : void 0,
|
|
1035
1202
|
onClick: () => {
|
|
1036
1203
|
props.face.act(target, buttonAction);
|
|
1037
1204
|
}
|
|
@@ -1078,6 +1245,7 @@ window.__ModuleLoader__.load({
|
|
|
1078
1245
|
const target = props.target;
|
|
1079
1246
|
const row = target ? props.face.rows[target.key] : null;
|
|
1080
1247
|
const busy = row?.phase === "installing";
|
|
1248
|
+
const locked = busy || hasInstallingRow(props.face.rows) || props.face.updatingAll;
|
|
1081
1249
|
const manual = (target ? manualForDisplay(target, row?.failure?.manual ?? row?.outcome?.manual ?? null) : null) ?? props.fallbackCommand;
|
|
1082
1250
|
const installed = target ? target.installedVersion ?? target.runningVersion : null;
|
|
1083
1251
|
const state = slotStateOf(target, props.inLedger);
|
|
@@ -1097,7 +1265,7 @@ window.__ModuleLoader__.load({
|
|
|
1097
1265
|
}
|
|
1098
1266
|
return react.createElement("div", { style: PANEL_STYLE.reco }, react.createElement("div", null, headline), state === "absent" ? react.createElement("button", {
|
|
1099
1267
|
type: "button",
|
|
1100
|
-
style:
|
|
1268
|
+
style: locked ? {
|
|
1101
1269
|
...PANEL_STYLE.btn,
|
|
1102
1270
|
marginTop: 8,
|
|
1103
1271
|
opacity: .55
|
|
@@ -1105,13 +1273,14 @@ window.__ModuleLoader__.load({
|
|
|
1105
1273
|
...PANEL_STYLE.btn,
|
|
1106
1274
|
marginTop: 8
|
|
1107
1275
|
},
|
|
1108
|
-
disabled:
|
|
1276
|
+
disabled: locked,
|
|
1277
|
+
title: locked ? reasonText("update-busy") : void 0,
|
|
1109
1278
|
onClick: () => {
|
|
1110
1279
|
if (target) props.face.act(target, "install");
|
|
1111
1280
|
}
|
|
1112
1281
|
}, busy ? "正在装,请稍候…" : "装上") : null, retryAction === "recheck" && target ? react.createElement("button", {
|
|
1113
1282
|
type: "button",
|
|
1114
|
-
style:
|
|
1283
|
+
style: locked ? {
|
|
1115
1284
|
...PANEL_STYLE.btn,
|
|
1116
1285
|
marginTop: 8,
|
|
1117
1286
|
marginLeft: 8,
|
|
@@ -1121,7 +1290,8 @@ window.__ModuleLoader__.load({
|
|
|
1121
1290
|
marginTop: 8,
|
|
1122
1291
|
marginLeft: 8
|
|
1123
1292
|
},
|
|
1124
|
-
disabled:
|
|
1293
|
+
disabled: locked,
|
|
1294
|
+
title: locked ? reasonText("update-busy") : void 0,
|
|
1125
1295
|
onClick: () => props.face.act(target, "recheck")
|
|
1126
1296
|
}, ACTION_LABEL.recheck) : null, reason ? react.createElement("div", { style: PANEL_STYLE.reason }, reason) : null, react.createElement("code", { style: PANEL_STYLE.cmd }, manual));
|
|
1127
1297
|
}
|
|
@@ -1386,7 +1556,7 @@ window.__ModuleLoader__.load({
|
|
|
1386
1556
|
/** 面板上的文字色与分隔线(与 `client.ts` 同一套别名)。 */
|
|
1387
1557
|
const INK = "var(--dsw-alias-label-primary, inherit)";
|
|
1388
1558
|
const INK_DIM = "var(--dsw-alias-label-secondary, #9a9a9a)";
|
|
1389
|
-
const BORDER = "var(--dsw-alias-border, rgba(128,128,128,.35))";
|
|
1559
|
+
const BORDER = "var(--dsw-alias-border-l1, rgba(128,128,128,.35))";
|
|
1390
1560
|
const HEALTH_STYLE = {
|
|
1391
1561
|
box: {
|
|
1392
1562
|
padding: "10px 12px",
|
|
@@ -2262,7 +2432,7 @@ window.__ModuleLoader__.load({
|
|
|
2262
2432
|
textOverflow: "ellipsis",
|
|
2263
2433
|
whiteSpace: "nowrap"
|
|
2264
2434
|
},
|
|
2265
|
-
error: { color: "var(--dsw-alias-
|
|
2435
|
+
error: { color: "var(--dsw-alias-state-error-primary, #e66)" }
|
|
2266
2436
|
};
|
|
2267
2437
|
/** 面包屑:从文件系统根到当前层,每一格都是跳转目标(`listing.crumbs` 由取数方给)。 */
|
|
2268
2438
|
function crumbRow(state, onEnter) {
|
|
@@ -2572,11 +2742,23 @@ window.__ModuleLoader__.load({
|
|
|
2572
2742
|
fontSize: 14,
|
|
2573
2743
|
lineHeight: 1
|
|
2574
2744
|
},
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2745
|
+
/** #933:页签格里「配置卡 + 体检卡」这一列的堆叠间距(唯一口径,别再给某一张卡加 margin)。 */
|
|
2746
|
+
tabStack: {
|
|
2747
|
+
display: "flex",
|
|
2748
|
+
flexDirection: "column",
|
|
2749
|
+
gap: 10
|
|
2750
|
+
},
|
|
2751
|
+
/** #937:标题行里那枚版本胶囊(形状照真源 v3.1 的 `.ic-ver`:10px 字、1px 描边、999 圆角)。 */
|
|
2752
|
+
versionCapsule: {
|
|
2753
|
+
fontSize: 10.5,
|
|
2754
|
+
lineHeight: 1.6,
|
|
2755
|
+
color: "var(--dsw-alias-label-tertiary, #adb2b8)",
|
|
2756
|
+
border: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))",
|
|
2757
|
+
borderRadius: 999,
|
|
2758
|
+
padding: "0 7px",
|
|
2759
|
+
whiteSpace: "nowrap",
|
|
2760
|
+
flex: "0 0 auto",
|
|
2761
|
+
fontWeight: 400
|
|
2580
2762
|
},
|
|
2581
2763
|
tablist: {
|
|
2582
2764
|
display: "flex",
|
|
@@ -2585,7 +2767,7 @@ window.__ModuleLoader__.load({
|
|
|
2585
2767
|
marginBottom: 10
|
|
2586
2768
|
},
|
|
2587
2769
|
tab: {
|
|
2588
|
-
border: "1px solid var(--dsw-alias-border, rgba(128,128,128,.35))",
|
|
2770
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35))",
|
|
2589
2771
|
background: "transparent",
|
|
2590
2772
|
color: "var(--dsw-alias-label-primary, inherit)",
|
|
2591
2773
|
borderRadius: 999,
|
|
@@ -2596,13 +2778,13 @@ window.__ModuleLoader__.load({
|
|
|
2596
2778
|
tabActive: {
|
|
2597
2779
|
background: "var(--dsw-alias-brand-primary, #0a84ff)",
|
|
2598
2780
|
borderColor: "transparent",
|
|
2599
|
-
color: "#
|
|
2781
|
+
color: "var(--dsw-alias-label-primary-foreground, #0f1115)",
|
|
2600
2782
|
fontWeight: 700
|
|
2601
2783
|
},
|
|
2602
2784
|
reco: {
|
|
2603
2785
|
padding: "12px 14px",
|
|
2604
2786
|
borderRadius: 10,
|
|
2605
|
-
border: "1px dashed var(--dsw-alias-border, rgba(128,128,128,.45))",
|
|
2787
|
+
border: "1px dashed var(--dsw-alias-border-l1, rgba(128,128,128,.45))",
|
|
2606
2788
|
color: "var(--dsw-alias-label-secondary, #9a9a9a)",
|
|
2607
2789
|
fontSize: 13,
|
|
2608
2790
|
lineHeight: 1.7
|
|
@@ -2622,7 +2804,7 @@ window.__ModuleLoader__.load({
|
|
|
2622
2804
|
marginTop: 16,
|
|
2623
2805
|
padding: "10px 12px",
|
|
2624
2806
|
borderRadius: 10,
|
|
2625
|
-
border: "1px solid var(--dsw-alias-border, rgba(128,128,128,.35))"
|
|
2807
|
+
border: "1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35))"
|
|
2626
2808
|
},
|
|
2627
2809
|
moreTitle: {
|
|
2628
2810
|
fontSize: 13,
|
|
@@ -2714,22 +2896,47 @@ window.__ModuleLoader__.load({
|
|
|
2714
2896
|
style: S.moreRow
|
|
2715
2897
|
}, react.createElement("span", { style: S.morePkg }, row.pkg), react.createElement("span", { style: S.moreDesc }, row.desc), react.createElement("span", { style: S.moreIcon }, react.createElement(ExternalIcon, null)))));
|
|
2716
2898
|
}
|
|
2717
|
-
/** 总管自述版本(票 #737
|
|
2899
|
+
/** 总管自述版本(票 #737;#937 改口):**不手写**,问宿主——宿主读自己这份已安装包的 `package.json`。
|
|
2718
2900
|
*
|
|
2719
|
-
*
|
|
2901
|
+
* 三态(#937):`loading`(首帧就画胶囊占位 `…`)/`ready`(读到,画包名 · 版本)/`missing`(读不到,画「版本未知」)。
|
|
2902
|
+
* **失败要重试**:原先只取一次、一失败就永远停在未知(#937 查出来的真缺陷);这里按 1s/3s/8s 退避重试三次。
|
|
2720
2903
|
* 与卡路里 #130 同一条路:面板是浏览器产物、禁 node 内建,读盘只许在宿主半。 */
|
|
2904
|
+
/** 读中 / 读不到 两种胶囊文案(#937:占位符用 `…`,读不到给人话,不把 `unknown` 印给人看)。 */
|
|
2905
|
+
const VERSION_PENDING_TEXT = "…";
|
|
2906
|
+
const VERSION_MISSING_TEXT = "版本未知";
|
|
2907
|
+
/** 重试间隔(#937):三次退避,之后不再打扰宿主。 */
|
|
2908
|
+
const VERSION_RETRY_MS = [
|
|
2909
|
+
1e3,
|
|
2910
|
+
3e3,
|
|
2911
|
+
8e3
|
|
2912
|
+
];
|
|
2721
2913
|
function useManagerVersion(getCall) {
|
|
2722
|
-
const [
|
|
2914
|
+
const [state, setState] = react.useState({ kind: "loading" });
|
|
2723
2915
|
react.useEffect(() => {
|
|
2724
2916
|
let alive = true;
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2917
|
+
const timers = [];
|
|
2918
|
+
const attempt = (index) => {
|
|
2919
|
+
loadManagerVersion(getCall()).then((next) => {
|
|
2920
|
+
if (!alive) return;
|
|
2921
|
+
if (next !== "unknown") {
|
|
2922
|
+
setState({
|
|
2923
|
+
kind: "ready",
|
|
2924
|
+
version: next
|
|
2925
|
+
});
|
|
2926
|
+
return;
|
|
2927
|
+
}
|
|
2928
|
+
setState({ kind: "missing" });
|
|
2929
|
+
const wait = VERSION_RETRY_MS[index];
|
|
2930
|
+
if (wait !== void 0) timers.push(setTimeout(() => attempt(index + 1), wait));
|
|
2931
|
+
});
|
|
2932
|
+
};
|
|
2933
|
+
attempt(0);
|
|
2728
2934
|
return () => {
|
|
2729
2935
|
alive = false;
|
|
2936
|
+
for (const id of timers) clearTimeout(id);
|
|
2730
2937
|
};
|
|
2731
2938
|
}, [getCall]);
|
|
2732
|
-
return
|
|
2939
|
+
return state;
|
|
2733
2940
|
}
|
|
2734
2941
|
/** 爱生活面板:总设置区 + 检查更新(七家)+ 爱生活页签条(slot 驱动)+ 技能设置页投影/缺席卡。 */
|
|
2735
2942
|
function LifePackSection(props) {
|
|
@@ -2759,6 +2966,27 @@ window.__ModuleLoader__.load({
|
|
|
2759
2966
|
return /* @__PURE__ */ new Set([...previous, active]);
|
|
2760
2967
|
});
|
|
2761
2968
|
}, [active]);
|
|
2969
|
+
/** 预热:section 一挂载,就把其余几家**错峰**挂上(隐藏面板照常挂载、只切 `hidden`),
|
|
2970
|
+
* 让各家那一次配置读提前跑掉——用户点哪个页签,多半值已经在了。
|
|
2971
|
+
* 错峰(每 150ms 一家)是为了不让六家子进程同时抢开工:首帧那一口气优先留给框架。
|
|
2972
|
+
* 只在页签账本非空时起一次(`rows` 要等各家 slot 注入后才齐)。
|
|
2973
|
+
*
|
|
2974
|
+
* **一次性**(p10 定时器门的例外依据):每个页签只挂一次(`warmed` 闸门 + 一批固定数量的定时器),
|
|
2975
|
+
* 不是轮询、也不是重试——它没有「次数」可言;卸载即 `clearTimeout` 清干净。
|
|
2976
|
+
* 不做轮询是本意:这里只补一次提前量,取数与刷新仍旧由各家用例自己的通道走。 */
|
|
2977
|
+
const warmed = react.useRef(false);
|
|
2978
|
+
const tabCount = rows.length;
|
|
2979
|
+
react.useEffect(() => {
|
|
2980
|
+
if (warmed.current || tabCount === 0) return;
|
|
2981
|
+
warmed.current = true;
|
|
2982
|
+
const STAGGER_MS = 150;
|
|
2983
|
+
const timers = MANAGER_TABS.filter((tab) => rows.some((row) => row.id === tab.plugin)).map((tab, index) => setTimeout(() => {
|
|
2984
|
+
setVisitedIds((previous) => previous.has(tab.plugin) ? previous : /* @__PURE__ */ new Set([...previous, tab.plugin]));
|
|
2985
|
+
}, STAGGER_MS * (index + 1)));
|
|
2986
|
+
return () => {
|
|
2987
|
+
for (const timer of timers) clearTimeout(timer);
|
|
2988
|
+
};
|
|
2989
|
+
}, [tabCount]);
|
|
2762
2990
|
function labelFor(tab) {
|
|
2763
2991
|
const row = rows.find((r) => r.id === tab.plugin);
|
|
2764
2992
|
return row && row.label.length > 0 ? row.label : tab.title;
|
|
@@ -2788,7 +3016,10 @@ window.__ModuleLoader__.load({
|
|
|
2788
3016
|
setActiveId(next.plugin);
|
|
2789
3017
|
tabRefs.current[nextIndex]?.focus();
|
|
2790
3018
|
}
|
|
2791
|
-
return react.createElement("div", null, react.createElement("div", { style: S.headRow }, react.createElement("div", { style: S.head }, "爱生活"), react.createElement("
|
|
3019
|
+
return react.createElement("div", null, react.createElement("div", { style: S.headRow }, react.createElement("div", { style: S.head }, "爱生活"), react.createElement("span", {
|
|
3020
|
+
style: S.versionCapsule,
|
|
3021
|
+
"data-ilife-version": "capsule"
|
|
3022
|
+
}, managerVersion.kind === "ready" ? "dsh-life-pack · " + managerVersion.version : managerVersion.kind === "missing" ? VERSION_MISSING_TEXT : VERSION_PENDING_TEXT), react.createElement("div", { style: S.headActions }, react.createElement(CheckUpdateButton, { face }), react.createElement(PanelActions, null))), react.createElement(UpdateResults, { face }), react.createElement(HealthSummaryLine, {
|
|
2792
3023
|
lights,
|
|
2793
3024
|
running: health.running,
|
|
2794
3025
|
error: summaryErrorOf(health),
|
|
@@ -2848,8 +3079,8 @@ window.__ModuleLoader__.load({
|
|
|
2848
3079
|
id: tabsId + "-panel-" + tab.plugin,
|
|
2849
3080
|
role: "tabpanel",
|
|
2850
3081
|
"aria-labelledby": tabsId + "-tab-" + tab.plugin,
|
|
2851
|
-
|
|
2852
|
-
}, present.has(tab.plugin) ? react.createElement("div",
|
|
3082
|
+
style: selected ? void 0 : { display: "none" }
|
|
3083
|
+
}, present.has(tab.plugin) ? react.createElement("div", { style: S.tabStack }, props.renderSlot(CONFIG_TAB_SLOT, {}, { only: tab.plugin }), react.createElement(HealthTable, {
|
|
2853
3084
|
title: labelFor(tab),
|
|
2854
3085
|
phase: health.rows[tab.plugin]?.phase ?? "idle",
|
|
2855
3086
|
report: health.rows[tab.plugin]?.report ?? null,
|