@lmzhen/dsh-evolution-ui 0.32.6 → 0.34.0
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/lib/client.js +738 -595
- package/lib/types/panels/history/client/Workbench.d.ts +11 -24
- package/lib/types/panels/history/client/WorkbenchStore.d.ts +16 -0
- package/lib/types/panels/history/client/index.d.ts +1 -1
- package/lib/types/panels/settings/ParamCard.d.ts +4 -9
- package/lib/types/panels/settings/generated-about.d.ts +1 -1
- package/lib/types/panels/settings/messages.d.ts +6 -0
- package/lib/types/panels/settings/seam.d.ts +20 -2
- package/lib/types/panels/settings/source.d.ts +2 -1
- package/package.json +4 -4
package/lib/client.js
CHANGED
|
@@ -815,326 +815,6 @@ window.__ModuleLoader__.load({
|
|
|
815
815
|
}
|
|
816
816
|
];
|
|
817
817
|
//#endregion
|
|
818
|
-
//#region ../evolution-client-kit/src/generated/states.generated.ts
|
|
819
|
-
/** A neutral/ warn/ fail block; a fail block is role=alert so assistive tech announces it. */
|
|
820
|
-
function Block(props) {
|
|
821
|
-
const tone = props.tone ?? "neutral";
|
|
822
|
-
return (0, react.createElement)("div", {
|
|
823
|
-
"data-tone": tone,
|
|
824
|
-
...tone === "fail" ? { role: "alert" } : {}
|
|
825
|
-
}, props.title === void 0 ? null : (0, react.createElement)("div", { "data-tone-title": tone }, props.title), props.children);
|
|
826
|
-
}
|
|
827
|
-
/** One classified failure as a reader-facing sentence. */
|
|
828
|
-
function failureText(failure, t) {
|
|
829
|
-
switch (failure.kind) {
|
|
830
|
-
case "unauthenticated": return t("failureUnauthorized");
|
|
831
|
-
case "forbidden": return t("failureForbidden");
|
|
832
|
-
case "unmounted": return t("failureUnmounted");
|
|
833
|
-
case "server": return failure.status === 0 ? t("failureServer") : t("failureServer") + " (HTTP " + String(failure.status) + ")";
|
|
834
|
-
case "refused": return failure.message;
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
/** The failed-state block: sentence + (when the surface exists but is unreachable) the command fallback. */
|
|
838
|
-
function FailureBlock(props) {
|
|
839
|
-
const hint = props.fallback === void 0 ? null : (0, react.createElement)("div", { "data-tone-hint": "true" }, props.t("fallbackHint", { command: props.fallback.command }));
|
|
840
|
-
return Block({
|
|
841
|
-
tone: "fail",
|
|
842
|
-
title: failureText(props.failure, props.t),
|
|
843
|
-
children: hint
|
|
844
|
-
});
|
|
845
|
-
}
|
|
846
|
-
//#endregion
|
|
847
|
-
//#region src/panels/settings/DiagnosticsCard.ts
|
|
848
|
-
/**
|
|
849
|
-
* The diagnostics card (W7 S7): the settings face of the SAME DiagnosticsView the CLI and the
|
|
850
|
-
* `/api/dsh-evolution/diagnostics` route answer.
|
|
851
|
-
*
|
|
852
|
-
* Read-only by construction: it fetches once on mount and only refetches on an explicit Refresh; a
|
|
853
|
-
* failed read renders the shared FailureBlock with the command-face fallback. The client owns no
|
|
854
|
-
* key list and no status vocabulary: it renders the host view's frozen runtime rows and leads with
|
|
855
|
-
* a plain-language summary. The view's documentation facts are a CLI/JSON concern and are
|
|
856
|
-
* deliberately NOT rendered here (0.27.2 real-machine feedback: a settings page is not a spec
|
|
857
|
-
* sheet; the runtime window is the whole reading).
|
|
858
|
-
* @module @lmzhen/dsh-evolution-ui/panels/settings/client/DiagnosticsCard
|
|
859
|
-
*/
|
|
860
|
-
/** Which word one status gets (the frozen W1 status set). */
|
|
861
|
-
const STATUS_KEYS = Object.freeze({
|
|
862
|
-
ok: "diagStatusOk",
|
|
863
|
-
warn: "diagStatusWarn",
|
|
864
|
-
fail: "diagStatusFail",
|
|
865
|
-
unknown: "diagStatusUnknown"
|
|
866
|
-
});
|
|
867
|
-
/** The summary word for a badge status (the same frozen set; ok has no count). */
|
|
868
|
-
const SUMMARY_KEYS = Object.freeze({
|
|
869
|
-
ok: "diagSummaryOk",
|
|
870
|
-
warn: "diagSummaryWarn",
|
|
871
|
-
fail: "diagSummaryFail",
|
|
872
|
-
unknown: "diagSummaryUnknown"
|
|
873
|
-
});
|
|
874
|
-
/** The one status the card leads with: fail > warn > unknown > ok. */
|
|
875
|
-
function summaryOf(rows) {
|
|
876
|
-
const count = (status) => rows.filter((row) => row.status === status).length;
|
|
877
|
-
for (const status of [
|
|
878
|
-
"fail",
|
|
879
|
-
"warn",
|
|
880
|
-
"unknown"
|
|
881
|
-
]) {
|
|
882
|
-
const n = count(status);
|
|
883
|
-
if (n > 0) return {
|
|
884
|
-
status,
|
|
885
|
-
count: n
|
|
886
|
-
};
|
|
887
|
-
}
|
|
888
|
-
return {
|
|
889
|
-
status: "ok",
|
|
890
|
-
count: 0
|
|
891
|
-
};
|
|
892
|
-
}
|
|
893
|
-
/**
|
|
894
|
-
* Render the card.
|
|
895
|
-
* @param props - the copy seat, the host call and the command fallback.
|
|
896
|
-
* @returns the card.
|
|
897
|
-
*/
|
|
898
|
-
function DiagnosticsCard(props) {
|
|
899
|
-
const { t, api } = props;
|
|
900
|
-
const translate = (key, values) => t(key, values);
|
|
901
|
-
const [state, setState] = (0, react.useState)({ phase: "loading" });
|
|
902
|
-
const [refreshing, setRefreshing] = (0, react.useState)(false);
|
|
903
|
-
const [open, setOpen] = (0, react.useState)(false);
|
|
904
|
-
const ticket = (0, react.useRef)(0);
|
|
905
|
-
const read = (keepView) => {
|
|
906
|
-
const current = ticket.current + 1;
|
|
907
|
-
ticket.current = current;
|
|
908
|
-
if (keepView) setRefreshing(true);
|
|
909
|
-
else setState({ phase: "loading" });
|
|
910
|
-
api.view().then((answer) => {
|
|
911
|
-
if (ticket.current !== current) return;
|
|
912
|
-
setState(answer.ok ? {
|
|
913
|
-
phase: "ready",
|
|
914
|
-
view: answer.data
|
|
915
|
-
} : {
|
|
916
|
-
phase: "failed",
|
|
917
|
-
failure: answer.failure
|
|
918
|
-
});
|
|
919
|
-
}).finally(() => {
|
|
920
|
-
if (ticket.current === current) setRefreshing(false);
|
|
921
|
-
});
|
|
922
|
-
};
|
|
923
|
-
(0, react.useEffect)(() => {
|
|
924
|
-
read(false);
|
|
925
|
-
}, []);
|
|
926
|
-
const refresh = () => {
|
|
927
|
-
read(true);
|
|
928
|
-
};
|
|
929
|
-
const summary = state.phase === "ready" ? summaryOf(state.view.runtime) : void 0;
|
|
930
|
-
const header = (0, react.createElement)("button", {
|
|
931
|
-
type: "button",
|
|
932
|
-
className: "evolution-param-head",
|
|
933
|
-
"aria-expanded": open ? "true" : "false",
|
|
934
|
-
onClick: () => {
|
|
935
|
-
setOpen(!open);
|
|
936
|
-
}
|
|
937
|
-
}, (0, react.createElement)("span", { className: "evolution-param-card-title" }, t("diagTitle")), summary === void 0 ? null : (0, react.createElement)("span", {
|
|
938
|
-
className: "evo-diag-badge",
|
|
939
|
-
"data-status": summary.status
|
|
940
|
-
}, t(SUMMARY_KEYS[summary.status], summary.count === 0 ? void 0 : { n: summary.count })), (0, react.createElement)("span", { className: "evolution-param-chevron" }, open ? "▲" : "▼"));
|
|
941
|
-
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", {
|
|
942
|
-
key: "runtime-" + row.key,
|
|
943
|
-
className: "evo-diag-item",
|
|
944
|
-
"data-kind": "runtime",
|
|
945
|
-
"data-status": row.status
|
|
946
|
-
}, (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)))));
|
|
947
|
-
const body = () => {
|
|
948
|
-
if (state.phase === "loading") return (0, react.createElement)("p", { className: "evolution-param-loading" }, t("stateLoading"));
|
|
949
|
-
if (state.phase === "failed") return FailureBlock({
|
|
950
|
-
failure: state.failure,
|
|
951
|
-
t: translate,
|
|
952
|
-
fallback: props.fallback
|
|
953
|
-
});
|
|
954
|
-
const view = state.view;
|
|
955
|
-
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", {
|
|
956
|
-
type: "button",
|
|
957
|
-
className: "evolution-param-button",
|
|
958
|
-
onClick: refresh,
|
|
959
|
-
disabled: refreshing,
|
|
960
|
-
"data-action": "diag-refresh"
|
|
961
|
-
}, t(refreshing ? "diagRefreshing" : "diagRefresh"))), runtime(view), view.runtime.length === 0 ? (0, react.createElement)("p", { className: "evolution-param-note" }, t("diagNoRows")) : null);
|
|
962
|
-
};
|
|
963
|
-
return (0, react.createElement)("section", {
|
|
964
|
-
className: "evolution-param-card",
|
|
965
|
-
"data-card": "diagnostics"
|
|
966
|
-
}, header, open ? (0, react.createElement)("div", { className: "evolution-param-body" }, body()) : null);
|
|
967
|
-
}
|
|
968
|
-
//#endregion
|
|
969
|
-
//#region ../evolution-client-kit/src/generated/routes.generated.ts
|
|
970
|
-
/**
|
|
971
|
-
* Every family host path, generated from evolution-core's HOST_ROUTES (the ONE declaration).
|
|
972
|
-
* A browser half cannot import the host package at runtime (client bundles keep @deepseek-ai/*
|
|
973
|
-
* external), so the table is mirrored here mechanically; a rename in core regenerates all three
|
|
974
|
-
* client copies, and the generator's --check makes a stale copy red.
|
|
975
|
-
*/
|
|
976
|
-
const ROUTES = {
|
|
977
|
-
approval: {
|
|
978
|
-
pending: "/api/dsh-evolution/approval/pending",
|
|
979
|
-
preview: "/api/dsh-evolution/approval/preview",
|
|
980
|
-
approve: "/api/dsh-evolution/approval/approve",
|
|
981
|
-
reject: "/api/dsh-evolution/approval/reject",
|
|
982
|
-
release: "/api/dsh-evolution/approval/release"
|
|
983
|
-
},
|
|
984
|
-
"skill-history": {
|
|
985
|
-
skills: "/api/dsh-evolution/skill-history/skills",
|
|
986
|
-
versions: "/api/dsh-evolution/skill-history/versions",
|
|
987
|
-
diff: "/api/dsh-evolution/skill-history/versions/diff",
|
|
988
|
-
body: "/api/dsh-evolution/skill-history/versions/body",
|
|
989
|
-
undo: "/api/dsh-evolution/skill-history/undo",
|
|
990
|
-
health: "/api/dsh-evolution/skill-history/health"
|
|
991
|
-
},
|
|
992
|
-
"skill-health": { rows: "/api/dsh-evolution/skill-health/rows" },
|
|
993
|
-
merge: {
|
|
994
|
-
view: "/api/dsh-evolution/merge/view",
|
|
995
|
-
rows: "/api/dsh-evolution/merge/rows",
|
|
996
|
-
run: "/api/dsh-evolution/merge/run",
|
|
997
|
-
runs: "/api/dsh-evolution/merge/runs",
|
|
998
|
-
body: "/api/dsh-evolution/merge/body",
|
|
999
|
-
history: "/api/dsh-evolution/merge/history"
|
|
1000
|
-
},
|
|
1001
|
-
diagnostics: { view: "/api/dsh-evolution/diagnostics" },
|
|
1002
|
-
settings: {
|
|
1003
|
-
describe: "/api/dsh-evolution/settings/describe",
|
|
1004
|
-
mutate: "/api/dsh-evolution/settings/mutate"
|
|
1005
|
-
}
|
|
1006
|
-
};
|
|
1007
|
-
//#endregion
|
|
1008
|
-
//#region ../evolution-client-kit/src/generated/route-client.generated.ts
|
|
1009
|
-
/** Classify a non-2xx status: 401/403/404 keep their own kind, everything else is a server answer. */
|
|
1010
|
-
function classify(status) {
|
|
1011
|
-
if (status === 401) return {
|
|
1012
|
-
kind: "unauthenticated",
|
|
1013
|
-
status: 401
|
|
1014
|
-
};
|
|
1015
|
-
if (status === 403) return {
|
|
1016
|
-
kind: "forbidden",
|
|
1017
|
-
status: 403
|
|
1018
|
-
};
|
|
1019
|
-
if (status === 404) return {
|
|
1020
|
-
kind: "unmounted",
|
|
1021
|
-
status: 404
|
|
1022
|
-
};
|
|
1023
|
-
return {
|
|
1024
|
-
kind: "server",
|
|
1025
|
-
status
|
|
1026
|
-
};
|
|
1027
|
-
}
|
|
1028
|
-
/**
|
|
1029
|
-
* One request, no retries and no body parsing for the platform's admission answers.
|
|
1030
|
-
* @param doFetch - the fetch in use (the browser's, or a spec's stub).
|
|
1031
|
-
* @param path - the exact route path from the generated ROUTES table.
|
|
1032
|
-
* @param init - standard RequestInit.
|
|
1033
|
-
* @returns the data, or the classified failure (network/parse failures read as 'server').
|
|
1034
|
-
*/
|
|
1035
|
-
async function requestJson(doFetch, path, init) {
|
|
1036
|
-
let response;
|
|
1037
|
-
try {
|
|
1038
|
-
response = await doFetch(path, init);
|
|
1039
|
-
} catch (error) {
|
|
1040
|
-
return {
|
|
1041
|
-
ok: false,
|
|
1042
|
-
failure: {
|
|
1043
|
-
kind: "server",
|
|
1044
|
-
status: 0,
|
|
1045
|
-
detail: error instanceof Error ? error.message : String(error)
|
|
1046
|
-
}
|
|
1047
|
-
};
|
|
1048
|
-
}
|
|
1049
|
-
if (!response.ok) return {
|
|
1050
|
-
ok: false,
|
|
1051
|
-
failure: classify(response.status)
|
|
1052
|
-
};
|
|
1053
|
-
let body;
|
|
1054
|
-
try {
|
|
1055
|
-
body = await response.json();
|
|
1056
|
-
} catch {
|
|
1057
|
-
return {
|
|
1058
|
-
ok: false,
|
|
1059
|
-
failure: {
|
|
1060
|
-
kind: "server",
|
|
1061
|
-
status: response.status
|
|
1062
|
-
}
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
if (typeof body === "object" && body !== null) {
|
|
1066
|
-
const record = body;
|
|
1067
|
-
if (record.ok === true) return {
|
|
1068
|
-
ok: true,
|
|
1069
|
-
data: record.data
|
|
1070
|
-
};
|
|
1071
|
-
const nested = record.error;
|
|
1072
|
-
const errorRecord = typeof nested === "object" && nested !== null ? nested : void 0;
|
|
1073
|
-
const code = typeof record.code === "string" && record.code !== "" ? record.code : typeof errorRecord?.code === "string" ? errorRecord.code : void 0;
|
|
1074
|
-
const message = typeof record.message === "string" && record.message !== "" ? record.message : typeof errorRecord?.message === "string" ? errorRecord.message : void 0;
|
|
1075
|
-
if (record.ok === false && code !== void 0 && message !== void 0 && message !== "") return {
|
|
1076
|
-
ok: false,
|
|
1077
|
-
failure: {
|
|
1078
|
-
kind: "refused",
|
|
1079
|
-
code,
|
|
1080
|
-
message
|
|
1081
|
-
}
|
|
1082
|
-
};
|
|
1083
|
-
}
|
|
1084
|
-
return {
|
|
1085
|
-
ok: false,
|
|
1086
|
-
failure: {
|
|
1087
|
-
kind: "server",
|
|
1088
|
-
status: response.status
|
|
1089
|
-
}
|
|
1090
|
-
};
|
|
1091
|
-
}
|
|
1092
|
-
/** The one-sentence rendering legacy callers attach to a classified failure. */
|
|
1093
|
-
function failureMessage(failure) {
|
|
1094
|
-
switch (failure.kind) {
|
|
1095
|
-
case "refused": return failure.message;
|
|
1096
|
-
case "server": return failure.status === 0 ? failure.detail === void 0 || failure.detail === "" ? "网络错误:无法连接宿主" : failure.detail : "HTTP " + String(failure.status);
|
|
1097
|
-
case "unauthenticated": return "HTTP 401";
|
|
1098
|
-
case "forbidden": return "HTTP 403";
|
|
1099
|
-
case "unmounted": return "HTTP 404";
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
//#endregion
|
|
1103
|
-
//#region src/panels/settings/diagnostics-state.ts
|
|
1104
|
-
/**
|
|
1105
|
-
* The diagnostics card's data face (W7 S7): ONE request to `HOST_ROUTES.diagnostics.view` through the
|
|
1106
|
-
* GENERATED route client, and the four-state answer the card renders.
|
|
1107
|
-
*
|
|
1108
|
-
* The Host route is the SAME `buildDoctorView` the CLI prints (W6/W8), so this client owns no facts
|
|
1109
|
-
* and no computation — it only renders.
|
|
1110
|
-
* @module @lmzhen/dsh-evolution-ui/panels/settings/client/diagnostics-state
|
|
1111
|
-
*/
|
|
1112
|
-
/**
|
|
1113
|
-
* Build the diagnostics face over one fetch implementation.
|
|
1114
|
-
* @param doFetch - the browser fetch, or a spec stub.
|
|
1115
|
-
*/
|
|
1116
|
-
function createDiagnosticsApi(doFetch = fetch) {
|
|
1117
|
-
return { view: async () => await requestJson(doFetch, ROUTES.diagnostics.view, { headers: { accept: "application/json" } }) };
|
|
1118
|
-
}
|
|
1119
|
-
//#endregion
|
|
1120
|
-
//#region ../evolution-client-kit/src/generated/fallbacks.generated.ts
|
|
1121
|
-
/**
|
|
1122
|
-
* The command-face fallback for every Host surface, generated from evolution-commands'
|
|
1123
|
-
* COMMAND_FALLBACKS (the ONE home). When the platform's connection carrier is absent (E-325) the
|
|
1124
|
-
* panels render this hint instead of a bare HTTP failure — same use case, command face.
|
|
1125
|
-
*/
|
|
1126
|
-
const FALLBACKS = {
|
|
1127
|
-
approval: { command: "/evolution pending … /evolution approve <id> … /evolution reject <id> … /evolution release <id>" },
|
|
1128
|
-
"skill-history": { command: "/evolution skill undo <name> [--to v<N>](查看:/evolution skill history <name>)" },
|
|
1129
|
-
"skill-health": { command: "/evolution skills refresh(健康行:/evolution doctor)" },
|
|
1130
|
-
merge: { command: "/evolution merge status / pending / approve <id> / report" },
|
|
1131
|
-
diagnostics: {
|
|
1132
|
-
command: "/evolution doctor",
|
|
1133
|
-
platformCli: "dsh --json /evolution doctor"
|
|
1134
|
-
},
|
|
1135
|
-
settings: { command: "/evolution policy set <id> <value>(查看:/evolution policy)" }
|
|
1136
|
-
};
|
|
1137
|
-
//#endregion
|
|
1138
818
|
//#region src/panels/settings/messages.ts
|
|
1139
819
|
/**
|
|
1140
820
|
* Copy for the evolution settings section and its parameter cards.
|
|
@@ -1172,10 +852,14 @@ window.__ModuleLoader__.load({
|
|
|
1172
852
|
invalidSelect: "不在允许的取值里",
|
|
1173
853
|
invalidAtLeast: "不能小于同组的 {n}",
|
|
1174
854
|
approvalConfirmNamed: "即将批准:{n}",
|
|
855
|
+
approvalRawSummary: "原始摘要",
|
|
1175
856
|
approvalConfirmYes: "确认批准",
|
|
1176
857
|
approvalConfirmNo: "取消",
|
|
1177
858
|
resetTo: "复位为 {value}",
|
|
1178
859
|
saved: "已保存。",
|
|
860
|
+
saveReadbackFailed: "写入已被接受,但回读失败;点刷新重读后再确认。",
|
|
861
|
+
readbackStale: "显示的是最后一次成功读取的值;当前回读失败,刷新后可能变化。",
|
|
862
|
+
saveConflict: "这个值没有被接受:设置已被其他页面修改。请重读后再保存。",
|
|
1179
863
|
resetDone: "已恢复默认值。",
|
|
1180
864
|
refused: "这个值没有被接受(界面上保持原样)。想看原因:在对话里让它说明。",
|
|
1181
865
|
resetRefused: "恢复默认值没有生效(这一行还是你的值)。想看原因:在对话里让它说明。",
|
|
@@ -1243,6 +927,8 @@ window.__ModuleLoader__.load({
|
|
|
1243
927
|
diagStatusUnknown: "未知",
|
|
1244
928
|
diagRemedy: "建议:",
|
|
1245
929
|
diagNoRows: "没有可显示的运行状态",
|
|
930
|
+
diagPurpose: "用途:检查插件挂载、平台服务和数据面是否正常,不修改任何状态。",
|
|
931
|
+
diagFooter: "诊断 schema v{version} · 生成于 {at}",
|
|
1246
932
|
stateLoading: "正在读取…",
|
|
1247
933
|
failureUnauthorized: "需要登录:重新打开这个页面完成登录后再试。",
|
|
1248
934
|
failureForbidden: "平台拒绝了这个请求(身份不是 owner)。",
|
|
@@ -1274,10 +960,14 @@ window.__ModuleLoader__.load({
|
|
|
1274
960
|
invalidSelect: "Not one of the allowed values",
|
|
1275
961
|
invalidAtLeast: "must not be below {n}",
|
|
1276
962
|
approvalConfirmNamed: "About to approve: {n}",
|
|
963
|
+
approvalRawSummary: "Raw summary",
|
|
1277
964
|
approvalConfirmYes: "Approve",
|
|
1278
965
|
approvalConfirmNo: "Cancel",
|
|
1279
966
|
resetTo: "Restores {value}",
|
|
1280
967
|
saved: "Saved.",
|
|
968
|
+
saveReadbackFailed: "The write was accepted, but reading it back failed; refresh and confirm.",
|
|
969
|
+
readbackStale: "Showing the last successfully read values; the current re-read failed and may change after refresh.",
|
|
970
|
+
saveConflict: "The value was not accepted: settings changed in another page. Re-read before saving again.",
|
|
1281
971
|
resetDone: "Default restored.",
|
|
1282
972
|
refused: "The value was not accepted (the page keeps what you see). For the reason, ask in the conversation.",
|
|
1283
973
|
resetRefused: "The reset did not take (this row still holds your value). For the reason, ask in the conversation.",
|
|
@@ -1345,6 +1035,8 @@ window.__ModuleLoader__.load({
|
|
|
1345
1035
|
diagStatusUnknown: "unknown",
|
|
1346
1036
|
diagRemedy: "Suggested: ",
|
|
1347
1037
|
diagNoRows: "no runtime status to show",
|
|
1038
|
+
diagPurpose: "Purpose: check plugin mounting, platform services and data planes; nothing is changed.",
|
|
1039
|
+
diagFooter: "diagnostics schema v{version} · generated {at}",
|
|
1348
1040
|
stateLoading: "reading…",
|
|
1349
1041
|
failureUnauthorized: "Sign-in is required: reopen this page and sign in, then retry.",
|
|
1350
1042
|
failureForbidden: "The platform refused this request (not the owner principal).",
|
|
@@ -1589,7 +1281,7 @@ window.__ModuleLoader__.load({
|
|
|
1589
1281
|
* @returns the card.
|
|
1590
1282
|
*/
|
|
1591
1283
|
function ParamCardView(props) {
|
|
1592
|
-
const { t, fields, mutateOnce, clear, namespace, snapshot } = props;
|
|
1284
|
+
const { t, fields, mutateOnce, saveOnce, clear, namespace, snapshot } = props;
|
|
1593
1285
|
const lang = props.lang?.() ?? "zh";
|
|
1594
1286
|
const [open, setOpen] = (0, react.useState)(false);
|
|
1595
1287
|
const [draft, setDraft] = (0, react.useState)({});
|
|
@@ -1616,8 +1308,12 @@ window.__ModuleLoader__.load({
|
|
|
1616
1308
|
setPending(null);
|
|
1617
1309
|
setBusy(false);
|
|
1618
1310
|
setClearing(null);
|
|
1311
|
+
if (pending.readbackFailed === true) {
|
|
1312
|
+
setError(t("saveReadbackFailed"));
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1619
1315
|
if (!pending.accepted) {
|
|
1620
|
-
setError(t(pending.resets ? "resetRefused" : "refused"));
|
|
1316
|
+
setError(t(pending.resets ? "resetRefused" : pending.kind === "conflict" ? "saveConflict" : "refused"));
|
|
1621
1317
|
return;
|
|
1622
1318
|
}
|
|
1623
1319
|
if (pending.accepted) if (pending.resets) {
|
|
@@ -1708,12 +1404,19 @@ window.__ModuleLoader__.load({
|
|
|
1708
1404
|
setNote("");
|
|
1709
1405
|
(async () => {
|
|
1710
1406
|
try {
|
|
1407
|
+
const ops = dirty.map((field) => ({
|
|
1408
|
+
op: "set",
|
|
1409
|
+
path: [keyOf(field)],
|
|
1410
|
+
value: parseFor(field.control, textOf(field))
|
|
1411
|
+
}));
|
|
1412
|
+
const result = saveOnce === void 0 ? {
|
|
1413
|
+
written: await mutateOnce(ops),
|
|
1414
|
+
readback: "ok"
|
|
1415
|
+
} : await saveOnce(ops);
|
|
1711
1416
|
setPending({
|
|
1712
|
-
accepted:
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
value: parseFor(field.control, textOf(field))
|
|
1716
|
-
}))),
|
|
1417
|
+
accepted: result.written && result.readback === "ok",
|
|
1418
|
+
readbackFailed: result.written && result.readback === "failed",
|
|
1419
|
+
...result.kind === void 0 ? {} : { kind: result.kind },
|
|
1717
1420
|
resets: false
|
|
1718
1421
|
});
|
|
1719
1422
|
} catch (caught) {
|
|
@@ -1729,7 +1432,11 @@ window.__ModuleLoader__.load({
|
|
|
1729
1432
|
onClick: () => {
|
|
1730
1433
|
setOpen(!open);
|
|
1731
1434
|
}
|
|
1732
|
-
}, (0, react.createElement)("span", { className: "evolution-param-card-title" }, title), changed === 0 ? null : (0, react.createElement)("span", { className: "evolution-param-count" }, t("changed", { n: changed })), (0, react.createElement)("span", { className: "evolution-param-chevron" }, open ? "▲" : "▼")), open ? (0, react.createElement)("div", { className: "evolution-param-body" }, hintKey === void 0 ? null : (0, react.createElement)("p", { className: "evolution-param-note" }, t(hintKey)),
|
|
1435
|
+
}, (0, react.createElement)("span", { className: "evolution-param-card-title" }, title), changed === 0 ? null : (0, react.createElement)("span", { className: "evolution-param-count" }, t("changed", { n: changed })), (0, react.createElement)("span", { className: "evolution-param-chevron" }, open ? "▲" : "▼")), open ? (0, react.createElement)("div", { className: "evolution-param-body" }, hintKey === void 0 ? null : (0, react.createElement)("p", { className: "evolution-param-note" }, t(hintKey)), snapshot.stale === true ? (0, react.createElement)("p", {
|
|
1436
|
+
className: "evolution-param-note",
|
|
1437
|
+
role: "status",
|
|
1438
|
+
"data-role": "stale-note"
|
|
1439
|
+
}, t("readbackStale")) : null, disabled ? (0, react.createElement)("p", {
|
|
1733
1440
|
className: "evolution-param-note",
|
|
1734
1441
|
role: "status"
|
|
1735
1442
|
}, t("readonly")) : null, fields.length === 0 ? (0, react.createElement)("p", {
|
|
@@ -1787,17 +1494,6 @@ window.__ModuleLoader__.load({
|
|
|
1787
1494
|
* @param props - the injected card face.
|
|
1788
1495
|
* @returns the card with its live row snapshot.
|
|
1789
1496
|
*/
|
|
1790
|
-
const pluginSources = /* @__PURE__ */ new Map();
|
|
1791
|
-
/**
|
|
1792
|
-
* Register a plugin-surface form source by namespace. The platform's
|
|
1793
|
-
* `settings.plugin.item` slot does not bind the card's `hooks` compartment, so
|
|
1794
|
-
* the owning settings apply hands the stable source here instead.
|
|
1795
|
-
* @param namespace - settings namespace.
|
|
1796
|
-
* @param source - the stable external-store source.
|
|
1797
|
-
*/
|
|
1798
|
-
function registerPluginParamSource(namespace, source) {
|
|
1799
|
-
pluginSources.set(namespace, source);
|
|
1800
|
-
}
|
|
1801
1497
|
const MISSING_SNAPSHOT = {
|
|
1802
1498
|
status: "unavailable",
|
|
1803
1499
|
value: void 0,
|
|
@@ -1812,7 +1508,7 @@ window.__ModuleLoader__.load({
|
|
|
1812
1508
|
revive: () => {}
|
|
1813
1509
|
};
|
|
1814
1510
|
function PluginParamCard(props) {
|
|
1815
|
-
const source = props.hooks?.paramSection ??
|
|
1511
|
+
const source = props.hooks?.paramSection ?? MISSING_SOURCE;
|
|
1816
1512
|
const snapshot = (0, react.useSyncExternalStore)(source.subscribe, source.getSnapshot, source.getSnapshot);
|
|
1817
1513
|
return ParamCardView({
|
|
1818
1514
|
namespace: props.namespace,
|
|
@@ -1820,16 +1516,114 @@ window.__ModuleLoader__.load({
|
|
|
1820
1516
|
t: props.t,
|
|
1821
1517
|
...props.lang === void 0 ? {} : { lang: props.lang },
|
|
1822
1518
|
mutateOnce: props.mutateOnce,
|
|
1519
|
+
...props.saveOnce === void 0 ? {} : { saveOnce: props.saveOnce },
|
|
1823
1520
|
clear: props.clear,
|
|
1824
1521
|
snapshot
|
|
1825
1522
|
});
|
|
1826
1523
|
}
|
|
1827
1524
|
//#endregion
|
|
1828
|
-
//#region src/
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1525
|
+
//#region ../evolution-client-kit/src/panel.ts
|
|
1526
|
+
function interpolate(text, values) {
|
|
1527
|
+
if (values === void 0) return text;
|
|
1528
|
+
return text.replace(/\{(\w+)\}/g, (_match, key) => String(values[key] ?? ""));
|
|
1529
|
+
}
|
|
1530
|
+
function injectStyles$1(doc, css, tagId) {
|
|
1531
|
+
if (doc === void 0) return () => {};
|
|
1532
|
+
doc.querySelector("style[data-plugin-css=\"" + tagId + "\"]")?.remove();
|
|
1533
|
+
const tag = doc.createElement("style");
|
|
1534
|
+
tag.setAttribute("data-plugin-css", tagId);
|
|
1535
|
+
tag.textContent = css;
|
|
1536
|
+
doc.head.append(tag);
|
|
1537
|
+
return () => {
|
|
1538
|
+
tag.remove();
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
/**
|
|
1542
|
+
* Register one sidebar row and its main body under one lifecycle.
|
|
1543
|
+
* @param ctx - the client context carrying the slots and locale seats.
|
|
1544
|
+
* @param spec - the feature-specific row/body descriptor.
|
|
1545
|
+
*/
|
|
1546
|
+
function registerSidebarPanel(ctx, spec) {
|
|
1547
|
+
const seam = ctx;
|
|
1548
|
+
let registered = false;
|
|
1549
|
+
ctx.effect(() => {
|
|
1550
|
+
try {
|
|
1551
|
+
const dispose = seam.locale.register(spec.namespace, spec.dictionaries);
|
|
1552
|
+
registered = true;
|
|
1553
|
+
return dispose;
|
|
1554
|
+
} catch (error) {
|
|
1555
|
+
ctx.logger.warn(spec.namespace + ": the locale seat refused the dictionary (" + String(error) + "); the panel falls back to the bundle copy");
|
|
1556
|
+
return () => {};
|
|
1557
|
+
}
|
|
1558
|
+
}, spec.namespace + ": locale");
|
|
1559
|
+
ctx.effect(() => injectStyles$1(typeof document === "undefined" ? void 0 : document, spec.styles.css, spec.styles.tagId), spec.namespace + ": styles");
|
|
1560
|
+
const fallback = spec.dictionaries[spec.fallback ?? "zh"];
|
|
1561
|
+
const lookup = (key) => {
|
|
1562
|
+
const own = fallback[key] ?? key;
|
|
1563
|
+
if (!registered) return own;
|
|
1564
|
+
try {
|
|
1565
|
+
return seam.locale.bind(spec.namespace)(key);
|
|
1566
|
+
} catch {
|
|
1567
|
+
return own;
|
|
1568
|
+
}
|
|
1569
|
+
};
|
|
1570
|
+
const t = (key, values) => interpolate(lookup(key), values);
|
|
1571
|
+
const label = () => lookup("entry.label");
|
|
1572
|
+
seam.slots.inject("sidebar.panellist", function* () {
|
|
1573
|
+
yield seam.slots.register({
|
|
1574
|
+
name: "sidebar.panellist",
|
|
1575
|
+
id: spec.id,
|
|
1576
|
+
order: spec.order,
|
|
1577
|
+
label
|
|
1578
|
+
}, spec.icon);
|
|
1579
|
+
yield seam.slots.register({
|
|
1580
|
+
name: "main",
|
|
1581
|
+
key: spec.id,
|
|
1582
|
+
inject: () => spec.body(t),
|
|
1583
|
+
...spec.children === void 0 ? {} : { children: spec.children },
|
|
1584
|
+
...spec.store === void 0 ? {} : { store: spec.store }
|
|
1585
|
+
}, spec.component);
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
//#endregion
|
|
1589
|
+
//#region ../evolution-client-kit/src/generated/routes.generated.ts
|
|
1590
|
+
/**
|
|
1591
|
+
* Every family host path, generated from evolution-core's HOST_ROUTES (the ONE declaration).
|
|
1592
|
+
* A browser half cannot import the host package at runtime (client bundles keep @deepseek-ai/*
|
|
1593
|
+
* external), so the table is mirrored here mechanically; a rename in core regenerates all three
|
|
1594
|
+
* client copies, and the generator's --check makes a stale copy red.
|
|
1595
|
+
*/
|
|
1596
|
+
const ROUTES = {
|
|
1597
|
+
approval: {
|
|
1598
|
+
pending: "/api/dsh-evolution/approval/pending",
|
|
1599
|
+
preview: "/api/dsh-evolution/approval/preview",
|
|
1600
|
+
approve: "/api/dsh-evolution/approval/approve",
|
|
1601
|
+
reject: "/api/dsh-evolution/approval/reject",
|
|
1602
|
+
release: "/api/dsh-evolution/approval/release"
|
|
1603
|
+
},
|
|
1604
|
+
"skill-history": {
|
|
1605
|
+
skills: "/api/dsh-evolution/skill-history/skills",
|
|
1606
|
+
versions: "/api/dsh-evolution/skill-history/versions",
|
|
1607
|
+
diff: "/api/dsh-evolution/skill-history/versions/diff",
|
|
1608
|
+
body: "/api/dsh-evolution/skill-history/versions/body",
|
|
1609
|
+
undo: "/api/dsh-evolution/skill-history/undo",
|
|
1610
|
+
health: "/api/dsh-evolution/skill-history/health"
|
|
1611
|
+
},
|
|
1612
|
+
"skill-health": { rows: "/api/dsh-evolution/skill-health/rows" },
|
|
1613
|
+
merge: {
|
|
1614
|
+
view: "/api/dsh-evolution/merge/view",
|
|
1615
|
+
rows: "/api/dsh-evolution/merge/rows",
|
|
1616
|
+
run: "/api/dsh-evolution/merge/run",
|
|
1617
|
+
runs: "/api/dsh-evolution/merge/runs",
|
|
1618
|
+
body: "/api/dsh-evolution/merge/body",
|
|
1619
|
+
history: "/api/dsh-evolution/merge/history"
|
|
1620
|
+
},
|
|
1621
|
+
diagnostics: { view: "/api/dsh-evolution/diagnostics" },
|
|
1622
|
+
settings: {
|
|
1623
|
+
describe: "/api/dsh-evolution/settings/describe",
|
|
1624
|
+
mutate: "/api/dsh-evolution/settings/mutate"
|
|
1625
|
+
}
|
|
1626
|
+
};
|
|
1833
1627
|
//#endregion
|
|
1834
1628
|
//#region ../evolution-client-kit/src/settings-seat.ts
|
|
1835
1629
|
const SETTINGS_DESCRIBE = ROUTES.settings.describe;
|
|
@@ -1893,34 +1687,51 @@ window.__ModuleLoader__.load({
|
|
|
1893
1687
|
...EMPTY,
|
|
1894
1688
|
reason: "seat-missing"
|
|
1895
1689
|
};
|
|
1690
|
+
let lastReady;
|
|
1691
|
+
let generation = 0;
|
|
1896
1692
|
const listeners = /* @__PURE__ */ new Set();
|
|
1897
|
-
let
|
|
1898
|
-
let listening = false;
|
|
1693
|
+
let remoteOff;
|
|
1899
1694
|
const notify = () => {
|
|
1900
1695
|
for (const listener of listeners) listener();
|
|
1901
1696
|
};
|
|
1902
|
-
const
|
|
1903
|
-
|
|
1697
|
+
const absorb = (next) => {
|
|
1698
|
+
if (next.status === "ready") {
|
|
1699
|
+
lastReady = next;
|
|
1700
|
+
snapshot = next;
|
|
1701
|
+
} else if (lastReady !== void 0) snapshot = {
|
|
1702
|
+
...lastReady,
|
|
1703
|
+
stale: true,
|
|
1704
|
+
reason: next.reason ?? "readback-failed"
|
|
1705
|
+
};
|
|
1706
|
+
else snapshot = next;
|
|
1904
1707
|
notify();
|
|
1905
1708
|
};
|
|
1906
|
-
const
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
if (
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1709
|
+
const refresh = async () => {
|
|
1710
|
+
const ticket = ++generation;
|
|
1711
|
+
const next = await describe(namespace);
|
|
1712
|
+
if (ticket !== generation) return;
|
|
1713
|
+
absorb(next);
|
|
1714
|
+
};
|
|
1715
|
+
const remote = () => ctx.get?.("remote");
|
|
1716
|
+
const stopRemote = () => {
|
|
1717
|
+
remoteOff?.();
|
|
1718
|
+
remoteOff = void 0;
|
|
1719
|
+
};
|
|
1720
|
+
const startRemote = () => {
|
|
1721
|
+
if (remoteOff !== void 0) return;
|
|
1722
|
+
remoteOff = remote()?.$on?.("settings/document-updated", (changed) => {
|
|
1723
|
+
if (changed === namespace) refresh();
|
|
1724
|
+
});
|
|
1916
1725
|
};
|
|
1917
1726
|
const form = {
|
|
1918
1727
|
getSnapshot: () => snapshot,
|
|
1919
1728
|
subscribe: (listener) => {
|
|
1920
1729
|
listeners.add(listener);
|
|
1921
|
-
|
|
1730
|
+
startRemote();
|
|
1731
|
+
refresh();
|
|
1922
1732
|
return () => {
|
|
1923
1733
|
listeners.delete(listener);
|
|
1734
|
+
if (listeners.size === 0) stopRemote();
|
|
1924
1735
|
};
|
|
1925
1736
|
},
|
|
1926
1737
|
set: async (field, value) => await form.mutate([{
|
|
@@ -1934,35 +1745,39 @@ window.__ModuleLoader__.load({
|
|
|
1934
1745
|
}]),
|
|
1935
1746
|
mutate: async (ops, expectedRevision) => {
|
|
1936
1747
|
const outcome = await write(namespace, ops, expectedRevision ?? snapshot.revision);
|
|
1937
|
-
if (outcome.snapshot !== void 0)
|
|
1938
|
-
|
|
1939
|
-
notify();
|
|
1940
|
-
} else await refresh();
|
|
1748
|
+
if (outcome.snapshot !== void 0) absorb(outcome.snapshot);
|
|
1749
|
+
else await refresh();
|
|
1941
1750
|
return outcome.ok;
|
|
1751
|
+
},
|
|
1752
|
+
refresh,
|
|
1753
|
+
dispose: () => {
|
|
1754
|
+
stopRemote();
|
|
1755
|
+
listeners.clear();
|
|
1942
1756
|
}
|
|
1943
1757
|
};
|
|
1944
1758
|
return form;
|
|
1945
1759
|
}
|
|
1946
1760
|
/**
|
|
1947
|
-
* Build one
|
|
1948
|
-
*
|
|
1949
|
-
*
|
|
1761
|
+
* Build one seat per client registration; every form's remote listener is released by
|
|
1762
|
+
* the caller's effect. No module-level WeakMap: a replaced registration must not share
|
|
1763
|
+
* a dead seat with the previous one.
|
|
1950
1764
|
*/
|
|
1951
|
-
const seats = /* @__PURE__ */ new WeakMap();
|
|
1952
1765
|
function createSettingsSeat(ctx) {
|
|
1953
|
-
const cached = seats.get(ctx);
|
|
1954
|
-
if (cached !== void 0) return cached;
|
|
1955
1766
|
const forms = /* @__PURE__ */ new Map();
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
form
|
|
1960
|
-
|
|
1767
|
+
return {
|
|
1768
|
+
get(namespace) {
|
|
1769
|
+
let form = forms.get(namespace);
|
|
1770
|
+
if (form === void 0) {
|
|
1771
|
+
form = createForm(namespace, ctx);
|
|
1772
|
+
forms.set(namespace, form);
|
|
1773
|
+
}
|
|
1774
|
+
return form;
|
|
1775
|
+
},
|
|
1776
|
+
dispose: () => {
|
|
1777
|
+
for (const form of forms.values()) form.dispose?.();
|
|
1778
|
+
forms.clear();
|
|
1961
1779
|
}
|
|
1962
|
-
|
|
1963
|
-
} };
|
|
1964
|
-
seats.set(ctx, seat);
|
|
1965
|
-
return seat;
|
|
1780
|
+
};
|
|
1966
1781
|
}
|
|
1967
1782
|
//#endregion
|
|
1968
1783
|
//#region ../evolution-client-kit/src/action.ts
|
|
@@ -1972,6 +1787,53 @@ window.__ModuleLoader__.load({
|
|
|
1972
1787
|
* @module @lmzhen/dsh-evolution-client-kit
|
|
1973
1788
|
*/
|
|
1974
1789
|
//#endregion
|
|
1790
|
+
//#region ../evolution-client-kit/src/generated/fallbacks.generated.ts
|
|
1791
|
+
/**
|
|
1792
|
+
* The command-face fallback for every Host surface, generated from evolution-commands'
|
|
1793
|
+
* COMMAND_FALLBACKS (the ONE home). When the platform's connection carrier is absent (E-325) the
|
|
1794
|
+
* panels render this hint instead of a bare HTTP failure — same use case, command face.
|
|
1795
|
+
*/
|
|
1796
|
+
const FALLBACKS = {
|
|
1797
|
+
approval: { command: "/evolution pending … /evolution approve <id> … /evolution reject <id> … /evolution release <id>" },
|
|
1798
|
+
"skill-history": { command: "/evolution skill undo <name> [--to v<N>](查看:/evolution skill history <name>)" },
|
|
1799
|
+
"skill-health": { command: "/evolution skills refresh(健康行:/evolution doctor)" },
|
|
1800
|
+
merge: { command: "/evolution merge status / pending / approve <id> / report" },
|
|
1801
|
+
diagnostics: {
|
|
1802
|
+
command: "/evolution doctor",
|
|
1803
|
+
platformCli: "dsh --json /evolution doctor"
|
|
1804
|
+
},
|
|
1805
|
+
settings: { command: "/evolution policy set <id> <value>(查看:/evolution policy)" }
|
|
1806
|
+
};
|
|
1807
|
+
//#endregion
|
|
1808
|
+
//#region ../evolution-client-kit/src/generated/states.generated.ts
|
|
1809
|
+
/** A neutral/ warn/ fail block; a fail block is role=alert so assistive tech announces it. */
|
|
1810
|
+
function Block(props) {
|
|
1811
|
+
const tone = props.tone ?? "neutral";
|
|
1812
|
+
return (0, react.createElement)("div", {
|
|
1813
|
+
"data-tone": tone,
|
|
1814
|
+
...tone === "fail" ? { role: "alert" } : {}
|
|
1815
|
+
}, props.title === void 0 ? null : (0, react.createElement)("div", { "data-tone-title": tone }, props.title), props.children);
|
|
1816
|
+
}
|
|
1817
|
+
/** One classified failure as a reader-facing sentence. */
|
|
1818
|
+
function failureText(failure, t) {
|
|
1819
|
+
switch (failure.kind) {
|
|
1820
|
+
case "unauthenticated": return t("failureUnauthorized");
|
|
1821
|
+
case "forbidden": return t("failureForbidden");
|
|
1822
|
+
case "unmounted": return t("failureUnmounted");
|
|
1823
|
+
case "server": return failure.status === 0 ? t("failureServer") : t("failureServer") + " (HTTP " + String(failure.status) + ")";
|
|
1824
|
+
case "refused": return failure.message;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
/** The failed-state block: sentence + (when the surface exists but is unreachable) the command fallback. */
|
|
1828
|
+
function FailureBlock(props) {
|
|
1829
|
+
const hint = props.fallback === void 0 ? null : (0, react.createElement)("div", { "data-tone-hint": "true" }, props.t("fallbackHint", { command: props.fallback.command }));
|
|
1830
|
+
return Block({
|
|
1831
|
+
tone: "fail",
|
|
1832
|
+
title: failureText(props.failure, props.t),
|
|
1833
|
+
children: hint
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
//#endregion
|
|
1975
1837
|
//#region ../evolution-client-kit/src/tokens.ts
|
|
1976
1838
|
/**
|
|
1977
1839
|
* The scale, declared on one element so it never leaks into the host shell.
|
|
@@ -1981,29 +1843,8 @@ window.__ModuleLoader__.load({
|
|
|
1981
1843
|
function tokenVars(scope) {
|
|
1982
1844
|
return scope + "{--evo-type-lg:calc(1em + 2px);--evo-type-body:1em;--evo-type-sm:calc(1em - 1px);--evo-type-xs:calc(1em - 2px);--evo-space-2:2px;--evo-space-4:4px;--evo-space-6:6px;--evo-space-8:8px;--evo-space-10:10px;--evo-space-12:12px;--evo-space-14:14px;--evo-space-16:16px;--evo-space-20:20px;--evo-radius-chip:4px;--evo-radius-control:8px;--evo-radius-surface:12px;--evo-radius-pill:999px;--evo-hairline-width:.5px;--evo-hairline-marker:2px;--evo-measure-read:80ch;--evo-leading-base:1.5;--evo-tone-quiet:var(--dsw-alias-label-secondary);--evo-tone-primary:var(--dsw-alias-brand-primary);--evo-tone-danger:var(--dsw-alias-state-error-primary);--evo-tone-success:var(--dsw-alias-state-success-primary);--evo-focus-ring:2px solid var(--dsw-alias-label-primary);--evo-focus-offset:-2px;--evo-cap-aside:280px;--evo-cap-block:260px}";
|
|
1983
1845
|
}
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
/**
|
|
1987
|
-
* The section shell: title, one-line explanation, then one card per namespace — with the staged-write
|
|
1988
|
-
* window's card ahead of them all, because it is the only card here that waits on a decision.
|
|
1989
|
-
*
|
|
1990
|
-
* This is the shape the platform's own settings sections use — a heading plus the
|
|
1991
|
-
* child slots it declared in its registration. The cards themselves are separate
|
|
1992
|
-
* registrations keyed by namespace, so a namespace the Host does not serve simply
|
|
1993
|
-
* renders nothing and a deployment that drops this row loses the whole section.
|
|
1994
|
-
* @module @lmzhen/dsh-evolution-ui/panels/settings/client
|
|
1995
|
-
*/
|
|
1996
|
-
/**
|
|
1997
|
-
* Render the section.
|
|
1998
|
-
* @param face - the shell's inject face: translator and child-slot renderer.
|
|
1999
|
-
* @returns the section element.
|
|
2000
|
-
*/
|
|
2001
|
-
function SettingsSection(face) {
|
|
2002
|
-
return (0, react.createElement)("div", { className: "evolution-params" }, (0, react.createElement)("h2", { className: "evolution-params-title" }, face.t("title")), (0, react.createElement)("p", { className: "evolution-params-subtitle" }, face.t("subtitle")), (0, react.createElement)("p", { className: "evolution-params-about" }, "0.32.6 · " + face.t("aboutPurpose")), (0, react.createElement)("div", {
|
|
2003
|
-
key: DIAGNOSTICS_CARD_KEY,
|
|
2004
|
-
className: "evolution-params-group"
|
|
2005
|
-
}, face.renderSlot(CARD_SLOT, {}, { entryKey: DIAGNOSTICS_CARD_KEY })));
|
|
2006
|
-
}
|
|
1846
|
+
/** Stylesheet for {@link PAGE_SURFACE_CLASS}; consumers append it to their own CSS. */
|
|
1847
|
+
const PAGE_SURFACE_CSS = ".evolution-page-surface{min-height:100%;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary)}";
|
|
2007
1848
|
//#endregion
|
|
2008
1849
|
//#region src/panels/settings/source.ts
|
|
2009
1850
|
/** What a card shows when this UI found no settings seat at all. */
|
|
@@ -2038,7 +1879,8 @@ window.__ModuleLoader__.load({
|
|
|
2038
1879
|
user: snapshot.user,
|
|
2039
1880
|
writable: snapshot.writable,
|
|
2040
1881
|
base: isRecord(snapshot.base) ? snapshot.base : void 0,
|
|
2041
|
-
...snapshot.status === "unavailable" ? { reason: snapshot.reason ?? "not-served" } : {}
|
|
1882
|
+
...snapshot.status === "unavailable" ? { reason: snapshot.reason ?? "not-served" } : {},
|
|
1883
|
+
...snapshot.stale === true ? { stale: true } : {}
|
|
2042
1884
|
};
|
|
2043
1885
|
}
|
|
2044
1886
|
/**
|
|
@@ -2141,12 +1983,50 @@ window.__ModuleLoader__.load({
|
|
|
2141
1983
|
*/
|
|
2142
1984
|
function writeFaceFor(probe, namespace) {
|
|
2143
1985
|
const form = () => probe()?.get(namespace);
|
|
1986
|
+
const saveOnce = async (ops) => {
|
|
1987
|
+
const seat = form();
|
|
1988
|
+
if (seat === void 0) return {
|
|
1989
|
+
written: false,
|
|
1990
|
+
readback: "failed",
|
|
1991
|
+
kind: "transport"
|
|
1992
|
+
};
|
|
1993
|
+
let written = false;
|
|
1994
|
+
try {
|
|
1995
|
+
written = await seat.mutate(ops);
|
|
1996
|
+
} catch (failure) {
|
|
1997
|
+
return {
|
|
1998
|
+
written: false,
|
|
1999
|
+
readback: "failed",
|
|
2000
|
+
kind: "transport",
|
|
2001
|
+
failure
|
|
2002
|
+
};
|
|
2003
|
+
}
|
|
2004
|
+
if (!written) return {
|
|
2005
|
+
written: false,
|
|
2006
|
+
readback: "failed",
|
|
2007
|
+
kind: "conflict"
|
|
2008
|
+
};
|
|
2009
|
+
try {
|
|
2010
|
+
await seat.refresh?.();
|
|
2011
|
+
} catch {}
|
|
2012
|
+
const snapshot = seat.getSnapshot();
|
|
2013
|
+
if (snapshot.status !== "ready") return {
|
|
2014
|
+
written: true,
|
|
2015
|
+
readback: "failed",
|
|
2016
|
+
kind: "transport"
|
|
2017
|
+
};
|
|
2018
|
+
return {
|
|
2019
|
+
written: true,
|
|
2020
|
+
readback: snapshot.stale === true ? "failed" : "ok",
|
|
2021
|
+
...snapshot.stale === true ? { kind: "transport" } : {}
|
|
2022
|
+
};
|
|
2023
|
+
};
|
|
2144
2024
|
return {
|
|
2145
2025
|
mutateOnce: async (ops) => {
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
return await seat.mutate(ops);
|
|
2026
|
+
if (form() === void 0) return false;
|
|
2027
|
+
return (await saveOnce(ops)).written;
|
|
2149
2028
|
},
|
|
2029
|
+
saveOnce,
|
|
2150
2030
|
clear: async (field) => {
|
|
2151
2031
|
const seat = form();
|
|
2152
2032
|
if (seat === void 0) return false;
|
|
@@ -2283,7 +2163,7 @@ window.__ModuleLoader__.load({
|
|
|
2283
2163
|
* @param host - the document to inject into; defaults to the global one.
|
|
2284
2164
|
* @returns the disposal; a no-op when there is no document or the tag was already present.
|
|
2285
2165
|
*/
|
|
2286
|
-
function injectStyles
|
|
2166
|
+
function injectStyles(host) {
|
|
2287
2167
|
const doc = host ?? globalThis.document;
|
|
2288
2168
|
if (doc === void 0) return () => {};
|
|
2289
2169
|
doc.querySelector("style[data-plugin-css=\"@lmzhen/dsh-evolution-ui/settings.css\"]")?.remove?.();
|
|
@@ -2305,7 +2185,7 @@ window.__ModuleLoader__.load({
|
|
|
2305
2185
|
* @param ctx - client cordis context.
|
|
2306
2186
|
*/
|
|
2307
2187
|
function apply$2(ctx) {
|
|
2308
|
-
ctx.effect(() => injectStyles
|
|
2188
|
+
ctx.effect(() => injectStyles(), "evolution-settings-ui: styles");
|
|
2309
2189
|
const seam = ctx;
|
|
2310
2190
|
let registered = false;
|
|
2311
2191
|
ctx.effect(() => {
|
|
@@ -2348,54 +2228,132 @@ window.__ModuleLoader__.load({
|
|
|
2348
2228
|
ctx.inject?.(["remote"], () => {
|
|
2349
2229
|
sources.revive();
|
|
2350
2230
|
});
|
|
2351
|
-
|
|
2352
|
-
yield seam.slots.register({
|
|
2353
|
-
name: "settings.section",
|
|
2354
|
-
id: SECTION_ID,
|
|
2355
|
-
order: 25,
|
|
2356
|
-
label: () => t("title"),
|
|
2357
|
-
locale: NS$1,
|
|
2358
|
-
children: { [CARD_SLOT]: {
|
|
2359
|
-
kind: "keyed",
|
|
2360
|
-
scope: "root"
|
|
2361
|
-
} }
|
|
2362
|
-
}, SettingsSection);
|
|
2363
|
-
});
|
|
2364
|
-
for (const [index, section] of CLIENT_PARAM_SECTIONS.entries()) {
|
|
2231
|
+
const cards = CLIENT_PARAM_SECTIONS.map((section) => {
|
|
2365
2232
|
const source = sources.sourceFor(section.seatKey);
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
const face = () => ({
|
|
2233
|
+
const { mutateOnce, saveOnce, clear } = writeFaceFor(probe, section.namespace);
|
|
2234
|
+
return {
|
|
2369
2235
|
namespace: section.namespace,
|
|
2370
2236
|
fields: section.fields,
|
|
2371
2237
|
t,
|
|
2372
2238
|
lang: language,
|
|
2373
2239
|
mutateOnce,
|
|
2240
|
+
saveOnce,
|
|
2374
2241
|
clear,
|
|
2375
2242
|
hooks: { paramSection: source }
|
|
2376
|
-
}
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2243
|
+
};
|
|
2244
|
+
});
|
|
2245
|
+
seam.slots.inject("settings.plugins.tab", function* () {
|
|
2246
|
+
yield seam.slots.register({
|
|
2247
|
+
name: "settings.plugins.tab",
|
|
2248
|
+
id: SECTION_ID,
|
|
2249
|
+
order: 25,
|
|
2250
|
+
label: () => t("title"),
|
|
2251
|
+
locale: NS$1,
|
|
2252
|
+
inject: () => ({ cards })
|
|
2253
|
+
}, EvolutionSettingsTab);
|
|
2254
|
+
});
|
|
2255
|
+
}
|
|
2256
|
+
/** One family tab: every parameter card lives inside the platform's Plugins page. */
|
|
2257
|
+
function EvolutionSettingsTab(props) {
|
|
2258
|
+
return (0, react.createElement)("div", { className: "evolution-settings-tab" }, ...props.cards.map((face, index) => (0, react.createElement)(PluginParamCard, {
|
|
2259
|
+
key: face.namespace + ":" + String(index),
|
|
2260
|
+
...face
|
|
2261
|
+
})));
|
|
2262
|
+
}
|
|
2263
|
+
//#endregion
|
|
2264
|
+
//#region ../evolution-client-kit/src/generated/route-client.generated.ts
|
|
2265
|
+
/** Classify a non-2xx status: 401/403/404 keep their own kind, everything else is a server answer. */
|
|
2266
|
+
function classify(status) {
|
|
2267
|
+
if (status === 401) return {
|
|
2268
|
+
kind: "unauthenticated",
|
|
2269
|
+
status: 401
|
|
2270
|
+
};
|
|
2271
|
+
if (status === 403) return {
|
|
2272
|
+
kind: "forbidden",
|
|
2273
|
+
status: 403
|
|
2274
|
+
};
|
|
2275
|
+
if (status === 404) return {
|
|
2276
|
+
kind: "unmounted",
|
|
2277
|
+
status: 404
|
|
2278
|
+
};
|
|
2279
|
+
return {
|
|
2280
|
+
kind: "server",
|
|
2281
|
+
status
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
/**
|
|
2285
|
+
* One request, no retries and no body parsing for the platform's admission answers.
|
|
2286
|
+
* @param doFetch - the fetch in use (the browser's, or a spec's stub).
|
|
2287
|
+
* @param path - the exact route path from the generated ROUTES table.
|
|
2288
|
+
* @param init - standard RequestInit.
|
|
2289
|
+
* @returns the data, or the classified failure (network/parse failures read as 'server').
|
|
2290
|
+
*/
|
|
2291
|
+
async function requestJson(doFetch, path, init) {
|
|
2292
|
+
let response;
|
|
2293
|
+
try {
|
|
2294
|
+
response = await doFetch(path, init);
|
|
2295
|
+
} catch (error) {
|
|
2296
|
+
return {
|
|
2297
|
+
ok: false,
|
|
2298
|
+
failure: {
|
|
2299
|
+
kind: "server",
|
|
2300
|
+
status: 0,
|
|
2301
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
2302
|
+
}
|
|
2303
|
+
};
|
|
2304
|
+
}
|
|
2305
|
+
if (!response.ok) return {
|
|
2306
|
+
ok: false,
|
|
2307
|
+
failure: classify(response.status)
|
|
2308
|
+
};
|
|
2309
|
+
let body;
|
|
2310
|
+
try {
|
|
2311
|
+
body = await response.json();
|
|
2312
|
+
} catch {
|
|
2313
|
+
return {
|
|
2314
|
+
ok: false,
|
|
2315
|
+
failure: {
|
|
2316
|
+
kind: "server",
|
|
2317
|
+
status: response.status
|
|
2318
|
+
}
|
|
2319
|
+
};
|
|
2320
|
+
}
|
|
2321
|
+
if (typeof body === "object" && body !== null) {
|
|
2322
|
+
const record = body;
|
|
2323
|
+
if (record.ok === true) return {
|
|
2324
|
+
ok: true,
|
|
2325
|
+
data: record.data
|
|
2326
|
+
};
|
|
2327
|
+
const nested = record.error;
|
|
2328
|
+
const errorRecord = typeof nested === "object" && nested !== null ? nested : void 0;
|
|
2329
|
+
const code = typeof record.code === "string" && record.code !== "" ? record.code : typeof errorRecord?.code === "string" ? errorRecord.code : void 0;
|
|
2330
|
+
const message = typeof record.message === "string" && record.message !== "" ? record.message : typeof errorRecord?.message === "string" ? errorRecord.message : void 0;
|
|
2331
|
+
if (record.ok === false && code !== void 0 && message !== void 0 && message !== "") return {
|
|
2332
|
+
ok: false,
|
|
2333
|
+
failure: {
|
|
2334
|
+
kind: "refused",
|
|
2335
|
+
code,
|
|
2336
|
+
message
|
|
2337
|
+
}
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2340
|
+
return {
|
|
2341
|
+
ok: false,
|
|
2342
|
+
failure: {
|
|
2343
|
+
kind: "server",
|
|
2344
|
+
status: response.status
|
|
2345
|
+
}
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
/** The one-sentence rendering legacy callers attach to a classified failure. */
|
|
2349
|
+
function failureMessage(failure) {
|
|
2350
|
+
switch (failure.kind) {
|
|
2351
|
+
case "refused": return failure.message;
|
|
2352
|
+
case "server": return failure.status === 0 ? failure.detail === void 0 || failure.detail === "" ? "网络错误:无法连接宿主" : failure.detail : "HTTP " + String(failure.status);
|
|
2353
|
+
case "unauthenticated": return "HTTP 401";
|
|
2354
|
+
case "forbidden": return "HTTP 403";
|
|
2355
|
+
case "unmounted": return "HTTP 404";
|
|
2386
2356
|
}
|
|
2387
|
-
seam.slots.inject(CARD_SLOT, function* () {
|
|
2388
|
-
const diagApi = createDiagnosticsApi((input, init) => fetch(input, init));
|
|
2389
|
-
yield seam.slots.register({
|
|
2390
|
-
name: CARD_SLOT,
|
|
2391
|
-
key: DIAGNOSTICS_CARD_KEY,
|
|
2392
|
-
inject: () => ({
|
|
2393
|
-
t,
|
|
2394
|
-
api: diagApi,
|
|
2395
|
-
fallback: FALLBACKS.diagnostics
|
|
2396
|
-
})
|
|
2397
|
-
}, DiagnosticsCard);
|
|
2398
|
-
});
|
|
2399
2357
|
}
|
|
2400
2358
|
//#endregion
|
|
2401
2359
|
//#region src/panels/history/client/api.ts
|
|
@@ -2600,8 +2558,7 @@ window.__ModuleLoader__.load({
|
|
|
2600
2558
|
* independent review flagged that this reader had no case at all (P2-11).
|
|
2601
2559
|
*/
|
|
2602
2560
|
function confirmModeFrom(user) {
|
|
2603
|
-
|
|
2604
|
-
return value === "ask" || value === "timeout" ? "ask" : "auto";
|
|
2561
|
+
return (typeof user === "object" && user !== null ? user.approvalConfirm : void 0) === "auto" ? "auto" : "ask";
|
|
2605
2562
|
}
|
|
2606
2563
|
/** Which word one age bucket gets; an unknown bucket falls back to the count form. */
|
|
2607
2564
|
const AGE_KEYS = Object.freeze({
|
|
@@ -2638,7 +2595,7 @@ window.__ModuleLoader__.load({
|
|
|
2638
2595
|
function PendingCard(props) {
|
|
2639
2596
|
const { t, api } = props;
|
|
2640
2597
|
const translate = (key, values) => t(key, values);
|
|
2641
|
-
const confirmMode = props.confirmMode?.() ?? "
|
|
2598
|
+
const confirmMode = props.confirmMode?.() ?? "ask";
|
|
2642
2599
|
const [armed, setArmed] = (0, react.useState)(null);
|
|
2643
2600
|
const [state, setState] = (0, react.useState)(INITIAL_PENDING_STATE);
|
|
2644
2601
|
const [open, setOpen] = (0, react.useState)(true);
|
|
@@ -2664,6 +2621,8 @@ window.__ModuleLoader__.load({
|
|
|
2664
2621
|
* read, because a half-list would read as a complete window.
|
|
2665
2622
|
*/
|
|
2666
2623
|
const readTicket = (0, react.useRef)(0);
|
|
2624
|
+
const previewTicket = (0, react.useRef)(0);
|
|
2625
|
+
const actionTickets = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
2667
2626
|
const load = async () => {
|
|
2668
2627
|
apply(loading);
|
|
2669
2628
|
const ticket = readTicket.current + 1;
|
|
@@ -2691,9 +2650,13 @@ window.__ModuleLoader__.load({
|
|
|
2691
2650
|
};
|
|
2692
2651
|
}, []);
|
|
2693
2652
|
const decide = (id, action) => {
|
|
2653
|
+
if (latest.current.busy.includes(id)) return;
|
|
2654
|
+
const ticket = (actionTickets.current.get(id) ?? 0) + 1;
|
|
2655
|
+
actionTickets.current.set(id, ticket);
|
|
2694
2656
|
apply((current) => beginAction(current, id));
|
|
2695
2657
|
(async () => {
|
|
2696
2658
|
const answer = action === "approve" ? await api.approve(id) : action === "reject" ? await api.reject(id) : await api.release(id);
|
|
2659
|
+
if (actionTickets.current.get(id) !== ticket) return;
|
|
2697
2660
|
if (!answer.ok) {
|
|
2698
2661
|
apply((current) => noticed(endAction(current, id), failureText(answer.failure, translate)));
|
|
2699
2662
|
return;
|
|
@@ -2709,7 +2672,9 @@ window.__ModuleLoader__.load({
|
|
|
2709
2672
|
};
|
|
2710
2673
|
/** Ask the host what this record would store; the answer lands only if the row is still open. */
|
|
2711
2674
|
const loadPreview = async (id) => {
|
|
2675
|
+
const ticket = ++previewTicket.current;
|
|
2712
2676
|
const answer = await api.preview(id);
|
|
2677
|
+
if (ticket !== previewTicket.current || latest.current.open !== id) return;
|
|
2713
2678
|
const view = answer.ok ? answer.data.available ? {
|
|
2714
2679
|
kind: "ready",
|
|
2715
2680
|
answer: answer.data
|
|
@@ -2763,7 +2728,13 @@ window.__ModuleLoader__.load({
|
|
|
2763
2728
|
}, t("approvalDiffCut"))] : []
|
|
2764
2729
|
];
|
|
2765
2730
|
const lines = [];
|
|
2731
|
+
const multiHunk = view.answer.hunks.length > 1 || view.answer.hunks.some((hunk) => hunk.path !== view.answer.path);
|
|
2766
2732
|
for (const [hunkIndex, hunk] of view.answer.hunks.entries()) {
|
|
2733
|
+
if (multiHunk) lines.push((0, react.createElement)("div", {
|
|
2734
|
+
key: "hunk-" + String(hunkIndex),
|
|
2735
|
+
className: "evolution-param-hunk-path",
|
|
2736
|
+
"data-role": "hunk-path"
|
|
2737
|
+
}, hunk.path));
|
|
2767
2738
|
for (const [index, line] of hunk.oldText.split("\n").entries()) {
|
|
2768
2739
|
if (hunk.oldText === "" && line === "") continue;
|
|
2769
2740
|
lines.push((0, react.createElement)("div", {
|
|
@@ -2821,7 +2792,7 @@ window.__ModuleLoader__.load({
|
|
|
2821
2792
|
}, (0, react.createElement)("div", { className: "evolution-param-label" }, (0, react.createElement)("span", { className: "evolution-param-source" }, t(KIND_KEYS[entry.kind])), (0, react.createElement)("span", {
|
|
2822
2793
|
className: "evolution-param-value",
|
|
2823
2794
|
title: entry.summary
|
|
2824
|
-
}, summaryLine(entry))), (0, react.createElement)("p", { className: "evolution-param-hint" }, age(entry)), armed === entry.id ? (0, react.createElement)("p", {
|
|
2795
|
+
}, summaryLine(entry))), (0, react.createElement)("p", { className: "evolution-param-hint" }, age(entry)), summaryLine(entry) === entry.summary ? null : (0, react.createElement)("details", { className: "evolution-param-details" }, (0, react.createElement)("summary", null, t("approvalRawSummary")), (0, react.createElement)("p", { className: "evolution-param-hint" }, entry.summary)), armed === entry.id ? (0, react.createElement)("p", {
|
|
2825
2796
|
className: "evolution-param-note",
|
|
2826
2797
|
role: "status"
|
|
2827
2798
|
}, (0, react.createElement)("span", { className: "evolution-param-value" }, t("approvalConfirmNamed", { n: summaryLine(entry) })), (0, react.createElement)("button", {
|
|
@@ -2900,6 +2871,23 @@ window.__ModuleLoader__.load({
|
|
|
2900
2871
|
}, state.refreshing ? t("diagRefreshing") : t("approvalRefresh"))), open ? (0, react.createElement)("div", { className: "evolution-param-body" }, body()) : null);
|
|
2901
2872
|
}
|
|
2902
2873
|
//#endregion
|
|
2874
|
+
//#region src/panels/settings/diagnostics-state.ts
|
|
2875
|
+
/**
|
|
2876
|
+
* The diagnostics card's data face (W7 S7): ONE request to `HOST_ROUTES.diagnostics.view` through the
|
|
2877
|
+
* GENERATED route client, and the four-state answer the card renders.
|
|
2878
|
+
*
|
|
2879
|
+
* The Host route is the SAME `buildDoctorView` the CLI prints (W6/W8), so this client owns no facts
|
|
2880
|
+
* and no computation — it only renders.
|
|
2881
|
+
* @module @lmzhen/dsh-evolution-ui/panels/settings/client/diagnostics-state
|
|
2882
|
+
*/
|
|
2883
|
+
/**
|
|
2884
|
+
* Build the diagnostics face over one fetch implementation.
|
|
2885
|
+
* @param doFetch - the browser fetch, or a spec stub.
|
|
2886
|
+
*/
|
|
2887
|
+
function createDiagnosticsApi(doFetch = fetch) {
|
|
2888
|
+
return { view: async () => await requestJson(doFetch, ROUTES.diagnostics.view, { headers: { accept: "application/json" } }) };
|
|
2889
|
+
}
|
|
2890
|
+
//#endregion
|
|
2903
2891
|
//#region src/panels/history/client/messages.ts
|
|
2904
2892
|
/**
|
|
2905
2893
|
* Panel copy, in a zh/en dictionary registered through the platform’s locale seat.
|
|
@@ -2919,6 +2907,7 @@ window.__ModuleLoader__.load({
|
|
|
2919
2907
|
"workbench.pending": "待批",
|
|
2920
2908
|
"workbench.merge": "合并",
|
|
2921
2909
|
"workbench.health": "健康",
|
|
2910
|
+
"workbench.diagnostics": "诊断",
|
|
2922
2911
|
hint: "每次技能写入留下的内容版本。回退只改 SKILL.md 正文,标记、使用计数与策展状态不变。内容相同的版本会同时标「当前」。",
|
|
2923
2912
|
search: "按名字或描述筛选",
|
|
2924
2913
|
"search.none": "没有匹配的技能。",
|
|
@@ -3010,6 +2999,7 @@ window.__ModuleLoader__.load({
|
|
|
3010
2999
|
"workbench.pending": "Pending",
|
|
3011
3000
|
"workbench.merge": "Merge",
|
|
3012
3001
|
"workbench.health": "Health",
|
|
3002
|
+
"workbench.diagnostics": "Diagnostics",
|
|
3013
3003
|
hint: "The content versions every skill write leaves behind. Restoring changes the SKILL.md body only: markers, usage counts and curation state stay as they are. Versions holding the same content are all marked “current”.",
|
|
3014
3004
|
search: "Filter by name or description",
|
|
3015
3005
|
"search.none": "No skill matches.",
|
|
@@ -3784,16 +3774,18 @@ window.__ModuleLoader__.load({
|
|
|
3784
3774
|
* @param name - the skill to open.
|
|
3785
3775
|
* @param onFailure - what to do with a failed read.
|
|
3786
3776
|
*/
|
|
3787
|
-
const showVersions = (name, onFailure = failed) => {
|
|
3777
|
+
const showVersions = (name, onFailure = failed, keepContent = false) => {
|
|
3788
3778
|
const ticket = readTicket.current + 1;
|
|
3789
3779
|
readTicket.current = ticket;
|
|
3790
3780
|
setSelected(name);
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3781
|
+
if (!keepContent) {
|
|
3782
|
+
setPayload(void 0);
|
|
3783
|
+
setVersionsFailed(void 0);
|
|
3784
|
+
setPending(void 0);
|
|
3785
|
+
setExpanded(void 0);
|
|
3786
|
+
setDiffs(/* @__PURE__ */ new Map());
|
|
3787
|
+
setBodies(/* @__PURE__ */ new Map());
|
|
3788
|
+
}
|
|
3797
3789
|
face.loadVersions(name).then((next) => {
|
|
3798
3790
|
if (readTicket.current === ticket) {
|
|
3799
3791
|
setPayload(next);
|
|
@@ -3816,7 +3808,7 @@ window.__ModuleLoader__.load({
|
|
|
3816
3808
|
/** What the refresh control does: both reads again, and the sentence stays. */
|
|
3817
3809
|
const refresh = () => {
|
|
3818
3810
|
reloadSkills();
|
|
3819
|
-
if (selected !== void 0) showVersions(selected);
|
|
3811
|
+
if (selected !== void 0) showVersions(selected, failed, true);
|
|
3820
3812
|
};
|
|
3821
3813
|
(0, react.useEffect)(() => {
|
|
3822
3814
|
if (face.focusSkill === void 0 || face.focusSkill === "") return;
|
|
@@ -3981,6 +3973,137 @@ window.__ModuleLoader__.load({
|
|
|
3981
3973
|
return next;
|
|
3982
3974
|
}
|
|
3983
3975
|
//#endregion
|
|
3976
|
+
//#region src/panels/settings/DiagnosticsCard.ts
|
|
3977
|
+
/**
|
|
3978
|
+
* The diagnostics card (W7 S7): the settings face of the SAME DiagnosticsView the CLI and the
|
|
3979
|
+
* `/api/dsh-evolution/diagnostics` route answer.
|
|
3980
|
+
*
|
|
3981
|
+
* Read-only by construction: it fetches once on mount and only refetches on an explicit Refresh; a
|
|
3982
|
+
* failed read renders the shared FailureBlock with the command-face fallback. The client owns no
|
|
3983
|
+
* key list and no status vocabulary: it renders the host view's frozen runtime rows and leads with
|
|
3984
|
+
* a plain-language summary. The view's documentation facts are a CLI/JSON concern and are
|
|
3985
|
+
* deliberately NOT rendered here (0.27.2 real-machine feedback: a settings page is not a spec
|
|
3986
|
+
* sheet; the runtime window is the whole reading).
|
|
3987
|
+
* @module @lmzhen/dsh-evolution-ui/panels/settings/client/DiagnosticsCard
|
|
3988
|
+
*/
|
|
3989
|
+
/** Which word one status gets (the frozen W1 status set). */
|
|
3990
|
+
const STATUS_KEYS = Object.freeze({
|
|
3991
|
+
ok: "diagStatusOk",
|
|
3992
|
+
warn: "diagStatusWarn",
|
|
3993
|
+
fail: "diagStatusFail",
|
|
3994
|
+
unknown: "diagStatusUnknown"
|
|
3995
|
+
});
|
|
3996
|
+
/** The summary word for a badge status (the same frozen set; ok has no count). */
|
|
3997
|
+
const SUMMARY_KEYS = Object.freeze({
|
|
3998
|
+
ok: "diagSummaryOk",
|
|
3999
|
+
warn: "diagSummaryWarn",
|
|
4000
|
+
fail: "diagSummaryFail",
|
|
4001
|
+
unknown: "diagSummaryUnknown"
|
|
4002
|
+
});
|
|
4003
|
+
/** The one status the card leads with: fail > warn > unknown > ok. */
|
|
4004
|
+
function summaryOf(rows) {
|
|
4005
|
+
const count = (status) => rows.filter((row) => row.status === status).length;
|
|
4006
|
+
for (const status of [
|
|
4007
|
+
"fail",
|
|
4008
|
+
"warn",
|
|
4009
|
+
"unknown"
|
|
4010
|
+
]) {
|
|
4011
|
+
const n = count(status);
|
|
4012
|
+
if (n > 0) return {
|
|
4013
|
+
status,
|
|
4014
|
+
count: n
|
|
4015
|
+
};
|
|
4016
|
+
}
|
|
4017
|
+
return {
|
|
4018
|
+
status: "ok",
|
|
4019
|
+
count: 0
|
|
4020
|
+
};
|
|
4021
|
+
}
|
|
4022
|
+
/**
|
|
4023
|
+
* Render the card.
|
|
4024
|
+
* @param props - the copy seat, the host call and the command fallback.
|
|
4025
|
+
* @returns the card.
|
|
4026
|
+
*/
|
|
4027
|
+
function DiagnosticsCard(props) {
|
|
4028
|
+
const { t, api } = props;
|
|
4029
|
+
const translate = (key, values) => t(key, values);
|
|
4030
|
+
const [state, setState] = (0, react.useState)({ phase: "loading" });
|
|
4031
|
+
const [refreshing, setRefreshing] = (0, react.useState)(false);
|
|
4032
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
4033
|
+
const ticket = (0, react.useRef)(0);
|
|
4034
|
+
const read = (keepView) => {
|
|
4035
|
+
const current = ticket.current + 1;
|
|
4036
|
+
ticket.current = current;
|
|
4037
|
+
if (keepView) setRefreshing(true);
|
|
4038
|
+
else setState({ phase: "loading" });
|
|
4039
|
+
api.view().then((answer) => {
|
|
4040
|
+
if (ticket.current !== current) return;
|
|
4041
|
+
setState(answer.ok ? {
|
|
4042
|
+
phase: "ready",
|
|
4043
|
+
view: answer.data
|
|
4044
|
+
} : {
|
|
4045
|
+
phase: "failed",
|
|
4046
|
+
failure: answer.failure
|
|
4047
|
+
});
|
|
4048
|
+
}).finally(() => {
|
|
4049
|
+
if (ticket.current === current) setRefreshing(false);
|
|
4050
|
+
});
|
|
4051
|
+
};
|
|
4052
|
+
(0, react.useEffect)(() => {
|
|
4053
|
+
read(false);
|
|
4054
|
+
}, []);
|
|
4055
|
+
const refresh = () => {
|
|
4056
|
+
read(true);
|
|
4057
|
+
};
|
|
4058
|
+
const summary = state.phase === "ready" ? summaryOf(state.view.runtime) : void 0;
|
|
4059
|
+
const header = (0, react.createElement)("button", {
|
|
4060
|
+
type: "button",
|
|
4061
|
+
className: "evolution-param-head",
|
|
4062
|
+
"aria-expanded": open ? "true" : "false",
|
|
4063
|
+
onClick: () => {
|
|
4064
|
+
setOpen(!open);
|
|
4065
|
+
}
|
|
4066
|
+
}, (0, react.createElement)("span", { className: "evolution-param-card-title" }, t("diagTitle")), summary === void 0 ? null : (0, react.createElement)("span", {
|
|
4067
|
+
className: "evo-diag-badge",
|
|
4068
|
+
"data-status": summary.status
|
|
4069
|
+
}, t(SUMMARY_KEYS[summary.status], summary.count === 0 ? void 0 : { n: summary.count })), (0, react.createElement)("span", { className: "evolution-param-chevron" }, open ? "▲" : "▼"));
|
|
4070
|
+
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", {
|
|
4071
|
+
key: "runtime-" + row.key,
|
|
4072
|
+
className: "evo-diag-item",
|
|
4073
|
+
"data-kind": "runtime",
|
|
4074
|
+
"data-status": row.status
|
|
4075
|
+
}, (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)))));
|
|
4076
|
+
const body = () => {
|
|
4077
|
+
if (state.phase === "loading") return (0, react.createElement)("p", { className: "evolution-param-loading" }, t("stateLoading"));
|
|
4078
|
+
if (state.phase === "failed") return FailureBlock({
|
|
4079
|
+
failure: state.failure,
|
|
4080
|
+
t: translate,
|
|
4081
|
+
fallback: props.fallback
|
|
4082
|
+
});
|
|
4083
|
+
const view = state.view;
|
|
4084
|
+
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", {
|
|
4085
|
+
type: "button",
|
|
4086
|
+
className: "evolution-param-button",
|
|
4087
|
+
onClick: refresh,
|
|
4088
|
+
disabled: refreshing,
|
|
4089
|
+
"data-action": "diag-refresh"
|
|
4090
|
+
}, t(refreshing ? "diagRefreshing" : "diagRefresh"))), runtime(view), view.runtime.length === 0 ? (0, react.createElement)("p", { className: "evolution-param-note" }, t("diagNoRows")) : null, (0, react.createElement)("p", {
|
|
4091
|
+
className: "evolution-param-note",
|
|
4092
|
+
"data-role": "diag-purpose"
|
|
4093
|
+
}, t("diagPurpose")), (0, react.createElement)("p", {
|
|
4094
|
+
className: "evolution-param-hint",
|
|
4095
|
+
"data-role": "diag-footer"
|
|
4096
|
+
}, t("diagFooter", {
|
|
4097
|
+
version: String(view.schemaVersion),
|
|
4098
|
+
at: new Date(view.generatedAt).toLocaleString()
|
|
4099
|
+
})));
|
|
4100
|
+
};
|
|
4101
|
+
return (0, react.createElement)("section", {
|
|
4102
|
+
className: "evolution-param-card",
|
|
4103
|
+
"data-card": "diagnostics"
|
|
4104
|
+
}, header, open ? (0, react.createElement)("div", { className: "evolution-param-body" }, body()) : null);
|
|
4105
|
+
}
|
|
4106
|
+
//#endregion
|
|
3984
4107
|
//#region src/panels/history/client/Workbench.ts
|
|
3985
4108
|
/**
|
|
3986
4109
|
* The family's one task page. G3 owns the history view; later groups add health
|
|
@@ -3989,11 +4112,6 @@ window.__ModuleLoader__.load({
|
|
|
3989
4112
|
*/
|
|
3990
4113
|
/** Stable page id used by the sidebar row, the page slot key, and navigation params. */
|
|
3991
4114
|
const WORKBENCH_ID = "evolution.workbench";
|
|
3992
|
-
/** Convert a navigation params value to a skill name only when it is a non-empty string. */
|
|
3993
|
-
function skillOf(params) {
|
|
3994
|
-
const value = params?.["skillName"];
|
|
3995
|
-
return typeof value === "string" && value !== "" ? value : void 0;
|
|
3996
|
-
}
|
|
3997
4115
|
/**
|
|
3998
4116
|
* Render the workbench page. The platform keeps it mounted; this component only
|
|
3999
4117
|
* consumes a new explicit revision exactly once.
|
|
@@ -4001,41 +4119,41 @@ window.__ModuleLoader__.load({
|
|
|
4001
4119
|
* @returns the page surface.
|
|
4002
4120
|
*/
|
|
4003
4121
|
function WorkbenchPage(props) {
|
|
4004
|
-
const
|
|
4005
|
-
const
|
|
4006
|
-
const
|
|
4122
|
+
const state = (0, react.useSyncExternalStore)(props.store.subscribe, props.store.getState, props.store.getState);
|
|
4123
|
+
const view = state.view;
|
|
4124
|
+
const selectedSkill = state.selectedSkill;
|
|
4125
|
+
const focusRevision = state.focusRevision;
|
|
4126
|
+
const openHistory = (skill) => {
|
|
4127
|
+
props.store.openHistory(skill);
|
|
4128
|
+
};
|
|
4129
|
+
const hasMerge = props.hasView?.("merge") ?? true;
|
|
4130
|
+
const hasHealth = props.hasView?.("health") ?? true;
|
|
4007
4131
|
(0, react.useEffect)(() => {
|
|
4008
|
-
if (
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
setView("history");
|
|
4016
|
-
const skill = skillOf(props.params);
|
|
4017
|
-
if (skill !== void 0) setSelectedSkill(skill);
|
|
4018
|
-
}, [props.params, props.revision]);
|
|
4132
|
+
if (view === "merge" && !hasMerge || view === "health" && !hasHealth) props.store.setView("history");
|
|
4133
|
+
}, [
|
|
4134
|
+
view,
|
|
4135
|
+
hasMerge,
|
|
4136
|
+
hasHealth,
|
|
4137
|
+
props.store
|
|
4138
|
+
]);
|
|
4019
4139
|
const viewOrder = [
|
|
4020
4140
|
"history",
|
|
4021
4141
|
"pending",
|
|
4022
4142
|
"merge",
|
|
4023
|
-
"health"
|
|
4024
|
-
|
|
4143
|
+
"health",
|
|
4144
|
+
"diagnostics"
|
|
4145
|
+
].filter((value) => (value !== "merge" || hasMerge) && (value !== "health" || hasHealth));
|
|
4025
4146
|
const onTabKey = (event, current) => {
|
|
4026
4147
|
const delta = event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0;
|
|
4027
4148
|
if (delta === 0 && event.key !== "Home" && event.key !== "End") return;
|
|
4028
4149
|
event.preventDefault();
|
|
4029
4150
|
const index = viewOrder.indexOf(current);
|
|
4030
4151
|
const next = event.key === "Home" ? viewOrder[0] : event.key === "End" ? viewOrder[viewOrder.length - 1] : viewOrder[(index + delta + viewOrder.length) % viewOrder.length];
|
|
4031
|
-
setView(next);
|
|
4152
|
+
props.store.setView(next);
|
|
4032
4153
|
requestAnimationFrame(() => {
|
|
4033
4154
|
document.getElementById("evo-workbench-tab-" + next)?.focus();
|
|
4034
4155
|
});
|
|
4035
4156
|
};
|
|
4036
|
-
const hasMerge = props.hasView?.("merge") ?? true;
|
|
4037
|
-
const hasHealth = props.hasView?.("health") ?? true;
|
|
4038
|
-
const focusRevision = props.revision === lastRevision.current ? props.revision : void 0;
|
|
4039
4157
|
return (0, react.createElement)("section", {
|
|
4040
4158
|
className: "evolution-page-surface evo-workbench",
|
|
4041
4159
|
"data-page": view
|
|
@@ -4052,7 +4170,7 @@ window.__ModuleLoader__.load({
|
|
|
4052
4170
|
"aria-controls": "evo-workbench-panel",
|
|
4053
4171
|
"data-active": view === "history" ? "true" : void 0,
|
|
4054
4172
|
onClick: () => {
|
|
4055
|
-
setView("history");
|
|
4173
|
+
props.store.setView("history");
|
|
4056
4174
|
},
|
|
4057
4175
|
onKeyDown: (event) => {
|
|
4058
4176
|
onTabKey(event, "history");
|
|
@@ -4066,7 +4184,7 @@ window.__ModuleLoader__.load({
|
|
|
4066
4184
|
"aria-controls": "evo-workbench-panel",
|
|
4067
4185
|
"data-active": view === "pending" ? "true" : void 0,
|
|
4068
4186
|
onClick: () => {
|
|
4069
|
-
setView("pending");
|
|
4187
|
+
props.store.setView("pending");
|
|
4070
4188
|
},
|
|
4071
4189
|
onKeyDown: (event) => {
|
|
4072
4190
|
onTabKey(event, "pending");
|
|
@@ -4080,7 +4198,7 @@ window.__ModuleLoader__.load({
|
|
|
4080
4198
|
"aria-controls": "evo-workbench-panel",
|
|
4081
4199
|
"data-active": view === "merge" ? "true" : void 0,
|
|
4082
4200
|
onClick: () => {
|
|
4083
|
-
setView("merge");
|
|
4201
|
+
props.store.setView("merge");
|
|
4084
4202
|
},
|
|
4085
4203
|
onKeyDown: (event) => {
|
|
4086
4204
|
onTabKey(event, "merge");
|
|
@@ -4094,12 +4212,26 @@ window.__ModuleLoader__.load({
|
|
|
4094
4212
|
"aria-controls": "evo-workbench-panel",
|
|
4095
4213
|
"data-active": view === "health" ? "true" : void 0,
|
|
4096
4214
|
onClick: () => {
|
|
4097
|
-
setView("health");
|
|
4215
|
+
props.store.setView("health");
|
|
4098
4216
|
},
|
|
4099
4217
|
onKeyDown: (event) => {
|
|
4100
4218
|
onTabKey(event, "health");
|
|
4101
4219
|
}
|
|
4102
|
-
}, props.t("workbench.health"))
|
|
4220
|
+
}, props.t("workbench.health")), (0, react.createElement)("button", {
|
|
4221
|
+
id: "evo-workbench-tab-diagnostics",
|
|
4222
|
+
type: "button",
|
|
4223
|
+
role: "tab",
|
|
4224
|
+
tabIndex: view === "diagnostics" ? 0 : -1,
|
|
4225
|
+
"aria-selected": view === "diagnostics" ? "true" : "false",
|
|
4226
|
+
"aria-controls": "evo-workbench-panel",
|
|
4227
|
+
"data-active": view === "diagnostics" ? "true" : void 0,
|
|
4228
|
+
onClick: () => {
|
|
4229
|
+
props.store.setView("diagnostics");
|
|
4230
|
+
},
|
|
4231
|
+
onKeyDown: (event) => {
|
|
4232
|
+
onTabKey(event, "diagnostics");
|
|
4233
|
+
}
|
|
4234
|
+
}, props.t("workbench.diagnostics")))), (0, react.createElement)("div", {
|
|
4103
4235
|
className: "evo-workbench-body",
|
|
4104
4236
|
id: "evo-workbench-panel",
|
|
4105
4237
|
role: "tabpanel"
|
|
@@ -4107,27 +4239,30 @@ window.__ModuleLoader__.load({
|
|
|
4107
4239
|
...props.history,
|
|
4108
4240
|
focusSkill: selectedSkill,
|
|
4109
4241
|
focusRevision
|
|
4110
|
-
}) : view === "pending" ? (0, react.createElement)(PendingCard, props.pending) : null, (0, react.createElement)("div", {
|
|
4242
|
+
}) : view === "pending" ? (0, react.createElement)(PendingCard, props.pending) : view === "diagnostics" ? (0, react.createElement)(DiagnosticsCard, props.diagnostics) : null, (0, react.createElement)("div", {
|
|
4111
4243
|
hidden: view !== "merge" && view !== "health",
|
|
4112
4244
|
className: "evo-workbench-feature"
|
|
4113
|
-
}, props.renderSlot?.("evolution.workbench.view", {
|
|
4245
|
+
}, props.renderSlot?.("evolution.workbench.view", {
|
|
4246
|
+
activeView: view,
|
|
4247
|
+
openHistory
|
|
4248
|
+
}) ?? null)));
|
|
4114
4249
|
}
|
|
4115
|
-
/**
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
}
|
|
4250
|
+
/** The sidebar icon; the platform renders the row label, tooltip and active state. */
|
|
4251
|
+
function WorkbenchIcon(props) {
|
|
4252
|
+
const size = props.size ?? 16;
|
|
4253
|
+
return (0, react.createElement)("svg", {
|
|
4254
|
+
width: size,
|
|
4255
|
+
height: size,
|
|
4256
|
+
viewBox: "0 0 16 16",
|
|
4257
|
+
"aria-hidden": "true",
|
|
4258
|
+
focusable: "false"
|
|
4259
|
+
}, (0, react.createElement)("path", {
|
|
4260
|
+
d: "M2 3.5h12M2 8h12M2 12.5h8",
|
|
4261
|
+
fill: "none",
|
|
4262
|
+
stroke: "currentColor",
|
|
4263
|
+
"stroke-width": props.active === true ? 1.7 : 1.4,
|
|
4264
|
+
"stroke-linecap": "round"
|
|
4265
|
+
}));
|
|
4131
4266
|
}
|
|
4132
4267
|
/** Workbench shell and sidebar row; colours come from the platform tokens. */
|
|
4133
4268
|
const WORKBENCH_CSS = [
|
|
@@ -4140,62 +4275,87 @@ window.__ModuleLoader__.load({
|
|
|
4140
4275
|
".evo-workbench-tabs button:focus-visible{outline:var(--evo-focus-ring);outline-offset:var(--evo-focus-offset)}",
|
|
4141
4276
|
".evo-workbench-title{margin:0;font-size:var(--evo-type-lg);font-weight:600;line-height:var(--evo-leading-base)}",
|
|
4142
4277
|
".evo-workbench-body{display:flex;flex:1;min-height:0;min-width:0;overflow:hidden}",
|
|
4143
|
-
".evo-workbench-
|
|
4144
|
-
".evo-workbench-nav:hover,.evo-workbench-nav[data-active=\"true\"]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}",
|
|
4145
|
-
".evo-workbench-nav:focus-visible{outline:var(--evo-focus-ring);outline-offset:var(--evo-focus-offset)}",
|
|
4278
|
+
".evo-workbench-body>.evolution-param-card{flex:1;min-width:0;overflow:auto}",
|
|
4146
4279
|
"@media (max-width:720px){.evo-workbench-head{flex-wrap:wrap}.evo-workbench-tabs{flex-wrap:wrap}}"
|
|
4147
4280
|
].join("");
|
|
4148
4281
|
//#endregion
|
|
4149
|
-
//#region src/panels/history/client/
|
|
4150
|
-
function
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4282
|
+
//#region src/panels/history/client/WorkbenchStore.ts
|
|
4283
|
+
function createWorkbenchStore(initial = {
|
|
4284
|
+
view: "history",
|
|
4285
|
+
focusRevision: 0
|
|
4286
|
+
}) {
|
|
4287
|
+
let state = initial;
|
|
4288
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
4289
|
+
const emit = (next) => {
|
|
4290
|
+
state = next;
|
|
4291
|
+
for (const listener of listeners) listener();
|
|
4292
|
+
};
|
|
4293
|
+
return {
|
|
4294
|
+
getState: () => state,
|
|
4295
|
+
subscribe(listener) {
|
|
4296
|
+
listeners.add(listener);
|
|
4297
|
+
return () => {
|
|
4298
|
+
listeners.delete(listener);
|
|
4299
|
+
};
|
|
4300
|
+
},
|
|
4301
|
+
setView(view) {
|
|
4302
|
+
if (state.view !== view) emit({
|
|
4303
|
+
...state,
|
|
4304
|
+
view
|
|
4305
|
+
});
|
|
4306
|
+
},
|
|
4307
|
+
openHistory(skill) {
|
|
4308
|
+
emit({
|
|
4309
|
+
view: "history",
|
|
4310
|
+
...skill === void 0 ? {} : { selectedSkill: skill },
|
|
4311
|
+
focusRevision: state.focusRevision + 1
|
|
4312
|
+
});
|
|
4313
|
+
},
|
|
4314
|
+
clearSelection() {
|
|
4315
|
+
const { selectedSkill: _selected, ...rest } = state;
|
|
4316
|
+
emit({
|
|
4317
|
+
...rest,
|
|
4318
|
+
focusRevision: state.focusRevision + 1
|
|
4319
|
+
});
|
|
4320
|
+
}
|
|
4159
4321
|
};
|
|
4160
4322
|
}
|
|
4323
|
+
//#endregion
|
|
4324
|
+
//#region src/panels/history/client/index.ts
|
|
4161
4325
|
/**
|
|
4162
|
-
* Register the locale namespace, the workbench
|
|
4326
|
+
* Register the locale namespace, the workbench main occupant and its sidebar row.
|
|
4163
4327
|
* @param ctx - client cordis context.
|
|
4164
4328
|
*/
|
|
4165
4329
|
function apply$1(ctx) {
|
|
4166
|
-
const seam = ctx;
|
|
4167
4330
|
const api = createSkillHistoryApi();
|
|
4168
|
-
|
|
4169
|
-
ctx.effect(() => {
|
|
4331
|
+
const settingsSeat = createSettingsSeat(ctx);
|
|
4332
|
+
ctx.effect(() => () => {
|
|
4333
|
+
settingsSeat.dispose?.();
|
|
4334
|
+
}, "evolution-ui: settings seat");
|
|
4335
|
+
let approvalConfirmMode = "ask";
|
|
4336
|
+
const approvalForm = settingsSeat.get("evolution-approval");
|
|
4337
|
+
const stopApprovalWatch = approvalForm?.subscribe(() => {
|
|
4338
|
+
approvalConfirmMode = confirmModeFrom(approvalForm.getSnapshot().user);
|
|
4339
|
+
});
|
|
4340
|
+
if (stopApprovalWatch !== void 0) ctx.effect(() => stopApprovalWatch, "evolution-history: approval confirm mode");
|
|
4341
|
+
const confirmMode = () => approvalConfirmMode;
|
|
4342
|
+
const hasView = (view) => {
|
|
4170
4343
|
try {
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
});
|
|
4175
|
-
registered = true;
|
|
4176
|
-
return dispose;
|
|
4177
|
-
} catch (error) {
|
|
4178
|
-
ctx.logger.warn("evolution-skill-history: the locale seat refused the dictionary (" + String(error) + "); the workbench falls back to the bundle copy");
|
|
4179
|
-
return () => {};
|
|
4344
|
+
return (ctx.slots?.entries?.("evolution.workbench.view") ?? []).some((entry) => entry.options?.id === (view === "merge" ? "merge" : "skill-health"));
|
|
4345
|
+
} catch {
|
|
4346
|
+
return false;
|
|
4180
4347
|
}
|
|
4181
|
-
}
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
const lookup = (key) => {
|
|
4185
|
-
const own = fallback[key] ?? key;
|
|
4186
|
-
if (!registered) return own;
|
|
4348
|
+
};
|
|
4349
|
+
const pendingT = (key, values) => {
|
|
4350
|
+
let template;
|
|
4187
4351
|
try {
|
|
4188
|
-
|
|
4352
|
+
template = ctx.locale.bind(NS$1)(key);
|
|
4189
4353
|
} catch {
|
|
4190
|
-
|
|
4354
|
+
template = message(key);
|
|
4191
4355
|
}
|
|
4356
|
+
return values === void 0 ? template : fill(template, values);
|
|
4192
4357
|
};
|
|
4193
|
-
const
|
|
4194
|
-
const template = lookup(key);
|
|
4195
|
-
if (values === void 0) return template;
|
|
4196
|
-
return template.replace(/\{(\w+)\}/g, (match, slot) => values[slot] === void 0 ? match : String(values[slot]));
|
|
4197
|
-
};
|
|
4198
|
-
const historyFace = () => ({
|
|
4358
|
+
const historyFace = (t) => ({
|
|
4199
4359
|
t,
|
|
4200
4360
|
format: (key, values) => t(key, values),
|
|
4201
4361
|
loadSkills: api.skills,
|
|
@@ -4209,61 +4369,44 @@ window.__ModuleLoader__.load({
|
|
|
4209
4369
|
footnotes: t("markdown.footnotes")
|
|
4210
4370
|
}
|
|
4211
4371
|
});
|
|
4212
|
-
const pendingT = (key, values) => {
|
|
4213
|
-
let template;
|
|
4214
|
-
try {
|
|
4215
|
-
template = seam.locale.bind(NS$1)(key);
|
|
4216
|
-
} catch {
|
|
4217
|
-
template = message(key);
|
|
4218
|
-
}
|
|
4219
|
-
return values === void 0 ? template : fill(template, values);
|
|
4220
|
-
};
|
|
4221
|
-
const settingsSeat = createSettingsSeat(ctx);
|
|
4222
|
-
let approvalConfirmMode = "ask";
|
|
4223
|
-
const approvalForm = settingsSeat.get("evolution-approval");
|
|
4224
|
-
const stopApprovalWatch = approvalForm?.subscribe(() => {
|
|
4225
|
-
approvalConfirmMode = confirmModeFrom(approvalForm.getSnapshot().user);
|
|
4226
|
-
});
|
|
4227
|
-
if (stopApprovalWatch !== void 0) ctx.effect(() => stopApprovalWatch, "evolution-history: approval confirm mode");
|
|
4228
|
-
const confirmMode = () => approvalConfirmMode;
|
|
4229
|
-
const hasView = (view) => {
|
|
4230
|
-
try {
|
|
4231
|
-
return (seam.slots.entries?.("evolution.workbench.view") ?? []).some((entry) => entry.options?.id === (view === "merge" ? "merge" : "skill-health"));
|
|
4232
|
-
} catch {
|
|
4233
|
-
return false;
|
|
4234
|
-
}
|
|
4235
|
-
};
|
|
4236
4372
|
const pendingFace = () => ({
|
|
4237
4373
|
t: pendingT,
|
|
4238
4374
|
api: createApprovalApi((input, init) => fetch(input, init)),
|
|
4239
4375
|
confirmMode,
|
|
4240
4376
|
fallback: FALLBACKS.approval
|
|
4241
4377
|
});
|
|
4242
|
-
const
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
id: WORKBENCH_ID,
|
|
4247
|
-
order: 35,
|
|
4248
|
-
label,
|
|
4249
|
-
inject: () => ({ t })
|
|
4250
|
-
}, WorkbenchNav);
|
|
4378
|
+
const diagnosticsFace = () => ({
|
|
4379
|
+
t: pendingT,
|
|
4380
|
+
api: createDiagnosticsApi((input, init) => fetch(input, init)),
|
|
4381
|
+
fallback: FALLBACKS.diagnostics
|
|
4251
4382
|
});
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4383
|
+
const workbenchStore = createWorkbenchStore();
|
|
4384
|
+
registerSidebarPanel(ctx, {
|
|
4385
|
+
id: WORKBENCH_ID,
|
|
4386
|
+
order: 35,
|
|
4387
|
+
namespace: NS,
|
|
4388
|
+
dictionaries: {
|
|
4389
|
+
zh,
|
|
4390
|
+
en
|
|
4391
|
+
},
|
|
4392
|
+
styles: {
|
|
4393
|
+
css: CSS + WORKBENCH_CSS + PAGE_SURFACE_CSS,
|
|
4394
|
+
tagId: CSS_TAG_ID
|
|
4395
|
+
},
|
|
4396
|
+
icon: WorkbenchIcon,
|
|
4397
|
+
component: WorkbenchPage,
|
|
4398
|
+
body: (t) => ({
|
|
4399
|
+
t,
|
|
4400
|
+
store: workbenchStore,
|
|
4401
|
+
history: historyFace(t),
|
|
4402
|
+
pending: pendingFace(),
|
|
4403
|
+
diagnostics: diagnosticsFace(),
|
|
4404
|
+
hasView
|
|
4405
|
+
}),
|
|
4406
|
+
children: { "evolution.workbench.view": {
|
|
4407
|
+
kind: "list",
|
|
4408
|
+
scope: "root"
|
|
4409
|
+
} }
|
|
4267
4410
|
});
|
|
4268
4411
|
}
|
|
4269
4412
|
//#endregion
|