dsh-life-pack 0.2.6 → 0.2.7
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 +273 -144
- package/dist/client.js.map +1 -1
- package/dist/health-view.d.ts +46 -10
- package/dist/health-view.d.ts.map +1 -1
- package/dist/health-view.js +100 -81
- package/dist/health-view.js.map +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -494,17 +494,40 @@ window.__ModuleLoader__.load({
|
|
|
494
494
|
marginBottom: 8,
|
|
495
495
|
borderRadius: 10,
|
|
496
496
|
border: "1px solid var(--dsw-alias-border, rgba(128,128,128,.3))",
|
|
497
|
+
borderLeftWidth: 3,
|
|
497
498
|
fontSize: 13,
|
|
498
499
|
lineHeight: 1.7
|
|
499
500
|
},
|
|
500
501
|
rowHead: {
|
|
501
|
-
|
|
502
|
+
display: "flex",
|
|
503
|
+
alignItems: "baseline",
|
|
504
|
+
gap: 6,
|
|
502
505
|
marginBottom: 2
|
|
503
506
|
},
|
|
507
|
+
rowTitle: {
|
|
508
|
+
fontWeight: 700,
|
|
509
|
+
color: "var(--dsw-alias-label-primary, inherit)"
|
|
510
|
+
},
|
|
511
|
+
rowPkg: {
|
|
512
|
+
color: "var(--dsw-alias-label-tertiary, #8a8a8a)",
|
|
513
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
514
|
+
fontSize: 12
|
|
515
|
+
},
|
|
504
516
|
rowMeta: {
|
|
505
517
|
color: "var(--dsw-alias-label-secondary, #9a9a9a)",
|
|
506
518
|
fontSize: 12
|
|
507
519
|
},
|
|
520
|
+
/** 版本号用等宽字:数字要能对齐着读(「当前」「最新」两列一眼比大小)。 */
|
|
521
|
+
ver: {
|
|
522
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
523
|
+
color: "var(--dsw-alias-label-primary, inherit)",
|
|
524
|
+
fontWeight: 600
|
|
525
|
+
},
|
|
526
|
+
/** 结论那一行:**只有要动的那几档上色**(绿/灰不抢注意力,照 health-view 的三档口径)。 */
|
|
527
|
+
verdictLine: {
|
|
528
|
+
fontSize: 12.5,
|
|
529
|
+
marginTop: 2
|
|
530
|
+
},
|
|
508
531
|
reason: {
|
|
509
532
|
color: "var(--dsw-alias-state-error-primary, #ff6b6b)",
|
|
510
533
|
fontSize: 12,
|
|
@@ -513,7 +536,26 @@ window.__ModuleLoader__.load({
|
|
|
513
536
|
skill: {
|
|
514
537
|
color: "var(--dsw-alias-label-tertiary, #8a8a8a)",
|
|
515
538
|
fontSize: 12,
|
|
516
|
-
marginTop: 4
|
|
539
|
+
marginTop: 4,
|
|
540
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace"
|
|
541
|
+
},
|
|
542
|
+
/** 行内主按钮(装上/装上更新):品牌底色 —— 全屏只有「能点的」这一种是这个颜色。 */
|
|
543
|
+
btnPrimary: {
|
|
544
|
+
border: "1px solid transparent",
|
|
545
|
+
background: "var(--dsw-alias-brand-primary, #0a84ff)",
|
|
546
|
+
color: "#fff",
|
|
547
|
+
borderRadius: 8,
|
|
548
|
+
padding: "5px 14px",
|
|
549
|
+
fontSize: 13,
|
|
550
|
+
fontWeight: 700,
|
|
551
|
+
cursor: "pointer",
|
|
552
|
+
marginTop: 6
|
|
553
|
+
},
|
|
554
|
+
/** 分档标记:**形状是第二条读数**(灰度截图、色弱视角下也分得开哪一行要动)。 */
|
|
555
|
+
mark: {
|
|
556
|
+
fontWeight: 700,
|
|
557
|
+
fontSize: 13,
|
|
558
|
+
lineHeight: 1
|
|
517
559
|
},
|
|
518
560
|
overlay: {
|
|
519
561
|
position: "fixed",
|
|
@@ -525,12 +567,14 @@ window.__ModuleLoader__.load({
|
|
|
525
567
|
padding: 16,
|
|
526
568
|
background: "rgba(0,0,0,.45)"
|
|
527
569
|
},
|
|
570
|
+
/** 浮层是**竖排两段**:题头固定、结果列表自己滚(题头跟着滚会把关闭键滚出视野 —— 用户实测反馈)。 */
|
|
528
571
|
dialog: {
|
|
529
572
|
width: "100%",
|
|
530
573
|
maxWidth: 560,
|
|
531
574
|
maxHeight: "80vh",
|
|
532
|
-
|
|
533
|
-
|
|
575
|
+
display: "flex",
|
|
576
|
+
flexDirection: "column",
|
|
577
|
+
overflow: "hidden",
|
|
534
578
|
borderRadius: 12,
|
|
535
579
|
border: "1px solid var(--dsw-alias-border, rgba(128,128,128,.4))",
|
|
536
580
|
background: "var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base, #222))",
|
|
@@ -542,12 +586,21 @@ window.__ModuleLoader__.load({
|
|
|
542
586
|
alignItems: "center",
|
|
543
587
|
justifyContent: "space-between",
|
|
544
588
|
gap: 8,
|
|
545
|
-
|
|
589
|
+
flex: "0 0 auto",
|
|
590
|
+
padding: "12px 14px",
|
|
591
|
+
borderBottom: "1px solid var(--dsw-alias-border, rgba(128,128,128,.3))"
|
|
546
592
|
},
|
|
547
593
|
dialogTitle: {
|
|
548
594
|
fontSize: 14,
|
|
549
595
|
fontWeight: 700
|
|
550
596
|
},
|
|
597
|
+
/** 唯一会滚的那一段。 */
|
|
598
|
+
dialogBody: {
|
|
599
|
+
flex: "1 1 auto",
|
|
600
|
+
minHeight: 0,
|
|
601
|
+
overflowY: "auto",
|
|
602
|
+
padding: "12px 14px"
|
|
603
|
+
},
|
|
551
604
|
close: {
|
|
552
605
|
border: "1px solid var(--dsw-alias-border, rgba(128,128,128,.35))",
|
|
553
606
|
background: "transparent",
|
|
@@ -565,6 +618,45 @@ window.__ModuleLoader__.load({
|
|
|
565
618
|
marginTop: 4
|
|
566
619
|
}
|
|
567
620
|
};
|
|
621
|
+
const ROW_TONE = {
|
|
622
|
+
update: {
|
|
623
|
+
mark: "↑",
|
|
624
|
+
color: "var(--dsw-alias-brand-primary, #0a84ff)",
|
|
625
|
+
tint: "rgba(10,132,255,.08)"
|
|
626
|
+
},
|
|
627
|
+
blocked: {
|
|
628
|
+
mark: "!",
|
|
629
|
+
color: "var(--dsw-alias-state-warning-primary, #d8a300)",
|
|
630
|
+
tint: "rgba(216,163,0,.10)"
|
|
631
|
+
},
|
|
632
|
+
failed: {
|
|
633
|
+
mark: "✕",
|
|
634
|
+
color: "var(--dsw-alias-state-error-primary, #ff6b6b)",
|
|
635
|
+
tint: "rgba(255,107,107,.10)"
|
|
636
|
+
},
|
|
637
|
+
ok: {
|
|
638
|
+
mark: "✓",
|
|
639
|
+
color: "var(--dsw-alias-state-success-primary, #4ec9a0)",
|
|
640
|
+
tint: "transparent"
|
|
641
|
+
},
|
|
642
|
+
idle: {
|
|
643
|
+
mark: "·",
|
|
644
|
+
color: "var(--dsw-alias-label-tertiary, #8a8a8a)",
|
|
645
|
+
tint: "transparent"
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
/** 这一行算哪一档:取数失败 > 装不上 > 有新版 > 待重启/源码装 > 已是最新 > 未查。 */
|
|
649
|
+
function rowToneOf(row, verdict) {
|
|
650
|
+
if (row.failure !== null || row.phase === "failed") return "failed";
|
|
651
|
+
if (row.phase === "installing") return "update";
|
|
652
|
+
if (verdict === null) return "idle";
|
|
653
|
+
switch (verdict.kind) {
|
|
654
|
+
case "update-available": return "update";
|
|
655
|
+
case "blocked": return "blocked";
|
|
656
|
+
case "up-to-date": return "ok";
|
|
657
|
+
default: return "idle";
|
|
658
|
+
}
|
|
659
|
+
}
|
|
568
660
|
const IDLE$1 = {
|
|
569
661
|
phase: "idle",
|
|
570
662
|
outcome: null,
|
|
@@ -749,18 +841,43 @@ window.__ModuleLoader__.load({
|
|
|
749
841
|
const manual = manualForDisplay(target, row.failure?.manual ?? row.outcome?.manual ?? null);
|
|
750
842
|
const busy = row.phase === "installing";
|
|
751
843
|
const showManual = manual !== null && (row.phase === "failed" || verdict?.kind === "blocked");
|
|
844
|
+
const tone = rowToneOf(row, verdict);
|
|
845
|
+
const paint = ROW_TONE[tone];
|
|
846
|
+
/** 这一行「当前/最新」两个版本号:不等才算有新版,把「最新」那格上色(用户扫一眼先看有没有箭头)。 */
|
|
847
|
+
const running = snapshot?.runningVersion ?? target.runningVersion ?? null;
|
|
848
|
+
const latest = snapshot?.latestVersion ?? null;
|
|
849
|
+
const newer = running !== null && latest !== null && latest !== running;
|
|
850
|
+
const lit = tone === "update" || tone === "blocked" || tone === "failed";
|
|
752
851
|
return react.createElement("div", {
|
|
753
852
|
key: target.key,
|
|
754
|
-
style:
|
|
755
|
-
|
|
853
|
+
style: {
|
|
854
|
+
...PANEL_STYLE.row,
|
|
855
|
+
borderLeftColor: paint.color,
|
|
856
|
+
background: paint.tint
|
|
857
|
+
}
|
|
858
|
+
}, react.createElement("div", { style: PANEL_STYLE.rowHead }, react.createElement("span", {
|
|
859
|
+
style: {
|
|
860
|
+
...PANEL_STYLE.mark,
|
|
861
|
+
color: paint.color
|
|
862
|
+
},
|
|
863
|
+
"aria-hidden": "true"
|
|
864
|
+
}, paint.mark), react.createElement("span", { style: PANEL_STYLE.rowTitle }, target.title), react.createElement("span", { style: PANEL_STYLE.rowPkg }, "· " + target.packageName)), react.createElement("div", { style: PANEL_STYLE.rowMeta }, "当前 ", react.createElement("b", { style: PANEL_STYLE.ver }, String(running ?? "未安装")), " · 最新 ", react.createElement("b", { style: newer ? {
|
|
865
|
+
...PANEL_STYLE.ver,
|
|
866
|
+
color: paint.color
|
|
867
|
+
} : PANEL_STYLE.ver }, String(latest ?? "未查"))), react.createElement("div", { style: lit ? {
|
|
868
|
+
...PANEL_STYLE.verdictLine,
|
|
869
|
+
color: paint.color,
|
|
870
|
+
fontWeight: 600
|
|
871
|
+
} : {
|
|
872
|
+
...PANEL_STYLE.verdictLine,
|
|
873
|
+
color: "var(--dsw-alias-label-secondary, #9a9a9a)"
|
|
874
|
+
} }, busy ? "正在装,请稍候…" : verdict?.text ?? (checking ? "检查中…" : "")), verdict?.action ? react.createElement("button", {
|
|
756
875
|
type: "button",
|
|
757
876
|
style: busy ? {
|
|
758
|
-
...PANEL_STYLE.
|
|
759
|
-
opacity: .55
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
marginTop: 6
|
|
763
|
-
},
|
|
877
|
+
...PANEL_STYLE.btnPrimary,
|
|
878
|
+
opacity: .55,
|
|
879
|
+
cursor: "default"
|
|
880
|
+
} : PANEL_STYLE.btnPrimary,
|
|
764
881
|
disabled: busy,
|
|
765
882
|
onClick: () => props.face.act(target)
|
|
766
883
|
}, verdict.action === "install" ? "装上" : "装上更新") : null, row.failure ? react.createElement("div", { style: PANEL_STYLE.reason }, "装不上:" + row.failure.message) : null, versionLines(target, snapshot).map((line, index) => react.createElement("div", {
|
|
@@ -782,13 +899,13 @@ window.__ModuleLoader__.load({
|
|
|
782
899
|
onKeyDown: (event) => {
|
|
783
900
|
if (event.key === "Escape") close();
|
|
784
901
|
}
|
|
785
|
-
}, react.createElement("div", { style: PANEL_STYLE.dialogHead }, react.createElement("div", { style: PANEL_STYLE.dialogTitle }, "
|
|
902
|
+
}, react.createElement("div", { style: PANEL_STYLE.dialogHead }, react.createElement("div", { style: PANEL_STYLE.dialogTitle }, "检查更新"), react.createElement("button", {
|
|
786
903
|
type: "button",
|
|
787
904
|
style: PANEL_STYLE.close,
|
|
788
905
|
onClick: () => close(),
|
|
789
906
|
title: "关闭",
|
|
790
907
|
"aria-label": "关闭"
|
|
791
|
-
}, "×")), body, react.createElement("div", { style: PANEL_STYLE.dialogNote }, "装与更新都在宿主后台跑:关掉这块不影响正在进行的安装,再点「检查更新」可以看到最新状态。"))) : null);
|
|
908
|
+
}, "×")), react.createElement("div", { style: PANEL_STYLE.dialogBody }, body, react.createElement("div", { style: PANEL_STYLE.dialogNote }, "装与更新都在宿主后台跑:关掉这块不影响正在进行的安装,再点「检查更新」可以看到最新状态。")))) : null);
|
|
792
909
|
}
|
|
793
910
|
/** 缺席卡:**按两处独立事实分三态**,每态只给能改变状态的动作。
|
|
794
911
|
*
|
|
@@ -1070,45 +1187,40 @@ window.__ModuleLoader__.load({
|
|
|
1070
1187
|
fontWeight: 700,
|
|
1071
1188
|
color: INK
|
|
1072
1189
|
},
|
|
1073
|
-
/**
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
border: "1px solid " + BORDER,
|
|
1077
|
-
background: "transparent",
|
|
1078
|
-
color: INK,
|
|
1079
|
-
borderRadius: 8,
|
|
1080
|
-
padding: "6px 16px",
|
|
1081
|
-
fontSize: 13,
|
|
1082
|
-
fontWeight: 600,
|
|
1083
|
-
cursor: "pointer"
|
|
1084
|
-
},
|
|
1085
|
-
lightsRow: {
|
|
1190
|
+
/** 顶部那一行汇总(票 #732):一行装下「配置体检 + 一句总账 + 体检按钮」,三样都不独占行。
|
|
1191
|
+
* 它替掉了原先「标题行 + 单独一行的按钮 + 一排灯按钮 + 两行图例」(同屏实测 153px → 一行)。 */
|
|
1192
|
+
summaryRow: {
|
|
1086
1193
|
display: "flex",
|
|
1087
|
-
flexWrap: "wrap",
|
|
1088
|
-
gap: 8
|
|
1089
|
-
},
|
|
1090
|
-
light: {
|
|
1091
|
-
display: "inline-flex",
|
|
1092
1194
|
alignItems: "center",
|
|
1093
|
-
gap:
|
|
1094
|
-
|
|
1095
|
-
border: "1px solid " + BORDER,
|
|
1096
|
-
borderRadius: 999,
|
|
1097
|
-
padding: "3px 10px",
|
|
1098
|
-
fontSize: 12.5,
|
|
1099
|
-
color: INK,
|
|
1100
|
-
cursor: "pointer"
|
|
1195
|
+
gap: 10,
|
|
1196
|
+
marginBottom: 10
|
|
1101
1197
|
},
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1198
|
+
summaryText: {
|
|
1199
|
+
flex: "1 1 auto",
|
|
1200
|
+
minWidth: 0,
|
|
1201
|
+
fontSize: 12.5,
|
|
1202
|
+
whiteSpace: "nowrap",
|
|
1203
|
+
overflow: "hidden",
|
|
1204
|
+
textOverflow: "ellipsis"
|
|
1105
1205
|
},
|
|
1206
|
+
/** 各家那张表里的说明行(「还没体检:点上面那个…」那几句)。 */
|
|
1106
1207
|
meta: {
|
|
1107
1208
|
color: INK_DIM,
|
|
1108
1209
|
fontSize: 12,
|
|
1109
1210
|
lineHeight: 1.7,
|
|
1110
1211
|
marginTop: 12
|
|
1111
1212
|
},
|
|
1213
|
+
btn: {
|
|
1214
|
+
border: "1px solid " + BORDER,
|
|
1215
|
+
background: "transparent",
|
|
1216
|
+
color: INK,
|
|
1217
|
+
borderRadius: 8,
|
|
1218
|
+
flex: "0 0 auto",
|
|
1219
|
+
padding: "3px 10px",
|
|
1220
|
+
fontSize: 12.5,
|
|
1221
|
+
fontWeight: 600,
|
|
1222
|
+
cursor: "pointer"
|
|
1223
|
+
},
|
|
1112
1224
|
error: {
|
|
1113
1225
|
color: STATUS_COLOR.red,
|
|
1114
1226
|
fontSize: 12,
|
|
@@ -1192,11 +1304,12 @@ window.__ModuleLoader__.load({
|
|
|
1192
1304
|
id: tab.id,
|
|
1193
1305
|
title: tab.title,
|
|
1194
1306
|
status: report ? worstStatus(items) : null,
|
|
1195
|
-
counts: countByStatus(items)
|
|
1307
|
+
counts: countByStatus(items),
|
|
1308
|
+
hasChannel: tab.hasChannel ?? true
|
|
1196
1309
|
};
|
|
1197
1310
|
});
|
|
1198
1311
|
}
|
|
1199
|
-
/** 一家的一句话读数:「红 1 黄 2
|
|
1312
|
+
/** 一家的一句话读数:「红 1 黄 2」,全绿就是「绿」。面板侧要**上色**的那几处不用它(见 `countSegments`)。 */
|
|
1200
1313
|
function countsText(counts) {
|
|
1201
1314
|
const parts = [];
|
|
1202
1315
|
if (counts.red > 0) parts.push("红 " + String(counts.red));
|
|
@@ -1204,25 +1317,46 @@ window.__ModuleLoader__.load({
|
|
|
1204
1317
|
if (parts.length === 0) return "绿";
|
|
1205
1318
|
return parts.join(" · ");
|
|
1206
1319
|
}
|
|
1207
|
-
/**
|
|
1208
|
-
*
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
fontWeight: 700
|
|
1216
|
-
}
|
|
1217
|
-
}, "—")];
|
|
1218
|
-
const parts = [];
|
|
1320
|
+
/** 把一份计数拆成**按档分色**的几截。红黄各自一截、各自上色,互不串色;
|
|
1321
|
+
* 「黄 3」这一截为什么不能被染成红——见 `countSegments` 的注释(出图复评逮到的真缺陷)。 */
|
|
1322
|
+
function countSegsOf(light) {
|
|
1323
|
+
if (light.status === null) return [{
|
|
1324
|
+
status: null,
|
|
1325
|
+
text: "—"
|
|
1326
|
+
}];
|
|
1327
|
+
const segs = [];
|
|
1219
1328
|
for (const status of ["red", "yellow"]) {
|
|
1220
1329
|
const count = light.counts[status];
|
|
1330
|
+
if (count > 0) segs.push({
|
|
1331
|
+
status,
|
|
1332
|
+
text: STATUS_LABEL[status] + " " + String(count)
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
return segs.length > 0 ? segs : [{
|
|
1336
|
+
status: "green",
|
|
1337
|
+
text: "绿"
|
|
1338
|
+
}];
|
|
1339
|
+
}
|
|
1340
|
+
/** 几截读数之间那个分隔符:不许染档位色(它不属于任何一档)。 */
|
|
1341
|
+
const SEG_SEP = " · ";
|
|
1342
|
+
/** 汇总行那一句:绿要显眼(它是「正常」这件事本身),没通道的那几家不冒充绿。 */
|
|
1343
|
+
function overallText(lights) {
|
|
1344
|
+
const pending = lights.filter((light) => !light.hasChannel).length;
|
|
1345
|
+
const withReport = lights.filter((light) => light.hasChannel);
|
|
1346
|
+
const tail = pending > 0 ? " · " + String(pending) + " 家还没体检" : "";
|
|
1347
|
+
if (withReport.length > 0 && withReport.every((light) => light.status === "green")) return react.createElement("span", { style: {
|
|
1348
|
+
color: STATUS_COLOR.green,
|
|
1349
|
+
fontWeight: 700
|
|
1350
|
+
} }, "六家正常" + tail);
|
|
1351
|
+
const parts = [];
|
|
1352
|
+
for (const status of ["red", "yellow"]) {
|
|
1353
|
+
let count = 0;
|
|
1354
|
+
for (const light of lights) count += light.counts[status];
|
|
1221
1355
|
if (count === 0) continue;
|
|
1222
1356
|
if (parts.length > 0) parts.push(react.createElement("span", {
|
|
1223
1357
|
key: status + "-sep",
|
|
1224
1358
|
style: { color: INK_DIM }
|
|
1225
|
-
},
|
|
1359
|
+
}, SEG_SEP));
|
|
1226
1360
|
parts.push(react.createElement("span", {
|
|
1227
1361
|
key: status,
|
|
1228
1362
|
style: {
|
|
@@ -1231,21 +1365,55 @@ window.__ModuleLoader__.load({
|
|
|
1231
1365
|
}
|
|
1232
1366
|
}, STATUS_LABEL[status] + " " + String(count)));
|
|
1233
1367
|
}
|
|
1234
|
-
if (parts.length === 0) return
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1368
|
+
if (parts.length === 0) return react.createElement("span", { style: { color: INK_DIM } }, "还没体检" + tail);
|
|
1369
|
+
parts.push(react.createElement("span", {
|
|
1370
|
+
key: "todo",
|
|
1371
|
+
style: { color: INK }
|
|
1372
|
+
}, " 要处理" + tail));
|
|
1373
|
+
return react.createElement("span", null, parts);
|
|
1374
|
+
}
|
|
1375
|
+
/** 那枚小数字用的底色(比灯色浅一档,压在页签上不抢眼)。 */
|
|
1376
|
+
const STATUS_DOT = {
|
|
1377
|
+
red: STATUS_COLOR.red,
|
|
1378
|
+
yellow: STATUS_COLOR.yellow,
|
|
1379
|
+
green: STATUS_COLOR.green
|
|
1380
|
+
};
|
|
1381
|
+
/** 页签圆点的颜色与语义(票 #732):从「实心/空心=装没装」改成「颜色=体检档位」。 */
|
|
1382
|
+
const TAB_DOT = {
|
|
1383
|
+
/** 没装(页签槽账本里没有这一家)。 */
|
|
1384
|
+
absent: STATUS_DOT.green,
|
|
1385
|
+
/** 装了、但还没跑过体检。 */
|
|
1386
|
+
unchecked: INK_DIM
|
|
1387
|
+
};
|
|
1388
|
+
/** 页签圆点该用什么色:装了按档位(红黄绿),没装/没跑过走缺席态。 */
|
|
1389
|
+
function tabDotColor(light) {
|
|
1390
|
+
if (!light.hasChannel) return TAB_DOT.absent;
|
|
1391
|
+
return light.status === null ? TAB_DOT.unchecked : STATUS_DOT[light.status];
|
|
1392
|
+
}
|
|
1393
|
+
/** 页签上那枚小数字(票 #732):红黄那几家,家名后带一条计数;全绿与没跑过不带。 */
|
|
1394
|
+
const TAB_NOTE_STYLE = {
|
|
1395
|
+
marginLeft: 5,
|
|
1396
|
+
border: "1px solid " + BORDER,
|
|
1397
|
+
borderRadius: 999,
|
|
1398
|
+
padding: "0 5px",
|
|
1399
|
+
fontSize: 11,
|
|
1400
|
+
fontWeight: 700
|
|
1401
|
+
};
|
|
1402
|
+
/** 那枚小数字写什么:只报「要处理」的那两档;一家里红黄都有时先报红那一档(圆点已经按最严重的档上色了)。 */
|
|
1403
|
+
function tabNote(light) {
|
|
1404
|
+
if (!light.hasChannel || light.status === null || light.status === "green") return null;
|
|
1405
|
+
return countSegsOf(light)[0] ?? null;
|
|
1242
1406
|
}
|
|
1243
|
-
/**
|
|
1244
|
-
|
|
1407
|
+
/** 顶部那一行汇总(票 #732:不再一家一盏灯按钮,一行总账 + 一个体检按钮)。
|
|
1408
|
+
*
|
|
1409
|
+
* 与页签的分工:这行只答「一共几条要处理」;「哪几家、各几条」由页签的圆点与小数字答;
|
|
1410
|
+
* 「具体哪一条坏了、去哪修」由那家页签里的 `HealthTable` 答。 */
|
|
1411
|
+
function HealthSummaryLine(props) {
|
|
1412
|
+
const hasLights = props.lights.length > 0;
|
|
1245
1413
|
return react.createElement("div", {
|
|
1246
|
-
style: HEALTH_STYLE.
|
|
1247
|
-
"data-ilife-health": "
|
|
1248
|
-
}, react.createElement("div", { style: HEALTH_STYLE.
|
|
1414
|
+
style: HEALTH_STYLE.summaryRow,
|
|
1415
|
+
"data-ilife-health": "summary"
|
|
1416
|
+
}, react.createElement("div", { style: HEALTH_STYLE.head }, "配置体检"), react.createElement("div", { style: HEALTH_STYLE.summaryText }, props.error !== null ? react.createElement("span", { style: { color: STATUS_TEXT.red } }, props.error) : hasLights ? overallText(props.lights) : react.createElement("span", { style: { color: INK_DIM } }, "还没体检")), react.createElement("button", {
|
|
1249
1417
|
type: "button",
|
|
1250
1418
|
style: props.running ? {
|
|
1251
1419
|
...HEALTH_STYLE.btn,
|
|
@@ -1254,63 +1422,8 @@ window.__ModuleLoader__.load({
|
|
|
1254
1422
|
} : HEALTH_STYLE.btn,
|
|
1255
1423
|
disabled: props.running,
|
|
1256
1424
|
onClick: props.onRun,
|
|
1257
|
-
title: "
|
|
1258
|
-
}, props.running ? "体检中…" : "体检一次"))
|
|
1259
|
-
style: HEALTH_STYLE.lightsRow,
|
|
1260
|
-
"data-ilife-health": "lights"
|
|
1261
|
-
}, props.lights.map((light) => react.createElement("button", {
|
|
1262
|
-
key: light.id,
|
|
1263
|
-
type: "button",
|
|
1264
|
-
"data-ilife-health": "light",
|
|
1265
|
-
"data-skill": light.id,
|
|
1266
|
-
style: light.status === null ? {
|
|
1267
|
-
...HEALTH_STYLE.light,
|
|
1268
|
-
...HEALTH_STYLE.lightIdle
|
|
1269
|
-
} : {
|
|
1270
|
-
...HEALTH_STYLE.light,
|
|
1271
|
-
borderColor: STATUS_COLOR[light.status],
|
|
1272
|
-
borderWidth: 1.5,
|
|
1273
|
-
background: STATUS_TINT[light.status]
|
|
1274
|
-
},
|
|
1275
|
-
disabled: light.status === null,
|
|
1276
|
-
onClick: () => props.onJump(light.id),
|
|
1277
|
-
title: light.status === null ? "还没体检(或这一家没装)" : light.title + ":" + countsText(light.counts)
|
|
1278
|
-
}, react.createElement("span", { style: {
|
|
1279
|
-
width: 10,
|
|
1280
|
-
height: 10,
|
|
1281
|
-
borderRadius: "50%",
|
|
1282
|
-
background: light.status === null ? INK_DIM : STATUS_COLOR[light.status],
|
|
1283
|
-
display: "inline-block"
|
|
1284
|
-
} }), react.createElement("span", { style: {
|
|
1285
|
-
color: INK,
|
|
1286
|
-
fontWeight: 650
|
|
1287
|
-
} }, light.title), ...countSegments(light)))), react.createElement("div", { style: HEALTH_STYLE.meta }, props.error !== null ? props.error : [
|
|
1288
|
-
react.createElement("span", {
|
|
1289
|
-
key: "r",
|
|
1290
|
-
style: {
|
|
1291
|
-
color: STATUS_TEXT.red,
|
|
1292
|
-
fontWeight: 700
|
|
1293
|
-
}
|
|
1294
|
-
}, "红"),
|
|
1295
|
-
react.createElement("span", { key: "r2" }, "=坏了 · "),
|
|
1296
|
-
react.createElement("span", {
|
|
1297
|
-
key: "y",
|
|
1298
|
-
style: {
|
|
1299
|
-
color: STATUS_TEXT.yellow,
|
|
1300
|
-
fontWeight: 700
|
|
1301
|
-
}
|
|
1302
|
-
}, "黄"),
|
|
1303
|
-
react.createElement("span", { key: "y2" }, "=还没配 · "),
|
|
1304
|
-
react.createElement("span", {
|
|
1305
|
-
key: "g",
|
|
1306
|
-
style: {
|
|
1307
|
-
color: STATUS_TEXT.green,
|
|
1308
|
-
fontWeight: 700
|
|
1309
|
-
}
|
|
1310
|
-
}, "绿"),
|
|
1311
|
-
react.createElement("span", { key: "g2" }, "=正常 | 只看不改:不建目录、不改配置、不自动重置"),
|
|
1312
|
-
react.createElement("span", { key: "tab" }, " · 上面那排圆点是体检灯,点一下跳到那家配置页;下面那排圆点是页签(实心=装上了)")
|
|
1313
|
-
]));
|
|
1425
|
+
title: "对六家各跑一次配置体检(只看不改:不建目录、不改配置、不自动重置)"
|
|
1426
|
+
}, props.running ? "体检中…" : "体检一次"));
|
|
1314
1427
|
}
|
|
1315
1428
|
/** 正正常常的那些:绿条只留「名字」,一句话与「去哪修」都不印(票面验收第一条:正常的收成一行)。 */
|
|
1316
1429
|
function isAttentionItem(item) {
|
|
@@ -1608,6 +1721,13 @@ window.__ModuleLoader__.load({
|
|
|
1608
1721
|
id: row.id,
|
|
1609
1722
|
channel: row.channel
|
|
1610
1723
|
})), [rows]));
|
|
1724
|
+
/** 六家的灯(票 #732):按**页签槽账本**那一排过(装了但没体检通道的也在内,它显缺席态)。 */
|
|
1725
|
+
const lights = react.useMemo(() => lightsOf(rows.map((row) => ({
|
|
1726
|
+
id: row.id,
|
|
1727
|
+
title: row.label,
|
|
1728
|
+
hasChannel: row.channel.length > 0
|
|
1729
|
+
})), Object.fromEntries(Object.entries(health.rows).map(([id, row]) => [id, row.report ?? void 0]))), [rows, health.rows]);
|
|
1730
|
+
const lightOf = (id) => lights.find((light) => light.id === id);
|
|
1611
1731
|
const [activeId, setActiveId] = react.useState(void 0);
|
|
1612
1732
|
const [visitedIds, setVisitedIds] = react.useState(() => /* @__PURE__ */ new Set());
|
|
1613
1733
|
const active = MANAGER_TABS.some((t) => t.plugin === activeId) ? activeId : MANAGER_TABS[0].plugin;
|
|
@@ -1621,13 +1741,6 @@ window.__ModuleLoader__.load({
|
|
|
1621
1741
|
const row = rows.find((r) => r.id === tab.plugin);
|
|
1622
1742
|
return row && row.label.length > 0 ? row.label : tab.title;
|
|
1623
1743
|
}
|
|
1624
|
-
/** 按包名取页签标题(总览那行按账本行过,账本里没有的退到导航表的标题)。 */
|
|
1625
|
-
function labelForTab(id) {
|
|
1626
|
-
const row = rows.find((r) => r.id === id);
|
|
1627
|
-
if (row && row.label.length > 0) return row.label;
|
|
1628
|
-
const tab = MANAGER_TABS.find((t) => t.plugin === id);
|
|
1629
|
-
return tab ? tab.title : id;
|
|
1630
|
-
}
|
|
1631
1744
|
function targetForTab(tab) {
|
|
1632
1745
|
return face.targets.find((t) => t.packageName === tab.plugin) ?? null;
|
|
1633
1746
|
}
|
|
@@ -1653,21 +1766,20 @@ window.__ModuleLoader__.load({
|
|
|
1653
1766
|
setActiveId(next.plugin);
|
|
1654
1767
|
tabRefs.current[nextIndex]?.focus();
|
|
1655
1768
|
}
|
|
1656
|
-
return react.createElement("div", null, react.createElement("div", { style: S.headRow }, react.createElement("div", { style: S.head }, "爱生活"), react.createElement("div", { style: S.headActions }, react.createElement(CheckUpdateButton, { face }), react.createElement(PanelActions, null))), react.createElement("div", { style: S.meta }, react.createElement("div", null, "总开关 · 开关(缺省启用,只读)"), react.createElement("div", null, "总管 dsh-life-pack · 0.2.6")), react.createElement(UpdateResults, { face }), react.createElement(
|
|
1657
|
-
lights
|
|
1658
|
-
id: row.id,
|
|
1659
|
-
title: labelForTab(row.id)
|
|
1660
|
-
})), Object.fromEntries(Object.entries(health.rows).map(([id, row]) => [id, row.report ?? void 0]))),
|
|
1769
|
+
return react.createElement("div", null, react.createElement("div", { style: S.headRow }, react.createElement("div", { style: S.head }, "爱生活"), react.createElement("div", { style: S.headActions }, react.createElement(CheckUpdateButton, { face }), react.createElement(PanelActions, null))), react.createElement("div", { style: S.meta }, react.createElement("div", null, "总开关 · 开关(缺省启用,只读)"), react.createElement("div", null, "总管 dsh-life-pack · 0.2.6")), react.createElement(UpdateResults, { face }), react.createElement(HealthSummaryLine, {
|
|
1770
|
+
lights,
|
|
1661
1771
|
running: health.running,
|
|
1662
1772
|
error: health.error,
|
|
1663
|
-
onRun: health.run
|
|
1664
|
-
onJump: setActiveId
|
|
1773
|
+
onRun: health.run
|
|
1665
1774
|
}), react.createElement("div", {
|
|
1666
1775
|
role: "tablist",
|
|
1667
1776
|
"aria-label": "爱生活技能页签",
|
|
1668
1777
|
style: S.tablist
|
|
1669
1778
|
}, MANAGER_TABS.map((tab, index) => {
|
|
1670
1779
|
const selected = tab.plugin === active;
|
|
1780
|
+
const light = lightOf(tab.plugin);
|
|
1781
|
+
const dotColor = light === void 0 ? present.has(tab.plugin) ? TAB_DOT.unchecked : TAB_DOT.absent : tabDotColor(light);
|
|
1782
|
+
const note = light === void 0 ? null : tabNote(light);
|
|
1671
1783
|
return react.createElement("button", {
|
|
1672
1784
|
key: tab.plugin,
|
|
1673
1785
|
ref: (element) => {
|
|
@@ -1688,8 +1800,25 @@ window.__ModuleLoader__.load({
|
|
|
1688
1800
|
},
|
|
1689
1801
|
onKeyDown: (event) => {
|
|
1690
1802
|
onTabKeyDown(event, index);
|
|
1803
|
+
},
|
|
1804
|
+
title: light === void 0 ? "这一家没装(或产物没注册页签槽)" : light.title + ":" + (light.hasChannel ? light.status === null ? "还没体检" : "体检读数见本页签下方的表" : "没有配置体检出口")
|
|
1805
|
+
}, react.createElement("span", {
|
|
1806
|
+
"data-ilife-health": "tab-dot",
|
|
1807
|
+
style: {
|
|
1808
|
+
width: 6,
|
|
1809
|
+
height: 6,
|
|
1810
|
+
borderRadius: "50%",
|
|
1811
|
+
background: dotColor,
|
|
1812
|
+
display: "inline-block",
|
|
1813
|
+
flex: "0 0 auto"
|
|
1814
|
+
}
|
|
1815
|
+
}), labelFor(tab), note === null ? null : react.createElement("span", {
|
|
1816
|
+
"data-ilife-health": "tab-note",
|
|
1817
|
+
style: {
|
|
1818
|
+
...TAB_NOTE_STYLE,
|
|
1819
|
+
color: note.status === null ? void 0 : STATUS_TEXT[note.status]
|
|
1691
1820
|
}
|
|
1692
|
-
},
|
|
1821
|
+
}, note.text));
|
|
1693
1822
|
})), MANAGER_TABS.filter((tab) => tab.plugin === active || visitedIds.has(tab.plugin)).map((tab) => {
|
|
1694
1823
|
const selected = tab.plugin === active;
|
|
1695
1824
|
return react.createElement("div", {
|