@lmzhen/dsh-evolution-ui 0.32.6 → 0.33.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 +584 -550
- package/lib/types/panels/history/client/Workbench.d.ts +9 -19
- 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 +3 -0
- package/lib/types/panels/settings/seam.d.ts +17 -1
- 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,13 @@ 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
|
+
saveConflict: "这个值没有被接受:设置已被其他页面修改。请重读后再保存。",
|
|
1179
862
|
resetDone: "已恢复默认值。",
|
|
1180
863
|
refused: "这个值没有被接受(界面上保持原样)。想看原因:在对话里让它说明。",
|
|
1181
864
|
resetRefused: "恢复默认值没有生效(这一行还是你的值)。想看原因:在对话里让它说明。",
|
|
@@ -1274,10 +957,13 @@ window.__ModuleLoader__.load({
|
|
|
1274
957
|
invalidSelect: "Not one of the allowed values",
|
|
1275
958
|
invalidAtLeast: "must not be below {n}",
|
|
1276
959
|
approvalConfirmNamed: "About to approve: {n}",
|
|
960
|
+
approvalRawSummary: "Raw summary",
|
|
1277
961
|
approvalConfirmYes: "Approve",
|
|
1278
962
|
approvalConfirmNo: "Cancel",
|
|
1279
963
|
resetTo: "Restores {value}",
|
|
1280
964
|
saved: "Saved.",
|
|
965
|
+
saveReadbackFailed: "The write was accepted, but reading it back failed; refresh and confirm.",
|
|
966
|
+
saveConflict: "The value was not accepted: settings changed in another page. Re-read before saving again.",
|
|
1281
967
|
resetDone: "Default restored.",
|
|
1282
968
|
refused: "The value was not accepted (the page keeps what you see). For the reason, ask in the conversation.",
|
|
1283
969
|
resetRefused: "The reset did not take (this row still holds your value). For the reason, ask in the conversation.",
|
|
@@ -1589,7 +1275,7 @@ window.__ModuleLoader__.load({
|
|
|
1589
1275
|
* @returns the card.
|
|
1590
1276
|
*/
|
|
1591
1277
|
function ParamCardView(props) {
|
|
1592
|
-
const { t, fields, mutateOnce, clear, namespace, snapshot } = props;
|
|
1278
|
+
const { t, fields, mutateOnce, saveOnce, clear, namespace, snapshot } = props;
|
|
1593
1279
|
const lang = props.lang?.() ?? "zh";
|
|
1594
1280
|
const [open, setOpen] = (0, react.useState)(false);
|
|
1595
1281
|
const [draft, setDraft] = (0, react.useState)({});
|
|
@@ -1616,8 +1302,12 @@ window.__ModuleLoader__.load({
|
|
|
1616
1302
|
setPending(null);
|
|
1617
1303
|
setBusy(false);
|
|
1618
1304
|
setClearing(null);
|
|
1305
|
+
if (pending.readbackFailed === true) {
|
|
1306
|
+
setError(t("saveReadbackFailed"));
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1619
1309
|
if (!pending.accepted) {
|
|
1620
|
-
setError(t(pending.resets ? "resetRefused" : "refused"));
|
|
1310
|
+
setError(t(pending.resets ? "resetRefused" : pending.kind === "conflict" ? "saveConflict" : "refused"));
|
|
1621
1311
|
return;
|
|
1622
1312
|
}
|
|
1623
1313
|
if (pending.accepted) if (pending.resets) {
|
|
@@ -1708,12 +1398,19 @@ window.__ModuleLoader__.load({
|
|
|
1708
1398
|
setNote("");
|
|
1709
1399
|
(async () => {
|
|
1710
1400
|
try {
|
|
1401
|
+
const ops = dirty.map((field) => ({
|
|
1402
|
+
op: "set",
|
|
1403
|
+
path: [keyOf(field)],
|
|
1404
|
+
value: parseFor(field.control, textOf(field))
|
|
1405
|
+
}));
|
|
1406
|
+
const result = saveOnce === void 0 ? {
|
|
1407
|
+
written: await mutateOnce(ops),
|
|
1408
|
+
readback: "ok"
|
|
1409
|
+
} : await saveOnce(ops);
|
|
1711
1410
|
setPending({
|
|
1712
|
-
accepted:
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
value: parseFor(field.control, textOf(field))
|
|
1716
|
-
}))),
|
|
1411
|
+
accepted: result.written && result.readback === "ok",
|
|
1412
|
+
readbackFailed: result.written && result.readback === "failed",
|
|
1413
|
+
...result.kind === void 0 ? {} : { kind: result.kind },
|
|
1717
1414
|
resets: false
|
|
1718
1415
|
});
|
|
1719
1416
|
} catch (caught) {
|
|
@@ -1787,17 +1484,6 @@ window.__ModuleLoader__.load({
|
|
|
1787
1484
|
* @param props - the injected card face.
|
|
1788
1485
|
* @returns the card with its live row snapshot.
|
|
1789
1486
|
*/
|
|
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
1487
|
const MISSING_SNAPSHOT = {
|
|
1802
1488
|
status: "unavailable",
|
|
1803
1489
|
value: void 0,
|
|
@@ -1812,7 +1498,7 @@ window.__ModuleLoader__.load({
|
|
|
1812
1498
|
revive: () => {}
|
|
1813
1499
|
};
|
|
1814
1500
|
function PluginParamCard(props) {
|
|
1815
|
-
const source = props.hooks?.paramSection ??
|
|
1501
|
+
const source = props.hooks?.paramSection ?? MISSING_SOURCE;
|
|
1816
1502
|
const snapshot = (0, react.useSyncExternalStore)(source.subscribe, source.getSnapshot, source.getSnapshot);
|
|
1817
1503
|
return ParamCardView({
|
|
1818
1504
|
namespace: props.namespace,
|
|
@@ -1820,16 +1506,114 @@ window.__ModuleLoader__.load({
|
|
|
1820
1506
|
t: props.t,
|
|
1821
1507
|
...props.lang === void 0 ? {} : { lang: props.lang },
|
|
1822
1508
|
mutateOnce: props.mutateOnce,
|
|
1509
|
+
...props.saveOnce === void 0 ? {} : { saveOnce: props.saveOnce },
|
|
1823
1510
|
clear: props.clear,
|
|
1824
1511
|
snapshot
|
|
1825
1512
|
});
|
|
1826
1513
|
}
|
|
1827
1514
|
//#endregion
|
|
1828
|
-
//#region src/
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1515
|
+
//#region ../evolution-client-kit/src/panel.ts
|
|
1516
|
+
function interpolate(text, values) {
|
|
1517
|
+
if (values === void 0) return text;
|
|
1518
|
+
return text.replace(/\{(\w+)\}/g, (_match, key) => String(values[key] ?? ""));
|
|
1519
|
+
}
|
|
1520
|
+
function injectStyles$1(doc, css, tagId) {
|
|
1521
|
+
if (doc === void 0) return () => {};
|
|
1522
|
+
doc.querySelector("style[data-plugin-css=\"" + tagId + "\"]")?.remove();
|
|
1523
|
+
const tag = doc.createElement("style");
|
|
1524
|
+
tag.setAttribute("data-plugin-css", tagId);
|
|
1525
|
+
tag.textContent = css;
|
|
1526
|
+
doc.head.append(tag);
|
|
1527
|
+
return () => {
|
|
1528
|
+
tag.remove();
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Register one sidebar row and its main body under one lifecycle.
|
|
1533
|
+
* @param ctx - the client context carrying the slots and locale seats.
|
|
1534
|
+
* @param spec - the feature-specific row/body descriptor.
|
|
1535
|
+
*/
|
|
1536
|
+
function registerSidebarPanel(ctx, spec) {
|
|
1537
|
+
const seam = ctx;
|
|
1538
|
+
let registered = false;
|
|
1539
|
+
ctx.effect(() => {
|
|
1540
|
+
try {
|
|
1541
|
+
const dispose = seam.locale.register(spec.namespace, spec.dictionaries);
|
|
1542
|
+
registered = true;
|
|
1543
|
+
return dispose;
|
|
1544
|
+
} catch (error) {
|
|
1545
|
+
ctx.logger.warn(spec.namespace + ": the locale seat refused the dictionary (" + String(error) + "); the panel falls back to the bundle copy");
|
|
1546
|
+
return () => {};
|
|
1547
|
+
}
|
|
1548
|
+
}, spec.namespace + ": locale");
|
|
1549
|
+
ctx.effect(() => injectStyles$1(typeof document === "undefined" ? void 0 : document, spec.styles.css, spec.styles.tagId), spec.namespace + ": styles");
|
|
1550
|
+
const fallback = spec.dictionaries[spec.fallback ?? "zh"];
|
|
1551
|
+
const lookup = (key) => {
|
|
1552
|
+
const own = fallback[key] ?? key;
|
|
1553
|
+
if (!registered) return own;
|
|
1554
|
+
try {
|
|
1555
|
+
return seam.locale.bind(spec.namespace)(key);
|
|
1556
|
+
} catch {
|
|
1557
|
+
return own;
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
const t = (key, values) => interpolate(lookup(key), values);
|
|
1561
|
+
const label = () => lookup("entry.label");
|
|
1562
|
+
seam.slots.inject("sidebar.panellist", function* () {
|
|
1563
|
+
yield seam.slots.register({
|
|
1564
|
+
name: "sidebar.panellist",
|
|
1565
|
+
id: spec.id,
|
|
1566
|
+
order: spec.order,
|
|
1567
|
+
label
|
|
1568
|
+
}, spec.icon);
|
|
1569
|
+
yield seam.slots.register({
|
|
1570
|
+
name: "main",
|
|
1571
|
+
key: spec.id,
|
|
1572
|
+
inject: () => spec.body(t),
|
|
1573
|
+
...spec.children === void 0 ? {} : { children: spec.children },
|
|
1574
|
+
...spec.store === void 0 ? {} : { store: spec.store }
|
|
1575
|
+
}, spec.component);
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1578
|
+
//#endregion
|
|
1579
|
+
//#region ../evolution-client-kit/src/generated/routes.generated.ts
|
|
1580
|
+
/**
|
|
1581
|
+
* Every family host path, generated from evolution-core's HOST_ROUTES (the ONE declaration).
|
|
1582
|
+
* A browser half cannot import the host package at runtime (client bundles keep @deepseek-ai/*
|
|
1583
|
+
* external), so the table is mirrored here mechanically; a rename in core regenerates all three
|
|
1584
|
+
* client copies, and the generator's --check makes a stale copy red.
|
|
1585
|
+
*/
|
|
1586
|
+
const ROUTES = {
|
|
1587
|
+
approval: {
|
|
1588
|
+
pending: "/api/dsh-evolution/approval/pending",
|
|
1589
|
+
preview: "/api/dsh-evolution/approval/preview",
|
|
1590
|
+
approve: "/api/dsh-evolution/approval/approve",
|
|
1591
|
+
reject: "/api/dsh-evolution/approval/reject",
|
|
1592
|
+
release: "/api/dsh-evolution/approval/release"
|
|
1593
|
+
},
|
|
1594
|
+
"skill-history": {
|
|
1595
|
+
skills: "/api/dsh-evolution/skill-history/skills",
|
|
1596
|
+
versions: "/api/dsh-evolution/skill-history/versions",
|
|
1597
|
+
diff: "/api/dsh-evolution/skill-history/versions/diff",
|
|
1598
|
+
body: "/api/dsh-evolution/skill-history/versions/body",
|
|
1599
|
+
undo: "/api/dsh-evolution/skill-history/undo",
|
|
1600
|
+
health: "/api/dsh-evolution/skill-history/health"
|
|
1601
|
+
},
|
|
1602
|
+
"skill-health": { rows: "/api/dsh-evolution/skill-health/rows" },
|
|
1603
|
+
merge: {
|
|
1604
|
+
view: "/api/dsh-evolution/merge/view",
|
|
1605
|
+
rows: "/api/dsh-evolution/merge/rows",
|
|
1606
|
+
run: "/api/dsh-evolution/merge/run",
|
|
1607
|
+
runs: "/api/dsh-evolution/merge/runs",
|
|
1608
|
+
body: "/api/dsh-evolution/merge/body",
|
|
1609
|
+
history: "/api/dsh-evolution/merge/history"
|
|
1610
|
+
},
|
|
1611
|
+
diagnostics: { view: "/api/dsh-evolution/diagnostics" },
|
|
1612
|
+
settings: {
|
|
1613
|
+
describe: "/api/dsh-evolution/settings/describe",
|
|
1614
|
+
mutate: "/api/dsh-evolution/settings/mutate"
|
|
1615
|
+
}
|
|
1616
|
+
};
|
|
1833
1617
|
//#endregion
|
|
1834
1618
|
//#region ../evolution-client-kit/src/settings-seat.ts
|
|
1835
1619
|
const SETTINGS_DESCRIBE = ROUTES.settings.describe;
|
|
@@ -1972,6 +1756,53 @@ window.__ModuleLoader__.load({
|
|
|
1972
1756
|
* @module @lmzhen/dsh-evolution-client-kit
|
|
1973
1757
|
*/
|
|
1974
1758
|
//#endregion
|
|
1759
|
+
//#region ../evolution-client-kit/src/generated/fallbacks.generated.ts
|
|
1760
|
+
/**
|
|
1761
|
+
* The command-face fallback for every Host surface, generated from evolution-commands'
|
|
1762
|
+
* COMMAND_FALLBACKS (the ONE home). When the platform's connection carrier is absent (E-325) the
|
|
1763
|
+
* panels render this hint instead of a bare HTTP failure — same use case, command face.
|
|
1764
|
+
*/
|
|
1765
|
+
const FALLBACKS = {
|
|
1766
|
+
approval: { command: "/evolution pending … /evolution approve <id> … /evolution reject <id> … /evolution release <id>" },
|
|
1767
|
+
"skill-history": { command: "/evolution skill undo <name> [--to v<N>](查看:/evolution skill history <name>)" },
|
|
1768
|
+
"skill-health": { command: "/evolution skills refresh(健康行:/evolution doctor)" },
|
|
1769
|
+
merge: { command: "/evolution merge status / pending / approve <id> / report" },
|
|
1770
|
+
diagnostics: {
|
|
1771
|
+
command: "/evolution doctor",
|
|
1772
|
+
platformCli: "dsh --json /evolution doctor"
|
|
1773
|
+
},
|
|
1774
|
+
settings: { command: "/evolution policy set <id> <value>(查看:/evolution policy)" }
|
|
1775
|
+
};
|
|
1776
|
+
//#endregion
|
|
1777
|
+
//#region ../evolution-client-kit/src/generated/states.generated.ts
|
|
1778
|
+
/** A neutral/ warn/ fail block; a fail block is role=alert so assistive tech announces it. */
|
|
1779
|
+
function Block(props) {
|
|
1780
|
+
const tone = props.tone ?? "neutral";
|
|
1781
|
+
return (0, react.createElement)("div", {
|
|
1782
|
+
"data-tone": tone,
|
|
1783
|
+
...tone === "fail" ? { role: "alert" } : {}
|
|
1784
|
+
}, props.title === void 0 ? null : (0, react.createElement)("div", { "data-tone-title": tone }, props.title), props.children);
|
|
1785
|
+
}
|
|
1786
|
+
/** One classified failure as a reader-facing sentence. */
|
|
1787
|
+
function failureText(failure, t) {
|
|
1788
|
+
switch (failure.kind) {
|
|
1789
|
+
case "unauthenticated": return t("failureUnauthorized");
|
|
1790
|
+
case "forbidden": return t("failureForbidden");
|
|
1791
|
+
case "unmounted": return t("failureUnmounted");
|
|
1792
|
+
case "server": return failure.status === 0 ? t("failureServer") : t("failureServer") + " (HTTP " + String(failure.status) + ")";
|
|
1793
|
+
case "refused": return failure.message;
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
/** The failed-state block: sentence + (when the surface exists but is unreachable) the command fallback. */
|
|
1797
|
+
function FailureBlock(props) {
|
|
1798
|
+
const hint = props.fallback === void 0 ? null : (0, react.createElement)("div", { "data-tone-hint": "true" }, props.t("fallbackHint", { command: props.fallback.command }));
|
|
1799
|
+
return Block({
|
|
1800
|
+
tone: "fail",
|
|
1801
|
+
title: failureText(props.failure, props.t),
|
|
1802
|
+
children: hint
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
//#endregion
|
|
1975
1806
|
//#region ../evolution-client-kit/src/tokens.ts
|
|
1976
1807
|
/**
|
|
1977
1808
|
* The scale, declared on one element so it never leaks into the host shell.
|
|
@@ -1981,29 +1812,8 @@ window.__ModuleLoader__.load({
|
|
|
1981
1812
|
function tokenVars(scope) {
|
|
1982
1813
|
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
1814
|
}
|
|
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
|
-
}
|
|
1815
|
+
/** Stylesheet for {@link PAGE_SURFACE_CLASS}; consumers append it to their own CSS. */
|
|
1816
|
+
const PAGE_SURFACE_CSS = ".evolution-page-surface{min-height:100%;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary)}";
|
|
2007
1817
|
//#endregion
|
|
2008
1818
|
//#region src/panels/settings/source.ts
|
|
2009
1819
|
/** What a card shows when this UI found no settings seat at all. */
|
|
@@ -2141,12 +1951,47 @@ window.__ModuleLoader__.load({
|
|
|
2141
1951
|
*/
|
|
2142
1952
|
function writeFaceFor(probe, namespace) {
|
|
2143
1953
|
const form = () => probe()?.get(namespace);
|
|
1954
|
+
const saveOnce = async (ops) => {
|
|
1955
|
+
const seat = form();
|
|
1956
|
+
if (seat === void 0) return {
|
|
1957
|
+
written: false,
|
|
1958
|
+
readback: "failed",
|
|
1959
|
+
kind: "transport"
|
|
1960
|
+
};
|
|
1961
|
+
const before = seat.getSnapshot().revision;
|
|
1962
|
+
let written = false;
|
|
1963
|
+
try {
|
|
1964
|
+
written = await seat.mutate(ops);
|
|
1965
|
+
} catch (failure) {
|
|
1966
|
+
return {
|
|
1967
|
+
written: false,
|
|
1968
|
+
readback: "failed",
|
|
1969
|
+
kind: "transport",
|
|
1970
|
+
failure
|
|
1971
|
+
};
|
|
1972
|
+
}
|
|
1973
|
+
const snapshot = seat.getSnapshot();
|
|
1974
|
+
if (!written) return {
|
|
1975
|
+
written: false,
|
|
1976
|
+
readback: "failed",
|
|
1977
|
+
kind: "conflict"
|
|
1978
|
+
};
|
|
1979
|
+
if (snapshot.status !== "ready") return {
|
|
1980
|
+
written: true,
|
|
1981
|
+
readback: "failed",
|
|
1982
|
+
kind: "transport"
|
|
1983
|
+
};
|
|
1984
|
+
return {
|
|
1985
|
+
written: true,
|
|
1986
|
+
readback: snapshot.revision !== before ? "ok" : "failed"
|
|
1987
|
+
};
|
|
1988
|
+
};
|
|
2144
1989
|
return {
|
|
2145
1990
|
mutateOnce: async (ops) => {
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
return await seat.mutate(ops);
|
|
1991
|
+
if (form() === void 0) return false;
|
|
1992
|
+
return (await saveOnce(ops)).written;
|
|
2149
1993
|
},
|
|
1994
|
+
saveOnce,
|
|
2150
1995
|
clear: async (field) => {
|
|
2151
1996
|
const seat = form();
|
|
2152
1997
|
if (seat === void 0) return false;
|
|
@@ -2283,7 +2128,7 @@ window.__ModuleLoader__.load({
|
|
|
2283
2128
|
* @param host - the document to inject into; defaults to the global one.
|
|
2284
2129
|
* @returns the disposal; a no-op when there is no document or the tag was already present.
|
|
2285
2130
|
*/
|
|
2286
|
-
function injectStyles
|
|
2131
|
+
function injectStyles(host) {
|
|
2287
2132
|
const doc = host ?? globalThis.document;
|
|
2288
2133
|
if (doc === void 0) return () => {};
|
|
2289
2134
|
doc.querySelector("style[data-plugin-css=\"@lmzhen/dsh-evolution-ui/settings.css\"]")?.remove?.();
|
|
@@ -2305,7 +2150,7 @@ window.__ModuleLoader__.load({
|
|
|
2305
2150
|
* @param ctx - client cordis context.
|
|
2306
2151
|
*/
|
|
2307
2152
|
function apply$2(ctx) {
|
|
2308
|
-
ctx.effect(() => injectStyles
|
|
2153
|
+
ctx.effect(() => injectStyles(), "evolution-settings-ui: styles");
|
|
2309
2154
|
const seam = ctx;
|
|
2310
2155
|
let registered = false;
|
|
2311
2156
|
ctx.effect(() => {
|
|
@@ -2348,54 +2193,132 @@ window.__ModuleLoader__.load({
|
|
|
2348
2193
|
ctx.inject?.(["remote"], () => {
|
|
2349
2194
|
sources.revive();
|
|
2350
2195
|
});
|
|
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()) {
|
|
2196
|
+
const cards = CLIENT_PARAM_SECTIONS.map((section) => {
|
|
2365
2197
|
const source = sources.sourceFor(section.seatKey);
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
const face = () => ({
|
|
2198
|
+
const { mutateOnce, saveOnce, clear } = writeFaceFor(probe, section.namespace);
|
|
2199
|
+
return {
|
|
2369
2200
|
namespace: section.namespace,
|
|
2370
2201
|
fields: section.fields,
|
|
2371
2202
|
t,
|
|
2372
2203
|
lang: language,
|
|
2373
2204
|
mutateOnce,
|
|
2205
|
+
saveOnce,
|
|
2374
2206
|
clear,
|
|
2375
2207
|
hooks: { paramSection: source }
|
|
2376
|
-
}
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2208
|
+
};
|
|
2209
|
+
});
|
|
2210
|
+
seam.slots.inject("settings.plugins.tab", function* () {
|
|
2211
|
+
yield seam.slots.register({
|
|
2212
|
+
name: "settings.plugins.tab",
|
|
2213
|
+
id: SECTION_ID,
|
|
2214
|
+
order: 25,
|
|
2215
|
+
label: () => t("title"),
|
|
2216
|
+
locale: NS$1,
|
|
2217
|
+
inject: () => ({ cards })
|
|
2218
|
+
}, EvolutionSettingsTab);
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
/** One family tab: every parameter card lives inside the platform's Plugins page. */
|
|
2222
|
+
function EvolutionSettingsTab(props) {
|
|
2223
|
+
return (0, react.createElement)("div", { className: "evolution-settings-tab" }, ...props.cards.map((face, index) => (0, react.createElement)(PluginParamCard, {
|
|
2224
|
+
key: face.namespace + ":" + String(index),
|
|
2225
|
+
...face
|
|
2226
|
+
})));
|
|
2227
|
+
}
|
|
2228
|
+
//#endregion
|
|
2229
|
+
//#region ../evolution-client-kit/src/generated/route-client.generated.ts
|
|
2230
|
+
/** Classify a non-2xx status: 401/403/404 keep their own kind, everything else is a server answer. */
|
|
2231
|
+
function classify(status) {
|
|
2232
|
+
if (status === 401) return {
|
|
2233
|
+
kind: "unauthenticated",
|
|
2234
|
+
status: 401
|
|
2235
|
+
};
|
|
2236
|
+
if (status === 403) return {
|
|
2237
|
+
kind: "forbidden",
|
|
2238
|
+
status: 403
|
|
2239
|
+
};
|
|
2240
|
+
if (status === 404) return {
|
|
2241
|
+
kind: "unmounted",
|
|
2242
|
+
status: 404
|
|
2243
|
+
};
|
|
2244
|
+
return {
|
|
2245
|
+
kind: "server",
|
|
2246
|
+
status
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2249
|
+
/**
|
|
2250
|
+
* One request, no retries and no body parsing for the platform's admission answers.
|
|
2251
|
+
* @param doFetch - the fetch in use (the browser's, or a spec's stub).
|
|
2252
|
+
* @param path - the exact route path from the generated ROUTES table.
|
|
2253
|
+
* @param init - standard RequestInit.
|
|
2254
|
+
* @returns the data, or the classified failure (network/parse failures read as 'server').
|
|
2255
|
+
*/
|
|
2256
|
+
async function requestJson(doFetch, path, init) {
|
|
2257
|
+
let response;
|
|
2258
|
+
try {
|
|
2259
|
+
response = await doFetch(path, init);
|
|
2260
|
+
} catch (error) {
|
|
2261
|
+
return {
|
|
2262
|
+
ok: false,
|
|
2263
|
+
failure: {
|
|
2264
|
+
kind: "server",
|
|
2265
|
+
status: 0,
|
|
2266
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
2267
|
+
}
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
if (!response.ok) return {
|
|
2271
|
+
ok: false,
|
|
2272
|
+
failure: classify(response.status)
|
|
2273
|
+
};
|
|
2274
|
+
let body;
|
|
2275
|
+
try {
|
|
2276
|
+
body = await response.json();
|
|
2277
|
+
} catch {
|
|
2278
|
+
return {
|
|
2279
|
+
ok: false,
|
|
2280
|
+
failure: {
|
|
2281
|
+
kind: "server",
|
|
2282
|
+
status: response.status
|
|
2283
|
+
}
|
|
2284
|
+
};
|
|
2285
|
+
}
|
|
2286
|
+
if (typeof body === "object" && body !== null) {
|
|
2287
|
+
const record = body;
|
|
2288
|
+
if (record.ok === true) return {
|
|
2289
|
+
ok: true,
|
|
2290
|
+
data: record.data
|
|
2291
|
+
};
|
|
2292
|
+
const nested = record.error;
|
|
2293
|
+
const errorRecord = typeof nested === "object" && nested !== null ? nested : void 0;
|
|
2294
|
+
const code = typeof record.code === "string" && record.code !== "" ? record.code : typeof errorRecord?.code === "string" ? errorRecord.code : void 0;
|
|
2295
|
+
const message = typeof record.message === "string" && record.message !== "" ? record.message : typeof errorRecord?.message === "string" ? errorRecord.message : void 0;
|
|
2296
|
+
if (record.ok === false && code !== void 0 && message !== void 0 && message !== "") return {
|
|
2297
|
+
ok: false,
|
|
2298
|
+
failure: {
|
|
2299
|
+
kind: "refused",
|
|
2300
|
+
code,
|
|
2301
|
+
message
|
|
2302
|
+
}
|
|
2303
|
+
};
|
|
2304
|
+
}
|
|
2305
|
+
return {
|
|
2306
|
+
ok: false,
|
|
2307
|
+
failure: {
|
|
2308
|
+
kind: "server",
|
|
2309
|
+
status: response.status
|
|
2310
|
+
}
|
|
2311
|
+
};
|
|
2312
|
+
}
|
|
2313
|
+
/** The one-sentence rendering legacy callers attach to a classified failure. */
|
|
2314
|
+
function failureMessage(failure) {
|
|
2315
|
+
switch (failure.kind) {
|
|
2316
|
+
case "refused": return failure.message;
|
|
2317
|
+
case "server": return failure.status === 0 ? failure.detail === void 0 || failure.detail === "" ? "网络错误:无法连接宿主" : failure.detail : "HTTP " + String(failure.status);
|
|
2318
|
+
case "unauthenticated": return "HTTP 401";
|
|
2319
|
+
case "forbidden": return "HTTP 403";
|
|
2320
|
+
case "unmounted": return "HTTP 404";
|
|
2386
2321
|
}
|
|
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
2322
|
}
|
|
2400
2323
|
//#endregion
|
|
2401
2324
|
//#region src/panels/history/client/api.ts
|
|
@@ -2600,8 +2523,7 @@ window.__ModuleLoader__.load({
|
|
|
2600
2523
|
* independent review flagged that this reader had no case at all (P2-11).
|
|
2601
2524
|
*/
|
|
2602
2525
|
function confirmModeFrom(user) {
|
|
2603
|
-
|
|
2604
|
-
return value === "ask" || value === "timeout" ? "ask" : "auto";
|
|
2526
|
+
return (typeof user === "object" && user !== null ? user.approvalConfirm : void 0) === "auto" ? "auto" : "ask";
|
|
2605
2527
|
}
|
|
2606
2528
|
/** Which word one age bucket gets; an unknown bucket falls back to the count form. */
|
|
2607
2529
|
const AGE_KEYS = Object.freeze({
|
|
@@ -2638,7 +2560,7 @@ window.__ModuleLoader__.load({
|
|
|
2638
2560
|
function PendingCard(props) {
|
|
2639
2561
|
const { t, api } = props;
|
|
2640
2562
|
const translate = (key, values) => t(key, values);
|
|
2641
|
-
const confirmMode = props.confirmMode?.() ?? "
|
|
2563
|
+
const confirmMode = props.confirmMode?.() ?? "ask";
|
|
2642
2564
|
const [armed, setArmed] = (0, react.useState)(null);
|
|
2643
2565
|
const [state, setState] = (0, react.useState)(INITIAL_PENDING_STATE);
|
|
2644
2566
|
const [open, setOpen] = (0, react.useState)(true);
|
|
@@ -2821,7 +2743,7 @@ window.__ModuleLoader__.load({
|
|
|
2821
2743
|
}, (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
2744
|
className: "evolution-param-value",
|
|
2823
2745
|
title: entry.summary
|
|
2824
|
-
}, summaryLine(entry))), (0, react.createElement)("p", { className: "evolution-param-hint" }, age(entry)), armed === entry.id ? (0, react.createElement)("p", {
|
|
2746
|
+
}, 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
2747
|
className: "evolution-param-note",
|
|
2826
2748
|
role: "status"
|
|
2827
2749
|
}, (0, react.createElement)("span", { className: "evolution-param-value" }, t("approvalConfirmNamed", { n: summaryLine(entry) })), (0, react.createElement)("button", {
|
|
@@ -2900,6 +2822,23 @@ window.__ModuleLoader__.load({
|
|
|
2900
2822
|
}, state.refreshing ? t("diagRefreshing") : t("approvalRefresh"))), open ? (0, react.createElement)("div", { className: "evolution-param-body" }, body()) : null);
|
|
2901
2823
|
}
|
|
2902
2824
|
//#endregion
|
|
2825
|
+
//#region src/panels/settings/diagnostics-state.ts
|
|
2826
|
+
/**
|
|
2827
|
+
* The diagnostics card's data face (W7 S7): ONE request to `HOST_ROUTES.diagnostics.view` through the
|
|
2828
|
+
* GENERATED route client, and the four-state answer the card renders.
|
|
2829
|
+
*
|
|
2830
|
+
* The Host route is the SAME `buildDoctorView` the CLI prints (W6/W8), so this client owns no facts
|
|
2831
|
+
* and no computation — it only renders.
|
|
2832
|
+
* @module @lmzhen/dsh-evolution-ui/panels/settings/client/diagnostics-state
|
|
2833
|
+
*/
|
|
2834
|
+
/**
|
|
2835
|
+
* Build the diagnostics face over one fetch implementation.
|
|
2836
|
+
* @param doFetch - the browser fetch, or a spec stub.
|
|
2837
|
+
*/
|
|
2838
|
+
function createDiagnosticsApi(doFetch = fetch) {
|
|
2839
|
+
return { view: async () => await requestJson(doFetch, ROUTES.diagnostics.view, { headers: { accept: "application/json" } }) };
|
|
2840
|
+
}
|
|
2841
|
+
//#endregion
|
|
2903
2842
|
//#region src/panels/history/client/messages.ts
|
|
2904
2843
|
/**
|
|
2905
2844
|
* Panel copy, in a zh/en dictionary registered through the platform’s locale seat.
|
|
@@ -2919,6 +2858,7 @@ window.__ModuleLoader__.load({
|
|
|
2919
2858
|
"workbench.pending": "待批",
|
|
2920
2859
|
"workbench.merge": "合并",
|
|
2921
2860
|
"workbench.health": "健康",
|
|
2861
|
+
"workbench.diagnostics": "诊断",
|
|
2922
2862
|
hint: "每次技能写入留下的内容版本。回退只改 SKILL.md 正文,标记、使用计数与策展状态不变。内容相同的版本会同时标「当前」。",
|
|
2923
2863
|
search: "按名字或描述筛选",
|
|
2924
2864
|
"search.none": "没有匹配的技能。",
|
|
@@ -3010,6 +2950,7 @@ window.__ModuleLoader__.load({
|
|
|
3010
2950
|
"workbench.pending": "Pending",
|
|
3011
2951
|
"workbench.merge": "Merge",
|
|
3012
2952
|
"workbench.health": "Health",
|
|
2953
|
+
"workbench.diagnostics": "Diagnostics",
|
|
3013
2954
|
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
2955
|
search: "Filter by name or description",
|
|
3015
2956
|
"search.none": "No skill matches.",
|
|
@@ -3784,16 +3725,18 @@ window.__ModuleLoader__.load({
|
|
|
3784
3725
|
* @param name - the skill to open.
|
|
3785
3726
|
* @param onFailure - what to do with a failed read.
|
|
3786
3727
|
*/
|
|
3787
|
-
const showVersions = (name, onFailure = failed) => {
|
|
3728
|
+
const showVersions = (name, onFailure = failed, keepContent = false) => {
|
|
3788
3729
|
const ticket = readTicket.current + 1;
|
|
3789
3730
|
readTicket.current = ticket;
|
|
3790
3731
|
setSelected(name);
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3732
|
+
if (!keepContent) {
|
|
3733
|
+
setPayload(void 0);
|
|
3734
|
+
setVersionsFailed(void 0);
|
|
3735
|
+
setPending(void 0);
|
|
3736
|
+
setExpanded(void 0);
|
|
3737
|
+
setDiffs(/* @__PURE__ */ new Map());
|
|
3738
|
+
setBodies(/* @__PURE__ */ new Map());
|
|
3739
|
+
}
|
|
3797
3740
|
face.loadVersions(name).then((next) => {
|
|
3798
3741
|
if (readTicket.current === ticket) {
|
|
3799
3742
|
setPayload(next);
|
|
@@ -3816,7 +3759,7 @@ window.__ModuleLoader__.load({
|
|
|
3816
3759
|
/** What the refresh control does: both reads again, and the sentence stays. */
|
|
3817
3760
|
const refresh = () => {
|
|
3818
3761
|
reloadSkills();
|
|
3819
|
-
if (selected !== void 0) showVersions(selected);
|
|
3762
|
+
if (selected !== void 0) showVersions(selected, failed, true);
|
|
3820
3763
|
};
|
|
3821
3764
|
(0, react.useEffect)(() => {
|
|
3822
3765
|
if (face.focusSkill === void 0 || face.focusSkill === "") return;
|
|
@@ -3981,6 +3924,128 @@ window.__ModuleLoader__.load({
|
|
|
3981
3924
|
return next;
|
|
3982
3925
|
}
|
|
3983
3926
|
//#endregion
|
|
3927
|
+
//#region src/panels/settings/DiagnosticsCard.ts
|
|
3928
|
+
/**
|
|
3929
|
+
* The diagnostics card (W7 S7): the settings face of the SAME DiagnosticsView the CLI and the
|
|
3930
|
+
* `/api/dsh-evolution/diagnostics` route answer.
|
|
3931
|
+
*
|
|
3932
|
+
* Read-only by construction: it fetches once on mount and only refetches on an explicit Refresh; a
|
|
3933
|
+
* failed read renders the shared FailureBlock with the command-face fallback. The client owns no
|
|
3934
|
+
* key list and no status vocabulary: it renders the host view's frozen runtime rows and leads with
|
|
3935
|
+
* a plain-language summary. The view's documentation facts are a CLI/JSON concern and are
|
|
3936
|
+
* deliberately NOT rendered here (0.27.2 real-machine feedback: a settings page is not a spec
|
|
3937
|
+
* sheet; the runtime window is the whole reading).
|
|
3938
|
+
* @module @lmzhen/dsh-evolution-ui/panels/settings/client/DiagnosticsCard
|
|
3939
|
+
*/
|
|
3940
|
+
/** Which word one status gets (the frozen W1 status set). */
|
|
3941
|
+
const STATUS_KEYS = Object.freeze({
|
|
3942
|
+
ok: "diagStatusOk",
|
|
3943
|
+
warn: "diagStatusWarn",
|
|
3944
|
+
fail: "diagStatusFail",
|
|
3945
|
+
unknown: "diagStatusUnknown"
|
|
3946
|
+
});
|
|
3947
|
+
/** The summary word for a badge status (the same frozen set; ok has no count). */
|
|
3948
|
+
const SUMMARY_KEYS = Object.freeze({
|
|
3949
|
+
ok: "diagSummaryOk",
|
|
3950
|
+
warn: "diagSummaryWarn",
|
|
3951
|
+
fail: "diagSummaryFail",
|
|
3952
|
+
unknown: "diagSummaryUnknown"
|
|
3953
|
+
});
|
|
3954
|
+
/** The one status the card leads with: fail > warn > unknown > ok. */
|
|
3955
|
+
function summaryOf(rows) {
|
|
3956
|
+
const count = (status) => rows.filter((row) => row.status === status).length;
|
|
3957
|
+
for (const status of [
|
|
3958
|
+
"fail",
|
|
3959
|
+
"warn",
|
|
3960
|
+
"unknown"
|
|
3961
|
+
]) {
|
|
3962
|
+
const n = count(status);
|
|
3963
|
+
if (n > 0) return {
|
|
3964
|
+
status,
|
|
3965
|
+
count: n
|
|
3966
|
+
};
|
|
3967
|
+
}
|
|
3968
|
+
return {
|
|
3969
|
+
status: "ok",
|
|
3970
|
+
count: 0
|
|
3971
|
+
};
|
|
3972
|
+
}
|
|
3973
|
+
/**
|
|
3974
|
+
* Render the card.
|
|
3975
|
+
* @param props - the copy seat, the host call and the command fallback.
|
|
3976
|
+
* @returns the card.
|
|
3977
|
+
*/
|
|
3978
|
+
function DiagnosticsCard(props) {
|
|
3979
|
+
const { t, api } = props;
|
|
3980
|
+
const translate = (key, values) => t(key, values);
|
|
3981
|
+
const [state, setState] = (0, react.useState)({ phase: "loading" });
|
|
3982
|
+
const [refreshing, setRefreshing] = (0, react.useState)(false);
|
|
3983
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
3984
|
+
const ticket = (0, react.useRef)(0);
|
|
3985
|
+
const read = (keepView) => {
|
|
3986
|
+
const current = ticket.current + 1;
|
|
3987
|
+
ticket.current = current;
|
|
3988
|
+
if (keepView) setRefreshing(true);
|
|
3989
|
+
else setState({ phase: "loading" });
|
|
3990
|
+
api.view().then((answer) => {
|
|
3991
|
+
if (ticket.current !== current) return;
|
|
3992
|
+
setState(answer.ok ? {
|
|
3993
|
+
phase: "ready",
|
|
3994
|
+
view: answer.data
|
|
3995
|
+
} : {
|
|
3996
|
+
phase: "failed",
|
|
3997
|
+
failure: answer.failure
|
|
3998
|
+
});
|
|
3999
|
+
}).finally(() => {
|
|
4000
|
+
if (ticket.current === current) setRefreshing(false);
|
|
4001
|
+
});
|
|
4002
|
+
};
|
|
4003
|
+
(0, react.useEffect)(() => {
|
|
4004
|
+
read(false);
|
|
4005
|
+
}, []);
|
|
4006
|
+
const refresh = () => {
|
|
4007
|
+
read(true);
|
|
4008
|
+
};
|
|
4009
|
+
const summary = state.phase === "ready" ? summaryOf(state.view.runtime) : void 0;
|
|
4010
|
+
const header = (0, react.createElement)("button", {
|
|
4011
|
+
type: "button",
|
|
4012
|
+
className: "evolution-param-head",
|
|
4013
|
+
"aria-expanded": open ? "true" : "false",
|
|
4014
|
+
onClick: () => {
|
|
4015
|
+
setOpen(!open);
|
|
4016
|
+
}
|
|
4017
|
+
}, (0, react.createElement)("span", { className: "evolution-param-card-title" }, t("diagTitle")), summary === void 0 ? null : (0, react.createElement)("span", {
|
|
4018
|
+
className: "evo-diag-badge",
|
|
4019
|
+
"data-status": summary.status
|
|
4020
|
+
}, t(SUMMARY_KEYS[summary.status], summary.count === 0 ? void 0 : { n: summary.count })), (0, react.createElement)("span", { className: "evolution-param-chevron" }, open ? "▲" : "▼"));
|
|
4021
|
+
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", {
|
|
4022
|
+
key: "runtime-" + row.key,
|
|
4023
|
+
className: "evo-diag-item",
|
|
4024
|
+
"data-kind": "runtime",
|
|
4025
|
+
"data-status": row.status
|
|
4026
|
+
}, (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)))));
|
|
4027
|
+
const body = () => {
|
|
4028
|
+
if (state.phase === "loading") return (0, react.createElement)("p", { className: "evolution-param-loading" }, t("stateLoading"));
|
|
4029
|
+
if (state.phase === "failed") return FailureBlock({
|
|
4030
|
+
failure: state.failure,
|
|
4031
|
+
t: translate,
|
|
4032
|
+
fallback: props.fallback
|
|
4033
|
+
});
|
|
4034
|
+
const view = state.view;
|
|
4035
|
+
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", {
|
|
4036
|
+
type: "button",
|
|
4037
|
+
className: "evolution-param-button",
|
|
4038
|
+
onClick: refresh,
|
|
4039
|
+
disabled: refreshing,
|
|
4040
|
+
"data-action": "diag-refresh"
|
|
4041
|
+
}, t(refreshing ? "diagRefreshing" : "diagRefresh"))), runtime(view), view.runtime.length === 0 ? (0, react.createElement)("p", { className: "evolution-param-note" }, t("diagNoRows")) : null);
|
|
4042
|
+
};
|
|
4043
|
+
return (0, react.createElement)("section", {
|
|
4044
|
+
className: "evolution-param-card",
|
|
4045
|
+
"data-card": "diagnostics"
|
|
4046
|
+
}, header, open ? (0, react.createElement)("div", { className: "evolution-param-body" }, body()) : null);
|
|
4047
|
+
}
|
|
4048
|
+
//#endregion
|
|
3984
4049
|
//#region src/panels/history/client/Workbench.ts
|
|
3985
4050
|
/**
|
|
3986
4051
|
* The family's one task page. G3 owns the history view; later groups add health
|
|
@@ -3989,11 +4054,6 @@ window.__ModuleLoader__.load({
|
|
|
3989
4054
|
*/
|
|
3990
4055
|
/** Stable page id used by the sidebar row, the page slot key, and navigation params. */
|
|
3991
4056
|
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
4057
|
/**
|
|
3998
4058
|
* Render the workbench page. The platform keeps it mounted; this component only
|
|
3999
4059
|
* consumes a new explicit revision exactly once.
|
|
@@ -4003,25 +4063,21 @@ window.__ModuleLoader__.load({
|
|
|
4003
4063
|
function WorkbenchPage(props) {
|
|
4004
4064
|
const [selectedSkill, setSelectedSkill] = (0, react.useState)(void 0);
|
|
4005
4065
|
const [view, setView] = (0, react.useState)("history");
|
|
4006
|
-
const
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
lastRevision.current = props.revision;
|
|
4010
|
-
const requested = props.params?.["view"];
|
|
4011
|
-
if (requested === "pending" || requested === "merge" || requested === "health") {
|
|
4012
|
-
setView(requested);
|
|
4013
|
-
return;
|
|
4014
|
-
}
|
|
4066
|
+
const [focusRevision, setFocusRevision] = (0, react.useState)(0);
|
|
4067
|
+
const openHistory = (skill) => {
|
|
4068
|
+
setSelectedSkill(skill);
|
|
4015
4069
|
setView("history");
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4070
|
+
setFocusRevision((value) => value + 1);
|
|
4071
|
+
};
|
|
4072
|
+
const hasMerge = props.hasView?.("merge") ?? true;
|
|
4073
|
+
const hasHealth = props.hasView?.("health") ?? true;
|
|
4019
4074
|
const viewOrder = [
|
|
4020
4075
|
"history",
|
|
4021
4076
|
"pending",
|
|
4022
4077
|
"merge",
|
|
4023
|
-
"health"
|
|
4024
|
-
|
|
4078
|
+
"health",
|
|
4079
|
+
"diagnostics"
|
|
4080
|
+
].filter((value) => (value !== "merge" || hasMerge) && (value !== "health" || hasHealth));
|
|
4025
4081
|
const onTabKey = (event, current) => {
|
|
4026
4082
|
const delta = event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0;
|
|
4027
4083
|
if (delta === 0 && event.key !== "Home" && event.key !== "End") return;
|
|
@@ -4033,9 +4089,6 @@ window.__ModuleLoader__.load({
|
|
|
4033
4089
|
document.getElementById("evo-workbench-tab-" + next)?.focus();
|
|
4034
4090
|
});
|
|
4035
4091
|
};
|
|
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
4092
|
return (0, react.createElement)("section", {
|
|
4040
4093
|
className: "evolution-page-surface evo-workbench",
|
|
4041
4094
|
"data-page": view
|
|
@@ -4099,7 +4152,21 @@ window.__ModuleLoader__.load({
|
|
|
4099
4152
|
onKeyDown: (event) => {
|
|
4100
4153
|
onTabKey(event, "health");
|
|
4101
4154
|
}
|
|
4102
|
-
}, props.t("workbench.health"))
|
|
4155
|
+
}, props.t("workbench.health")), (0, react.createElement)("button", {
|
|
4156
|
+
id: "evo-workbench-tab-diagnostics",
|
|
4157
|
+
type: "button",
|
|
4158
|
+
role: "tab",
|
|
4159
|
+
tabIndex: view === "diagnostics" ? 0 : -1,
|
|
4160
|
+
"aria-selected": view === "diagnostics" ? "true" : "false",
|
|
4161
|
+
"aria-controls": "evo-workbench-panel",
|
|
4162
|
+
"data-active": view === "diagnostics" ? "true" : void 0,
|
|
4163
|
+
onClick: () => {
|
|
4164
|
+
setView("diagnostics");
|
|
4165
|
+
},
|
|
4166
|
+
onKeyDown: (event) => {
|
|
4167
|
+
onTabKey(event, "diagnostics");
|
|
4168
|
+
}
|
|
4169
|
+
}, props.t("workbench.diagnostics")))), (0, react.createElement)("div", {
|
|
4103
4170
|
className: "evo-workbench-body",
|
|
4104
4171
|
id: "evo-workbench-panel",
|
|
4105
4172
|
role: "tabpanel"
|
|
@@ -4107,27 +4174,30 @@ window.__ModuleLoader__.load({
|
|
|
4107
4174
|
...props.history,
|
|
4108
4175
|
focusSkill: selectedSkill,
|
|
4109
4176
|
focusRevision
|
|
4110
|
-
}) : view === "pending" ? (0, react.createElement)(PendingCard, props.pending) : null, (0, react.createElement)("div", {
|
|
4177
|
+
}) : view === "pending" ? (0, react.createElement)(PendingCard, props.pending) : view === "diagnostics" ? (0, react.createElement)(DiagnosticsCard, props.diagnostics) : null, (0, react.createElement)("div", {
|
|
4111
4178
|
hidden: view !== "merge" && view !== "health",
|
|
4112
4179
|
className: "evo-workbench-feature"
|
|
4113
|
-
}, props.renderSlot?.("evolution.workbench.view", {
|
|
4180
|
+
}, props.renderSlot?.("evolution.workbench.view", {
|
|
4181
|
+
activeView: view,
|
|
4182
|
+
openHistory
|
|
4183
|
+
}) ?? null)));
|
|
4114
4184
|
}
|
|
4115
|
-
/**
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
}
|
|
4185
|
+
/** The sidebar icon; the platform renders the row label, tooltip and active state. */
|
|
4186
|
+
function WorkbenchIcon(props) {
|
|
4187
|
+
const size = props.size ?? 16;
|
|
4188
|
+
return (0, react.createElement)("svg", {
|
|
4189
|
+
width: size,
|
|
4190
|
+
height: size,
|
|
4191
|
+
viewBox: "0 0 16 16",
|
|
4192
|
+
"aria-hidden": "true",
|
|
4193
|
+
focusable: "false"
|
|
4194
|
+
}, (0, react.createElement)("path", {
|
|
4195
|
+
d: "M2 3.5h12M2 8h12M2 12.5h8",
|
|
4196
|
+
fill: "none",
|
|
4197
|
+
stroke: "currentColor",
|
|
4198
|
+
"stroke-width": props.active === true ? 1.7 : 1.4,
|
|
4199
|
+
"stroke-linecap": "round"
|
|
4200
|
+
}));
|
|
4131
4201
|
}
|
|
4132
4202
|
/** Workbench shell and sidebar row; colours come from the platform tokens. */
|
|
4133
4203
|
const WORKBENCH_CSS = [
|
|
@@ -4140,6 +4210,7 @@ window.__ModuleLoader__.load({
|
|
|
4140
4210
|
".evo-workbench-tabs button:focus-visible{outline:var(--evo-focus-ring);outline-offset:var(--evo-focus-offset)}",
|
|
4141
4211
|
".evo-workbench-title{margin:0;font-size:var(--evo-type-lg);font-weight:600;line-height:var(--evo-leading-base)}",
|
|
4142
4212
|
".evo-workbench-body{display:flex;flex:1;min-height:0;min-width:0;overflow:hidden}",
|
|
4213
|
+
".evo-workbench-body>.evolution-param-card{flex:1;min-width:0;overflow:auto}",
|
|
4143
4214
|
".evo-workbench-nav{display:flex;width:100%;align-items:center;gap:var(--evo-space-8);padding:var(--evo-space-6) var(--evo-space-12);border:0;border-radius:var(--evo-radius-control);background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:var(--evo-type-sm);text-align:left;cursor:pointer}",
|
|
4144
4215
|
".evo-workbench-nav:hover,.evo-workbench-nav[data-active=\"true\"]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}",
|
|
4145
4216
|
".evo-workbench-nav:focus-visible{outline:var(--evo-focus-ring);outline-offset:var(--evo-focus-offset)}",
|
|
@@ -4147,55 +4218,37 @@ window.__ModuleLoader__.load({
|
|
|
4147
4218
|
].join("");
|
|
4148
4219
|
//#endregion
|
|
4149
4220
|
//#region src/panels/history/client/index.ts
|
|
4150
|
-
function injectStyles(documentRef, css) {
|
|
4151
|
-
if (documentRef === void 0) return () => {};
|
|
4152
|
-
documentRef.querySelector("style[data-plugin-css=\"@lmzhen/dsh-evolution-ui/panel.css\"]")?.remove();
|
|
4153
|
-
const tag = documentRef.createElement("style");
|
|
4154
|
-
tag.setAttribute("data-plugin-css", CSS_TAG_ID);
|
|
4155
|
-
tag.textContent = css;
|
|
4156
|
-
documentRef.head.appendChild(tag);
|
|
4157
|
-
return () => {
|
|
4158
|
-
tag.remove();
|
|
4159
|
-
};
|
|
4160
|
-
}
|
|
4161
4221
|
/**
|
|
4162
|
-
* Register the locale namespace, the workbench
|
|
4222
|
+
* Register the locale namespace, the workbench main occupant and its sidebar row.
|
|
4163
4223
|
* @param ctx - client cordis context.
|
|
4164
4224
|
*/
|
|
4165
4225
|
function apply$1(ctx) {
|
|
4166
|
-
const seam = ctx;
|
|
4167
4226
|
const api = createSkillHistoryApi();
|
|
4168
|
-
|
|
4169
|
-
|
|
4227
|
+
const settingsSeat = createSettingsSeat(ctx);
|
|
4228
|
+
let approvalConfirmMode = "ask";
|
|
4229
|
+
const approvalForm = settingsSeat.get("evolution-approval");
|
|
4230
|
+
const stopApprovalWatch = approvalForm?.subscribe(() => {
|
|
4231
|
+
approvalConfirmMode = confirmModeFrom(approvalForm.getSnapshot().user);
|
|
4232
|
+
});
|
|
4233
|
+
if (stopApprovalWatch !== void 0) ctx.effect(() => stopApprovalWatch, "evolution-history: approval confirm mode");
|
|
4234
|
+
const confirmMode = () => approvalConfirmMode;
|
|
4235
|
+
const hasView = (view) => {
|
|
4170
4236
|
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 () => {};
|
|
4237
|
+
return (ctx.slots?.entries?.("evolution.workbench.view") ?? []).some((entry) => entry.options?.id === (view === "merge" ? "merge" : "skill-health"));
|
|
4238
|
+
} catch {
|
|
4239
|
+
return false;
|
|
4180
4240
|
}
|
|
4181
|
-
}
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
const lookup = (key) => {
|
|
4185
|
-
const own = fallback[key] ?? key;
|
|
4186
|
-
if (!registered) return own;
|
|
4241
|
+
};
|
|
4242
|
+
const pendingT = (key, values) => {
|
|
4243
|
+
let template;
|
|
4187
4244
|
try {
|
|
4188
|
-
|
|
4245
|
+
template = ctx.locale.bind(NS$1)(key);
|
|
4189
4246
|
} catch {
|
|
4190
|
-
|
|
4247
|
+
template = message(key);
|
|
4191
4248
|
}
|
|
4249
|
+
return values === void 0 ? template : fill(template, values);
|
|
4192
4250
|
};
|
|
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 = () => ({
|
|
4251
|
+
const historyFace = (t) => ({
|
|
4199
4252
|
t,
|
|
4200
4253
|
format: (key, values) => t(key, values),
|
|
4201
4254
|
loadSkills: api.skills,
|
|
@@ -4209,61 +4262,42 @@ window.__ModuleLoader__.load({
|
|
|
4209
4262
|
footnotes: t("markdown.footnotes")
|
|
4210
4263
|
}
|
|
4211
4264
|
});
|
|
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
4265
|
const pendingFace = () => ({
|
|
4237
4266
|
t: pendingT,
|
|
4238
4267
|
api: createApprovalApi((input, init) => fetch(input, init)),
|
|
4239
4268
|
confirmMode,
|
|
4240
4269
|
fallback: FALLBACKS.approval
|
|
4241
4270
|
});
|
|
4242
|
-
const
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
id: WORKBENCH_ID,
|
|
4247
|
-
order: 35,
|
|
4248
|
-
label,
|
|
4249
|
-
inject: () => ({ t })
|
|
4250
|
-
}, WorkbenchNav);
|
|
4271
|
+
const diagnosticsFace = () => ({
|
|
4272
|
+
t: pendingT,
|
|
4273
|
+
api: createDiagnosticsApi((input, init) => fetch(input, init)),
|
|
4274
|
+
fallback: FALLBACKS.diagnostics
|
|
4251
4275
|
});
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4276
|
+
registerSidebarPanel(ctx, {
|
|
4277
|
+
id: WORKBENCH_ID,
|
|
4278
|
+
order: 35,
|
|
4279
|
+
namespace: NS,
|
|
4280
|
+
dictionaries: {
|
|
4281
|
+
zh,
|
|
4282
|
+
en
|
|
4283
|
+
},
|
|
4284
|
+
styles: {
|
|
4285
|
+
css: CSS + WORKBENCH_CSS + PAGE_SURFACE_CSS,
|
|
4286
|
+
tagId: CSS_TAG_ID
|
|
4287
|
+
},
|
|
4288
|
+
icon: WorkbenchIcon,
|
|
4289
|
+
component: WorkbenchPage,
|
|
4290
|
+
body: (t) => ({
|
|
4291
|
+
t,
|
|
4292
|
+
history: historyFace(t),
|
|
4293
|
+
pending: pendingFace(),
|
|
4294
|
+
diagnostics: diagnosticsFace(),
|
|
4295
|
+
hasView
|
|
4296
|
+
}),
|
|
4297
|
+
children: { "evolution.workbench.view": {
|
|
4298
|
+
kind: "list",
|
|
4299
|
+
scope: "root"
|
|
4300
|
+
} }
|
|
4267
4301
|
});
|
|
4268
4302
|
}
|
|
4269
4303
|
//#endregion
|