@lmzhen/dsh-evolution-settings-ui 0.27.1 → 0.27.2

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/README.md CHANGED
@@ -63,6 +63,13 @@ escaping as an unhandled rejection. `settle.ts` holds that one verdict function
63
63
  uses; an unavailable preview prints the host's reason. A late answer for a row the reader already left is
64
64
  dropped rather than painted under another row's heading.
65
65
 
66
+ ## The diagnostics card
67
+
68
+ One card in the same section is the read-only **self-check** (`data-card="diagnostics"`): it renders the Host's one `DiagnosticsView` — the same object the CLI and `/api/dsh-evolution/diagnostics` answer — and owns no key list and no status vocabulary.
69
+
70
+ It leads with a plain-language verdict derived from the frozen runtime statuses (`运行正常` / `{n} 项需要注意` / `{n} 项异常` / `{n} 项待确认`, decided fail > warn > unknown > ok), then groups the view as **环境信息** (fact label above value) and **运行状态** (status dot and word, detail line, remedy line). The raw material — view schema, check time and each fact's source home — sits behind one **技术详情** disclosure, so the default reading path carries no identifiers.
71
+
72
+ The card is collapsible with the same `.evolution-param-head` button and body inset as the parameter cards; Refresh is an explicit control in the body (one read on mount, one per click), and a failed read renders the shared failure block with the `/evolution doctor` command fallback.
66
73
  ## A seat that arrives late revives the card
67
74
 
68
75
  The seat is probed lazily (never captured at apply time), but probing alone only answers the NEXT read —
package/lib/client.js CHANGED
@@ -1073,8 +1073,11 @@ window.__ModuleLoader__.load({
1073
1073
  * `/api/dsh-evolution/diagnostics` route answer.
1074
1074
  *
1075
1075
  * Read-only by construction: it fetches once on mount and only refetches on an explicit Refresh; a
1076
- * failed read renders the shared FailureBlock with the command-face fallback. Facts and runtime rows
1077
- * are rendered in the view's own order — the client owns no key list and no status vocabulary.
1076
+ * failed read renders the shared FailureBlock with the command-face fallback. The client still owns
1077
+ * no key list and no status vocabulary: it renders the host view's own order, groups the two
1078
+ * sections the view carries (environment facts / runtime rows) and gives a plain-language summary
1079
+ * from the frozen status set. Raw identifiers, source paths and the view schema live behind the
1080
+ * one `技术详情` disclosure, not in the main reading path.
1078
1081
  * @module @lmzhen/dsh-evolution-settings-ui/client/DiagnosticsCard
1079
1082
  */
1080
1083
  /** Which word one status gets (the frozen W1 status set). */
@@ -1084,6 +1087,41 @@ window.__ModuleLoader__.load({
1084
1087
  fail: "diagStatusFail",
1085
1088
  unknown: "diagStatusUnknown"
1086
1089
  });
1090
+ /** The summary word for a badge status (the same frozen set; ok has no count). */
1091
+ const SUMMARY_KEYS = Object.freeze({
1092
+ ok: "diagSummaryOk",
1093
+ warn: "diagSummaryWarn",
1094
+ fail: "diagSummaryFail",
1095
+ unknown: "diagSummaryUnknown"
1096
+ });
1097
+ /** The one status the card leads with: fail > warn > unknown > ok. */
1098
+ function summaryOf(rows) {
1099
+ const count = (status) => rows.filter((row) => row.status === status).length;
1100
+ for (const status of [
1101
+ "fail",
1102
+ "warn",
1103
+ "unknown"
1104
+ ]) {
1105
+ const n = count(status);
1106
+ if (n > 0) return {
1107
+ status,
1108
+ count: n
1109
+ };
1110
+ }
1111
+ return {
1112
+ status: "ok",
1113
+ count: 0
1114
+ };
1115
+ }
1116
+ /** A locally formatted check time; an unparseable stamp stays visible instead of becoming NaN. */
1117
+ function checkedAt(iso) {
1118
+ try {
1119
+ const at = new Date(iso);
1120
+ return Number.isNaN(at.getTime()) ? iso : at.toLocaleString();
1121
+ } catch {
1122
+ return iso;
1123
+ }
1124
+ }
1087
1125
  /**
1088
1126
  * Render the card.
1089
1127
  * @param props - the copy seat, the host call and the command fallback.
@@ -1093,6 +1131,7 @@ window.__ModuleLoader__.load({
1093
1131
  const { t, api } = props;
1094
1132
  const translate = (key, values) => t(key, values);
1095
1133
  const [state, setState] = (0, react.useState)({ phase: "loading" });
1134
+ const [open, setOpen] = (0, react.useState)(true);
1096
1135
  const ticket = (0, react.useRef)(0);
1097
1136
  const read = (keepView) => {
1098
1137
  const current = ticket.current + 1;
@@ -1115,12 +1154,33 @@ window.__ModuleLoader__.load({
1115
1154
  const refresh = () => {
1116
1155
  read(true);
1117
1156
  };
1118
- const header = (0, react.createElement)("div", { className: "evolution-param-card-head" }, (0, react.createElement)("span", { className: "evolution-param-card-title" }, t("diagTitle")), (0, react.createElement)("button", {
1157
+ const summary = state.phase === "ready" ? summaryOf(state.view.runtime) : void 0;
1158
+ const header = (0, react.createElement)("button", {
1119
1159
  type: "button",
1120
- className: "evolution-param-button",
1121
- onClick: refresh,
1122
- "data-action": "diag-refresh"
1123
- }, state.phase === "loading" ? t("diagRefreshing") : t("diagRefresh")));
1160
+ className: "evolution-param-head",
1161
+ "aria-expanded": open ? "true" : "false",
1162
+ onClick: () => {
1163
+ setOpen(!open);
1164
+ }
1165
+ }, (0, react.createElement)("span", { className: "evolution-param-card-title" }, t("diagTitle")), summary === void 0 ? null : (0, react.createElement)("span", {
1166
+ className: "evo-diag-badge",
1167
+ "data-status": summary.status
1168
+ }, t(SUMMARY_KEYS[summary.status], summary.count === 0 ? void 0 : { n: summary.count })), (0, react.createElement)("span", { className: "evolution-param-chevron" }, open ? "▲" : "▼"));
1169
+ const facts = (view) => view.facts.length === 0 ? null : (0, react.createElement)("div", {}, (0, react.createElement)("h3", { className: "evolution-param-group" }, t("diagFactsTitle")), (0, react.createElement)("div", { className: "evo-diag-list" }, ...view.facts.map((fact) => (0, react.createElement)("div", {
1170
+ key: "fact-" + fact.key,
1171
+ className: "evo-diag-item",
1172
+ "data-kind": "fact"
1173
+ }, (0, react.createElement)("span", { className: "evo-diag-label" }, fact.label), (0, react.createElement)("span", { className: "evo-diag-value" }, fact.value)))));
1174
+ const runtime = (view) => view.runtime.length === 0 ? null : (0, react.createElement)("div", {}, (0, react.createElement)("h3", { className: "evolution-param-group" }, t("diagRuntimeTitle")), (0, react.createElement)("div", { className: "evo-diag-list" }, ...view.runtime.map((row) => (0, react.createElement)("div", {
1175
+ key: "runtime-" + row.key,
1176
+ className: "evo-diag-item",
1177
+ "data-kind": "runtime",
1178
+ "data-status": row.status
1179
+ }, (0, react.createElement)("div", { className: "evo-diag-row-head" }, (0, react.createElement)("span", { className: "evo-diag-dot" }), (0, react.createElement)("span", { className: "evo-diag-label" }, row.label), (0, react.createElement)("span", { className: "evo-diag-status" }, t(STATUS_KEYS[row.status]))), row.detail === void 0 ? null : (0, react.createElement)("span", { className: "evo-diag-detail" }, row.detail), row.remedy === void 0 ? null : (0, react.createElement)("span", { className: "evo-diag-remedy" }, t("diagRemedy") + row.remedy)))));
1180
+ const details = (view) => (0, react.createElement)("details", { className: "evo-diag-meta" }, (0, react.createElement)("summary", {}, t("diagDetails")), (0, react.createElement)("div", { className: "evo-diag-meta-body" }, (0, react.createElement)("span", {}, t("diagCheckedAt", { at: checkedAt(view.generatedAt) })), (0, react.createElement)("span", {}, t("diagSchema", { n: view.schemaVersion })), view.facts.length === 0 ? null : (0, react.createElement)("span", { className: "evo-diag-sources-title" }, t("diagSourcesTitle")), ...view.facts.map((fact) => (0, react.createElement)("span", {
1181
+ key: "source-" + fact.key,
1182
+ className: "evo-diag-source"
1183
+ }, (0, react.createElement)("span", { className: "evo-diag-source-label" }, fact.label), (0, react.createElement)("span", { className: "evo-diag-source-home" }, fact.home)))));
1124
1184
  const body = () => {
1125
1185
  if (state.phase === "loading") return (0, react.createElement)("p", { className: "evolution-param-loading" }, t("stateLoading"));
1126
1186
  if (state.phase === "failed") return FailureBlock({
@@ -1129,23 +1189,17 @@ window.__ModuleLoader__.load({
1129
1189
  fallback: props.fallback
1130
1190
  });
1131
1191
  const view = state.view;
1132
- const factRows = view.facts.map((fact) => (0, react.createElement)("div", {
1133
- key: "fact-" + fact.key,
1134
- className: "evo-diag-row",
1135
- "data-kind": "fact"
1136
- }, (0, react.createElement)("span", { className: "evo-diag-key" }, fact.label), (0, react.createElement)("span", { className: "evo-diag-value" }, fact.value), (0, react.createElement)("span", { className: "evo-diag-home" }, t("diagHomeCite", { home: fact.home }))));
1137
- const runtimeRows = view.runtime.map((row) => (0, react.createElement)("div", {
1138
- key: "runtime-" + row.key,
1139
- className: "evo-diag-row",
1140
- "data-kind": "runtime",
1141
- "data-status": row.status
1142
- }, (0, react.createElement)("span", { className: "evo-diag-key" }, row.label), (0, react.createElement)("span", { className: "evo-diag-status" }, t(STATUS_KEYS[row.status])), row.detail === void 0 ? null : (0, react.createElement)("span", { className: "evo-diag-detail" }, row.detail), row.remedy === void 0 ? null : (0, react.createElement)("span", { className: "evo-diag-remedy" }, t("diagRemedy") + row.remedy)));
1143
- return (0, react.createElement)("div", { className: "evo-diag-body" }, (0, react.createElement)("p", { className: "evolution-params-subtitle" }, t("diagSubtitle")), (0, react.createElement)("p", { className: "evo-diag-meta" }, t("diagSchema", { n: view.schemaVersion }) + " · " + t("diagGeneratedAt", { at: view.generatedAt })), (0, react.createElement)("h3", { className: "evo-diag-section" }, t("diagFactsTitle")), ...factRows, (0, react.createElement)("h3", { className: "evo-diag-section" }, t("diagRuntimeTitle")), ...runtimeRows);
1192
+ return (0, react.createElement)("div", {}, (0, react.createElement)("p", { className: "evolution-param-note" }, t("diagSubtitle")), (0, react.createElement)("div", { className: "evolution-param-actions" }, (0, react.createElement)("button", {
1193
+ type: "button",
1194
+ className: "evolution-param-button",
1195
+ onClick: refresh,
1196
+ "data-action": "diag-refresh"
1197
+ }, t("diagRefresh"))), facts(view), runtime(view), details(view));
1144
1198
  };
1145
- return (0, react.createElement)("div", {
1199
+ return (0, react.createElement)("section", {
1146
1200
  className: "evolution-param-card",
1147
1201
  "data-card": "diagnostics"
1148
- }, header, body());
1202
+ }, header, open ? (0, react.createElement)("div", { className: "evolution-param-body" }, body()) : null);
1149
1203
  }
1150
1204
  //#endregion
1151
1205
  //#region src/client/diagnostics-state.ts
@@ -1256,19 +1310,24 @@ window.__ModuleLoader__.load({
1256
1310
  approvalDiffRemoved: "{n} 行删除",
1257
1311
  approvalDiffCut: "改动区域已截断",
1258
1312
  diagTitle: "诊断",
1259
- diagSubtitle: "安装形态、平台线与运行面的当前读数;只读,不改任何东西。",
1260
- diagFactsTitle: "事实",
1261
- diagRuntimeTitle: "运行时",
1262
- diagSchema: "视图 schema v{n}",
1263
- diagGeneratedAt: "生成于 {at}",
1313
+ diagSubtitle: "安装、兼容与运行状态的只读自查;不会改动任何东西。",
1314
+ diagFactsTitle: "环境信息",
1315
+ diagRuntimeTitle: "运行状态",
1316
+ diagSummaryOk: "运行正常",
1317
+ diagSummaryWarn: "{n} 项需要注意",
1318
+ diagSummaryFail: "{n} 项异常",
1319
+ diagSummaryUnknown: "{n} 项待确认",
1264
1320
  diagRefresh: "刷新",
1265
1321
  diagRefreshing: "刷新中…",
1266
1322
  diagStatusOk: "正常",
1267
1323
  diagStatusWarn: "注意",
1268
1324
  diagStatusFail: "异常",
1269
1325
  diagStatusUnknown: "未知",
1270
- diagRemedy: "下一步:",
1271
- diagHomeCite: "出处:{home}",
1326
+ diagRemedy: "建议:",
1327
+ diagDetails: "技术详情",
1328
+ diagCheckedAt: "检查时间:{at}",
1329
+ diagSchema: "数据版本 v{n}",
1330
+ diagSourcesTitle: "数据来源",
1272
1331
  stateLoading: "正在读取…",
1273
1332
  failureUnauthorized: "需要登录:重新打开这个页面完成登录后再试。",
1274
1333
  failureForbidden: "平台拒绝了这个请求(身份不是 owner)。",
@@ -1338,19 +1397,24 @@ window.__ModuleLoader__.load({
1338
1397
  approvalDiffRemoved: "{n} lines removed",
1339
1398
  approvalDiffCut: "the changed region is truncated",
1340
1399
  diagTitle: "Diagnostics",
1341
- diagSubtitle: "Current readings for the install form, platform line and runtime surfaces; read-only.",
1342
- diagFactsTitle: "Facts",
1343
- diagRuntimeTitle: "Runtime",
1344
- diagSchema: "view schema v{n}",
1345
- diagGeneratedAt: "generated at {at}",
1400
+ diagSubtitle: "A read-only check of the install, compatibility and runtime state; nothing here changes anything.",
1401
+ diagFactsTitle: "Environment",
1402
+ diagRuntimeTitle: "Runtime status",
1403
+ diagSummaryOk: "All healthy",
1404
+ diagSummaryWarn: "{n} need attention",
1405
+ diagSummaryFail: "{n} failing",
1406
+ diagSummaryUnknown: "{n} unconfirmed",
1346
1407
  diagRefresh: "Refresh",
1347
1408
  diagRefreshing: "refreshing…",
1348
- diagStatusOk: "ok",
1349
- diagStatusWarn: "warn",
1350
- diagStatusFail: "fail",
1409
+ diagStatusOk: "healthy",
1410
+ diagStatusWarn: "needs attention",
1411
+ diagStatusFail: "failing",
1351
1412
  diagStatusUnknown: "unknown",
1352
- diagRemedy: "Next: ",
1353
- diagHomeCite: "source: {home}",
1413
+ diagRemedy: "Suggested: ",
1414
+ diagDetails: "Technical details",
1415
+ diagCheckedAt: "Checked: {at}",
1416
+ diagSchema: "data version v{n}",
1417
+ diagSourcesTitle: "Data sources",
1354
1418
  stateLoading: "reading…",
1355
1419
  failureUnauthorized: "Sign-in is required: reopen this page and sign in, then retry.",
1356
1420
  failureForbidden: "The platform refused this request (not the owner principal).",
@@ -2119,7 +2183,34 @@ window.__ModuleLoader__.load({
2119
2183
  ".evolution-param-fact+.evolution-param-fact::before{content:\"·\";margin:0 var(--evo-space-6);color:var(--dsw-alias-label-tertiary)}",
2120
2184
  ".evolution-param-diff{margin:0;padding:var(--evo-space-12) var(--evo-space-16);max-height:var(--evo-cap-block);overflow:auto;border:var(--evo-hairline-width) solid var(--dsw-alias-border-l1);border-radius:var(--evo-radius-surface);background:var(--dsw-alias-markdown-code-block);color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block-small);white-space:pre-wrap}",
2121
2185
  ".evolution-param-line-add{color:var(--evo-tone-success)}",
2122
- ".evolution-param-line-del{color:var(--evo-tone-danger)}"
2186
+ ".evolution-param-line-del{color:var(--evo-tone-danger)}",
2187
+ ".evolution-param-loading{margin:var(--evo-space-12) 0 0;color:var(--dsw-alias-label-tertiary);font-size:var(--evo-type-xs);line-height:var(--evo-leading-base)}",
2188
+ ".evo-diag-badge{display:inline-flex;align-items:center;height:var(--evo-space-20);padding:0 var(--evo-space-8);border-radius:var(--evo-radius-chip);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary);font-size:var(--evo-type-xs);line-height:var(--evo-space-20);flex:none}",
2189
+ ".evo-diag-badge[data-status=\"ok\"]{color:var(--dsw-alias-state-success-primary)}",
2190
+ ".evo-diag-badge[data-status=\"warn\"]{color:var(--dsw-alias-state-warn-primary)}",
2191
+ ".evo-diag-badge[data-status=\"fail\"]{color:var(--dsw-alias-state-error-primary)}",
2192
+ ".evo-diag-badge[data-status=\"unknown\"]{color:var(--dsw-alias-state-idle-primary)}",
2193
+ ".evo-diag-list{display:flex;flex-direction:column}",
2194
+ ".evo-diag-item{padding:var(--evo-space-12) 0}",
2195
+ ".evo-diag-item+.evo-diag-item{border-top:var(--evo-hairline-width) solid var(--dsw-alias-border-l2)}",
2196
+ ".evo-diag-label{display:block;color:var(--dsw-alias-label-tertiary);font-size:var(--evo-type-xs);line-height:var(--evo-leading-base)}",
2197
+ ".evo-diag-value{display:block;margin:var(--evo-space-2) 0 0;color:var(--dsw-alias-label-primary);font-size:var(--evo-type-sm);line-height:var(--evo-leading-base);overflow-wrap:anywhere}",
2198
+ ".evo-diag-row-head{display:flex;align-items:baseline;gap:var(--evo-space-6);flex-wrap:wrap}",
2199
+ ".evo-diag-dot{width:var(--evo-space-6);height:var(--evo-space-6);border-radius:var(--evo-radius-pill);background:var(--evo-diag-tone,var(--dsw-alias-state-idle-primary));flex:none}",
2200
+ ".evo-diag-status{color:var(--evo-diag-tone,var(--dsw-alias-state-idle-primary));font-size:var(--evo-type-xs);line-height:var(--evo-leading-base)}",
2201
+ ".evo-diag-item[data-status=\"ok\"]{--evo-diag-tone:var(--dsw-alias-state-success-primary)}",
2202
+ ".evo-diag-item[data-status=\"warn\"]{--evo-diag-tone:var(--dsw-alias-state-warn-primary)}",
2203
+ ".evo-diag-item[data-status=\"fail\"]{--evo-diag-tone:var(--dsw-alias-state-error-primary)}",
2204
+ ".evo-diag-item[data-status=\"unknown\"]{--evo-diag-tone:var(--dsw-alias-state-idle-primary)}",
2205
+ ".evo-diag-detail{display:block;margin:var(--evo-space-2) 0 0;color:var(--dsw-alias-label-secondary);font-size:var(--evo-type-xs);line-height:var(--evo-leading-base);overflow-wrap:anywhere}",
2206
+ ".evo-diag-remedy{display:block;margin:var(--evo-space-4) 0 0;color:var(--dsw-alias-label-tertiary);font-size:var(--evo-type-xs);line-height:var(--evo-leading-base)}",
2207
+ ".evo-diag-meta{margin:var(--evo-space-12) 0 0;padding-top:var(--evo-space-8);border-top:var(--evo-hairline-width) solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);font-size:var(--evo-type-xs);line-height:var(--evo-leading-base)}",
2208
+ ".evo-diag-meta>summary{cursor:pointer;user-select:none;padding:var(--evo-space-4) 0}",
2209
+ ".evo-diag-meta-body{display:flex;flex-direction:column;gap:var(--evo-space-2);padding:var(--evo-space-4) 0 0}",
2210
+ ".evo-diag-sources-title{padding-top:var(--evo-space-6)}",
2211
+ ".evo-diag-source{display:flex;justify-content:space-between;gap:var(--evo-space-8);overflow-wrap:anywhere}",
2212
+ ".evo-diag-source-label{flex:none}",
2213
+ ".evo-diag-source-home{text-align:right;overflow-wrap:anywhere}"
2123
2214
  ].join("\n");
2124
2215
  /**
2125
2216
  * Inject the stylesheet once per document.
@@ -3,8 +3,11 @@
3
3
  * `/api/dsh-evolution/diagnostics` route answer.
4
4
  *
5
5
  * Read-only by construction: it fetches once on mount and only refetches on an explicit Refresh; a
6
- * failed read renders the shared FailureBlock with the command-face fallback. Facts and runtime rows
7
- * are rendered in the view's own order — the client owns no key list and no status vocabulary.
6
+ * failed read renders the shared FailureBlock with the command-face fallback. The client still owns
7
+ * no key list and no status vocabulary: it renders the host view's own order, groups the two
8
+ * sections the view carries (environment facts / runtime rows) and gives a plain-language summary
9
+ * from the frozen status set. Raw identifiers, source paths and the view schema live behind the
10
+ * one `技术详情` disclosure, not in the main reading path.
8
11
  * @module @lmzhen/dsh-evolution-settings-ui/client/DiagnosticsCard
9
12
  */
10
13
  import { type ReactNode } from 'react';
@@ -72,19 +72,24 @@ export declare const zh: {
72
72
  readonly approvalDiffRemoved: "{n} 行删除";
73
73
  readonly approvalDiffCut: "改动区域已截断";
74
74
  readonly diagTitle: "诊断";
75
- readonly diagSubtitle: "安装形态、平台线与运行面的当前读数;只读,不改任何东西。";
76
- readonly diagFactsTitle: "事实";
77
- readonly diagRuntimeTitle: "运行时";
78
- readonly diagSchema: "视图 schema v{n}";
79
- readonly diagGeneratedAt: "生成于 {at}";
75
+ readonly diagSubtitle: "安装、兼容与运行状态的只读自查;不会改动任何东西。";
76
+ readonly diagFactsTitle: "环境信息";
77
+ readonly diagRuntimeTitle: "运行状态";
78
+ readonly diagSummaryOk: "运行正常";
79
+ readonly diagSummaryWarn: "{n} 项需要注意";
80
+ readonly diagSummaryFail: "{n} 项异常";
81
+ readonly diagSummaryUnknown: "{n} 项待确认";
80
82
  readonly diagRefresh: "刷新";
81
83
  readonly diagRefreshing: "刷新中…";
82
84
  readonly diagStatusOk: "正常";
83
85
  readonly diagStatusWarn: "注意";
84
86
  readonly diagStatusFail: "异常";
85
87
  readonly diagStatusUnknown: "未知";
86
- readonly diagRemedy: "下一步:";
87
- readonly diagHomeCite: "出处:{home}";
88
+ readonly diagRemedy: "建议:";
89
+ readonly diagDetails: "技术详情";
90
+ readonly diagCheckedAt: "检查时间:{at}";
91
+ readonly diagSchema: "数据版本 v{n}";
92
+ readonly diagSourcesTitle: "数据来源";
88
93
  readonly stateLoading: "正在读取…";
89
94
  readonly failureUnauthorized: "需要登录:重新打开这个页面完成登录后再试。";
90
95
  readonly failureForbidden: "平台拒绝了这个请求(身份不是 owner)。";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-settings-ui",
3
3
  "description": "Web settings cards for the evolution family's parameter namespaces (community build)",
4
- "version": "0.27.1",
4
+ "version": "0.27.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },