@lmzhen/dsh-evolution-ui 0.34.0 → 0.36.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 CHANGED
@@ -831,9 +831,12 @@ window.__ModuleLoader__.load({
831
831
  /** Chinese copy (the family's primary language). */
832
832
  const zh$1 = {
833
833
  title: "自进化",
834
+ "group.write": "写入与审批",
835
+ "group.organize": "自动整理",
836
+ "group.memory": "记忆与回顾",
834
837
  subtitle: "待你决定的写入在最上面;下面按功能列出你能改的参数,没有列出的由安装时的配置决定。",
835
838
  aboutPurpose: "自进化设置与技能历史",
836
- overridden: "已覆盖",
839
+ overridden: "已修改",
837
840
  deployment: "默认",
838
841
  loading: "正在读取设置…",
839
842
  unavailable: "这个功能没有启动(对应的插件没有安装)",
@@ -841,12 +844,12 @@ window.__ModuleLoader__.load({
841
844
  projectionFailed: "这一块读设置时出错,已记入日志;其它参数不受影响",
842
845
  readonly: "本次会话不允许写入设置(临时运行模式)",
843
846
  apply: "保存",
844
- reset: "恢复默认值",
847
+ reset: "复位",
845
848
  empty: "这里没有可改项",
846
- changed: "{n} 项已覆盖",
849
+ changed: "{n} 项已修改",
847
850
  discard: "放弃修改",
848
851
  saving: "保存中…",
849
- resetting: "恢复中…",
852
+ resetting: "复位中…",
850
853
  writeTargetHost: "保存去向:宿主配置(对所有会话生效)",
851
854
  invalidNumber: "需要一个数字",
852
855
  invalidSelect: "不在允许的取值里",
@@ -875,7 +878,12 @@ window.__ModuleLoader__.load({
875
878
  mergeCardHint: "写入审批=使用中生成/修改;合并审批=两轮工作流。",
876
879
  approvalTitle: "待批写入",
877
880
  approvalLoading: "正在读取待批项…",
878
- approvalEmpty: "没有待批的写入",
881
+ approvalEmpty: "没有待处理的写入",
882
+ approvalEmptyDescription: "当前没有需要你确认的技能变更。新的写入会在这里显示来源、内容和影响范围。",
883
+ approvalHistory: "查看技能历史",
884
+ approvalFactKind: "类型",
885
+ approvalFactAge: "时间",
886
+ approvalFactTarget: "对象",
879
887
  approvalFailed: "待批项读取失败",
880
888
  approvalRetry: "重试",
881
889
  approvalApprove: "批准",
@@ -915,6 +923,9 @@ window.__ModuleLoader__.load({
915
923
  diagTitle: "诊断",
916
924
  diagSubtitle: "运行状态的只读自查;不会改动任何东西。",
917
925
  diagRuntimeTitle: "运行状态",
926
+ diagComplete: "没有需要处理的异常",
927
+ diagOtherIssues: "其他 {n} 项异常",
928
+ diagNormal: "其他 {n} 项正常",
918
929
  diagSummaryOk: "运行正常",
919
930
  diagSummaryWarn: "{n} 项需要注意",
920
931
  diagSummaryFail: "{n} 项异常",
@@ -939,9 +950,12 @@ window.__ModuleLoader__.load({
939
950
  /** English copy, key for key with {@link zh}. */
940
951
  const en$1 = {
941
952
  title: "Self-evolution",
953
+ "group.write": "Writes and approval",
954
+ "group.organize": "Automatic tidy-up",
955
+ "group.memory": "Memory and review",
942
956
  subtitle: "Writes waiting for your decision come first; the changeable parameters are listed by feature below, and anything not listed comes from the deployment configuration.",
943
957
  aboutPurpose: "Self-evolution settings and skill history",
944
- overridden: "Overridden",
958
+ overridden: "Modified",
945
959
  deployment: "default",
946
960
  loading: "reading settings…",
947
961
  unavailable: "this feature is not running (its plugin is not installed)",
@@ -949,9 +963,9 @@ window.__ModuleLoader__.load({
949
963
  projectionFailed: "reading this row failed and the error is in the log; the other parameters are unaffected",
950
964
  readonly: "settings cannot be written in this session (temporary mode)",
951
965
  apply: "Save",
952
- reset: "Reset to default",
966
+ reset: "Reset",
953
967
  empty: "nothing to change here",
954
- changed: "{n} overridden",
968
+ changed: "{n} modified",
955
969
  discard: "Discard",
956
970
  saving: "Saving…",
957
971
  resetting: "Resetting…",
@@ -983,7 +997,12 @@ window.__ModuleLoader__.load({
983
997
  mergeCardHint: "Write approval = generate/modify while in use; merge approval = the two-round workflow.",
984
998
  approvalTitle: "Writes awaiting approval",
985
999
  approvalLoading: "reading the pending window…",
986
- approvalEmpty: "nothing is waiting for a decision",
1000
+ approvalEmpty: "Nothing is waiting",
1001
+ approvalEmptyDescription: "There are no skill changes to confirm. New writes will show their source, content and impact here.",
1002
+ approvalHistory: "View skill history",
1003
+ approvalFactKind: "Kind",
1004
+ approvalFactAge: "Time",
1005
+ approvalFactTarget: "Target",
987
1006
  approvalFailed: "the pending window could not be read",
988
1007
  approvalRetry: "Retry",
989
1008
  approvalApprove: "Approve",
@@ -1023,6 +1042,9 @@ window.__ModuleLoader__.load({
1023
1042
  diagTitle: "Diagnostics",
1024
1043
  diagSubtitle: "A read-only check of the runtime state; nothing here changes anything.",
1025
1044
  diagRuntimeTitle: "Runtime status",
1045
+ diagComplete: "Nothing needs attention",
1046
+ diagOtherIssues: "Other {n} issues",
1047
+ diagNormal: "Other {n} normal checks",
1026
1048
  diagSummaryOk: "All healthy",
1027
1049
  diagSummaryWarn: "{n} need attention",
1028
1050
  diagSummaryFail: "{n} failing",
@@ -1262,18 +1284,20 @@ window.__ModuleLoader__.load({
1262
1284
  return (0, react.createElement)("div", { className: "evolution-param-field" }, (0, react.createElement)("div", { className: "evolution-param-label" }, (0, react.createElement)("label", { htmlFor: controlId(field) }, labelOf(field, lang) === "" ? field.id : labelOf(field, lang)), unit, props.overridden ? (0, react.createElement)("span", {
1263
1285
  className: "evolution-param-source",
1264
1286
  "data-user": "true"
1265
- }, t("overridden")) : null), FieldControl(props), (0, react.createElement)("p", { className: "evolution-param-hint" }, hintOf(field, lang) === "" ? field.doc : hintOf(field, lang)), props.invalid === null ? null : (0, react.createElement)("p", {
1266
- id: controlId(field) + "-invalid",
1267
- className: "evolution-param-error",
1268
- role: "status"
1269
- }, props.invalid), props.overridden ? (0, react.createElement)("div", { className: "evolution-param-actions" }, (0, react.createElement)("button", {
1287
+ }, t("overridden")) : null, props.overridden ? (0, react.createElement)("button", {
1270
1288
  type: "button",
1271
- className: "evolution-param-button",
1289
+ className: "evolution-param-reset",
1272
1290
  disabled: props.disabled || props.clearing,
1291
+ "data-role": "reset",
1292
+ title: props.resetTo === null ? void 0 : t("resetTo", { value: props.resetTo }),
1273
1293
  onClick: () => {
1274
1294
  props.onClear(field.id);
1275
1295
  }
1276
- }, props.clearing ? t("resetting") : t("reset")), props.resetTo === null ? null : (0, react.createElement)("span", { className: "evolution-param-hint" }, t("resetTo", { value: props.resetTo }))) : null);
1296
+ }, props.clearing ? t("resetting") : t("reset")) : null), FieldControl(props), (0, react.createElement)("p", { className: "evolution-param-hint" }, hintOf(field, lang) === "" ? field.doc : hintOf(field, lang)), props.invalid === null ? null : (0, react.createElement)("p", {
1297
+ id: controlId(field) + "-invalid",
1298
+ className: "evolution-param-error",
1299
+ role: "status"
1300
+ }, props.invalid));
1277
1301
  }
1278
1302
  /**
1279
1303
  * Render one row's card body from a supplied snapshot.
@@ -1522,6 +1546,36 @@ window.__ModuleLoader__.load({
1522
1546
  });
1523
1547
  }
1524
1548
  //#endregion
1549
+ //#region src/panels/settings/settings-groups.ts
1550
+ const GROUP_OF = Object.freeze({
1551
+ "evolution-approval": "write",
1552
+ "evolution-merge": "write",
1553
+ "tool-skill-manage": "write",
1554
+ "evolution-curator": "organize",
1555
+ "evolution-commands": "organize",
1556
+ "memory-files": "memory",
1557
+ "tool-memory": "memory",
1558
+ "evolution-review": "memory"
1559
+ });
1560
+ /** Stable display order for the three task groups. */
1561
+ const SETTINGS_GROUP_ORDER = [
1562
+ "write",
1563
+ "organize",
1564
+ "memory"
1565
+ ];
1566
+ /**
1567
+ * Partition existing parameter cards by task. Unknown namespaces fall into organize so a future row
1568
+ * is visible rather than silently dropped.
1569
+ * @param cards - cards carrying only their namespace and payload.
1570
+ * @returns groups in display order, omitting empty groups.
1571
+ */
1572
+ function groupParamCards(cards) {
1573
+ return SETTINGS_GROUP_ORDER.map((group) => ({
1574
+ group,
1575
+ items: cards.filter((card) => (GROUP_OF[card.namespace] ?? "organize") === group)
1576
+ })).filter((entry) => entry.items.length > 0);
1577
+ }
1578
+ //#endregion
1525
1579
  //#region ../evolution-client-kit/src/panel.ts
1526
1580
  function interpolate(text, values) {
1527
1581
  if (values === void 0) return text;
@@ -1780,6 +1834,127 @@ window.__ModuleLoader__.load({
1780
1834
  };
1781
1835
  }
1782
1836
  //#endregion
1837
+ //#region ../evolution-client-kit/src/format.ts
1838
+ /**
1839
+ * Format an ISO string, epoch milliseconds, or null as local wall time.
1840
+ * Invalid values return null; callers decide how to say "unknown".
1841
+ * @param value - ISO string, epoch ms, or null.
1842
+ * @param options - seconds on/off; defaults to on.
1843
+ * @returns the formatted local time, or null when the value is absent/invalid.
1844
+ */
1845
+ function formatLocalDateTime(value, options = {}) {
1846
+ if (value === null || value === void 0) return null;
1847
+ const ms = typeof value === "number" ? value : Date.parse(value);
1848
+ if (!Number.isFinite(ms)) return null;
1849
+ const date = new Date(ms);
1850
+ if (Number.isNaN(date.getTime())) return null;
1851
+ const pad = (part) => String(part).padStart(2, "0");
1852
+ const head = String(date.getFullYear()) + "-" + pad(date.getMonth() + 1) + "-" + pad(date.getDate()) + " " + pad(date.getHours()) + ":" + pad(date.getMinutes());
1853
+ return options.seconds === false ? head : head + ":" + pad(date.getSeconds());
1854
+ }
1855
+ //#endregion
1856
+ //#region ../evolution-client-kit/src/notice.ts
1857
+ /** One inline state notice: semantic tone plus text, no surface-specific styling. */
1858
+ //#endregion
1859
+ //#region ../evolution-client-kit/src/presentation/decision-frame.ts
1860
+ /** One decision card: title, facts, at most one primary, secondary actions and details. */
1861
+ function actionButton$1(action, role) {
1862
+ return (0, react.createElement)("button", {
1863
+ key: action.id,
1864
+ type: "button",
1865
+ "data-role": role === "primary" ? "decision-primary" : "decision-secondary",
1866
+ "data-action": action.id,
1867
+ "aria-busy": action.busy === true ? "true" : void 0,
1868
+ ...action.attributes ?? {},
1869
+ disabled: action.disabled === true || action.busy === true,
1870
+ title: action.title ?? action.label,
1871
+ onClick: () => {
1872
+ if (action.disabled !== true && action.busy !== true) action.onSelect();
1873
+ }
1874
+ }, action.label);
1875
+ }
1876
+ /**
1877
+ * Render the one-card decision surface. The model has a singular primary on purpose.
1878
+ * @param props - decision model and optional surface class.
1879
+ * @returns the decision frame.
1880
+ */
1881
+ function DecisionFrame({ decision, className }) {
1882
+ const secondary = decision.secondary ?? [];
1883
+ return (0, react.createElement)("section", {
1884
+ "data-role": "decision-frame",
1885
+ ...className === void 0 ? {} : { className }
1886
+ }, (0, react.createElement)("h2", { "data-role": "decision-title" }, decision.title), decision.summary === void 0 || decision.summary === "" ? null : (0, react.createElement)("p", { "data-role": "decision-summary" }, decision.summary), decision.facts.length === 0 ? null : (0, react.createElement)("dl", { "data-role": "decision-facts" }, decision.facts.map((fact) => (0, react.createElement)("div", {
1887
+ key: fact.label,
1888
+ "data-role": "decision-fact"
1889
+ }, (0, react.createElement)("dt", null, fact.label), (0, react.createElement)("dd", null, fact.value)))), decision.details === void 0 ? null : (0, react.createElement)("div", { "data-role": "decision-details" }, decision.details), decision.primary === void 0 && secondary.length === 0 ? null : (0, react.createElement)("div", { "data-role": "decision-actions" }, decision.primary === void 0 ? null : actionButton$1(decision.primary, "primary"), secondary.map((action) => actionButton$1(action, "secondary"))));
1890
+ }
1891
+ //#endregion
1892
+ //#region ../evolution-client-kit/src/presentation/task-banner.ts
1893
+ /** A read-only task hint; it deliberately has no primary action. */
1894
+ //#endregion
1895
+ //#region ../evolution-client-kit/src/presentation/chips.ts
1896
+ /** Read-only status chips and toggleable filter chips. */
1897
+ //#endregion
1898
+ //#region ../evolution-client-kit/src/presentation/empty-state.ts
1899
+ /** A compact completed/empty state with at most one primary next step. */
1900
+ function actionButton(action, role) {
1901
+ return (0, react.createElement)("button", {
1902
+ key: action.id,
1903
+ type: "button",
1904
+ "data-role": role === "primary" ? "empty-primary" : "empty-secondary",
1905
+ "data-action": action.id,
1906
+ ...action.attributes ?? {},
1907
+ disabled: action.disabled === true || action.busy === true,
1908
+ title: action.title ?? action.label,
1909
+ onClick: () => {
1910
+ if (action.disabled !== true && action.busy !== true) action.onSelect();
1911
+ }
1912
+ }, action.label);
1913
+ }
1914
+ /** Render a quiet empty state; loading/failure remain the caller's separate states. */
1915
+ function EmptyState({ title, description, tone = "neutral", primary, secondary, className }) {
1916
+ const actions = primary === void 0 && secondary === void 0 ? null : (0, react.createElement)("div", { "data-role": "empty-actions" }, primary === void 0 ? null : actionButton(primary, "primary"), secondary === void 0 ? null : actionButton(secondary, "secondary"));
1917
+ return (0, react.createElement)("section", {
1918
+ "data-role": "empty-state",
1919
+ "data-tone": tone,
1920
+ ...className === void 0 ? {} : { className }
1921
+ }, (0, react.createElement)("h2", { "data-role": "empty-title" }, title), description === void 0 || description === "" ? null : (0, react.createElement)("p", { "data-role": "empty-description" }, description), actions);
1922
+ }
1923
+ //#endregion
1924
+ //#region ../evolution-client-kit/src/presentation/disclosure-group.ts
1925
+ /** A native details group for normal/secondary facts. */
1926
+ //#endregion
1927
+ //#region ../evolution-client-kit/src/presentation/time-text.ts
1928
+ /** The one local-time presentation used by reading surfaces. */
1929
+ //#endregion
1930
+ //#region ../evolution-client-kit/src/presentation/action-menu.ts
1931
+ /**
1932
+ * The one seam onto the platform's menu material, plus a small fallback for deployments
1933
+ * without the primitive row. No page owns a second menu implementation.
1934
+ */
1935
+ const PLATFORM_UI_MODULE$1 = "@deepseek-ai/dsh-client-ui-primitives";
1936
+ /**
1937
+ * Accept only the two component functions; a namespace row without them is not a menu.
1938
+ * @param row - the module row's exports.
1939
+ * @returns the two components, or undefined.
1940
+ */
1941
+ function menuPrimitivesFrom(row) {
1942
+ const surface = row.MenuSurface;
1943
+ const item = row.MenuItemButton;
1944
+ if (typeof surface !== "function" || typeof item !== "function") return void 0;
1945
+ return {
1946
+ MenuSurface: surface,
1947
+ MenuItemButton: item
1948
+ };
1949
+ }
1950
+ (() => {
1951
+ try {
1952
+ return menuPrimitivesFrom(require(PLATFORM_UI_MODULE$1));
1953
+ } catch {
1954
+ return;
1955
+ }
1956
+ })();
1957
+ //#endregion
1783
1958
  //#region ../evolution-client-kit/src/action.ts
1784
1959
  /**
1785
1960
  * Command-only actions. Approval decisions are a separate host API and must never
@@ -2089,6 +2264,8 @@ window.__ModuleLoader__.load({
2089
2264
  const CSS$1 = [
2090
2265
  tokenVars(".evolution-params,.evolution-param-card"),
2091
2266
  ".evolution-params{display:flex;flex-direction:column}",
2267
+ ".evolution-settings-tab{display:flex;flex-direction:column;gap:var(--evo-space-8)}",
2268
+ ".evolution-settings-group{margin:var(--evo-space-12) 0 0;font-size:var(--evo-type-sm);font-weight:600;color:var(--dsw-alias-label-secondary)}",
2092
2269
  ".evolution-params-title{margin:0 0 var(--evo-space-4);color:var(--dsw-alias-label-primary);font-size:var(--evo-type-lg);font-weight:600;line-height:var(--evo-leading-base)}",
2093
2270
  ".evolution-params-subtitle{margin:0 0 var(--evo-space-16);color:var(--dsw-alias-label-tertiary);font-size:var(--evo-type-xs);line-height:var(--evo-leading-base)}",
2094
2271
  ".evolution-params-about{margin:0 0 var(--evo-space-16);color:var(--dsw-alias-label-tertiary);font-size:var(--evo-type-xs)}",
@@ -2096,7 +2273,7 @@ window.__ModuleLoader__.load({
2096
2273
  ".evolution-param-head{display:flex;align-items:center;gap:var(--evo-space-8);width:100%;padding:var(--evo-space-12) var(--evo-space-14);background:none;border:0;text-align:left;cursor:pointer;color:inherit;font:inherit}",
2097
2274
  ".evolution-param-head:hover{background:var(--dsw-alias-interactive-bg-hover)}",
2098
2275
  ".evolution-param-card-title{flex:1;min-width:0;color:var(--dsw-alias-label-primary);font-size:var(--evo-type-sm);font-weight:500;line-height:var(--evo-leading-base)}",
2099
- ".evolution-param-count{color:var(--dsw-alias-brand-primary);font-size:var(--evo-type-xs);line-height:var(--evo-leading-base)}",
2276
+ ".evolution-param-count{color:var(--dsw-alias-label-tertiary);font-size:var(--evo-type-xs);line-height:var(--evo-leading-base)}",
2100
2277
  ".evolution-param-chevron{color:var(--dsw-alias-label-tertiary);font-size:var(--evo-type-xs);line-height:var(--evo-leading-base)}",
2101
2278
  ".evolution-param-body{padding:0 var(--evo-space-14) var(--evo-space-12);border-top:var(--evo-hairline-width) solid var(--dsw-alias-border-l2)}",
2102
2279
  ".evolution-param-dest{color:var(--dsw-alias-label-tertiary);font-size:var(--evo-type-xs)}",
@@ -2111,7 +2288,10 @@ window.__ModuleLoader__.load({
2111
2288
  ".evolution-param-input:focus-visible{outline:var(--evo-focus-ring);outline-offset:var(--evo-focus-offset)}",
2112
2289
  ".evolution-param-input:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}",
2113
2290
  ".evolution-param-source{display:inline-flex;align-items:center;height:var(--evo-space-20);padding:0 var(--evo-space-6);border-radius:var(--evo-radius-chip);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary);font-size:var(--evo-type-xs);line-height:var(--evo-space-20)}",
2114
- ".evolution-param-source[data-user=\"true\"]{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-label-primary-foreground)}",
2291
+ ".evolution-param-source[data-user=\"true\"]{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);font-size:var(--evo-type-xs)}",
2292
+ ".evolution-param-reset{margin-left:auto;padding:0;border:0;background:transparent;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:var(--evo-type-xs);cursor:pointer}",
2293
+ ".evolution-param-reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}",
2294
+ ".evolution-param-reset:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}",
2115
2295
  ".evolution-param-actions{display:flex;justify-content:flex-end;gap:var(--evo-space-8);padding-top:var(--evo-space-12)}",
2116
2296
  ".evolution-param-button{padding:var(--evo-space-6) var(--evo-space-12);border:var(--evo-hairline-width) solid var(--dsw-alias-border-l2);border-radius:var(--evo-radius-control);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font-size:var(--evo-type-sm);line-height:var(--evo-leading-base);cursor:pointer}",
2117
2297
  ".evolution-param-button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}",
@@ -2120,6 +2300,32 @@ window.__ModuleLoader__.load({
2120
2300
  ".evolution-param-button[data-primary=\"true\"]{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:transparent}",
2121
2301
  ".evolution-param-button[data-primary=\"true\"]:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}",
2122
2302
  ".evolution-param-button:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}",
2303
+ ".evolution-approval-item{padding:var(--evo-space-10) 0;border-top:var(--evo-hairline-width) solid var(--dsw-alias-border-l2)}",
2304
+ ".evolution-approval-item:first-of-type{border-top:0}",
2305
+ ".evolution-approval-decision{display:flex;flex-direction:column;gap:var(--evo-space-8);padding:var(--evo-space-12);border:var(--evo-hairline-width) solid var(--dsw-alias-border-l2);border-radius:var(--evo-radius-control);background:var(--dsw-alias-bg-layer-1)}",
2306
+ ".evolution-approval-decision [data-role=\"decision-title\"]{margin:0;font-size:var(--evo-type-sm);font-weight:600;line-height:var(--evo-leading-base)}",
2307
+ ".evolution-approval-decision [data-role=\"decision-summary\"]{margin:0;color:var(--dsw-alias-label-secondary);font-size:var(--evo-type-xs)}",
2308
+ ".evolution-approval-decision [data-role=\"decision-facts\"]{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:var(--evo-space-4) var(--evo-space-12);margin:0;font-size:var(--evo-type-xs)}",
2309
+ ".evolution-approval-decision [data-role=\"decision-fact\"]{display:contents}",
2310
+ ".evolution-approval-decision dt{color:var(--dsw-alias-label-tertiary)}",
2311
+ ".evolution-approval-decision dd{margin:0;color:var(--dsw-alias-label-primary);min-width:0;overflow-wrap:anywhere}",
2312
+ ".evolution-approval-decision [data-role=\"decision-actions\"]{display:flex;flex-wrap:wrap;align-items:center;gap:var(--evo-space-8);padding-top:var(--evo-space-8)}",
2313
+ ".evolution-approval-decision [data-role=\"decision-primary\"],.evolution-approval-decision [data-role=\"decision-secondary\"]{padding:var(--evo-space-6) var(--evo-space-12);border:var(--evo-hairline-width) solid var(--dsw-alias-border-l2);border-radius:var(--evo-radius-control);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font-size:var(--evo-type-sm);cursor:pointer}",
2314
+ ".evolution-approval-decision [data-role=\"decision-primary\"]{background:var(--dsw-alias-button-primary-fill);border-color:transparent;color:var(--dsw-alias-label-primary-foreground)}",
2315
+ ".evolution-approval-decision [data-role=\"decision-primary\"]:disabled,.evolution-approval-decision [data-role=\"decision-secondary\"]:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}",
2316
+ ".evolution-approval-decision [data-role=\"decision-details\"] details{margin:0}",
2317
+ ".evolution-approval-decision [data-role=\"decision-details\"] summary{cursor:pointer;color:var(--dsw-alias-label-tertiary);font-size:var(--evo-type-xs)}",
2318
+ ".evolution-approval-item [data-role=\"empty-state\"]{padding:var(--evo-space-20) var(--evo-space-16);text-align:center}",
2319
+ ".evolution-approval-item [data-role=\"empty-title\"]{margin:0 0 var(--evo-space-6);font-size:var(--evo-type-lg)}",
2320
+ ".evolution-approval-item [data-role=\"empty-description\"]{margin:0 auto var(--evo-space-12);max-width:34em;color:var(--dsw-alias-label-secondary);font-size:var(--evo-type-sm)}",
2321
+ ".evolution-approval-item [data-role=\"empty-actions\"]{display:flex;justify-content:center;gap:var(--evo-space-8)}",
2322
+ ".evolution-approval-item [data-role=\"empty-primary\"]{padding:var(--evo-space-6) var(--evo-space-12);border:0;border-radius:var(--evo-radius-control);background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);font-size:var(--evo-type-sm);cursor:pointer}",
2323
+ ".evolution-approval-item [data-role=\"empty-secondary\"]{padding:var(--evo-space-6) var(--evo-space-12);border:var(--evo-hairline-width) solid var(--dsw-alias-border-l2);border-radius:var(--evo-radius-control);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:var(--evo-type-sm);cursor:pointer}",
2324
+ ".evo-diag-issue{padding:var(--evo-space-10) var(--evo-space-12);border:var(--evo-hairline-width) solid var(--dsw-alias-state-warn-primary);border-radius:var(--evo-radius-control);background:var(--dsw-alias-bg-layer-1)}",
2325
+ ".evo-diag-issue[data-status=\"fail\"]{border-color:var(--dsw-alias-state-error-primary)}",
2326
+ ".evo-diag-complete{padding:var(--evo-space-10) var(--evo-space-12);border:var(--evo-hairline-width) solid var(--dsw-alias-state-success-primary);border-radius:var(--evo-radius-control);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-state-success-primary)}",
2327
+ ".evo-diag-group{display:flex;flex-direction:column;gap:var(--evo-space-4);margin-top:var(--evo-space-8)}",
2328
+ ".evo-diag-group>summary{cursor:pointer;color:var(--dsw-alias-label-tertiary);font-size:var(--evo-type-xs)}",
2123
2329
  ".evolution-param-select{width:100%;box-sizing:border-box;padding:var(--evo-space-8) var(--evo-space-10);border:var(--evo-hairline-width) solid var(--dsw-alias-border-l2);border-radius:var(--evo-radius-control);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-size:var(--evo-type-sm);line-height:var(--evo-leading-base)}",
2124
2330
  ".evolution-param-select:focus-visible{outline:var(--evo-focus-ring);outline-offset:var(--evo-focus-offset)}",
2125
2331
  ".evolution-param-select:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}",
@@ -2255,10 +2461,14 @@ window.__ModuleLoader__.load({
2255
2461
  }
2256
2462
  /** One family tab: every parameter card lives inside the platform's Plugins page. */
2257
2463
  function EvolutionSettingsTab(props) {
2258
- return (0, react.createElement)("div", { className: "evolution-settings-tab" }, ...props.cards.map((face, index) => (0, react.createElement)(PluginParamCard, {
2464
+ return (0, react.createElement)("div", { className: "evolution-settings-tab" }, ...groupParamCards(props.cards).flatMap(({ group, items }) => [(0, react.createElement)("h3", {
2465
+ key: "group-" + group,
2466
+ className: "evolution-settings-group",
2467
+ "data-role": "settings-group"
2468
+ }, message("group." + group)), ...items.map((face, index) => (0, react.createElement)(PluginParamCard, {
2259
2469
  key: face.namespace + ":" + String(index),
2260
2470
  ...face
2261
- })));
2471
+ }))]));
2262
2472
  }
2263
2473
  //#endregion
2264
2474
  //#region ../evolution-client-kit/src/generated/route-client.generated.ts
@@ -2602,7 +2812,7 @@ window.__ModuleLoader__.load({
2602
2812
  const viewKind = state.view.kind;
2603
2813
  const rowCount = state.view.kind === "ready" ? state.view.rows.length : 0;
2604
2814
  (0, react.useEffect)(() => {
2605
- if (viewKind === "empty") setOpen(false);
2815
+ if (viewKind === "empty") setOpen(true);
2606
2816
  else if (viewKind === "ready" && rowCount > 0) setOpen(true);
2607
2817
  }, [viewKind, rowCount]);
2608
2818
  const latest = (0, react.useRef)(state);
@@ -2754,81 +2964,115 @@ window.__ModuleLoader__.load({
2754
2964
  };
2755
2965
  const summaryLine = (entry) => entry.verb === void 0 || entry.target === void 0 ? entry.summary : t(VERB_KEYS[entry.verb] ?? "approvalVerbUpdate") + " " + entry.target + (entry.operationCount !== void 0 && entry.operationCount > 1 ? " ×" + String(entry.operationCount) : "");
2756
2966
  const row = (entry) => {
2757
- if (entry.status === "executing") return (0, react.createElement)("div", {
2758
- className: "evolution-param-field",
2759
- key: entry.id
2760
- }, (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", {
2761
- className: "evolution-param-value",
2762
- title: entry.summary
2763
- }, summaryLine(entry))), (0, react.createElement)("p", { className: "evolution-param-hint" }, age(entry)), (0, react.createElement)("p", {
2764
- className: "evolution-param-note",
2765
- role: "status"
2766
- }, t("approvalExecuting")), entry.claimedBy === void 0 ? null : (0, react.createElement)("p", { className: "evolution-param-hint" }, entry.claimedBy), (0, react.createElement)("div", { className: "evolution-param-actions" }, (0, react.createElement)("button", {
2767
- className: "evolution-param-button",
2768
- type: "button",
2769
- disabled: state.busy.includes(entry.id),
2770
- onClick: () => {
2967
+ const busy = state.busy.includes(entry.id);
2968
+ const executing = entry.status === "executing";
2969
+ const confirming = armed === entry.id;
2970
+ const title = summaryLine(entry);
2971
+ const facts = [
2972
+ {
2973
+ label: t("approvalFactKind"),
2974
+ value: t(KIND_KEYS[entry.kind])
2975
+ },
2976
+ {
2977
+ label: t("approvalFactAge"),
2978
+ value: age(entry)
2979
+ },
2980
+ ...entry.target === void 0 ? [] : [{
2981
+ label: t("approvalFactTarget"),
2982
+ value: entry.target
2983
+ }]
2984
+ ];
2985
+ const raw = title === entry.summary ? void 0 : (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));
2986
+ const previewAction = {
2987
+ id: "preview-" + entry.id,
2988
+ label: state.open === entry.id ? t("approvalPreviewClose") : t("approvalPreview"),
2989
+ onSelect: () => {
2990
+ togglePreview(entry.id);
2991
+ }
2992
+ };
2993
+ const primary = executing ? {
2994
+ id: "release-" + entry.id,
2995
+ label: busy ? t("approvalReleasing") : t("approvalRelease"),
2996
+ disabled: busy,
2997
+ onSelect: () => {
2771
2998
  decide(entry.id, "release");
2772
2999
  }
2773
- }, state.busy.includes(entry.id) ? t("approvalReleasing") : t("approvalRelease"))));
2774
- const busy = state.busy.includes(entry.id);
2775
- const button = (label, action, tone) => (0, react.createElement)("button", {
2776
- className: "evolution-param-button",
2777
- type: "button",
3000
+ } : confirming ? {
3001
+ id: "confirm-" + entry.id,
3002
+ label: t("approvalConfirmYes"),
3003
+ attributes: { "data-confirm": "yes" },
3004
+ onSelect: () => {
3005
+ setArmed(null);
3006
+ decide(entry.id, "approve");
3007
+ }
3008
+ } : {
3009
+ id: "approve-" + entry.id,
3010
+ label: busy ? t("approvalApproving") : t("approvalApprove"),
2778
3011
  disabled: busy,
2779
- ...tone === "primary" ? { "data-primary": "true" } : { "data-tone": tone },
2780
- onClick: () => {
2781
- if (action === "approve" && confirmMode === "ask" && armed !== entry.id) {
3012
+ onSelect: () => {
3013
+ if (confirmMode === "ask") {
2782
3014
  setArmed(entry.id);
2783
3015
  return;
2784
3016
  }
2785
- setArmed(null);
2786
- decide(entry.id, action);
2787
- }
2788
- }, busy ? t(action === "approve" ? "approvalApproving" : "approvalRejecting") : t(label));
2789
- return (0, react.createElement)("div", {
2790
- className: "evolution-param-field",
2791
- key: entry.id
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", {
2793
- className: "evolution-param-value",
2794
- title: entry.summary
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", {
2796
- className: "evolution-param-note",
2797
- role: "status"
2798
- }, (0, react.createElement)("span", { className: "evolution-param-value" }, t("approvalConfirmNamed", { n: summaryLine(entry) })), (0, react.createElement)("button", {
2799
- className: "evolution-param-button",
2800
- type: "button",
2801
- "data-confirm": "yes",
2802
- "data-primary": "true",
2803
- onClick: () => {
2804
- setArmed(null);
2805
3017
  decide(entry.id, "approve");
2806
3018
  }
2807
- }, t("approvalConfirmYes")), (0, react.createElement)("button", {
2808
- className: "evolution-param-button",
2809
- type: "button",
2810
- "data-tone": "ghost",
2811
- onClick: () => {
3019
+ };
3020
+ const secondary = executing ? [] : confirming ? [{
3021
+ id: "cancel-" + entry.id,
3022
+ label: t("approvalConfirmNo"),
3023
+ onSelect: () => {
2812
3024
  setArmed(null);
2813
3025
  }
2814
- }, t("approvalConfirmNo"))) : null, (0, react.createElement)("div", { className: "evolution-param-actions" }, button("approvalApprove", "approve", "primary"), button("approvalReject", "reject", "outline"), (0, react.createElement)("button", {
2815
- className: "evolution-param-button",
2816
- type: "button",
2817
- "data-tone": "ghost",
2818
- onClick: () => {
2819
- togglePreview(entry.id);
3026
+ }] : [{
3027
+ id: "reject-" + entry.id,
3028
+ label: busy ? t("approvalRejecting") : t("approvalReject"),
3029
+ disabled: busy,
3030
+ onSelect: () => {
3031
+ decide(entry.id, "reject");
2820
3032
  }
2821
- }, state.open === entry.id ? t("approvalPreviewClose") : t("approvalPreview"))), state.open === entry.id && state.preview !== null ? preview(state.preview) : null);
3033
+ }, previewAction];
3034
+ const summary = executing ? t("approvalExecuting") : confirming ? t("approvalConfirmNamed", { n: title }) : void 0;
3035
+ return (0, react.createElement)("div", {
3036
+ className: "evolution-approval-item",
3037
+ key: entry.id
3038
+ }, (0, react.createElement)(DecisionFrame, {
3039
+ className: "evolution-approval-decision",
3040
+ decision: {
3041
+ title,
3042
+ ...summary === void 0 ? {} : { summary },
3043
+ facts,
3044
+ primary,
3045
+ ...secondary.length === 0 ? {} : { secondary },
3046
+ ...raw === void 0 ? {} : { details: raw }
3047
+ }
3048
+ }), state.open === entry.id && state.preview !== null ? preview(state.preview) : null);
2822
3049
  };
2823
3050
  const body = () => {
2824
3051
  if (state.view.kind === "loading") return (0, react.createElement)("p", {
2825
3052
  className: "evolution-param-note",
2826
3053
  role: "status"
2827
3054
  }, t("approvalLoading"));
2828
- if (state.view.kind === "empty") return (0, react.createElement)("p", {
2829
- className: "evolution-param-note",
2830
- role: "status"
2831
- }, t("approvalEmpty"));
3055
+ if (state.view.kind === "empty") {
3056
+ const primary = props.onHistory === void 0 ? void 0 : {
3057
+ id: "approval-history",
3058
+ label: t("approvalHistory"),
3059
+ onSelect: props.onHistory
3060
+ };
3061
+ return (0, react.createElement)("div", { role: "status" }, (0, react.createElement)(EmptyState, {
3062
+ title: t("approvalEmpty"),
3063
+ description: t("approvalEmptyDescription"),
3064
+ tone: "ok",
3065
+ ...primary === void 0 ? {} : { primary },
3066
+ secondary: {
3067
+ id: "approval-refresh",
3068
+ label: state.refreshing ? t("diagRefreshing") : t("approvalRefresh"),
3069
+ busy: state.refreshing,
3070
+ onSelect: () => {
3071
+ load();
3072
+ }
3073
+ }
3074
+ }));
3075
+ }
2832
3076
  if (state.view.kind === "failed") return (0, react.createElement)("div", { className: "evolution-param-actions" }, FailureBlock({
2833
3077
  failure: state.view.failure,
2834
3078
  t: translate,
@@ -2858,10 +3102,7 @@ window.__ModuleLoader__.load({
2858
3102
  setOpen(!open);
2859
3103
  },
2860
3104
  "aria-expanded": open
2861
- }, (0, react.createElement)("span", { className: "evolution-param-card-title" }, t("approvalTitle")), state.view.kind === "empty" ? (0, react.createElement)("span", {
2862
- className: "evolution-param-count",
2863
- role: "status"
2864
- }, t("approvalEmpty")) : null, waiting === 0 ? null : (0, react.createElement)("span", { className: "evolution-param-count" }, t("approvalCount", { n: waiting })), running === 0 ? null : (0, react.createElement)("span", { className: "evolution-param-count" }, t("approvalRunningCount", { n: running })), (0, react.createElement)("span", { className: "evolution-param-chevron" }, open ? "▲" : "▼")), (0, react.createElement)("div", { className: "evolution-param-actions" }, (0, react.createElement)("button", {
3105
+ }, (0, react.createElement)("span", { className: "evolution-param-card-title" }, t("approvalTitle")), waiting === 0 ? null : (0, react.createElement)("span", { className: "evolution-param-count" }, t("approvalCount", { n: waiting })), running === 0 ? null : (0, react.createElement)("span", { className: "evolution-param-count" }, t("approvalRunningCount", { n: running })), (0, react.createElement)("span", { className: "evolution-param-chevron" }, open ? "▲" : "▼")), state.view.kind === "empty" ? null : (0, react.createElement)("div", { className: "evolution-param-actions" }, (0, react.createElement)("button", {
2865
3106
  className: "evolution-param-button",
2866
3107
  type: "button",
2867
3108
  disabled: state.refreshing,
@@ -2903,6 +3144,8 @@ window.__ModuleLoader__.load({
2903
3144
  const zh = {
2904
3145
  "entry.label": "技能历史",
2905
3146
  title: "技能历史",
3147
+ bodyTitle: "正文版本",
3148
+ "workbench.title": "自进化",
2906
3149
  "workbench.history": "技能历史",
2907
3150
  "workbench.pending": "待批",
2908
3151
  "workbench.merge": "合并",
@@ -2912,6 +3155,8 @@ window.__ModuleLoader__.load({
2912
3155
  search: "按名字或描述筛选",
2913
3156
  "search.none": "没有匹配的技能。",
2914
3157
  "group.content": "正文版本",
3158
+ "group.current": "当前版本",
3159
+ "group.history": "历史版本",
2915
3160
  "group.support": "文件版本(不可回退)",
2916
3161
  "group.support.note": "附带文件的字节与正文共用一份索引;回退不恢复它们。",
2917
3162
  "empty.skills": "还没有技能留下过内容版本。",
@@ -2932,7 +3177,9 @@ window.__ModuleLoader__.load({
2932
3177
  "failure.server": "读取失败,可以重试。",
2933
3178
  "fallback.hint": "可以改用命令:{command}",
2934
3179
  "undo.confirm": "确认回退(会写入一版,可再回退)",
3180
+ "undo.confirmUnit": "确认回退整个跨技能写入单元(会写入一版,可再回退)",
2935
3181
  "undo.no": "取消",
3182
+ more: "更多",
2936
3183
  current: "当前",
2937
3184
  "versions.count": "{n} 个版本",
2938
3185
  "row.version": "v{n}",
@@ -2942,8 +3189,8 @@ window.__ModuleLoader__.load({
2942
3189
  "row.delta.down": "(-{n} 字符)",
2943
3190
  "diff.show": "差异",
2944
3191
  "diff.source": "源码",
2945
- "preview.show": "预览",
2946
- "preview.hide": "收起预览",
3192
+ "preview.show": "查看正文",
3193
+ "preview.hide": "收起正文",
2947
3194
  "preview.truncated": "正文过长:只显示前 {shown} 个字符(共 {total} 个)",
2948
3195
  "path.unrecorded": "(路径未记录)",
2949
3196
  "diff.rendered": "渲染",
@@ -2972,6 +3219,7 @@ window.__ModuleLoader__.load({
2972
3219
  "action.archive": "归档",
2973
3220
  "action.archive.note": "这一版存的是被归档的正文",
2974
3221
  "action.consolidate": "合并",
3222
+ "action.merge": "合并",
2975
3223
  "action.restructure": "结构调整",
2976
3224
  "action.extract": "跨技能提取",
2977
3225
  "action.support-write": "写入附带文件",
@@ -2995,6 +3243,8 @@ window.__ModuleLoader__.load({
2995
3243
  const en = {
2996
3244
  "entry.label": "Skill history",
2997
3245
  title: "Skill history",
3246
+ bodyTitle: "Body versions",
3247
+ "workbench.title": "Self-evolution",
2998
3248
  "workbench.history": "Skill history",
2999
3249
  "workbench.pending": "Pending",
3000
3250
  "workbench.merge": "Merge",
@@ -3004,6 +3254,8 @@ window.__ModuleLoader__.load({
3004
3254
  search: "Filter by name or description",
3005
3255
  "search.none": "No skill matches.",
3006
3256
  "group.content": "Body versions",
3257
+ "group.current": "Current version",
3258
+ "group.history": "History",
3007
3259
  "group.support": "File versions (not restorable)",
3008
3260
  "group.support.note": "Support-file bytes share this index with the body; restoring the body does not bring them back.",
3009
3261
  "empty.skills": "No skill has recorded a content version yet.",
@@ -3024,7 +3276,9 @@ window.__ModuleLoader__.load({
3024
3276
  "failure.server": "The read failed; you can retry.",
3025
3277
  "fallback.hint": "Command fallback: {command}",
3026
3278
  "undo.confirm": "Confirm restore (writes a new version; can be undone)",
3279
+ "undo.confirmUnit": "Confirm restoring the whole cross-skill unit (writes a new version; can be undone)",
3027
3280
  "undo.no": "Cancel",
3281
+ more: "More",
3028
3282
  current: "Current",
3029
3283
  "versions.count": "{n} versions",
3030
3284
  "row.version": "v{n}",
@@ -3034,8 +3288,8 @@ window.__ModuleLoader__.load({
3034
3288
  "row.delta.down": "(-{n} chars)",
3035
3289
  "diff.show": "Diff",
3036
3290
  "diff.source": "Source",
3037
- "preview.show": "Preview",
3038
- "preview.hide": "Hide preview",
3291
+ "preview.show": "View body",
3292
+ "preview.hide": "Hide body",
3039
3293
  "preview.truncated": "the body is long: showing the first {shown} of {total} characters",
3040
3294
  "path.unrecorded": "(path not recorded)",
3041
3295
  "diff.rendered": "Rendered",
@@ -3064,6 +3318,7 @@ window.__ModuleLoader__.load({
3064
3318
  "action.archive": "archived",
3065
3319
  "action.archive.note": "this version holds the archived body",
3066
3320
  "action.consolidate": "merged",
3321
+ "action.merge": "merged",
3067
3322
  "action.restructure": "restructured",
3068
3323
  "action.extract": "extracted across skills",
3069
3324
  "action.support-write": "support file written",
@@ -3130,6 +3385,7 @@ window.__ModuleLoader__.load({
3130
3385
  ".evo-hist-skill[aria-current=\"true\"]{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary);font-weight:500}",
3131
3386
  ".evo-hist-cell-line{display:flex;align-items:baseline;gap:var(--evo-space-6);min-width:0}",
3132
3387
  ".evo-hist-cell-meta{margin-top:var(--evo-space-2)}",
3388
+ ".evo-hist-skill-description{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;margin-top:var(--evo-space-2);color:var(--dsw-alias-label-tertiary)}",
3133
3389
  ".evo-hist-cell{min-width:0;font-variant-numeric:tabular-nums}",
3134
3390
  ".evo-hist-slot-lead{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
3135
3391
  ".evo-hist-slot-meta{flex:none;white-space:nowrap}",
@@ -3146,12 +3402,23 @@ window.__ModuleLoader__.load({
3146
3402
  ".evo-hist-note[data-error=\"true\"]{color:var(--evo-tone-danger);background:var(--dsw-alias-bg-layer-2);border:var(--evo-hairline-width) solid var(--evo-tone-danger);border-radius:var(--evo-radius-control);padding:var(--evo-space-8) var(--evo-space-10)}",
3147
3403
  ".evo-hist-icon{flex:none;margin-top:.15em}",
3148
3404
  ".evo-hist-group{margin:var(--evo-space-16) 0 var(--evo-space-4);font-size:var(--evo-type-sm);font-weight:600;color:var(--dsw-alias-label-secondary)}",
3149
- ".evo-hist-row{display:flex;align-items:flex-start;gap:var(--evo-space-8);padding:var(--evo-space-8) 0;border-top:var(--evo-hairline-width) solid var(--dsw-alias-border-l2);max-width:calc(var(--evo-measure-read) * 1.5);color:var(--dsw-alias-label-primary)}",
3405
+ ".evo-hist-reader{max-width:calc(var(--evo-measure-read) * 1.5);margin-top:var(--evo-space-8);padding:var(--evo-space-12);border:var(--evo-hairline-width) solid var(--dsw-alias-border-l2);border-radius:var(--evo-radius-control);background:var(--dsw-alias-bg-layer-1)}",
3406
+ ".evo-hist-current{padding-bottom:var(--evo-space-6)}",
3407
+ ".evo-hist-current .evo-hist-group{margin-top:0}",
3408
+ ".evo-hist-current .evo-hist-row{max-width:none}",
3409
+ ".evo-hist-reader .evo-hist-group{margin-top:var(--evo-space-12)}",
3410
+ ".evo-hist-row{display:grid;grid-template-columns:minmax(0,1fr) auto;grid-template-areas:\"body actions\" \"expanded expanded\";align-items:start;gap:var(--evo-space-4) var(--evo-space-8);padding:var(--evo-space-8) 0;border-top:var(--evo-hairline-width) solid var(--dsw-alias-border-l2);max-width:calc(var(--evo-measure-read) * 1.5);color:var(--dsw-alias-label-primary)}",
3150
3411
  ".evo-hist-row:first-of-type{border-top:0}",
3151
- ".evo-hist-row-body{flex:1 1 auto;min-width:0}",
3412
+ ".evo-hist-row-body{grid-area:body;min-width:0}",
3152
3413
  ".evo-hist-row-title{display:flex;align-items:baseline;gap:var(--evo-space-8);min-width:0}",
3153
3414
  ".evo-hist-row-summary{margin-top:var(--evo-space-2);line-height:var(--evo-leading-base)}",
3154
- ".evo-hist-actions{display:flex;flex:none;align-items:center;gap:var(--evo-space-6)}",
3415
+ ".evo-hist-actions{grid-area:actions;display:flex;align-items:center;gap:var(--evo-space-6)}",
3416
+ ".evo-hist-row-expanded{grid-area:expanded;min-width:0}",
3417
+ ".evo-hist-more{position:relative;display:inline-flex;align-items:center}",
3418
+ ".evo-hist-more>summary{list-style:none;cursor:pointer;color:var(--dsw-alias-label-tertiary);font-size:var(--evo-type-xs);padding:var(--evo-space-2) var(--evo-space-4)}",
3419
+ ".evo-hist-more>summary::-webkit-details-marker{display:none}",
3420
+ ".evo-hist-more[open]>summary{color:var(--dsw-alias-label-secondary)}",
3421
+ ".evo-hist-more>button{margin-left:var(--evo-space-2)}",
3155
3422
  ".evo-hist-chip{flex:none;padding:var(--evo-space-2) var(--evo-space-8);border-radius:var(--evo-radius-pill);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary)}",
3156
3423
  ".evo-hist-button{display:inline-flex;flex:none;align-items:center;gap:var(--evo-space-2);font:inherit;padding:var(--evo-space-4) var(--evo-space-10);border:var(--evo-hairline-width) solid var(--dsw-alias-border-l2);border-radius:var(--evo-radius-control);background:transparent;color:var(--evo-tone-quiet);cursor:pointer}",
3157
3424
  ".evo-hist-button[data-kind=\"toggle\"],.evo-hist-button[data-kind=\"switch\"]{padding:var(--evo-space-2) var(--evo-space-4);border-color:transparent;color:var(--evo-tone-quiet)}",
@@ -3173,7 +3440,8 @@ window.__ModuleLoader__.load({
3173
3440
  ".evo-doc-tag{position:absolute;left:var(--evo-space-8);top:var(--evo-space-8);color:var(--dsw-alias-label-tertiary)}",
3174
3441
  ".evo-doc-line-add{color:var(--evo-tone-success)}",
3175
3442
  ".evo-doc-line-del{color:var(--evo-tone-danger)}",
3176
- "@media (max-width:720px){.evo-hist-root{flex-direction:column}.evo-hist-aside{flex:0 0 auto;width:100%;max-height:42%;border-right:0;border-bottom:var(--evo-hairline-width) solid var(--dsw-alias-border-l2)}.evo-hist-row{max-width:none}}"
3443
+ "@media (max-width:1100px){.evo-hist-row{grid-template-columns:minmax(0,1fr);grid-template-areas:\"body\" \"actions\" \"expanded\"}.evo-hist-actions{flex-wrap:wrap;justify-content:flex-start}}",
3444
+ "@media (max-width:900px){.evo-hist-root{flex-direction:column}.evo-hist-aside{flex:0 0 auto;width:100%;max-height:42%;border-right:0;border-bottom:var(--evo-hairline-width) solid var(--dsw-alias-border-l2)}.evo-hist-row{max-width:none}}"
3177
3445
  ].join("\n");
3178
3446
  //#endregion
3179
3447
  //#region src/panels/history/client/atoms.ts
@@ -3281,11 +3549,7 @@ window.__ModuleLoader__.load({
3281
3549
  */
3282
3550
  /** The local time an ISO timestamp names, as the operator's own clock shows it. */
3283
3551
  function localTime(at) {
3284
- const parsed = Date.parse(at);
3285
- if (Number.isNaN(parsed)) return at;
3286
- const date = new Date(parsed);
3287
- const pad = (value) => String(value).padStart(2, "0");
3288
- return String(date.getFullYear()) + "-" + pad(date.getMonth() + 1) + "-" + pad(date.getDate()) + " " + pad(date.getHours()) + ":" + pad(date.getMinutes());
3552
+ return formatLocalDateTime(at, { seconds: false }) ?? at;
3289
3553
  }
3290
3554
  /** The text one cell shows, resolved through the seat. */
3291
3555
  function cellText(copy, cell) {
@@ -3334,7 +3598,11 @@ window.__ModuleLoader__.load({
3334
3598
  className: "evo-hist-skill",
3335
3599
  "aria-current": selected ? "true" : void 0,
3336
3600
  onClick: onOpen
3337
- }, cellLine(row.cells.title, copy, "evo-hist-cell-line"), cellLine(row.cells.meta, copy, "evo-hist-cell-line evo-hist-cell-meta"));
3601
+ }, cellLine(row.cells.title, copy, "evo-hist-cell-line"), (() => {
3602
+ const description = row.cells.meta.filter((cell) => cell.key === "description");
3603
+ const facts = row.cells.meta.filter((cell) => cell.key !== "description");
3604
+ return [description.length === 0 ? null : cellLine(description, copy, "evo-hist-cell-line evo-hist-skill-description"), facts.length === 0 ? null : cellLine(facts, copy, "evo-hist-cell-line evo-hist-cell-meta")];
3605
+ })());
3338
3606
  }
3339
3607
  /**
3340
3608
  * One version row: what happened, when, how big — then the controls, then whatever is expanded.
@@ -3347,46 +3615,73 @@ window.__ModuleLoader__.load({
3347
3615
  */
3348
3616
  function versionRow(props) {
3349
3617
  const { copy, row, confirming, open, expanded, onRestore, onCancel, onToggle } = props;
3618
+ const current = row.undoable === false;
3350
3619
  const actions = [];
3351
- if (row.undoable === false) actions.push(chip({
3620
+ if (current) actions.push(chip({
3352
3621
  key: "current",
3353
3622
  children: copy.t("current")
3354
3623
  }));
3355
- if (row.undoable === true) {
3356
- if (confirming) actions.push(button({
3357
- key: "cancel",
3358
- onClick: onCancel,
3359
- children: copy.t("undo.no")
3360
- }));
3361
- actions.push(button({
3362
- key: "undo",
3363
- tone: confirming ? "primary" : "quiet",
3364
- onClick: onRestore,
3365
- children: confirming ? copy.t("undo.confirm") : copy.t("undo")
3366
- }));
3367
- }
3368
- if (row.undoable === true || row.charsDelta !== void 0) actions.push(button({
3369
- key: "diff",
3370
- kind: "toggle",
3371
- expanded: open === "diff",
3372
- onClick: () => {
3373
- onToggle("diff");
3374
- },
3375
- children: open === "diff" ? copy.t("diff.hide") : copy.t("diff.show")
3376
- }));
3377
- actions.push(button({
3624
+ if (current) actions.push(button({
3378
3625
  key: "preview",
3379
3626
  kind: "toggle",
3627
+ tone: "primary",
3380
3628
  expanded: open === "preview",
3381
3629
  onClick: () => {
3382
3630
  onToggle("preview");
3383
3631
  },
3384
3632
  children: open === "preview" ? copy.t("preview.hide") : copy.t("preview.show")
3385
3633
  }));
3634
+ if (current && row.charsDelta !== void 0) actions.push(button({
3635
+ key: "diff",
3636
+ kind: "toggle",
3637
+ expanded: open === "diff",
3638
+ onClick: () => {
3639
+ onToggle("diff");
3640
+ },
3641
+ children: open === "diff" ? copy.t("diff.hide") : copy.t("diff.show")
3642
+ }));
3643
+ if (!current) {
3644
+ actions.push(button({
3645
+ key: "preview",
3646
+ kind: "toggle",
3647
+ expanded: open === "preview",
3648
+ onClick: () => {
3649
+ onToggle("preview");
3650
+ },
3651
+ children: open === "preview" ? copy.t("preview.hide") : copy.t("preview.show")
3652
+ }));
3653
+ const more = [];
3654
+ if (row.undoable === true) {
3655
+ if (confirming) more.push(button({
3656
+ key: "cancel",
3657
+ onClick: onCancel,
3658
+ children: copy.t("undo.no")
3659
+ }));
3660
+ more.push(button({
3661
+ key: "undo",
3662
+ tone: confirming ? "primary" : "quiet",
3663
+ onClick: onRestore,
3664
+ children: confirming ? copy.t(row.unitId === void 0 ? "undo.confirm" : "undo.confirmUnit") : copy.t("undo")
3665
+ }));
3666
+ }
3667
+ if (row.undoable === true || row.charsDelta !== void 0) more.push(button({
3668
+ key: "diff",
3669
+ kind: "toggle",
3670
+ expanded: open === "diff",
3671
+ onClick: () => {
3672
+ onToggle("diff");
3673
+ },
3674
+ children: open === "diff" ? copy.t("diff.hide") : copy.t("diff.show")
3675
+ }));
3676
+ if (more.length > 0) actions.push((0, react.createElement)("details", {
3677
+ key: "more",
3678
+ className: "evo-hist-more"
3679
+ }, (0, react.createElement)("summary", null, copy.t("more")), ...more));
3680
+ }
3386
3681
  return (0, react.createElement)("div", {
3387
3682
  key: String(row.v),
3388
3683
  className: "evo-hist-row"
3389
- }, (0, react.createElement)("div", { className: "evo-hist-row-body" }, cellLine(row.cells.title, copy, "evo-hist-row-title", { time: localTime(row.at) }), row.cells.meta.length === 0 ? null : cellLine(row.cells.meta, copy, "evo-hist-row-summary"), expanded), (0, react.createElement)("div", { className: "evo-hist-actions" }, actions));
3684
+ }, (0, react.createElement)("div", { className: "evo-hist-row-body" }, cellLine(row.cells.title, copy, "evo-hist-row-title", { time: localTime(row.at) }), row.cells.meta.length === 0 ? null : cellLine(row.cells.meta, copy, "evo-hist-row-summary")), (0, react.createElement)("div", { className: "evo-hist-actions" }, actions), expanded === null || expanded === void 0 ? null : (0, react.createElement)("div", { className: "evo-hist-row-expanded" }, expanded));
3390
3685
  }
3391
3686
  /**
3392
3687
  * One group's heading, and the note that explains the group.
@@ -3422,8 +3717,8 @@ window.__ModuleLoader__.load({
3422
3717
  });
3423
3718
  }
3424
3719
  /** The pane's heading and its one control. */
3425
- function panelHead(copy, onRefresh) {
3426
- return (0, react.createElement)("div", { className: "evo-hist-head" }, (0, react.createElement)("h2", { className: "evo-hist-title" }, copy.t("title")), button({
3720
+ function panelHead(copy, onRefresh, title) {
3721
+ return (0, react.createElement)("div", { className: "evo-hist-head" }, (0, react.createElement)("h2", { className: "evo-hist-title" }, title ?? copy.t("title")), button({
3427
3722
  key: "refresh",
3428
3723
  title: copy.t("refresh"),
3429
3724
  onClick: onRefresh,
@@ -3684,6 +3979,25 @@ window.__ModuleLoader__.load({
3684
3979
  ] }), toggle), diffView(face, diff.diff, mode));
3685
3980
  }
3686
3981
  //#endregion
3982
+ //#region src/panels/history/client/history-presentation.ts
3983
+ /**
3984
+ * Split a version payload without inventing a version. The host marks current rows with
3985
+ * `undoable: false`; if an older payload lacks that marker, the first content row is the current one.
3986
+ * @param payload - content and support chains from the host.
3987
+ * @returns the display model.
3988
+ */
3989
+ function historyViewModel(payload) {
3990
+ const marked = payload.content.filter((row) => row.undoable === false);
3991
+ const current = marked.length > 0 ? marked : payload.content.slice(0, 1);
3992
+ const currentVersions = new Set(current.map((row) => row.v));
3993
+ return {
3994
+ current,
3995
+ older: payload.content.filter((row) => !currentVersions.has(row.v)),
3996
+ support: payload.support,
3997
+ hasAny: payload.content.length > 0 || payload.support.length > 0
3998
+ };
3999
+ }
4000
+ //#endregion
3687
4001
  //#region src/panels/history/client/Panel.ts
3688
4002
  /**
3689
4003
  * The panel: one read, one open skill, one open row — and the faces that draw them.
@@ -3759,6 +4073,36 @@ window.__ModuleLoader__.load({
3759
4073
  };
3760
4074
  /** The sentence one failed read carries, in ONE place: every reader below reports the same way. */
3761
4075
  const failedMessage = (error) => face.t("error") + ": " + (error instanceof Error ? error.message : String(error));
4076
+ /** Load one version body under the shared read ticket; used by the current default and the toggle. */
4077
+ const loadPreview = (name, v) => {
4078
+ const ticket = readTicket.current + 1;
4079
+ readTicket.current = ticket;
4080
+ setBodies((current) => new Map(current).set(v, {
4081
+ kind: "loading",
4082
+ ticket
4083
+ }));
4084
+ face.loadBody(name, v).then((body) => {
4085
+ if (readTicket.current !== ticket) {
4086
+ setBodies((current) => dropIfTicket(current, v, ticket));
4087
+ return;
4088
+ }
4089
+ setBodies((current) => new Map(current).set(v, {
4090
+ kind: "ready",
4091
+ body,
4092
+ ticket
4093
+ }));
4094
+ }).catch((error) => {
4095
+ if (readTicket.current !== ticket) {
4096
+ setBodies((current) => dropIfTicket(current, v, ticket));
4097
+ return;
4098
+ }
4099
+ setBodies((current) => new Map(current).set(v, {
4100
+ kind: "failed",
4101
+ message: failedMessage(error),
4102
+ ticket
4103
+ }));
4104
+ });
4105
+ };
3762
4106
  /** Read the skills again, keeping the sentence: a re-read is not an answer to anything. */
3763
4107
  const reloadSkills = () => {
3764
4108
  face.loadSkills().then((next) => {
@@ -3787,9 +4131,17 @@ window.__ModuleLoader__.load({
3787
4131
  setBodies(/* @__PURE__ */ new Map());
3788
4132
  }
3789
4133
  face.loadVersions(name).then((next) => {
3790
- if (readTicket.current === ticket) {
3791
- setPayload(next);
3792
- clearReadFailure();
4134
+ if (readTicket.current !== ticket) return;
4135
+ setPayload(next);
4136
+ clearReadFailure();
4137
+ if (keepContent) return;
4138
+ const current = historyViewModel(next).current[0];
4139
+ if (current !== void 0) {
4140
+ setExpanded({
4141
+ v: current.v,
4142
+ kind: "preview"
4143
+ });
4144
+ loadPreview(name, current.v);
3793
4145
  }
3794
4146
  }).catch((error) => {
3795
4147
  if (readTicket.current !== ticket) return;
@@ -3879,36 +4231,13 @@ window.__ModuleLoader__.load({
3879
4231
  });
3880
4232
  return;
3881
4233
  }
3882
- setBodies((current) => new Map(current).set(v, {
3883
- kind: "loading",
3884
- ticket
3885
- }));
3886
- face.loadBody(name, v).then((body) => {
3887
- if (readTicket.current !== ticket) {
3888
- setBodies((current) => dropIfTicket(current, v, ticket));
3889
- return;
3890
- }
3891
- setBodies((current) => new Map(current).set(v, {
3892
- kind: "ready",
3893
- body,
3894
- ticket
3895
- }));
3896
- }).catch((error) => {
3897
- if (readTicket.current !== ticket) {
3898
- setBodies((current) => dropIfTicket(current, v, ticket));
3899
- return;
3900
- }
3901
- setBodies((current) => new Map(current).set(v, {
3902
- kind: "failed",
3903
- message: failedMessage(error),
3904
- ticket
3905
- }));
3906
- });
4234
+ loadPreview(name, v);
3907
4235
  };
3908
4236
  (0, react.useEffect)(reloadSkills, []);
3909
4237
  const needle = query.trim().toLowerCase();
3910
4238
  const visible = (0, react.useMemo)(() => needle === "" ? skills : skills.filter((skill) => skill.name.toLowerCase().includes(needle) || skill.description.toLowerCase().includes(needle)), [skills, needle]);
3911
4239
  const loaded = payload !== void 0;
4240
+ const model = payload === void 0 ? void 0 : historyViewModel(payload);
3912
4241
  /** Drop the pending confirmation: the control that opened it must not be the only way out. */
3913
4242
  const cancelRestore = () => {
3914
4243
  setPending(void 0);
@@ -3949,10 +4278,26 @@ window.__ModuleLoader__.load({
3949
4278
  };
3950
4279
  return (0, react.createElement)("div", { className: "evo-hist-root" }, (0, react.createElement)("aside", { className: "evo-hist-aside" }, searchField(face, query, setQuery), (0, react.createElement)("div", { className: "evo-hist-list" }, skills.length === 0 ? emptyState(face.t(skillsLoaded ? "empty.skills" : "loading")) : visible.length === 0 ? emptyState(face.t("search.none")) : visible.map((skill) => skillLine(face, skill, skill.name === selected, () => {
3951
4280
  open(skill.name);
3952
- })))), (0, react.createElement)("div", { className: "evo-hist-main" }, panelHead(face, refresh), notice(noteText, noteError), failure === void 0 ? null : (0, react.createElement)("div", { className: "evo-hist-failure" }, failureBlock(face.t, failure)), selected === void 0 ? [emptyState(face.t(skills.length === 0 ? skillsLoaded ? "empty.skills" : "loading" : "empty.pick"), "empty"), (0, react.createElement)("p", {
4281
+ })))), (0, react.createElement)("div", { className: "evo-hist-main" }, panelHead(face, refresh, selected ?? face.t("bodyTitle")), notice(noteText, noteError), failure === void 0 ? null : (0, react.createElement)("div", { className: "evo-hist-failure" }, failureBlock(face.t, failure)), selected === void 0 ? [emptyState(face.t(skills.length === 0 ? skillsLoaded ? "empty.skills" : "loading" : "empty.pick"), "empty"), (0, react.createElement)("p", {
3953
4282
  key: "hint",
3954
4283
  className: "evo-hist-hint"
3955
- }, face.t("hint"))] : loaded ? null : emptyState(versionsFailed === void 0 ? face.t("loading") : `${face.t("empty.versions.failed")} — ${versionsFailed}`), loaded ? (0, react.createElement)("div", null, group(face.t("group.content"), payload.content, void 0), group(face.t("group.support"), payload.support, face.t("group.support.note")), payload.content.length === 0 && payload.support.length === 0 ? (0, react.createElement)("p", { className: "evo-hist-hint" }, face.t("empty.versions")) : null) : null));
4284
+ }, face.t("hint"))] : loaded ? null : emptyState(versionsFailed === void 0 ? face.t("loading") : `${face.t("empty.versions.failed")} — ${versionsFailed}`), loaded && model !== void 0 ? (0, react.createElement)("div", { className: "evo-hist-reader" }, model.current.length === 0 ? null : (0, react.createElement)("section", {
4285
+ key: "current",
4286
+ className: "evo-hist-current"
4287
+ }, ...groupHeading(face.t("group.current"), void 0), ...model.current.map((row) => versionRow({
4288
+ copy: face,
4289
+ row,
4290
+ confirming: pending === row.v,
4291
+ open: expanded !== void 0 && expanded.v === row.v ? expanded.kind : void 0,
4292
+ expanded: expandedOf(row),
4293
+ onRestore: () => {
4294
+ restore(row.v, row.unitId);
4295
+ },
4296
+ onCancel: cancelRestore,
4297
+ onToggle: (kind) => {
4298
+ toggle(row.v, kind);
4299
+ }
4300
+ }))), group(face.t("group.history"), model.older, void 0), model.support.length === 0 ? null : group(face.t("group.support"), model.support, face.t("group.support.note")), model.hasAny ? null : (0, react.createElement)("p", { className: "evo-hist-hint" }, face.t("empty.versions"))) : null));
3956
4301
  }
3957
4302
  /**
3958
4303
  * Drop a superseded lazy read ONLY if the entry still belongs to that read.
@@ -4000,6 +4345,24 @@ window.__ModuleLoader__.load({
4000
4345
  fail: "diagSummaryFail",
4001
4346
  unknown: "diagSummaryUnknown"
4002
4347
  });
4348
+ /**
4349
+ * Split the frozen runtime rows without dropping one. The top issue follows the same fail > warn > unknown order.
4350
+ * @param rows - runtime rows from the host view.
4351
+ * @returns the reading shape used by the card.
4352
+ */
4353
+ function diagnosticsReading(rows) {
4354
+ const order = [
4355
+ "fail",
4356
+ "warn",
4357
+ "unknown"
4358
+ ];
4359
+ const issues = [...rows].filter((row) => row.status !== "ok").sort((a, b) => order.indexOf(a.status) - order.indexOf(b.status));
4360
+ return {
4361
+ topIssue: issues[0],
4362
+ otherIssues: issues.slice(1),
4363
+ normal: rows.filter((row) => row.status === "ok")
4364
+ };
4365
+ }
4003
4366
  /** The one status the card leads with: fail > warn > unknown > ok. */
4004
4367
  function summaryOf(rows) {
4005
4368
  const count = (status) => rows.filter((row) => row.status === status).length;
@@ -4029,19 +4392,26 @@ window.__ModuleLoader__.load({
4029
4392
  const translate = (key, values) => t(key, values);
4030
4393
  const [state, setState] = (0, react.useState)({ phase: "loading" });
4031
4394
  const [refreshing, setRefreshing] = (0, react.useState)(false);
4395
+ const [refreshFailure, setRefreshFailure] = (0, react.useState)(void 0);
4032
4396
  const [open, setOpen] = (0, react.useState)(false);
4033
4397
  const ticket = (0, react.useRef)(0);
4034
4398
  const read = (keepView) => {
4035
4399
  const current = ticket.current + 1;
4036
4400
  ticket.current = current;
4037
- if (keepView) setRefreshing(true);
4038
- else setState({ phase: "loading" });
4401
+ if (keepView) {
4402
+ setRefreshing(true);
4403
+ setRefreshFailure(void 0);
4404
+ } else setState({ phase: "loading" });
4039
4405
  api.view().then((answer) => {
4040
4406
  if (ticket.current !== current) return;
4041
- setState(answer.ok ? {
4042
- phase: "ready",
4043
- view: answer.data
4044
- } : {
4407
+ if (answer.ok) {
4408
+ setState({
4409
+ phase: "ready",
4410
+ view: answer.data
4411
+ });
4412
+ setRefreshFailure(void 0);
4413
+ } else if (keepView) setRefreshFailure(answer.failure);
4414
+ else setState({
4045
4415
  phase: "failed",
4046
4416
  failure: answer.failure
4047
4417
  });
@@ -4056,6 +4426,7 @@ window.__ModuleLoader__.load({
4056
4426
  read(true);
4057
4427
  };
4058
4428
  const summary = state.phase === "ready" ? summaryOf(state.view.runtime) : void 0;
4429
+ const focusRow = summary === void 0 || summary.status === "ok" || state.phase !== "ready" ? void 0 : state.view.runtime.find((row) => row.status === summary.status);
4059
4430
  const header = (0, react.createElement)("button", {
4060
4431
  type: "button",
4061
4432
  className: "evolution-param-head",
@@ -4066,13 +4437,31 @@ window.__ModuleLoader__.load({
4066
4437
  }, (0, react.createElement)("span", { className: "evolution-param-card-title" }, t("diagTitle")), summary === void 0 ? null : (0, react.createElement)("span", {
4067
4438
  className: "evo-diag-badge",
4068
4439
  "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)))));
4440
+ }, t(SUMMARY_KEYS[summary.status], summary.count === 0 ? void 0 : { n: summary.count })), focusRow === void 0 ? null : (0, react.createElement)("span", { "data-role": "diag-focus" }, focusRow.label), (0, react.createElement)("span", { className: "evolution-param-chevron" }, open ? "▲" : "▼"));
4441
+ const runtime = (view) => {
4442
+ if (view.runtime.length === 0) return null;
4443
+ const reading = diagnosticsReading(view.runtime);
4444
+ const rowNode = (row) => (0, react.createElement)("div", {
4445
+ key: "runtime-" + row.key,
4446
+ className: "evo-diag-item",
4447
+ "data-kind": "runtime",
4448
+ "data-status": row.status
4449
+ }, (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));
4450
+ return (0, react.createElement)("div", {}, (0, react.createElement)("h3", { className: "evolution-param-group" }, t("diagRuntimeTitle")), reading.topIssue === void 0 ? (0, react.createElement)("div", {
4451
+ className: "evo-diag-complete",
4452
+ "data-role": "diag-complete"
4453
+ }, t("diagComplete")) : (0, react.createElement)("div", {
4454
+ className: "evo-diag-issue",
4455
+ "data-role": "diag-issue",
4456
+ "data-status": reading.topIssue.status
4457
+ }, rowNode(reading.topIssue)), reading.otherIssues.length === 0 ? null : (0, react.createElement)("details", {
4458
+ className: "evo-diag-group",
4459
+ "data-role": "diag-other-issues"
4460
+ }, (0, react.createElement)("summary", null, t("diagOtherIssues", { n: reading.otherIssues.length })), ...reading.otherIssues.map(rowNode)), reading.normal.length === 0 ? null : (0, react.createElement)("details", {
4461
+ className: "evo-diag-group",
4462
+ "data-role": "diag-normal"
4463
+ }, (0, react.createElement)("summary", null, t("diagNormal", { n: reading.normal.length })), ...reading.normal.map(rowNode)));
4464
+ };
4076
4465
  const body = () => {
4077
4466
  if (state.phase === "loading") return (0, react.createElement)("p", { className: "evolution-param-loading" }, t("stateLoading"));
4078
4467
  if (state.phase === "failed") return FailureBlock({
@@ -4081,7 +4470,11 @@ window.__ModuleLoader__.load({
4081
4470
  fallback: props.fallback
4082
4471
  });
4083
4472
  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", {
4473
+ return (0, react.createElement)("div", {}, (0, react.createElement)("p", { className: "evolution-param-note" }, t("diagSubtitle")), refreshFailure === void 0 ? null : FailureBlock({
4474
+ failure: refreshFailure,
4475
+ t: translate,
4476
+ fallback: props.fallback
4477
+ }), (0, react.createElement)("div", { className: "evolution-param-actions" }, (0, react.createElement)("button", {
4085
4478
  type: "button",
4086
4479
  className: "evolution-param-button",
4087
4480
  onClick: refresh,
@@ -4104,6 +4497,69 @@ window.__ModuleLoader__.load({
4104
4497
  }, header, open ? (0, react.createElement)("div", { className: "evolution-param-body" }, body()) : null);
4105
4498
  }
4106
4499
  //#endregion
4500
+ //#region src/panels/history/client/WorkbenchStore.ts
4501
+ /**
4502
+ * Resolve the first landing from task counts. Pending outranks merge; no task lands on history.
4503
+ * @param counts - read-only task counts.
4504
+ * @param hasMerge - whether the merge view is mounted.
4505
+ * @returns the view to open when the user has not chosen one.
4506
+ */
4507
+ function resolveDefaultView(counts, hasMerge) {
4508
+ if (counts.pending > 0) return "pending";
4509
+ if (hasMerge && counts.merge > 0) return "merge";
4510
+ return "history";
4511
+ }
4512
+ function createWorkbenchStore(initial = {
4513
+ view: "history",
4514
+ focusRevision: 0,
4515
+ explicitView: false
4516
+ }) {
4517
+ let state = initial;
4518
+ const listeners = /* @__PURE__ */ new Set();
4519
+ const emit = (next) => {
4520
+ state = next;
4521
+ for (const listener of listeners) listener();
4522
+ };
4523
+ return {
4524
+ getState: () => state,
4525
+ subscribe(listener) {
4526
+ listeners.add(listener);
4527
+ return () => {
4528
+ listeners.delete(listener);
4529
+ };
4530
+ },
4531
+ setView(view) {
4532
+ if (state.view !== view || state.explicitView !== true) emit({
4533
+ ...state,
4534
+ view,
4535
+ explicitView: true
4536
+ });
4537
+ },
4538
+ setDefaultView(view) {
4539
+ if (state.explicitView !== true && state.view !== view) emit({
4540
+ ...state,
4541
+ view,
4542
+ explicitView: false
4543
+ });
4544
+ },
4545
+ openHistory(skill) {
4546
+ emit({
4547
+ view: "history",
4548
+ ...skill === void 0 ? {} : { selectedSkill: skill },
4549
+ focusRevision: state.focusRevision + 1,
4550
+ explicitView: true
4551
+ });
4552
+ },
4553
+ clearSelection() {
4554
+ const { selectedSkill: _selected, ...rest } = state;
4555
+ emit({
4556
+ ...rest,
4557
+ focusRevision: state.focusRevision + 1
4558
+ });
4559
+ }
4560
+ };
4561
+ }
4562
+ //#endregion
4107
4563
  //#region src/panels/history/client/Workbench.ts
4108
4564
  /**
4109
4565
  * The family's one task page. G3 owns the history view; later groups add health
@@ -4129,13 +4585,29 @@ window.__ModuleLoader__.load({
4129
4585
  const hasMerge = props.hasView?.("merge") ?? true;
4130
4586
  const hasHealth = props.hasView?.("health") ?? true;
4131
4587
  (0, react.useEffect)(() => {
4132
- if (view === "merge" && !hasMerge || view === "health" && !hasHealth) props.store.setView("history");
4588
+ if (view === "merge" && !hasMerge || view === "health" && !hasHealth) props.store.setDefaultView("history");
4133
4589
  }, [
4134
4590
  view,
4135
4591
  hasMerge,
4136
4592
  hasHealth,
4137
4593
  props.store
4138
4594
  ]);
4595
+ (0, react.useEffect)(() => {
4596
+ const load = props.loadTaskCounts;
4597
+ if (load === void 0 || state.explicitView === true) return;
4598
+ let live = true;
4599
+ load().then((counts) => {
4600
+ if (live) props.store.setDefaultView(resolveDefaultView(counts, hasMerge));
4601
+ }).catch(() => {});
4602
+ return () => {
4603
+ live = false;
4604
+ };
4605
+ }, [
4606
+ props.loadTaskCounts,
4607
+ state.explicitView,
4608
+ hasMerge,
4609
+ props.store
4610
+ ]);
4139
4611
  const viewOrder = [
4140
4612
  "history",
4141
4613
  "pending",
@@ -4157,7 +4629,10 @@ window.__ModuleLoader__.load({
4157
4629
  return (0, react.createElement)("section", {
4158
4630
  className: "evolution-page-surface evo-workbench",
4159
4631
  "data-page": view
4160
- }, (0, react.createElement)("div", { className: "evo-workbench-head" }, (0, react.createElement)("h1", { className: "evo-workbench-title" }, props.t("entry.label")), (0, react.createElement)("div", {
4632
+ }, (0, react.createElement)("div", { className: "evo-workbench-head" }, (0, react.createElement)("h1", {
4633
+ className: "evo-workbench-title",
4634
+ "data-view": view
4635
+ }, props.t("workbench.title")), (0, react.createElement)("div", {
4161
4636
  className: "evo-workbench-tabs",
4162
4637
  role: "tablist",
4163
4638
  "aria-label": props.t("entry.label")
@@ -4239,7 +4714,12 @@ window.__ModuleLoader__.load({
4239
4714
  ...props.history,
4240
4715
  focusSkill: selectedSkill,
4241
4716
  focusRevision
4242
- }) : view === "pending" ? (0, react.createElement)(PendingCard, props.pending) : view === "diagnostics" ? (0, react.createElement)(DiagnosticsCard, props.diagnostics) : null, (0, react.createElement)("div", {
4717
+ }) : view === "pending" ? (0, react.createElement)(PendingCard, {
4718
+ ...props.pending,
4719
+ onHistory: () => {
4720
+ openHistory(void 0);
4721
+ }
4722
+ }) : view === "diagnostics" ? (0, react.createElement)(DiagnosticsCard, props.diagnostics) : null, (0, react.createElement)("div", {
4243
4723
  hidden: view !== "merge" && view !== "health",
4244
4724
  className: "evo-workbench-feature"
4245
4725
  }, props.renderSlot?.("evolution.workbench.view", {
@@ -4275,52 +4755,14 @@ window.__ModuleLoader__.load({
4275
4755
  ".evo-workbench-tabs button:focus-visible{outline:var(--evo-focus-ring);outline-offset:var(--evo-focus-offset)}",
4276
4756
  ".evo-workbench-title{margin:0;font-size:var(--evo-type-lg);font-weight:600;line-height:var(--evo-leading-base)}",
4277
4757
  ".evo-workbench-body{display:flex;flex:1;min-height:0;min-width:0;overflow:hidden}",
4758
+ ".evo-workbench-feature{display:flex;flex:1;min-width:0;min-height:0}",
4759
+ ".evo-workbench-feature[hidden]{display:none}",
4760
+ ".evo-workbench-feature>*{flex:1;min-width:0}",
4761
+ ".evo-workbench-feature .evolution-merge-panel,.evo-workbench-feature .evolution-skill-health-panel{flex:1 1 auto;min-width:0}",
4278
4762
  ".evo-workbench-body>.evolution-param-card{flex:1;min-width:0;overflow:auto}",
4279
4763
  "@media (max-width:720px){.evo-workbench-head{flex-wrap:wrap}.evo-workbench-tabs{flex-wrap:wrap}}"
4280
4764
  ].join("");
4281
4765
  //#endregion
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
- }
4321
- };
4322
- }
4323
- //#endregion
4324
4766
  //#region src/panels/history/client/index.ts
4325
4767
  /**
4326
4768
  * Register the locale namespace, the workbench main occupant and its sidebar row.
@@ -4369,9 +4811,10 @@ window.__ModuleLoader__.load({
4369
4811
  footnotes: t("markdown.footnotes")
4370
4812
  }
4371
4813
  });
4814
+ const approvalApi = createApprovalApi((input, init) => fetch(input, init));
4372
4815
  const pendingFace = () => ({
4373
4816
  t: pendingT,
4374
- api: createApprovalApi((input, init) => fetch(input, init)),
4817
+ api: approvalApi,
4375
4818
  confirmMode,
4376
4819
  fallback: FALLBACKS.approval
4377
4820
  });
@@ -4380,6 +4823,26 @@ window.__ModuleLoader__.load({
4380
4823
  api: createDiagnosticsApi((input, init) => fetch(input, init)),
4381
4824
  fallback: FALLBACKS.diagnostics
4382
4825
  });
4826
+ const loadTaskCounts = async () => {
4827
+ const pending = approvalApi.pending().then((answer) => answer.ok ? answer.data.length : 0).catch(() => 0);
4828
+ const merge = (async () => {
4829
+ try {
4830
+ const response = await fetch(ROUTES.merge.view, { headers: { accept: "application/json" } });
4831
+ if (!response.ok) return 0;
4832
+ const body = await response.json();
4833
+ const raw = body.ok === true ? body.data : body;
4834
+ const count = Number(raw?.counts?.active?.pending ?? raw?.counts?.pending ?? 0);
4835
+ return Number.isFinite(count) && count > 0 ? count : 0;
4836
+ } catch {
4837
+ return 0;
4838
+ }
4839
+ })();
4840
+ const [pendingCount, mergeCount] = await Promise.all([pending, merge]);
4841
+ return {
4842
+ pending: pendingCount,
4843
+ merge: mergeCount
4844
+ };
4845
+ };
4383
4846
  const workbenchStore = createWorkbenchStore();
4384
4847
  registerSidebarPanel(ctx, {
4385
4848
  id: WORKBENCH_ID,
@@ -4401,7 +4864,8 @@ window.__ModuleLoader__.load({
4401
4864
  history: historyFace(t),
4402
4865
  pending: pendingFace(),
4403
4866
  diagnostics: diagnosticsFace(),
4404
- hasView
4867
+ hasView,
4868
+ loadTaskCounts
4405
4869
  }),
4406
4870
  children: { "evolution.workbench.view": {
4407
4871
  kind: "list",