@lmzhen/dsh-evolution-merge-ui 0.34.0 → 0.35.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 +279 -174
- package/lib/types/client/MergeRow.d.ts +27 -0
- package/lib/types/client/MergeRunStrip.d.ts +16 -0
- package/lib/types/client/MergeSettings.d.ts +15 -0
- package/lib/types/client/Panel.d.ts +1 -1
- package/lib/types/client/api.d.ts +1 -1
- package/lib/types/client/merge-presentation.d.ts +20 -0
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -272,6 +272,25 @@ window.__ModuleLoader__.load({
|
|
|
272
272
|
} catch {}
|
|
273
273
|
}
|
|
274
274
|
//#endregion
|
|
275
|
+
//#region ../evolution-client-kit/src/format.ts
|
|
276
|
+
/**
|
|
277
|
+
* Format an ISO string, epoch milliseconds, or null as local wall time.
|
|
278
|
+
* Invalid values return null; callers decide how to say "unknown".
|
|
279
|
+
* @param value - ISO string, epoch ms, or null.
|
|
280
|
+
* @param options - seconds on/off; defaults to on.
|
|
281
|
+
* @returns the formatted local time, or null when the value is absent/invalid.
|
|
282
|
+
*/
|
|
283
|
+
function formatLocalDateTime(value, options = {}) {
|
|
284
|
+
if (value === null || value === void 0) return null;
|
|
285
|
+
const ms = typeof value === "number" ? value : Date.parse(value);
|
|
286
|
+
if (!Number.isFinite(ms)) return null;
|
|
287
|
+
const date = new Date(ms);
|
|
288
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
289
|
+
const pad = (part) => String(part).padStart(2, "0");
|
|
290
|
+
const head = String(date.getFullYear()) + "-" + pad(date.getMonth() + 1) + "-" + pad(date.getDate()) + " " + pad(date.getHours()) + ":" + pad(date.getMinutes());
|
|
291
|
+
return options.seconds === false ? head : head + ":" + pad(date.getSeconds());
|
|
292
|
+
}
|
|
293
|
+
//#endregion
|
|
275
294
|
//#region src/client/api.ts
|
|
276
295
|
/** Read-only merge wire client. Classifies every failure and never writes. */
|
|
277
296
|
const HOST_ROUTES = ROUTES.merge;
|
|
@@ -628,7 +647,7 @@ window.__ModuleLoader__.load({
|
|
|
628
647
|
reason: item.reason,
|
|
629
648
|
...item.reasonCode === void 0 ? {} : { reasonCode: item.reasonCode },
|
|
630
649
|
lastVersion: item.lastVersion,
|
|
631
|
-
archivedAt: new Date(item.archivedAt).toISOString(),
|
|
650
|
+
archivedAt: item.archivedAt === null ? null : new Date(item.archivedAt).toISOString(),
|
|
632
651
|
...item.sourceRunId === void 0 ? {} : { sourceRunId: item.sourceRunId }
|
|
633
652
|
})) }
|
|
634
653
|
} : answer;
|
|
@@ -647,7 +666,9 @@ window.__ModuleLoader__.load({
|
|
|
647
666
|
"degraded": "读模型降级:",
|
|
648
667
|
"copied": "已复制:",
|
|
649
668
|
"copyFailed": "复制失败:浏览器没有接受剪贴板写入。",
|
|
669
|
+
"copyRetry": "重试复制",
|
|
650
670
|
"row.noSummary": "无摘要",
|
|
671
|
+
"row.summaryBoth": "双方摘要",
|
|
651
672
|
"overlay.body": "正文",
|
|
652
673
|
"hint": "手动触发;后台逐对判定。这里只读,不写技能。",
|
|
653
674
|
"history": "历史",
|
|
@@ -691,6 +712,7 @@ window.__ModuleLoader__.load({
|
|
|
691
712
|
"row.failure": "失败",
|
|
692
713
|
"row.noRetry": "不可重试",
|
|
693
714
|
"row.copy": "复制命令",
|
|
715
|
+
"row.more": "更多命令",
|
|
694
716
|
"tab.expired": "已过期",
|
|
695
717
|
"row.targetCreated": "新建",
|
|
696
718
|
"row.verify": "判定状态",
|
|
@@ -730,6 +752,7 @@ window.__ModuleLoader__.load({
|
|
|
730
752
|
"disabled.unavailable": "暂不可用",
|
|
731
753
|
"history.empty": "没有历史。",
|
|
732
754
|
"history.archivedAt": "归档于 {date}",
|
|
755
|
+
"history.archivedAtUnknown": "归档时间未知",
|
|
733
756
|
"failureServer": "读取失败",
|
|
734
757
|
"failureUnauthorized": "未登录",
|
|
735
758
|
"failureForbidden": "无权访问",
|
|
@@ -808,6 +831,9 @@ window.__ModuleLoader__.load({
|
|
|
808
831
|
"settings.capabilityDisabled": "当前部署不可用",
|
|
809
832
|
"settings.readonly": "只读显示;修改请到平台设置页。",
|
|
810
833
|
"run.title": "运行",
|
|
834
|
+
"run.progressUnknown": "进度未知",
|
|
835
|
+
"run.current": "当前运行",
|
|
836
|
+
"run.last": "上次完成",
|
|
811
837
|
"run.id": "运行编号",
|
|
812
838
|
"run.state": "状态",
|
|
813
839
|
"run.stage": "阶段",
|
|
@@ -849,7 +875,9 @@ window.__ModuleLoader__.load({
|
|
|
849
875
|
"degraded": "Degraded read model:",
|
|
850
876
|
"copied": "Copied:",
|
|
851
877
|
"copyFailed": "Copy failed: the browser did not accept the clipboard write.",
|
|
878
|
+
"copyRetry": "Retry copy",
|
|
852
879
|
"row.noSummary": "No summary",
|
|
880
|
+
"row.summaryBoth": "Shared summary",
|
|
853
881
|
"overlay.body": "Body",
|
|
854
882
|
"hint": "Manual only; pairs are judged in the background. Read-only here.",
|
|
855
883
|
"history": "History",
|
|
@@ -893,6 +921,7 @@ window.__ModuleLoader__.load({
|
|
|
893
921
|
"row.failure": "Failure",
|
|
894
922
|
"row.noRetry": "not retryable",
|
|
895
923
|
"row.copy": "Copy command",
|
|
924
|
+
"row.more": "More commands",
|
|
896
925
|
"tab.expired": "Expired",
|
|
897
926
|
"row.targetCreated": "created",
|
|
898
927
|
"row.verify": "Verify state",
|
|
@@ -932,6 +961,7 @@ window.__ModuleLoader__.load({
|
|
|
932
961
|
"disabled.unavailable": "unavailable",
|
|
933
962
|
"history.empty": "No history.",
|
|
934
963
|
"history.archivedAt": "Archived {date}",
|
|
964
|
+
"history.archivedAtUnknown": "Archive time unknown",
|
|
935
965
|
"failureServer": "Read failed",
|
|
936
966
|
"failureUnauthorized": "Not signed in",
|
|
937
967
|
"failureForbidden": "Forbidden",
|
|
@@ -1010,6 +1040,9 @@ window.__ModuleLoader__.load({
|
|
|
1010
1040
|
"settings.capabilityDisabled": "Not available in this deployment",
|
|
1011
1041
|
"settings.readonly": "Read-only here; edit in platform settings.",
|
|
1012
1042
|
"run.title": "Run",
|
|
1043
|
+
"run.progressUnknown": "progress unknown",
|
|
1044
|
+
"run.current": "Current run",
|
|
1045
|
+
"run.last": "Last completed",
|
|
1013
1046
|
"run.id": "Run ID",
|
|
1014
1047
|
"run.state": "State",
|
|
1015
1048
|
"run.stage": "Stage",
|
|
@@ -1091,7 +1124,8 @@ window.__ModuleLoader__.load({
|
|
|
1091
1124
|
".evolution-merge-panel [data-role=\"title\"] { margin:0; font-size:var(--evo-type-lg); font-weight:600; }",
|
|
1092
1125
|
".evolution-merge-panel [data-role=\"start\"] { display:flex; gap:var(--evo-space-6); flex-wrap:wrap; }",
|
|
1093
1126
|
".evolution-merge-panel [data-role=\"degraded\"] { margin:0; color:var(--dsw-alias-state-warn-primary); font-size:var(--evo-type-xs); }",
|
|
1094
|
-
".evolution-merge-panel [data-role=\"expired-toggle\"]
|
|
1127
|
+
".evolution-merge-panel [data-role=\"expired-toggle\"] { display:inline-flex; align-items:center; gap:var(--evo-space-4); border:var(--evo-hairline-width) solid var(--dsw-alias-border-secondary); border-radius:var(--evo-radius-control); background:transparent; color:var(--dsw-alias-label-secondary); font:inherit; font-size:var(--evo-type-xs); padding:var(--evo-space-4) var(--evo-space-8); cursor:pointer; }",
|
|
1128
|
+
".evolution-merge-panel [data-role=\"expired-toggle\"][data-active=\"true\"] { color:var(--dsw-alias-state-warn-primary); border-color:var(--dsw-alias-state-warn-primary); background:transparent; }",
|
|
1095
1129
|
".evolution-merge-panel [data-role=\"failure\"] { display:flex; flex-direction:column; gap:var(--evo-space-6); }",
|
|
1096
1130
|
".evolution-merge-panel [data-role=\"failure\"] [data-tone=\"fail\"] { color:var(--dsw-alias-state-error-primary); }",
|
|
1097
1131
|
".evolution-merge-panel [data-role=\"fallback\"] { display:flex; gap:var(--evo-space-6); flex-wrap:wrap; }",
|
|
@@ -1120,7 +1154,9 @@ window.__ModuleLoader__.load({
|
|
|
1120
1154
|
".evolution-merge-panel [data-role=\"settings-card\"] { display:flex; flex-direction:column; gap:var(--evo-space-6); padding: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-1); }",
|
|
1121
1155
|
".evolution-merge-panel [data-role=\"settings-title\"] { margin:0; font-size:var(--evo-type-sm); }",
|
|
1122
1156
|
".evolution-merge-panel [data-role=\"settings-hint\"], .evolution-merge-panel [data-role=\"settings-readonly\"] { margin:0; color:var(--dsw-alias-label-tertiary); font-size:var(--evo-type-xs); }",
|
|
1123
|
-
".evolution-merge-panel [data-role=\"settings-
|
|
1157
|
+
".evolution-merge-panel [data-role=\"settings-chips\"] { display:flex; flex-wrap:wrap; gap:var(--evo-space-6); }",
|
|
1158
|
+
".evolution-merge-panel [data-role=\"settings-section\"] { display:inline-flex; align-items:center; gap:var(--evo-space-6); padding:var(--evo-space-2) var(--evo-space-8); border:var(--evo-hairline-width) solid var(--dsw-alias-border-l2); border-radius:var(--evo-radius-control); font-size:var(--evo-type-xs); }",
|
|
1159
|
+
".evolution-merge-panel [data-role=\"settings-label\"] { color:var(--dsw-alias-label-secondary); }",
|
|
1124
1160
|
".evolution-merge-panel [data-role=\"settings-section\"][data-disabled=\"true\"] { color:var(--dsw-alias-label-tertiary); }",
|
|
1125
1161
|
".evolution-merge-panel [data-role=\"settings-toggle\"] { margin:0; }",
|
|
1126
1162
|
".evolution-merge-panel [data-role=\"settings-value\"] { font-weight:600; min-width:2.5em; text-align:right; }",
|
|
@@ -1305,121 +1341,14 @@ window.__ModuleLoader__.load({
|
|
|
1305
1341
|
return edge === "top" ? [id, ...rest] : [...rest, id];
|
|
1306
1342
|
}
|
|
1307
1343
|
//#endregion
|
|
1308
|
-
//#region src/client/
|
|
1309
|
-
/**
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
"/evolution merge candidates",
|
|
1317
|
-
"/evolution merge status",
|
|
1318
|
-
"/evolution merge report",
|
|
1319
|
-
"/evolution doctor"
|
|
1320
|
-
];
|
|
1321
|
-
//#endregion
|
|
1322
|
-
//#region src/client/Panel.ts
|
|
1323
|
-
/** Batch 4 merge panel: keyed read store, four tabs, local overlays, localized errors. */
|
|
1324
|
-
const QUEUES = [
|
|
1325
|
-
"verify",
|
|
1326
|
-
"pending",
|
|
1327
|
-
"fuse",
|
|
1328
|
-
"skipped"
|
|
1329
|
-
];
|
|
1330
|
-
const DOCTOR_COMMAND = "/evolution doctor";
|
|
1331
|
-
function problemKey(problem) {
|
|
1332
|
-
switch (problem.code) {
|
|
1333
|
-
case "unauthenticated": return "failureUnauthorized";
|
|
1334
|
-
case "forbidden": return "failureForbidden";
|
|
1335
|
-
case "unmounted": return "failureUnmounted";
|
|
1336
|
-
case "capability": return "failureCapability";
|
|
1337
|
-
case "body-ambiguous": return "failureBodyAmbiguous";
|
|
1338
|
-
case "wire-version": return "failureWireVersion";
|
|
1339
|
-
case "body-missing": return "failureBodyMissing";
|
|
1340
|
-
case "bad-query": return "failureBadQuery";
|
|
1341
|
-
case "refused": return "failureRefused";
|
|
1342
|
-
case "server": return "failureServer";
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
function reasonText(t, reasonCode, fallback) {
|
|
1346
|
-
if (reasonCode === void 0) return fallback;
|
|
1347
|
-
const key = "history.reason." + reasonCode;
|
|
1348
|
-
const text = t(key);
|
|
1349
|
-
return text === key ? fallback : text;
|
|
1350
|
-
}
|
|
1351
|
-
function blockedReasonKey(failure) {
|
|
1352
|
-
const code = failure.readiness.code ?? failure.code;
|
|
1353
|
-
if (code.endsWith("needs-repair")) return "blocked.reason.needs-repair";
|
|
1354
|
-
if (code.endsWith("forward-only")) return "blocked.reason.forward-only";
|
|
1355
|
-
if (code.endsWith("migration-blocked")) return "blocked.reason.migration-blocked";
|
|
1356
|
-
return "blocked.reason.unknown";
|
|
1357
|
-
}
|
|
1358
|
-
function failureNode(t, failure, fallback, copy) {
|
|
1359
|
-
if (isBlocked(failure)) {
|
|
1360
|
-
const raw = failure.readiness.message;
|
|
1361
|
-
return (0, react.createElement)("div", {
|
|
1362
|
-
"data-role": "blocked",
|
|
1363
|
-
role: "alert",
|
|
1364
|
-
"data-block-code": failure.code,
|
|
1365
|
-
"data-readiness-code": failure.readiness.code ?? ""
|
|
1366
|
-
}, (0, react.createElement)("h3", { "data-role": "blocked-title" }, t("blocked.title")), (0, react.createElement)("p", {
|
|
1367
|
-
"data-role": "blocked-reason",
|
|
1368
|
-
title: raw ?? t(blockedReasonKey(failure))
|
|
1369
|
-
}, t(blockedReasonKey(failure))), (0, react.createElement)("p", { "data-role": "blocked-hint" }, t("blocked.hint")), raw === void 0 ? null : (0, react.createElement)("details", { "data-role": "blocked-detail" }, (0, react.createElement)("summary", null, t("blocked.detail")), (0, react.createElement)("p", { "data-role": "blocked-raw" }, raw)), (0, react.createElement)("div", { "data-role": "blocked-actions" }, (0, react.createElement)("button", {
|
|
1370
|
-
type: "button",
|
|
1371
|
-
"data-role": "doctor",
|
|
1372
|
-
onClick: () => {
|
|
1373
|
-
copy(DOCTOR_COMMAND);
|
|
1374
|
-
}
|
|
1375
|
-
}, t("blocked.doctor")), (0, react.createElement)("button", {
|
|
1376
|
-
type: "button",
|
|
1377
|
-
"data-role": "refresh",
|
|
1378
|
-
onClick: () => {
|
|
1379
|
-
window.dispatchEvent(new Event("evolution-merge:refresh"));
|
|
1380
|
-
}
|
|
1381
|
-
}, t("refresh"))));
|
|
1382
|
-
}
|
|
1383
|
-
const capability = failure.code === "capability";
|
|
1384
|
-
return (0, react.createElement)("div", {
|
|
1385
|
-
"data-role": capability ? "capability-failure" : "failure",
|
|
1386
|
-
"data-error-code": failure.code,
|
|
1387
|
-
"data-status": String(failure.status)
|
|
1388
|
-
}, (0, react.createElement)("p", {
|
|
1389
|
-
"data-role": "failure-title",
|
|
1390
|
-
title: failure.message
|
|
1391
|
-
}, t(problemKey(failure))), capability ? (0, react.createElement)("p", { "data-role": "capability-hint" }, t("failureCapabilityHint")) : null, failure.message === void 0 ? null : (0, react.createElement)("details", { "data-role": "failure-detail" }, (0, react.createElement)("summary", null, t("failureDetails")), (0, react.createElement)("p", { "data-role": "failure-raw" }, failure.message)), fallback.length === 0 ? null : (0, react.createElement)("div", { "data-role": "fallback" }, fallback.map((command) => (0, react.createElement)("button", {
|
|
1392
|
-
key: command,
|
|
1393
|
-
type: "button",
|
|
1394
|
-
"data-role": "fallback-command",
|
|
1395
|
-
onClick: () => {
|
|
1396
|
-
copy(command);
|
|
1397
|
-
}
|
|
1398
|
-
}, command))));
|
|
1399
|
-
}
|
|
1400
|
-
function summaryNode(row, t) {
|
|
1401
|
-
if (row.state !== "pending" || row.summary === void 0) return null;
|
|
1402
|
-
return (0, react.createElement)("div", { "data-role": "pending-summary" }, [
|
|
1403
|
-
["row.summaryA", row.summary.a],
|
|
1404
|
-
["row.summaryB", row.summary.b],
|
|
1405
|
-
["row.rationale", row.summary.rationale]
|
|
1406
|
-
].map(([label, value]) => (0, react.createElement)("div", {
|
|
1407
|
-
key: label,
|
|
1408
|
-
"data-role": "pending-summary-slot"
|
|
1409
|
-
}, (0, react.createElement)("span", { "data-role": "pending-summary-label" }, t(label)), (0, react.createElement)("span", { "data-role": "pending-summary-value" }, value === null || value === "" ? t("row.noSummary") : value))));
|
|
1410
|
-
}
|
|
1411
|
-
function stageNode(row, t) {
|
|
1412
|
-
const stage = row.stageView;
|
|
1413
|
-
if (stage === void 0) return null;
|
|
1414
|
-
const tone = stage.state === "lost" ? "fail" : stage.state === "expired" || stage.state === "reserved" ? "warn" : "neutral";
|
|
1415
|
-
const expiry = stage.expiresAt === void 0 ? "" : " · " + t("row.expires") + " " + new Date(stage.expiresAt).toLocaleString();
|
|
1416
|
-
return (0, react.createElement)("span", {
|
|
1417
|
-
"data-role": "stage-badge",
|
|
1418
|
-
"data-stage": stage.state,
|
|
1419
|
-
"data-tone": tone
|
|
1420
|
-
}, t("stage.state." + stage.state) + expiry);
|
|
1421
|
-
}
|
|
1422
|
-
function runNode(view, t, copy) {
|
|
1344
|
+
//#region src/client/MergeRunStrip.ts
|
|
1345
|
+
/** The merge run strips: active/last identity, state, progress, report and details. */
|
|
1346
|
+
/**
|
|
1347
|
+
* Render active and last runs as two independently labelled strips.
|
|
1348
|
+
* @param props - view, translator and command copier.
|
|
1349
|
+
* @returns the strips, or null when neither run exists.
|
|
1350
|
+
*/
|
|
1351
|
+
function MergeRunStrips({ view, t, copy }) {
|
|
1423
1352
|
const active = view.activeRun;
|
|
1424
1353
|
const last = view.lastRun;
|
|
1425
1354
|
if (active === void 0 && last === void 0) return null;
|
|
@@ -1441,7 +1370,7 @@ window.__ModuleLoader__.load({
|
|
|
1441
1370
|
run.endedAt === void 0 ? null : (0, react.createElement)("span", {
|
|
1442
1371
|
key: "ended",
|
|
1443
1372
|
"data-role": "run-ended"
|
|
1444
|
-
}, t("run.endedAt") + " " +
|
|
1373
|
+
}, t("run.endedAt") + " " + (formatLocalDateTime(run.endedAt) ?? "")),
|
|
1445
1374
|
run.failure === void 0 ? null : (0, react.createElement)("span", {
|
|
1446
1375
|
key: "failure",
|
|
1447
1376
|
"data-role": "run-failure",
|
|
@@ -1465,7 +1394,10 @@ window.__ModuleLoader__.load({
|
|
|
1465
1394
|
"data-role": "run-strip",
|
|
1466
1395
|
"data-run-role": role,
|
|
1467
1396
|
"data-run-state": run.state
|
|
1468
|
-
}, (0, react.createElement)("span", { "data-role": "run-
|
|
1397
|
+
}, (0, react.createElement)("span", { "data-role": "run-identity" }, t(role === "active" ? "run.current" : "run.last")), (0, react.createElement)("span", { "data-role": "run-state" }, t("run.state") + " " + t("run." + run.state)), run.progress === null || run.progress === void 0 ? run.state === "succeeded" ? (0, react.createElement)("span", {
|
|
1398
|
+
"data-role": "run-progress",
|
|
1399
|
+
"data-progress": "unknown"
|
|
1400
|
+
}, t("run.progressUnknown")) : null : (0, react.createElement)("span", { "data-role": "run-progress" }, t("run.progress") + " " + String(run.progress.done) + "/" + String(run.progress.total)), (0, react.createElement)("button", {
|
|
1469
1401
|
type: "button",
|
|
1470
1402
|
"data-role": "run-report",
|
|
1471
1403
|
"data-action": "report",
|
|
@@ -1486,45 +1418,68 @@ window.__ModuleLoader__.load({
|
|
|
1486
1418
|
}
|
|
1487
1419
|
}, t("action." + action.id))), (0, react.createElement)("details", { "data-role": "run-details" }, (0, react.createElement)("summary", null, t("run.details")), ...internals));
|
|
1488
1420
|
}
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1421
|
+
//#endregion
|
|
1422
|
+
//#region src/client/merge-presentation.ts
|
|
1423
|
+
/**
|
|
1424
|
+
* Compare the two source summaries after trimming. Equal non-empty text is one fact;
|
|
1425
|
+
* missing text stays missing rather than being invented.
|
|
1426
|
+
* @param a - source A summary, or null.
|
|
1427
|
+
* @param b - source B summary, or null.
|
|
1428
|
+
* @returns the presentation form.
|
|
1429
|
+
*/
|
|
1430
|
+
function summarizePair(a, b) {
|
|
1431
|
+
const left = a?.trim() ?? "";
|
|
1432
|
+
const right = b?.trim() ?? "";
|
|
1433
|
+
if (left === "" && right === "") return { kind: "missing" };
|
|
1434
|
+
if (left !== "" && left === right) return {
|
|
1435
|
+
kind: "same",
|
|
1436
|
+
text: left
|
|
1437
|
+
};
|
|
1438
|
+
return {
|
|
1439
|
+
kind: "different",
|
|
1440
|
+
a: left === "" ? null : left,
|
|
1441
|
+
b: right === "" ? null : right
|
|
1442
|
+
};
|
|
1499
1443
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
return (0, react.createElement)("div", { "data-role": "settings-card" }, (0, react.createElement)("h3", { "data-role": "settings-title" }, t("settings.title")), (0, react.createElement)("p", { "data-role": "settings-hint" }, t("settings.hint")), (0, react.createElement)("p", { "data-role": "settings-path" }, t("settings.openPlatformPath")), (0, react.createElement)(ReadOnlySetting, {
|
|
1506
|
-
section: "merge-approval",
|
|
1507
|
-
label: t("settings.mergeApproval"),
|
|
1508
|
-
value: mergeFallback,
|
|
1509
|
-
disabled: false,
|
|
1510
|
-
missing: false,
|
|
1511
|
-
t
|
|
1512
|
-
}), (0, react.createElement)(ReadOnlySetting, {
|
|
1513
|
-
section: "platform-write",
|
|
1514
|
-
label: t("settings.platformWrite"),
|
|
1515
|
-
value: platformFallback,
|
|
1516
|
-
disabled: platformCapability === false,
|
|
1517
|
-
missing: platform === void 0,
|
|
1518
|
-
note: t("settings.platformWriteLinkHint"),
|
|
1519
|
-
t
|
|
1520
|
-
}), (0, react.createElement)("p", { "data-role": "settings-readonly" }, t("settings.readonly")));
|
|
1444
|
+
//#endregion
|
|
1445
|
+
//#region src/client/MergeRow.ts
|
|
1446
|
+
/** One merge row: summary, stage, facts, primary decisions and the copy drawer. */
|
|
1447
|
+
function reasonText$1(t, reasonCode, fallback) {
|
|
1448
|
+
return reasonCode === void 0 ? fallback : t("history.reason." + reasonCode);
|
|
1521
1449
|
}
|
|
1522
|
-
function
|
|
1450
|
+
function summaryNode(row, t) {
|
|
1451
|
+
if (row.state !== "pending" || row.summary === void 0) return null;
|
|
1452
|
+
const pair = summarizePair(row.summary.a, row.summary.b);
|
|
1453
|
+
const slots = pair.kind === "same" ? [[t("row.summaryBoth"), pair.text]] : pair.kind === "missing" ? [[t("row.summaryBoth"), t("row.noSummary")]] : [[t("row.summaryA"), pair.a], [t("row.summaryB"), pair.b]];
|
|
1454
|
+
slots.push([t("row.rationale"), row.summary.rationale]);
|
|
1455
|
+
return (0, react.createElement)("div", { "data-role": "pending-summary" }, slots.map(([label, value]) => (0, react.createElement)("div", {
|
|
1456
|
+
key: label,
|
|
1457
|
+
"data-role": "pending-summary-slot"
|
|
1458
|
+
}, (0, react.createElement)("span", { "data-role": "pending-summary-label" }, label), (0, react.createElement)("span", { "data-role": "pending-summary-value" }, value === null || value === "" ? t("row.noSummary") : value))));
|
|
1459
|
+
}
|
|
1460
|
+
function stageNode(row, t) {
|
|
1461
|
+
const stage = row.stageView;
|
|
1462
|
+
if (stage === void 0) return null;
|
|
1463
|
+
const tone = stage.state === "lost" ? "fail" : stage.state === "expired" || stage.state === "reserved" ? "warn" : "neutral";
|
|
1464
|
+
const expiry = stage.expiresAt === void 0 ? "" : " · " + t("row.expires") + " " + new Date(stage.expiresAt).toLocaleString();
|
|
1465
|
+
return (0, react.createElement)("span", {
|
|
1466
|
+
"data-role": "stage-badge",
|
|
1467
|
+
"data-stage": stage.state,
|
|
1468
|
+
"data-tone": tone
|
|
1469
|
+
}, t("stage.state." + stage.state) + expiry);
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Render one row. Primary decisions stay visible; copy commands live in one details drawer.
|
|
1473
|
+
* @param props - row, translator, copier, sort hooks, body opener and decision callback.
|
|
1474
|
+
* @returns the row element.
|
|
1475
|
+
*/
|
|
1476
|
+
function MergeRow(props) {
|
|
1477
|
+
const { row, t, copy, sort, openBody, onDecide, busy } = props;
|
|
1523
1478
|
const first = [
|
|
1524
1479
|
row.target === void 0 ? void 0 : t("row.target") + " " + row.target,
|
|
1525
1480
|
row.failure === void 0 ? void 0 : t("row.failure") + " " + row.failure.code + (row.failure.retryable ? "" : " · " + t("row.noRetry")),
|
|
1526
1481
|
row.expired === void 0 ? void 0 : t("row.expired") + " " + t("expired." + row.expired),
|
|
1527
|
-
row.reasonCode === void 0 && row.reason === void 0 ? void 0 : t("row.reason") + " " + reasonText(t, row.reasonCode, row.reason ?? "")
|
|
1482
|
+
row.reasonCode === void 0 && row.reason === void 0 ? void 0 : t("row.reason") + " " + reasonText$1(t, row.reasonCode, row.reason ?? "")
|
|
1528
1483
|
].filter((value) => value !== void 0).join(" · ");
|
|
1529
1484
|
const details = [
|
|
1530
1485
|
t("row.id") + " " + row.id,
|
|
@@ -1534,9 +1489,9 @@ window.__ModuleLoader__.load({
|
|
|
1534
1489
|
row.attempts === void 0 ? void 0 : t("row.attempts") + " " + String(row.attempts),
|
|
1535
1490
|
row.confidence === void 0 ? void 0 : t("row.confidence") + " " + row.confidence.toFixed(2)
|
|
1536
1491
|
].filter((value) => value !== void 0).join(" · ");
|
|
1537
|
-
const actions = row.actions.
|
|
1492
|
+
const actions = row.actions.filter((action) => action.id !== "copy" && action.id !== "discard").map((action) => {
|
|
1538
1493
|
const decision = (action.id === "approve" || action.id === "reject") && onDecide !== void 0;
|
|
1539
|
-
|
|
1494
|
+
return (0, react.createElement)("button", {
|
|
1540
1495
|
key: action.id,
|
|
1541
1496
|
type: "button",
|
|
1542
1497
|
"data-role": decision ? "row-decision" : "row-action",
|
|
@@ -1550,18 +1505,6 @@ window.__ModuleLoader__.load({
|
|
|
1550
1505
|
else copy(action.command);
|
|
1551
1506
|
}
|
|
1552
1507
|
}, decision ? t("decision." + action.id) : t("action." + action.id));
|
|
1553
|
-
if (!decision) return [primary];
|
|
1554
|
-
return [primary, (0, react.createElement)("button", {
|
|
1555
|
-
key: action.id + "-copy",
|
|
1556
|
-
type: "button",
|
|
1557
|
-
"data-role": "row-copy",
|
|
1558
|
-
"data-action": action.id + "-copy",
|
|
1559
|
-
title: action.command,
|
|
1560
|
-
disabled: busy,
|
|
1561
|
-
onClick: () => {
|
|
1562
|
-
if (!busy) copy(action.command);
|
|
1563
|
-
}
|
|
1564
|
-
}, t("row.copy"))];
|
|
1565
1508
|
});
|
|
1566
1509
|
return (0, react.createElement)("div", {
|
|
1567
1510
|
key: row.id,
|
|
@@ -1633,7 +1576,151 @@ window.__ModuleLoader__.load({
|
|
|
1633
1576
|
openBody(bodyB.name, bodyB.side, bodyB.rowId);
|
|
1634
1577
|
}
|
|
1635
1578
|
}, "B · " + bodyB.name)];
|
|
1636
|
-
})(), actions)))
|
|
1579
|
+
})(), actions), (0, react.createElement)("details", { "data-role": "row-more" }, (0, react.createElement)("summary", null, t("row.more")), ...row.actions.map((action) => (0, react.createElement)("button", {
|
|
1580
|
+
key: "copy-" + action.id,
|
|
1581
|
+
type: "button",
|
|
1582
|
+
"data-role": "row-copy",
|
|
1583
|
+
"data-action": action.id + "-copy",
|
|
1584
|
+
title: action.command,
|
|
1585
|
+
disabled: busy,
|
|
1586
|
+
onClick: () => {
|
|
1587
|
+
if (!busy) copy(action.command);
|
|
1588
|
+
}
|
|
1589
|
+
}, t("row.copy") + " " + t("action." + action.id))))));
|
|
1590
|
+
}
|
|
1591
|
+
//#endregion
|
|
1592
|
+
//#region src/client/MergeSettings.ts
|
|
1593
|
+
/** The merge settings summary: two read-only state chips and one settings path. */
|
|
1594
|
+
function SettingChip({ section, label, value, disabled, missing, t }) {
|
|
1595
|
+
const text = missing ? t("settings.capabilityMissing") : disabled ? t("settings.capabilityDisabled") : value === true ? t("settings.on") : t("settings.off");
|
|
1596
|
+
const state = missing ? "missing" : disabled ? "unavailable" : value === true ? "on" : "off";
|
|
1597
|
+
return (0, react.createElement)("span", {
|
|
1598
|
+
"data-role": "settings-section",
|
|
1599
|
+
"data-section": section,
|
|
1600
|
+
"data-disabled": missing || disabled ? "true" : "false",
|
|
1601
|
+
"data-state": state
|
|
1602
|
+
}, (0, react.createElement)("span", { "data-role": "settings-label" }, label), (0, react.createElement)("span", {
|
|
1603
|
+
"data-role": "settings-value",
|
|
1604
|
+
"data-value": state
|
|
1605
|
+
}, text));
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Render the read-only approval state without the old large settings card.
|
|
1609
|
+
* @param props - the merge view and translator.
|
|
1610
|
+
* @returns two chips and the one current settings path.
|
|
1611
|
+
*/
|
|
1612
|
+
function MergeSettings({ view, t }) {
|
|
1613
|
+
const platform = view.approval.platformWrite;
|
|
1614
|
+
const platformCapability = platform?.capability === true;
|
|
1615
|
+
const platformFallback = platform?.enabled ?? false;
|
|
1616
|
+
return (0, react.createElement)("div", { "data-role": "settings-card" }, (0, react.createElement)("h3", { "data-role": "settings-title" }, t("settings.title")), (0, react.createElement)("p", { "data-role": "settings-path" }, t("settings.openPlatformPath")), (0, react.createElement)("div", { "data-role": "settings-chips" }, (0, react.createElement)(SettingChip, {
|
|
1617
|
+
section: "merge-approval",
|
|
1618
|
+
label: t("settings.mergeApproval"),
|
|
1619
|
+
value: view.approval.mergeApproval,
|
|
1620
|
+
disabled: false,
|
|
1621
|
+
missing: false,
|
|
1622
|
+
t
|
|
1623
|
+
}), (0, react.createElement)(SettingChip, {
|
|
1624
|
+
section: "platform-write",
|
|
1625
|
+
label: t("settings.platformWrite"),
|
|
1626
|
+
value: platformFallback,
|
|
1627
|
+
disabled: platformCapability === false,
|
|
1628
|
+
missing: platform === void 0,
|
|
1629
|
+
t
|
|
1630
|
+
})));
|
|
1631
|
+
}
|
|
1632
|
+
//#endregion
|
|
1633
|
+
//#region src/client/fallbacks.ts
|
|
1634
|
+
/** Static command-face fallback for a Facts read failure; the API fallback is unavailable then. */
|
|
1635
|
+
const FALLBACK_COMMANDS = [
|
|
1636
|
+
"/evolution merge scan",
|
|
1637
|
+
"/evolution merge pair <a> <b>",
|
|
1638
|
+
"/evolution merge pending",
|
|
1639
|
+
"/evolution merge pending --expired",
|
|
1640
|
+
"/evolution merge skipped",
|
|
1641
|
+
"/evolution merge candidates",
|
|
1642
|
+
"/evolution merge status",
|
|
1643
|
+
"/evolution merge report",
|
|
1644
|
+
"/evolution doctor"
|
|
1645
|
+
];
|
|
1646
|
+
//#endregion
|
|
1647
|
+
//#region src/client/Panel.ts
|
|
1648
|
+
/** Batch 4 merge panel: keyed read store, four tabs, local overlays, localized errors. */
|
|
1649
|
+
const QUEUES = [
|
|
1650
|
+
"verify",
|
|
1651
|
+
"pending",
|
|
1652
|
+
"fuse",
|
|
1653
|
+
"skipped"
|
|
1654
|
+
];
|
|
1655
|
+
const DOCTOR_COMMAND = "/evolution doctor";
|
|
1656
|
+
function problemKey(problem) {
|
|
1657
|
+
switch (problem.code) {
|
|
1658
|
+
case "unauthenticated": return "failureUnauthorized";
|
|
1659
|
+
case "forbidden": return "failureForbidden";
|
|
1660
|
+
case "unmounted": return "failureUnmounted";
|
|
1661
|
+
case "capability": return "failureCapability";
|
|
1662
|
+
case "body-ambiguous": return "failureBodyAmbiguous";
|
|
1663
|
+
case "wire-version": return "failureWireVersion";
|
|
1664
|
+
case "body-missing": return "failureBodyMissing";
|
|
1665
|
+
case "bad-query": return "failureBadQuery";
|
|
1666
|
+
case "refused": return "failureRefused";
|
|
1667
|
+
case "server": return "failureServer";
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
function reasonText(t, reasonCode, fallback) {
|
|
1671
|
+
if (reasonCode === void 0) return fallback;
|
|
1672
|
+
const key = "history.reason." + reasonCode;
|
|
1673
|
+
const text = t(key);
|
|
1674
|
+
return text === key ? fallback : text;
|
|
1675
|
+
}
|
|
1676
|
+
function blockedReasonKey(failure) {
|
|
1677
|
+
const code = failure.readiness.code ?? failure.code;
|
|
1678
|
+
if (code.endsWith("needs-repair")) return "blocked.reason.needs-repair";
|
|
1679
|
+
if (code.endsWith("forward-only")) return "blocked.reason.forward-only";
|
|
1680
|
+
if (code.endsWith("migration-blocked")) return "blocked.reason.migration-blocked";
|
|
1681
|
+
return "blocked.reason.unknown";
|
|
1682
|
+
}
|
|
1683
|
+
function failureNode(t, failure, fallback, copy) {
|
|
1684
|
+
if (isBlocked(failure)) {
|
|
1685
|
+
const raw = failure.readiness.message;
|
|
1686
|
+
return (0, react.createElement)("div", {
|
|
1687
|
+
"data-role": "blocked",
|
|
1688
|
+
role: "alert",
|
|
1689
|
+
"data-block-code": failure.code,
|
|
1690
|
+
"data-readiness-code": failure.readiness.code ?? ""
|
|
1691
|
+
}, (0, react.createElement)("h3", { "data-role": "blocked-title" }, t("blocked.title")), (0, react.createElement)("p", {
|
|
1692
|
+
"data-role": "blocked-reason",
|
|
1693
|
+
title: raw ?? t(blockedReasonKey(failure))
|
|
1694
|
+
}, t(blockedReasonKey(failure))), (0, react.createElement)("p", { "data-role": "blocked-hint" }, t("blocked.hint")), raw === void 0 ? null : (0, react.createElement)("details", { "data-role": "blocked-detail" }, (0, react.createElement)("summary", null, t("blocked.detail")), (0, react.createElement)("p", { "data-role": "blocked-raw" }, raw)), (0, react.createElement)("div", { "data-role": "blocked-actions" }, (0, react.createElement)("button", {
|
|
1695
|
+
type: "button",
|
|
1696
|
+
"data-role": "doctor",
|
|
1697
|
+
onClick: () => {
|
|
1698
|
+
copy(DOCTOR_COMMAND);
|
|
1699
|
+
}
|
|
1700
|
+
}, t("blocked.doctor")), (0, react.createElement)("button", {
|
|
1701
|
+
type: "button",
|
|
1702
|
+
"data-role": "refresh",
|
|
1703
|
+
onClick: () => {
|
|
1704
|
+
window.dispatchEvent(new Event("evolution-merge:refresh"));
|
|
1705
|
+
}
|
|
1706
|
+
}, t("refresh"))));
|
|
1707
|
+
}
|
|
1708
|
+
const capability = failure.code === "capability";
|
|
1709
|
+
return (0, react.createElement)("div", {
|
|
1710
|
+
"data-role": capability ? "capability-failure" : "failure",
|
|
1711
|
+
"data-error-code": failure.code,
|
|
1712
|
+
"data-status": String(failure.status)
|
|
1713
|
+
}, (0, react.createElement)("p", {
|
|
1714
|
+
"data-role": "failure-title",
|
|
1715
|
+
title: failure.message
|
|
1716
|
+
}, t(problemKey(failure))), capability ? (0, react.createElement)("p", { "data-role": "capability-hint" }, t("failureCapabilityHint")) : null, failure.message === void 0 ? null : (0, react.createElement)("details", { "data-role": "failure-detail" }, (0, react.createElement)("summary", null, t("failureDetails")), (0, react.createElement)("p", { "data-role": "failure-raw" }, failure.message)), fallback.length === 0 ? null : (0, react.createElement)("div", { "data-role": "fallback" }, fallback.map((command) => (0, react.createElement)("button", {
|
|
1717
|
+
key: command,
|
|
1718
|
+
type: "button",
|
|
1719
|
+
"data-role": "fallback-command",
|
|
1720
|
+
onClick: () => {
|
|
1721
|
+
copy(command);
|
|
1722
|
+
}
|
|
1723
|
+
}, command))));
|
|
1637
1724
|
}
|
|
1638
1725
|
const overlayKey = (event, onClose) => {
|
|
1639
1726
|
if (event.key === "Escape") {
|
|
@@ -1712,7 +1799,7 @@ window.__ModuleLoader__.load({
|
|
|
1712
1799
|
const kind = t("history.kind." + item.kind);
|
|
1713
1800
|
const reason = reasonText(t, item.reasonCode, item.reason);
|
|
1714
1801
|
return kind === reason ? kind : kind + " · " + reason;
|
|
1715
|
-
})() + " · v" + String(item.lastVersion) + " · " + t("history.archivedAt", { date:
|
|
1802
|
+
})() + " · v" + String(item.lastVersion) + " · " + (item.archivedAt === null ? t("history.archivedAtUnknown") : t("history.archivedAt", { date: formatLocalDateTime(item.archivedAt) ?? t("history.archivedAtUnknown") }))))) : null);
|
|
1716
1803
|
return Modal === void 0 ? inner : (0, react.createElement)(Modal, {
|
|
1717
1804
|
open: true,
|
|
1718
1805
|
onClose,
|
|
@@ -1909,7 +1996,15 @@ window.__ModuleLoader__.load({
|
|
|
1909
1996
|
"data-role": "rows",
|
|
1910
1997
|
id: "merge-rows",
|
|
1911
1998
|
role: "tabpanel"
|
|
1912
|
-
}, visibleRows.map((row) =>
|
|
1999
|
+
}, visibleRows.map((row) => (0, react.createElement)(MergeRow, {
|
|
2000
|
+
row,
|
|
2001
|
+
t,
|
|
2002
|
+
copy,
|
|
2003
|
+
sort: sortHandlers,
|
|
2004
|
+
openBody,
|
|
2005
|
+
onDecide: face.decide === void 0 ? void 0 : decideRow,
|
|
2006
|
+
busy: deciding === row.id
|
|
2007
|
+
}))) : null;
|
|
1913
2008
|
const empty = uiState === "empty" ? (0, react.createElement)("div", null, (0, react.createElement)("p", { "data-role": "note" }, t("empty." + snapshot.queue)), (0, react.createElement)("p", { "data-role": "empty-scope" }, t("empty.scope"))) : null;
|
|
1914
2009
|
const staleViewFailure = snapshot.viewState === "ready" ? snapshot.viewFailure : void 0;
|
|
1915
2010
|
const activeFailure = viewFailure !== void 0 ? viewFailure : staleViewFailure !== void 0 ? staleViewFailure : queue.status === "error" ? queue.failure : void 0;
|
|
@@ -1987,19 +2082,29 @@ window.__ModuleLoader__.load({
|
|
|
1987
2082
|
onClick: () => {
|
|
1988
2083
|
copy(view.startCommands.pair);
|
|
1989
2084
|
}
|
|
1990
|
-
}, t("start.pair"))), view.degraded ? (0, react.createElement)("p", { "data-role": "degraded" }, t("degraded") + " " + view.notes.join(",")) : null, (0, react.createElement)(
|
|
2085
|
+
}, t("start.pair"))), view.degraded ? (0, react.createElement)("p", { "data-role": "degraded" }, t("degraded") + " " + view.notes.join(",")) : null, (0, react.createElement)(MergeSettings, {
|
|
1991
2086
|
view,
|
|
1992
2087
|
t
|
|
1993
|
-
}),
|
|
2088
|
+
}), (0, react.createElement)(MergeRunStrips, {
|
|
2089
|
+
view,
|
|
2090
|
+
t,
|
|
2091
|
+
copy
|
|
2092
|
+
}), tabs, expiredToggle, snapshot.viewState === "loading" || queue.status === "loading" ? (0, react.createElement)("p", { "data-role": "note" }, t("loading")) : queue.refreshing === true ? (0, react.createElement)("p", {
|
|
1994
2093
|
"data-role": "note",
|
|
1995
2094
|
role: "status"
|
|
1996
2095
|
}, t("refreshing")) : null, activeFailure === void 0 ? null : failureNode(t, activeFailure, fallback, copy), copyState.kind === "idle" ? null : copyState.kind === "copied" ? (0, react.createElement)("p", {
|
|
1997
2096
|
"data-role": "copied",
|
|
1998
2097
|
role: "status"
|
|
1999
|
-
}, t("copied") + " " + (copyState.text ?? "")) : (0, react.createElement)("
|
|
2098
|
+
}, t("copied") + " " + (copyState.text ?? "")) : (0, react.createElement)("div", {
|
|
2000
2099
|
"data-role": "copy-failed",
|
|
2001
2100
|
role: "alert"
|
|
2002
|
-
}, t("copyFailed")),
|
|
2101
|
+
}, (0, react.createElement)("p", null, t("copyFailed")), copyState.text === void 0 ? null : (0, react.createElement)("code", { "data-role": "copy-failed-command" }, copyState.text), (0, react.createElement)("button", {
|
|
2102
|
+
type: "button",
|
|
2103
|
+
"data-role": "copy-retry",
|
|
2104
|
+
onClick: () => {
|
|
2105
|
+
if (copyState.text !== void 0) copy(copyState.text);
|
|
2106
|
+
}
|
|
2107
|
+
}, t("copyRetry"))), notice, list, empty, queue.nextCursor === void 0 || uiState !== "ready" ? null : (0, react.createElement)("button", {
|
|
2003
2108
|
type: "button",
|
|
2004
2109
|
"data-role": "load-more",
|
|
2005
2110
|
disabled: queue.status === "loading",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** One merge row: summary, stage, facts, primary decisions and the copy drawer. */
|
|
2
|
+
import { type DragEvent, type ReactNode } from 'react';
|
|
3
|
+
import type { MergeRowView } from './api.ts';
|
|
4
|
+
type Translate = (key: string, values?: Readonly<Record<string, string | number>>) => string;
|
|
5
|
+
export interface RowSortHandlers {
|
|
6
|
+
start(id: string): void;
|
|
7
|
+
over(event: DragEvent<HTMLDivElement>): void;
|
|
8
|
+
drop(id: string): void;
|
|
9
|
+
end(): void;
|
|
10
|
+
menu(id: string, x: number, y: number): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Render one row. Primary decisions stay visible; copy commands live in one details drawer.
|
|
14
|
+
* @param props - row, translator, copier, sort hooks, body opener and decision callback.
|
|
15
|
+
* @returns the row element.
|
|
16
|
+
*/
|
|
17
|
+
export declare function MergeRow(props: {
|
|
18
|
+
row: MergeRowView;
|
|
19
|
+
t: Translate;
|
|
20
|
+
copy: (command: string) => void;
|
|
21
|
+
sort: RowSortHandlers;
|
|
22
|
+
openBody: (name: string, side: 'live' | 'archive', rowId: string | undefined) => void;
|
|
23
|
+
onDecide: ((rowId: string, verb: 'approve' | 'reject') => void) | undefined;
|
|
24
|
+
busy: boolean;
|
|
25
|
+
}): ReactNode;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=MergeRow.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** The merge run strips: active/last identity, state, progress, report and details. */
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
import type { MergeViewPayload } from './api.ts';
|
|
4
|
+
type Translate = (key: string, values?: Readonly<Record<string, string | number>>) => string;
|
|
5
|
+
/**
|
|
6
|
+
* Render active and last runs as two independently labelled strips.
|
|
7
|
+
* @param props - view, translator and command copier.
|
|
8
|
+
* @returns the strips, or null when neither run exists.
|
|
9
|
+
*/
|
|
10
|
+
export declare function MergeRunStrips({ view, t, copy }: {
|
|
11
|
+
view: MergeViewPayload;
|
|
12
|
+
t: Translate;
|
|
13
|
+
copy: (command: string) => void;
|
|
14
|
+
}): ReactNode;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=MergeRunStrip.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** The merge settings summary: two read-only state chips and one settings path. */
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
import type { MergeViewPayload } from './api.ts';
|
|
4
|
+
type Translate = (key: string, values?: Readonly<Record<string, string | number>>) => string;
|
|
5
|
+
/**
|
|
6
|
+
* Render the read-only approval state without the old large settings card.
|
|
7
|
+
* @param props - the merge view and translator.
|
|
8
|
+
* @returns two chips and the one current settings path.
|
|
9
|
+
*/
|
|
10
|
+
export declare function MergeSettings({ view, t }: {
|
|
11
|
+
view: MergeViewPayload;
|
|
12
|
+
t: Translate;
|
|
13
|
+
}): ReactNode;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=MergeSettings.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Batch 4 merge panel: keyed read store, four tabs, local overlays, localized errors. */
|
|
2
2
|
import { type ReactNode } from 'react';
|
|
3
3
|
import type { MergeStore } from './MergeStore.ts';
|
|
4
|
-
import type
|
|
4
|
+
import { type SettingsFace } from '@lmzhen/dsh-evolution-client-kit';
|
|
5
5
|
export type Translate = (key: string, values?: Readonly<Record<string, string | number>>) => string;
|
|
6
6
|
export interface MergeFace {
|
|
7
7
|
store: MergeStore;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Pure presentation decisions for one merge row; no network or store state. */
|
|
2
|
+
export type PairSummary = {
|
|
3
|
+
readonly kind: 'same';
|
|
4
|
+
readonly text: string;
|
|
5
|
+
} | {
|
|
6
|
+
readonly kind: 'different';
|
|
7
|
+
readonly a: string | null;
|
|
8
|
+
readonly b: string | null;
|
|
9
|
+
} | {
|
|
10
|
+
readonly kind: 'missing';
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Compare the two source summaries after trimming. Equal non-empty text is one fact;
|
|
14
|
+
* missing text stays missing rather than being invented.
|
|
15
|
+
* @param a - source A summary, or null.
|
|
16
|
+
* @param b - source B summary, or null.
|
|
17
|
+
* @returns the presentation form.
|
|
18
|
+
*/
|
|
19
|
+
export declare function summarizePair(a: string | null | undefined, b: string | null | undefined): PairSummary;
|
|
20
|
+
//# sourceMappingURL=merge-presentation.d.ts.map
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
]
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
|
-
"version": "0.
|
|
14
|
+
"version": "0.35.0",
|
|
15
15
|
"publishConfig": {
|
|
16
16
|
"access": "public"
|
|
17
17
|
},
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"react-dom": "^18.2.0"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@lmzhen/dsh-evolution-client-kit": "^0.
|
|
54
|
+
"@lmzhen/dsh-evolution-client-kit": "^0.35.0"
|
|
55
55
|
},
|
|
56
56
|
"engines": {
|
|
57
57
|
"dsh": ">=0.2.1-rc.2 <0.2.2-0"
|