dsh-life-pack 0.2.4 → 0.2.6
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 +233 -42
- package/dist/client.js.map +1 -1
- package/dist/health-view.d.ts +12 -0
- package/dist/health-view.d.ts.map +1 -1
- package/dist/health-view.js +161 -14
- package/dist/health-view.js.map +1 -1
- package/dist/nav.d.ts +1 -1
- package/dist/nav.js +1 -1
- package/dist/update-client.d.ts.map +1 -1
- package/dist/update-client.js +3 -1
- package/dist/update-client.js.map +1 -1
- package/dist/update-contract.d.ts +8 -1
- package/dist/update-contract.d.ts.map +1 -1
- package/dist/update-contract.js +9 -1
- package/dist/update-contract.js.map +1 -1
- package/dist/update-env.d.ts +16 -0
- package/dist/update-env.d.ts.map +1 -1
- package/dist/update-env.js +70 -0
- package/dist/update-env.js.map +1 -1
- package/dist/update-host.d.ts.map +1 -1
- package/dist/update-host.js +4 -1
- package/dist/update-host.js.map +1 -1
- package/dist/update-view.d.ts +12 -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
|
@@ -132,6 +132,13 @@ window.__ModuleLoader__.load({
|
|
|
132
132
|
];
|
|
133
133
|
//#endregion
|
|
134
134
|
//#region src/update-contract.ts
|
|
135
|
+
/** 爱生活页签槽名(总管声明 `children`、各单品技能设置页注册进来;单段名,避开官方 `settings.*` 前缀)。
|
|
136
|
+
*
|
|
137
|
+
* 为什么住这里:这个名字**两侧都要用**——面板侧 `client.ts` 拿它注册/投影槽,
|
|
138
|
+
* 宿主侧(`update-env.ts` 的 `readPanelRegistered`)拿它判「已装产物里到底有没有注册代码」。
|
|
139
|
+
* 一处定义、两边引用(结构纪律「概念唯一」):名字改了只有这一行要动。
|
|
140
|
+
* 宿主判据为什么要读这个字面量、以及它的边界,见 `readPanelRegistered` 的头注。 */
|
|
141
|
+
const CONFIG_TAB_SLOT = "ilife.config-tab";
|
|
135
142
|
/** 载体:宿主注册 `connection.fetch.register` 的路径 / 面板 `connection.rpc.call` 的两段参数。 */
|
|
136
143
|
const MANAGER_RPC = {
|
|
137
144
|
/** 载体基段(`connection.rpc.call` 的**第一段**参数)。传错第一段就是 404:真机上踩过一次。 */
|
|
@@ -147,7 +154,7 @@ window.__ModuleLoader__.load({
|
|
|
147
154
|
const MANAGER_ACTIONS = {
|
|
148
155
|
/** 装上缺席的目标包:入参 `{packageName, version}`,回包 `{packageName, version}`。 */
|
|
149
156
|
install: "ilife-manager.install",
|
|
150
|
-
/** 七个更新目标的表:入参 `{}`,回包 `{targets: [{key, title, packageName, phones, runningVersion, installedVersion, skill}]}`。
|
|
157
|
+
/** 七个更新目标的表:入参 `{}`,回包 `{targets: [{key, title, packageName, phones, runningVersion, installedVersion, panelRegistered, skill}]}`。
|
|
151
158
|
*
|
|
152
159
|
* 电话名为什么由宿主转交而不是面板侧写死:更新包的三个电话名只有它自己知道
|
|
153
160
|
* (`update.phoneNames`,`host.ts:335`);面板侧既不能 import 更新包(浏览器产物纯度门)
|
|
@@ -170,6 +177,7 @@ window.__ModuleLoader__.load({
|
|
|
170
177
|
"check-expired": "上次查新版的凭证过期了:重新查一次再点安装(不要重试旧编号)。",
|
|
171
178
|
"update-busy": "同一个使用范围同时只装一个:等当前任务结束再点。",
|
|
172
179
|
"install-failed": "安装没跑成:按下面那条手工命令在终端执行一次。",
|
|
180
|
+
"manager-unreachable": "总管的电话没接上(宿主半没注册或版本对不上):重启宿主后再试;仍不行看宿主日志。",
|
|
173
181
|
"bad-request": "回执异常(请求形状不对):把宿主日志里的事件名报给作者。",
|
|
174
182
|
internal: "回执异常(总管内部错误):把宿主日志里的事件名报给作者。"
|
|
175
183
|
};
|
|
@@ -223,7 +231,7 @@ window.__ModuleLoader__.load({
|
|
|
223
231
|
payload
|
|
224
232
|
});
|
|
225
233
|
} catch (error) {
|
|
226
|
-
return failure("
|
|
234
|
+
return failure("manager-unreachable", { detail: String(error?.message ?? error) });
|
|
227
235
|
}
|
|
228
236
|
if (!result || typeof result !== "object" || typeof result.ok !== "boolean") return failure("internal", { detail: "回执信封异常(非 ok 信封)" });
|
|
229
237
|
if (result.ok) return {
|
|
@@ -307,6 +315,13 @@ window.__ModuleLoader__.load({
|
|
|
307
315
|
function isAbsent(target) {
|
|
308
316
|
return target.runningVersion === null && target.installedVersion === null;
|
|
309
317
|
}
|
|
318
|
+
/** 一家页签此刻的状态:账本里已注册 → `connected`(面板显示它自己的设置页),否则按宿主装机读数分三态。 */
|
|
319
|
+
function slotStateOf(target, inLedger) {
|
|
320
|
+
if (inLedger) return "connected";
|
|
321
|
+
if (target === null || isAbsent(target)) return "absent";
|
|
322
|
+
if (target.panelRegistered === false) return "unregistered-product";
|
|
323
|
+
return "not-connected";
|
|
324
|
+
}
|
|
310
325
|
/** 把快照翻成一行结论:还没查 / 已是最新 / 有新版 / 装不了(附人话原因)。 */
|
|
311
326
|
function verdictOf(target, snapshot) {
|
|
312
327
|
const absent = isAbsent(target);
|
|
@@ -563,20 +578,23 @@ window.__ModuleLoader__.load({
|
|
|
563
578
|
const [loadError, setLoadError] = react.useState(null);
|
|
564
579
|
const [checking, setChecking] = react.useState(false);
|
|
565
580
|
const [open, setOpen] = react.useState(false);
|
|
566
|
-
react.
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
};
|
|
581
|
+
const alive = react.useRef(true);
|
|
582
|
+
react.useEffect(() => () => {
|
|
583
|
+
alive.current = false;
|
|
584
|
+
}, []);
|
|
585
|
+
/** 重取七个目标的表(装机读数变了就重取:缺席卡的态是拿这张表算的,缓存住它会继续说旧话)。 */
|
|
586
|
+
const reload = react.useCallback(async () => {
|
|
587
|
+
const loaded = await loadTargets(getCall());
|
|
588
|
+
if (!alive.current) return;
|
|
589
|
+
if (loaded.ok) {
|
|
590
|
+
setTargets(loaded.value.targets);
|
|
591
|
+
setPollMs(loaded.value.pollMs);
|
|
592
|
+
setLoadError(null);
|
|
593
|
+
} else setLoadError(loaded.message);
|
|
579
594
|
}, [getCall]);
|
|
595
|
+
react.useEffect(() => {
|
|
596
|
+
reload();
|
|
597
|
+
}, [reload]);
|
|
580
598
|
const patch = react.useCallback((key, next) => {
|
|
581
599
|
setRows((previous) => ({
|
|
582
600
|
...previous,
|
|
@@ -657,6 +675,7 @@ window.__ModuleLoader__.load({
|
|
|
657
675
|
outcome: null,
|
|
658
676
|
failure: refreshed
|
|
659
677
|
});
|
|
678
|
+
await reload();
|
|
660
679
|
return;
|
|
661
680
|
}
|
|
662
681
|
patch(target.key, {
|
|
@@ -672,6 +691,7 @@ window.__ModuleLoader__.load({
|
|
|
672
691
|
getCall,
|
|
673
692
|
patch,
|
|
674
693
|
pollMs,
|
|
694
|
+
reload,
|
|
675
695
|
rows
|
|
676
696
|
])
|
|
677
697
|
};
|
|
@@ -770,15 +790,38 @@ window.__ModuleLoader__.load({
|
|
|
770
790
|
"aria-label": "关闭"
|
|
771
791
|
}, "×")), body, react.createElement("div", { style: PANEL_STYLE.dialogNote }, "装与更新都在宿主后台跑:关掉这块不影响正在进行的安装,再点「检查更新」可以看到最新状态。"))) : null);
|
|
772
792
|
}
|
|
773
|
-
/**
|
|
793
|
+
/** 缺席卡:**按两处独立事实分三态**,每态只给能改变状态的动作。
|
|
794
|
+
*
|
|
795
|
+
* - `absent`(磁盘上没有这个包)→「未安装」+「装上」按钮(本票原有的能力);
|
|
796
|
+
* - `unregistered-product`(装着,但产物里没有页签槽注册代码)→ 说实情、**不给按钮**:
|
|
797
|
+
* 重装改不了产物里有没有注册代码(真机实测:作息/居家/大厨/记账 的已发布 0.2.0
|
|
798
|
+
* 产物里根本没有注册代码,见票 #723);给可复制命令只是参考;
|
|
799
|
+
* - `not-connected`(装着、产物也有注册代码,设置页却没送到浏览器)→ 重启宿主;仍不行看日志;
|
|
800
|
+
* - `connected` 不归本卡管:那时面板显示的是那家自己的设置页。
|
|
801
|
+
*
|
|
802
|
+
* 原实现只看页签槽 ledger 就断言「未安装」——那对后两种情形是说假话,且点「装上」
|
|
803
|
+
* 会走进更新包的流程(它已经装着 ⇒ 没有新版 ⇒ 没有凭证)并报出误导的「查新版失败」。
|
|
804
|
+
*/
|
|
774
805
|
function AbsentCard(props) {
|
|
775
806
|
const target = props.target;
|
|
776
807
|
const row = target ? props.face.rows[target.key] : null;
|
|
777
808
|
const busy = row?.phase === "installing";
|
|
778
809
|
const manual = (target ? manualForDisplay(target, row?.failure?.manual ?? row?.outcome?.manual ?? null) : null) ?? props.fallbackCommand;
|
|
810
|
+
const installed = target ? target.installedVersion ?? target.runningVersion : null;
|
|
811
|
+
const state = slotStateOf(target, props.inLedger);
|
|
779
812
|
let reason = row?.failure?.message ?? null;
|
|
780
813
|
if (target && reason === null && row?.outcome && row.outcome.snapshot.blockedReason !== null) reason = verdictOf(target, row.outcome.snapshot).text;
|
|
781
|
-
|
|
814
|
+
let headline;
|
|
815
|
+
switch (state) {
|
|
816
|
+
case "absent":
|
|
817
|
+
headline = target ? "未安装[" + target.packageName + "],装上后这个页签就能用了。" : "这个页签对应的插件还没装。";
|
|
818
|
+
break;
|
|
819
|
+
case "unregistered-product":
|
|
820
|
+
headline = target.packageName + " " + String(installed) + " 装着,但它这一版的产物里没有爱生活页签的注册代码:得等它发一个带注册代码的新版才对得上。重启与重装都不会变(重装只是把同一版再装一次)。";
|
|
821
|
+
break;
|
|
822
|
+
default: headline = target.packageName + " " + String(installed) + " 装着、产物里也有注册代码,但它的设置页没送到浏览器:重启宿主后再看这个页签;仍然这样就去宿主日志里找它的报错。";
|
|
823
|
+
}
|
|
824
|
+
return react.createElement("div", { style: PANEL_STYLE.reco }, react.createElement("div", null, headline), state === "absent" ? react.createElement("button", {
|
|
782
825
|
type: "button",
|
|
783
826
|
style: busy ? {
|
|
784
827
|
...PANEL_STYLE.btn,
|
|
@@ -789,7 +832,9 @@ window.__ModuleLoader__.load({
|
|
|
789
832
|
marginTop: 8
|
|
790
833
|
},
|
|
791
834
|
disabled: busy,
|
|
792
|
-
onClick: () =>
|
|
835
|
+
onClick: () => {
|
|
836
|
+
if (target) props.face.act(target);
|
|
837
|
+
}
|
|
793
838
|
}, busy ? "正在装,请稍候…" : "装上") : null, reason ? react.createElement("div", { style: PANEL_STYLE.reason }, reason) : null, react.createElement("code", { style: PANEL_STYLE.cmd }, manual));
|
|
794
839
|
}
|
|
795
840
|
//#endregion
|
|
@@ -978,8 +1023,10 @@ window.__ModuleLoader__.load({
|
|
|
978
1023
|
green: "var(--dsw-alias-state-success-primary, #4ec9a0)"
|
|
979
1024
|
};
|
|
980
1025
|
/** 整行底色的浅一档:饱和降一档,免得红黄行「刺眼」压过正文(评审第二轮的扣分点)。 */
|
|
1026
|
+
/** 红黄两档整行上底的底色。红灯要**明显重于**黄灯:复评实测原先两个底色亮度几乎一样
|
|
1027
|
+
* (红 `#FBF3F3` 对黄 `#FCF3E5`,只差蓝通道),最严重那一档反而最淡——红色提到 0.14。 */
|
|
981
1028
|
const STATUS_TINT = {
|
|
982
|
-
red: "rgba(192, 57, 43, 0.
|
|
1029
|
+
red: "rgba(192, 57, 43, 0.14)",
|
|
983
1030
|
yellow: "rgba(224, 138, 0, 0.10)",
|
|
984
1031
|
green: "transparent"
|
|
985
1032
|
};
|
|
@@ -994,6 +1041,12 @@ window.__ModuleLoader__.load({
|
|
|
994
1041
|
yellow: "黄",
|
|
995
1042
|
green: "绿"
|
|
996
1043
|
};
|
|
1044
|
+
/** 档位的**形状**标记:颜色之外的第二条读数(灰度截图、色弱视角下也分得开谁要处理)。 */
|
|
1045
|
+
const STATUS_MARK = {
|
|
1046
|
+
red: "✕",
|
|
1047
|
+
yellow: "!",
|
|
1048
|
+
green: "✓"
|
|
1049
|
+
};
|
|
997
1050
|
/** 面板上的文字色与分隔线(与 `client.ts` 同一套别名)。 */
|
|
998
1051
|
const INK = "var(--dsw-alias-label-primary, inherit)";
|
|
999
1052
|
const INK_DIM = "var(--dsw-alias-label-secondary, #9a9a9a)";
|
|
@@ -1024,8 +1077,9 @@ window.__ModuleLoader__.load({
|
|
|
1024
1077
|
background: "transparent",
|
|
1025
1078
|
color: INK,
|
|
1026
1079
|
borderRadius: 8,
|
|
1027
|
-
padding: "
|
|
1080
|
+
padding: "6px 16px",
|
|
1028
1081
|
fontSize: 13,
|
|
1082
|
+
fontWeight: 600,
|
|
1029
1083
|
cursor: "pointer"
|
|
1030
1084
|
},
|
|
1031
1085
|
lightsRow: {
|
|
@@ -1072,15 +1126,73 @@ window.__ModuleLoader__.load({
|
|
|
1072
1126
|
const trimmed = action.trim();
|
|
1073
1127
|
return trimmed.startsWith("去哪修:") ? trimmed.slice(4).trim() : trimmed;
|
|
1074
1128
|
}
|
|
1129
|
+
/** 报文里能扫出来的每一段路径(盘符绝对路径;非 Windows 形状的退回 `/` 打头那一支)。 */
|
|
1130
|
+
function rawPathsIn(text) {
|
|
1131
|
+
return text.match(/[A-Za-z]:\/[^\s()「」,。;]+/g) ?? text.match(/\/[^\s()「」,。;]+/g) ?? [];
|
|
1132
|
+
}
|
|
1133
|
+
/** 一段落点的**根**(盘符):`C:/a/b` → `C:/`。用来把「同一个盘、但根下不同枝」的落点分开算,
|
|
1134
|
+
* 免得一个 D 盘上的旁证(比如「配了但目录不在:D:/media」)把 C 盘那几条的缩写全拖没。 */
|
|
1135
|
+
function rootOf(path) {
|
|
1136
|
+
const drive = /^[A-Za-z]:\//.exec(path);
|
|
1137
|
+
return drive === null ? "/" : drive[0];
|
|
1138
|
+
}
|
|
1139
|
+
/** 一段落点的公共目录前缀(按 `/` 切、整段比):`C:/a/b/c` + `C:/a/b/d` → `C:/a/b`。 */
|
|
1140
|
+
function commonDirOf(paths) {
|
|
1141
|
+
if (paths.length < 2) return "";
|
|
1142
|
+
const split = paths.map((path) => path.replace(/[.,。;]+$/, "").split("/"));
|
|
1143
|
+
const first = split[0];
|
|
1144
|
+
let size = 0;
|
|
1145
|
+
while (size < first.length && split.every((parts) => parts[size] === first[size])) size += 1;
|
|
1146
|
+
return size < 1 ? "" : first.slice(0, size).join("/");
|
|
1147
|
+
}
|
|
1148
|
+
/** 一份报文里**按根分开**的落点前缀(长的在前):只给「同一个根下至少两条落点」的根算前缀——
|
|
1149
|
+
* 这个根下只有一条落点时不缩(没有第二条可比,缩出来反而是瞎猜)。
|
|
1150
|
+
* 为什么要分根:一家报里常有别的盘的旁证(备忘录用 D 盘上那份媒体目录),只算一个总前缀,
|
|
1151
|
+
* 一条旁证就能把整张表的缩写全拖没(#706 出图复评:备忘录那张表整片没缩、记账那张缩了)。 */
|
|
1152
|
+
function dirPrefixesOf(report) {
|
|
1153
|
+
return dirPrefixesIn(pathsOf(report).join(" "));
|
|
1154
|
+
}
|
|
1155
|
+
/** `dirPrefixesOf` 的正文版:一段文字里(报文正文 + 表头两条混在一起)按根分组取公共目录前缀。 */
|
|
1156
|
+
function dirPrefixesIn(text) {
|
|
1157
|
+
const paths = rawPathsIn(text);
|
|
1158
|
+
const roots = [...new Set(paths.map(rootOf))];
|
|
1159
|
+
const prefixes = [];
|
|
1160
|
+
for (const root of roots) {
|
|
1161
|
+
const prefix = commonDirOf(paths.filter((path) => rootOf(path) === root));
|
|
1162
|
+
if (prefix !== "") prefixes.push(prefix);
|
|
1163
|
+
}
|
|
1164
|
+
return prefixes.sort((a, b) => b.length - a.length);
|
|
1165
|
+
}
|
|
1166
|
+
/** 一家里所有会印出来的路径。 */
|
|
1167
|
+
function pathsOf(report) {
|
|
1168
|
+
const out = [report.configPath, report.dataDir];
|
|
1169
|
+
for (const item of report.items) out.push(item.message);
|
|
1170
|
+
return out;
|
|
1171
|
+
}
|
|
1172
|
+
/** 把报文里那一长串路径按公共前缀缩短(只动打印,不动事实)。
|
|
1173
|
+
* 只按**扫得出来的那一段段落点**去缩,不做全串替换:既不误伤长路径,也不会把一段路劈成半截。 */
|
|
1174
|
+
function shortenPathsIn(text, prefixes) {
|
|
1175
|
+
const candidates = rawPathsIn(text);
|
|
1176
|
+
if (candidates.length === 0) return text;
|
|
1177
|
+
const longestFirst = [...new Set(candidates.map((path) => path.replace(/[.,。;]+$/, "")))].sort((a, b) => b.length - a.length);
|
|
1178
|
+
let out = text;
|
|
1179
|
+
for (const path of longestFirst) {
|
|
1180
|
+
const hit = prefixes.find((prefix) => path.startsWith(prefix + "/"));
|
|
1181
|
+
if (hit === void 0) continue;
|
|
1182
|
+
out = out.split(hit + "/").join("…/");
|
|
1183
|
+
}
|
|
1184
|
+
return out;
|
|
1185
|
+
}
|
|
1075
1186
|
/** 从六份报告数出那一行的灯(每家按最严重那一档上色)。 */
|
|
1076
1187
|
function lightsOf(tabs, reports) {
|
|
1077
1188
|
return tabs.map((tab) => {
|
|
1078
1189
|
const report = reports[tab.id];
|
|
1190
|
+
const items = report?.items ?? [];
|
|
1079
1191
|
return {
|
|
1080
1192
|
id: tab.id,
|
|
1081
1193
|
title: tab.title,
|
|
1082
|
-
status: report ? worstStatus(
|
|
1083
|
-
counts: countByStatus(
|
|
1194
|
+
status: report ? worstStatus(items) : null,
|
|
1195
|
+
counts: countByStatus(items)
|
|
1084
1196
|
};
|
|
1085
1197
|
});
|
|
1086
1198
|
}
|
|
@@ -1092,6 +1204,42 @@ window.__ModuleLoader__.load({
|
|
|
1092
1204
|
if (parts.length === 0) return "绿";
|
|
1093
1205
|
return parts.join(" · ");
|
|
1094
1206
|
}
|
|
1207
|
+
/** 「红 2 · 黄 3」那一截**按档上色**:每一档用它自己的字色、分隔符走淡色。
|
|
1208
|
+
* 为什么必须拆开:原先整截塞进一个 span、染「最严重那一档」的颜色,六家都红时「黄 3」就跟着印成红字——
|
|
1209
|
+
* 本页自己说「黄=还没配」,文字层却把它涂成红(出图复评五份里三份独立指到,取色同为 `#c03828`)。 */
|
|
1210
|
+
function countSegments(light) {
|
|
1211
|
+
if (light.status === null) return [react.createElement("span", {
|
|
1212
|
+
key: "dash",
|
|
1213
|
+
style: {
|
|
1214
|
+
color: INK_DIM,
|
|
1215
|
+
fontWeight: 700
|
|
1216
|
+
}
|
|
1217
|
+
}, "—")];
|
|
1218
|
+
const parts = [];
|
|
1219
|
+
for (const status of ["red", "yellow"]) {
|
|
1220
|
+
const count = light.counts[status];
|
|
1221
|
+
if (count === 0) continue;
|
|
1222
|
+
if (parts.length > 0) parts.push(react.createElement("span", {
|
|
1223
|
+
key: status + "-sep",
|
|
1224
|
+
style: { color: INK_DIM }
|
|
1225
|
+
}, " · "));
|
|
1226
|
+
parts.push(react.createElement("span", {
|
|
1227
|
+
key: status,
|
|
1228
|
+
style: {
|
|
1229
|
+
color: STATUS_TEXT[status],
|
|
1230
|
+
fontWeight: 700
|
|
1231
|
+
}
|
|
1232
|
+
}, STATUS_LABEL[status] + " " + String(count)));
|
|
1233
|
+
}
|
|
1234
|
+
if (parts.length === 0) return [react.createElement("span", {
|
|
1235
|
+
key: "green",
|
|
1236
|
+
style: {
|
|
1237
|
+
color: INK_DIM,
|
|
1238
|
+
fontWeight: 700
|
|
1239
|
+
}
|
|
1240
|
+
}, "绿")];
|
|
1241
|
+
return parts;
|
|
1242
|
+
}
|
|
1095
1243
|
/** 顶部那行总览:六家一盏灯 + 一个「体检一次」按钮。 */
|
|
1096
1244
|
function HealthOverview(props) {
|
|
1097
1245
|
return react.createElement("div", {
|
|
@@ -1136,10 +1284,33 @@ window.__ModuleLoader__.load({
|
|
|
1136
1284
|
} }), react.createElement("span", { style: {
|
|
1137
1285
|
color: INK,
|
|
1138
1286
|
fontWeight: 650
|
|
1139
|
-
} }, light.title), react.createElement("
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
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
|
+
]));
|
|
1143
1314
|
}
|
|
1144
1315
|
/** 正正常常的那些:绿条只留「名字」,一句话与「去哪修」都不印(票面验收第一条:正常的收成一行)。 */
|
|
1145
1316
|
function isAttentionItem(item) {
|
|
@@ -1149,13 +1320,18 @@ window.__ModuleLoader__.load({
|
|
|
1149
1320
|
* **正常的**(绿)收成一行并列的小字,点得到、扫得完(票面验收第一条:正常的收成一行,有问题的才展开)。 */
|
|
1150
1321
|
function HealthTable(props) {
|
|
1151
1322
|
const { report } = props;
|
|
1323
|
+
const prefixes = report ? dirPrefixesOf(report) : [];
|
|
1152
1324
|
return react.createElement("div", {
|
|
1153
1325
|
style: HEALTH_STYLE.box,
|
|
1154
1326
|
"data-ilife-health": "table"
|
|
1155
1327
|
}, react.createElement("div", { style: HEALTH_STYLE.headRow }, react.createElement("div", { style: HEALTH_STYLE.head }, props.title + " · 体检"), report ? react.createElement("div", { style: {
|
|
1156
1328
|
color: INK_DIM,
|
|
1157
1329
|
fontSize: 12
|
|
1158
|
-
} }, countsText(countByStatus(report.items))) : null), props.phase === "idle" ? react.createElement("div", { style: HEALTH_STYLE.meta }, "还没体检:点上面那个「体检一次」。") : null, props.phase === "running" ? react.createElement("div", { style: HEALTH_STYLE.meta }, "正在体检…") : null, props.error !== null ? react.createElement("div", { style: HEALTH_STYLE.error }, props.error) : null, report ? react.createElement("div", null,
|
|
1330
|
+
} }, countsText(countByStatus(report.items))) : null), props.phase === "idle" ? react.createElement("div", { style: HEALTH_STYLE.meta }, "还没体检:点上面那个「体检一次」。") : null, props.phase === "running" ? react.createElement("div", { style: HEALTH_STYLE.meta }, "正在体检…") : null, props.error !== null ? react.createElement("div", { style: HEALTH_STYLE.error }, props.error) : null, report ? react.createElement("div", null, report.dataDir !== "" ? react.createElement("div", { style: {
|
|
1331
|
+
color: INK_DIM,
|
|
1332
|
+
fontSize: 11.5,
|
|
1333
|
+
marginTop: 2
|
|
1334
|
+
} }, "落点根目录:" + report.dataDir + (report.configPath !== "" ? "(配置文件 " + report.configPath + ")" : "")) : null, report.items.filter(isAttentionItem).map((item) => react.createElement("div", {
|
|
1159
1335
|
key: item.id,
|
|
1160
1336
|
"data-ilife-health": "item",
|
|
1161
1337
|
"data-status": item.status,
|
|
@@ -1173,19 +1349,24 @@ window.__ModuleLoader__.load({
|
|
|
1173
1349
|
flexWrap: "wrap",
|
|
1174
1350
|
alignItems: "center",
|
|
1175
1351
|
gap: 6
|
|
1176
|
-
} }, react.createElement("span", {
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1352
|
+
} }, react.createElement("span", {
|
|
1353
|
+
style: {
|
|
1354
|
+
color: STATUS_TEXT[item.status],
|
|
1355
|
+
fontWeight: 700,
|
|
1356
|
+
fontSize: 13,
|
|
1357
|
+
lineHeight: 1
|
|
1358
|
+
},
|
|
1359
|
+
"aria-hidden": "true"
|
|
1360
|
+
}, STATUS_MARK[item.status]), react.createElement("span", { style: {
|
|
1183
1361
|
fontWeight: 650,
|
|
1184
1362
|
color: INK
|
|
1185
1363
|
} }, item.title), react.createElement("span", { style: {
|
|
1186
1364
|
color: STATUS_TEXT[item.status],
|
|
1187
1365
|
fontWeight: 700
|
|
1188
|
-
} }, STATUS_LABEL[item.status]), item.source
|
|
1366
|
+
} }, STATUS_LABEL[item.status]), item.source && item.source !== "默认值" ? react.createElement("span", { style: { color: INK_DIM } }, "· 来自" + item.source) : null), react.createElement("div", {
|
|
1367
|
+
style: { color: INK },
|
|
1368
|
+
title: item.message
|
|
1369
|
+
}, shortenPathsIn(item.message, prefixes)), item.action.length > 0 ? react.createElement("div", { style: { color: INK_DIM } }, "去哪修:" + actionText(item.action)) : null)), report.items.some((item) => !isAttentionItem(item)) ? react.createElement("div", {
|
|
1189
1370
|
"data-ilife-health": "ok",
|
|
1190
1371
|
style: {
|
|
1191
1372
|
display: "flex",
|
|
@@ -1193,16 +1374,27 @@ window.__ModuleLoader__.load({
|
|
|
1193
1374
|
alignItems: "center",
|
|
1194
1375
|
gap: "2px 10px",
|
|
1195
1376
|
marginTop: 8,
|
|
1196
|
-
|
|
1197
|
-
|
|
1377
|
+
padding: "6px 8px",
|
|
1378
|
+
borderLeft: "3px solid " + STATUS_COLOR.green,
|
|
1379
|
+
borderRadius: 6,
|
|
1198
1380
|
fontSize: 12,
|
|
1199
1381
|
lineHeight: 1.6,
|
|
1200
1382
|
color: INK_DIM
|
|
1201
1383
|
}
|
|
1202
|
-
}, react.createElement("span", {
|
|
1384
|
+
}, react.createElement("span", {
|
|
1385
|
+
style: {
|
|
1386
|
+
width: 8,
|
|
1387
|
+
height: 8,
|
|
1388
|
+
borderRadius: "50%",
|
|
1389
|
+
display: "inline-block",
|
|
1390
|
+
background: STATUS_COLOR.green,
|
|
1391
|
+
flex: "0 0 auto"
|
|
1392
|
+
},
|
|
1393
|
+
"aria-hidden": "true"
|
|
1394
|
+
}), react.createElement("span", { style: {
|
|
1203
1395
|
fontWeight: 650,
|
|
1204
1396
|
color: INK
|
|
1205
|
-
} }, "正常 " + String(countByStatus(report.items).green) + " 条"), report.items.filter((item) => !isAttentionItem(item)).map((item) => react.createElement("span", {
|
|
1397
|
+
} }, "正常 " + String(countByStatus(report.items).green) + " 条"), react.createElement("span", { style: { color: INK_DIM } }, "(下列各项都正常工作)"), report.items.filter((item) => !isAttentionItem(item)).map((item) => react.createElement("span", {
|
|
1206
1398
|
key: item.id,
|
|
1207
1399
|
"data-ilife-health": "ok-item"
|
|
1208
1400
|
}, "· " + item.title))) : null) : null);
|
|
@@ -1228,8 +1420,6 @@ window.__ModuleLoader__.load({
|
|
|
1228
1420
|
* 文案与网址都取自 nav.ts 的两张静态表,本文件只画不算。
|
|
1229
1421
|
*/
|
|
1230
1422
|
const inject = ["slots", "connection"];
|
|
1231
|
-
/** 爱生活页签槽(总管声明的 children,技能设置页注册进来;单段名,避开官方 settings.* 前缀)。 */
|
|
1232
|
-
const CONFIG_TAB_SLOT = "ilife.config-tab";
|
|
1233
1423
|
/** 总管视觉(内联 style;颜色走 DSH 主题别名,深浅主题自适应,写死值只做回退;与技能面板同语言)。 */
|
|
1234
1424
|
const S = {
|
|
1235
1425
|
headRow: {
|
|
@@ -1463,7 +1653,7 @@ window.__ModuleLoader__.load({
|
|
|
1463
1653
|
setActiveId(next.plugin);
|
|
1464
1654
|
tabRefs.current[nextIndex]?.focus();
|
|
1465
1655
|
}
|
|
1466
|
-
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.
|
|
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(HealthOverview, {
|
|
1467
1657
|
lights: lightsOf(rows.map((row) => ({
|
|
1468
1658
|
id: row.id,
|
|
1469
1659
|
title: labelForTab(row.id)
|
|
@@ -1516,6 +1706,7 @@ window.__ModuleLoader__.load({
|
|
|
1516
1706
|
})) : react.createElement(AbsentCard, {
|
|
1517
1707
|
target: targetForTab(tab),
|
|
1518
1708
|
fallbackCommand: recoFor(tab).installCmd,
|
|
1709
|
+
inLedger: present.has(tab.plugin),
|
|
1519
1710
|
face
|
|
1520
1711
|
}));
|
|
1521
1712
|
}), react.createElement(MorePluginsCard, null));
|