iterate-plugin 3.6.0 → 3.8.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/README.md +70 -7
- package/README.zh-CN.md +50 -6
- package/dist/config-loader.js +7 -1
- package/dist/file-lock.js +110 -21
- package/dist/git-scope.js +22 -2
- package/dist/index.js +7 -0
- package/dist/live.js +2 -1
- package/dist/session-hooks.js +6 -1
- package/dist/skill-prompt.js +2 -2
- package/dist/tools/checkpoint.js +24 -17
- package/dist/tools/config.js +42 -33
- package/dist/tools/decision-log.js +13 -99
- package/dist/tools/transcript.js +74 -44
- package/dist/tools/triage.js +52 -45
- package/dist/transcript.js +2 -2
- package/dist/working-directory.js +114 -0
- package/lib/client.js +1128 -468
- package/lib/parse.js +280 -62
- package/package.json +34 -11
- package/src/client/i18n.ts +517 -0
- package/src/client/index.ts +553 -344
- package/src/client/session-cache.ts +54 -0
- package/src/config-loader.ts +7 -1
- package/src/file-lock.ts +115 -18
- package/src/git-scope.ts +21 -2
- package/src/index.ts +7 -0
- package/src/live.ts +2 -1
- package/src/session-hooks.ts +6 -1
- package/src/skill-prompt.ts +2 -2
- package/src/tools/checkpoint.ts +23 -16
- package/src/tools/config.ts +40 -31
- package/src/tools/decision-log.ts +12 -82
- package/src/tools/transcript.ts +72 -43
- package/src/tools/triage.ts +47 -40
- package/src/transcript.ts +2 -2
- package/src/types.ts +1 -1
- package/src/working-directory.ts +126 -0
package/lib/client.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* iterate-plugin client source-digest: fc3f99c3c9a74c891ce54564ac49e7a336441432dfafe0cb21775ee4afe8f411 */
|
|
1
2
|
window.__ModuleLoader__.load({ id: "iterate-plugin", factory: (require) => {
|
|
2
3
|
var module = { exports: {} };
|
|
3
4
|
var exports = module.exports;
|
|
@@ -62,10 +63,10 @@ function safeKeys(o) {
|
|
|
62
63
|
}
|
|
63
64
|
function coerceJsonNode(v) {
|
|
64
65
|
if (typeof v !== "string") return v;
|
|
65
|
-
const
|
|
66
|
-
if (!
|
|
66
|
+
const t2 = v.trim();
|
|
67
|
+
if (!t2.startsWith("{") && !t2.startsWith("[")) return v;
|
|
67
68
|
try {
|
|
68
|
-
return JSON.parse(
|
|
69
|
+
return JSON.parse(t2);
|
|
69
70
|
} catch {
|
|
70
71
|
return v;
|
|
71
72
|
}
|
|
@@ -175,7 +176,8 @@ function isReviewReport(obj) {
|
|
|
175
176
|
obj
|
|
176
177
|
);
|
|
177
178
|
const convergence = safeGet(o, "convergence");
|
|
178
|
-
|
|
179
|
+
if (!convergence || typeof convergence !== "object" || Array.isArray(convergence)) return false;
|
|
180
|
+
return Array.isArray(safeGet(o, "findings")) && Array.isArray(safeGet(o, "rounds"));
|
|
179
181
|
}
|
|
180
182
|
function findReportInObject(obj, seen, maxDepth = 20) {
|
|
181
183
|
if (maxDepth <= 0) return null;
|
|
@@ -338,11 +340,11 @@ function extractTranscript(obj, seen, depth) {
|
|
|
338
340
|
obj
|
|
339
341
|
);
|
|
340
342
|
if (safeGet(o, "operation") === "capture") {
|
|
341
|
-
const
|
|
342
|
-
if (
|
|
343
|
+
const t2 = safeGet(o, "transcript");
|
|
344
|
+
if (t2 && typeof t2 === "object" && isTranscriptManifest(t2)) {
|
|
343
345
|
return attachLive(
|
|
344
346
|
/** @type {Record<string, unknown>} */
|
|
345
|
-
|
|
347
|
+
t2,
|
|
346
348
|
o
|
|
347
349
|
);
|
|
348
350
|
}
|
|
@@ -457,7 +459,11 @@ function scanSessionForTranscript(session) {
|
|
|
457
459
|
if (found) return found;
|
|
458
460
|
}
|
|
459
461
|
}
|
|
460
|
-
|
|
462
|
+
const loose = findFirstInObject(session, isTranscriptManifest);
|
|
463
|
+
return loose ? (
|
|
464
|
+
/** @type {Record<string, unknown>} */
|
|
465
|
+
loose
|
|
466
|
+
) : null;
|
|
461
467
|
}
|
|
462
468
|
function normalizeValidations(raw) {
|
|
463
469
|
if (!Array.isArray(raw)) return [];
|
|
@@ -502,12 +508,12 @@ function normalizeTranscript(manifest) {
|
|
|
502
508
|
) : [];
|
|
503
509
|
const rounds = asArray(safeGet(src, "rounds")).map((r) => ({
|
|
504
510
|
round: asNum(safeGet(r, "round")),
|
|
505
|
-
threads: asArray(safeGet(r, "threads")).map((
|
|
506
|
-
dimension: asStr(safeGet(
|
|
507
|
-
attempt: asNum(safeGet(
|
|
508
|
-
messages: asArray(safeGet(
|
|
509
|
-
readFiles: asArray(safeGet(
|
|
510
|
-
findings: asArray(safeGet(
|
|
511
|
+
threads: asArray(safeGet(r, "threads")).map((t2) => ({
|
|
512
|
+
dimension: asStr(safeGet(t2, "dimension")),
|
|
513
|
+
attempt: asNum(safeGet(t2, "attempt")),
|
|
514
|
+
messages: asArray(safeGet(t2, "messages")).map((m) => typeof m === "string" ? m : ""),
|
|
515
|
+
readFiles: asArray(safeGet(t2, "readFiles")).map((f) => typeof f === "string" ? f : ""),
|
|
516
|
+
findings: asArray(safeGet(t2, "findings")).map((f) => ({ ...f }))
|
|
511
517
|
}))
|
|
512
518
|
}));
|
|
513
519
|
const cp = safeGet(src, "checkpoint");
|
|
@@ -550,7 +556,7 @@ function normalizeTranscript(manifest) {
|
|
|
550
556
|
target: asStr(safeGet(e, "target"))
|
|
551
557
|
})),
|
|
552
558
|
checkpoint,
|
|
553
|
-
timeline: asArray(safeGet(src, "timeline")).map((
|
|
559
|
+
timeline: asArray(safeGet(src, "timeline")).map((t2) => ({ ...t2 })),
|
|
554
560
|
// #6: per-round validation rows (command / exitCode / allowed), captured
|
|
555
561
|
// by the workflow and persisted through `iterate_transcript capture`.
|
|
556
562
|
validations: normalizeValidations(safeGet(src, "validations")),
|
|
@@ -731,10 +737,12 @@ function normalizeQualityGateSnapshot(raw) {
|
|
|
731
737
|
}
|
|
732
738
|
function scanSessionForQualityGate(session) {
|
|
733
739
|
const node = latestToolResultNode(session, "iterate_quality_gate", findQualityGateInObject);
|
|
734
|
-
if (node
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
740
|
+
if (node !== null) {
|
|
741
|
+
const found = findQualityGateInObject(node);
|
|
742
|
+
if (found) return normalizeQualityGateSnapshot(found);
|
|
743
|
+
}
|
|
744
|
+
const loose = findFirstInObject(session, isQualityGateSnapshot);
|
|
745
|
+
return loose ? normalizeQualityGateSnapshot(loose) : null;
|
|
738
746
|
}
|
|
739
747
|
function normalizeExperienceBankResult(raw) {
|
|
740
748
|
const src = raw && typeof raw === "object" ? (
|
|
@@ -774,17 +782,19 @@ function normalizeExperienceBankResult(raw) {
|
|
|
774
782
|
hitCount: asNum(safeGet(rec, "hitCount")),
|
|
775
783
|
lastHitAt: asStr(safeGet(rec, "lastHitAt")) || null,
|
|
776
784
|
files: asArr(safeGet(rec, "files")).map((f) => typeof f === "string" ? f : ""),
|
|
777
|
-
tags: asArr(safeGet(rec, "tags")).map((
|
|
785
|
+
tags: asArr(safeGet(rec, "tags")).map((t2) => typeof t2 === "string" ? t2 : "")
|
|
778
786
|
};
|
|
779
787
|
})
|
|
780
788
|
};
|
|
781
789
|
}
|
|
782
790
|
function scanSessionForExperienceBank(session) {
|
|
783
791
|
const node = latestToolResultNode(session, "iterate_experience", findExperienceResultInObject);
|
|
784
|
-
if (node
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
792
|
+
if (node !== null) {
|
|
793
|
+
const found = findExperienceResultInObject(node);
|
|
794
|
+
if (found) return normalizeExperienceBankResult(found);
|
|
795
|
+
}
|
|
796
|
+
const loose = findFirstInObject(session, isExperienceBankResult);
|
|
797
|
+
return loose ? normalizeExperienceBankResult(loose) : null;
|
|
788
798
|
}
|
|
789
799
|
function normalizeDefenseEventsResult(raw) {
|
|
790
800
|
const src = raw && typeof raw === "object" ? (
|
|
@@ -837,10 +847,12 @@ function normalizeDefenseEventsResult(raw) {
|
|
|
837
847
|
}
|
|
838
848
|
function scanSessionForDefenseEvents(session) {
|
|
839
849
|
const node = latestToolResultNode(session, "iterate_defense_events", findDefenseResultInObject);
|
|
840
|
-
if (node
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
850
|
+
if (node !== null) {
|
|
851
|
+
const found = findDefenseResultInObject(node);
|
|
852
|
+
if (found) return normalizeDefenseEventsResult(found);
|
|
853
|
+
}
|
|
854
|
+
const loose = findFirstInObject(session, isDefenseEventsResult);
|
|
855
|
+
return loose ? normalizeDefenseEventsResult(loose) : null;
|
|
844
856
|
}
|
|
845
857
|
function isRunSummary(obj) {
|
|
846
858
|
if (!obj || typeof obj !== "object") return false;
|
|
@@ -1321,12 +1333,12 @@ function toolCalledInSession(session, toolName) {
|
|
|
1321
1333
|
toolCalls
|
|
1322
1334
|
) {
|
|
1323
1335
|
if (!call || typeof call !== "object") continue;
|
|
1324
|
-
const
|
|
1336
|
+
const t2 = String(safeGet(
|
|
1325
1337
|
/** @type {Record<string, unknown>} */
|
|
1326
1338
|
call,
|
|
1327
1339
|
"tool"
|
|
1328
1340
|
) ?? "");
|
|
1329
|
-
if (
|
|
1341
|
+
if (t2 === toolName || t2.endsWith(toolName)) return true;
|
|
1330
1342
|
}
|
|
1331
1343
|
}
|
|
1332
1344
|
const messages = safeGet(s, "messages");
|
|
@@ -1368,8 +1380,11 @@ function toolCalledInSession(session, toolName) {
|
|
|
1368
1380
|
}
|
|
1369
1381
|
return false;
|
|
1370
1382
|
}
|
|
1371
|
-
function diskEmptyStateText(pulled, topic) {
|
|
1372
|
-
const subject = topic || "\u6570\u636E";
|
|
1383
|
+
function diskEmptyStateText(pulled, topic, lang = "zh") {
|
|
1384
|
+
const subject = topic || (lang === "en" ? "data" : "\u6570\u636E");
|
|
1385
|
+
if (lang === "en") {
|
|
1386
|
+
return pulled ? `Pulled from disk: ${subject} has no records yet (the disk is empty).` : `Not pulled from disk yet: ${subject} lives under the project's .iterate/ \u2014 copy the "Pull disk snapshot" instruction back into the session and it will fill in.`;
|
|
1387
|
+
}
|
|
1373
1388
|
return pulled ? `\u5DF2\u4ECE\u78C1\u76D8\u62C9\u53D6\uFF1A${subject}\u6682\u65E0\u8BB0\u5F55\uFF08\u78C1\u76D8\u65E0\u6570\u636E\uFF09\u3002` : `\u5C1A\u672A\u4ECE\u78C1\u76D8\u62C9\u53D6${subject}\uFF1A\u6570\u636E\u5B58\u653E\u5728\u9879\u76EE .iterate/ \u4E0B\uFF0C\u70B9\u300C\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u300D\u590D\u5236\u6307\u4EE4\u53D1\u56DE\u4F1A\u8BDD\u5373\u53EF\u56DE\u586B\u3002`;
|
|
1374
1389
|
}
|
|
1375
1390
|
function normalizeFindingFilter(filter) {
|
|
@@ -1505,27 +1520,32 @@ function trendMax(points) {
|
|
|
1505
1520
|
}
|
|
1506
1521
|
return max;
|
|
1507
1522
|
}
|
|
1508
|
-
function stoppedReasonLabel(reason) {
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
}
|
|
1523
|
+
function stoppedReasonLabel(reason, lang = "zh") {
|
|
1524
|
+
const zh = {
|
|
1525
|
+
converged: "\u5DF2\u7ED3\u675F \xB7 \u5DF2\u6536\u655B\uFF08\u65E0\u65B0\u53D1\u73B0\uFF09",
|
|
1526
|
+
max_rounds_reached: "\u5DF2\u7ED3\u675F \xB7 \u8FBE\u5230\u8F6E\u6570\u4E0A\u9650",
|
|
1527
|
+
aborted_by_validation: "\u5DF2\u7ED3\u675F \xB7 \u9A8C\u8BC1\u5931\u8D25\u540E\u56DE\u6EDA\u505C\u6B62",
|
|
1528
|
+
aborted_by_config: "\u5DF2\u7ED3\u675F \xB7 \u9A8C\u8BC1\u547D\u4EE4\u4E0D\u5728\u767D\u540D\u5355\uFF08\u914D\u7F6E\u9700\u4FEE\u590D\uFF09",
|
|
1529
|
+
inconclusive: "\u5DF2\u7ED3\u675F \xB7 \u8F6E\u6B21\u7ED3\u8BBA\u4E0D\u660E\uFF08\u5BA1\u67E5\u8F93\u51FA\u65E0\u6548\uFF09",
|
|
1530
|
+
schema_invalid: "\u5DF2\u7ED3\u675F \xB7 \u5BA1\u67E5\u8F93\u51FA\u8FDE\u7EED schema \u6821\u9A8C\u5931\u8D25",
|
|
1531
|
+
no_usable_reviewer_output: "\u5DF2\u7ED3\u675F \xB7 \u65E0\u53EF\u7528\u5BA1\u67E5\u8F93\u51FA"
|
|
1532
|
+
};
|
|
1533
|
+
const en = {
|
|
1534
|
+
converged: "Done \xB7 converged (no new findings)",
|
|
1535
|
+
max_rounds_reached: "Done \xB7 reached the round limit",
|
|
1536
|
+
aborted_by_validation: "Done \xB7 rolled back after a validation failure",
|
|
1537
|
+
aborted_by_config: "Done \xB7 validation command not allow-listed (config needs fixing)",
|
|
1538
|
+
inconclusive: "Done \xB7 inconclusive rounds (invalid review output)",
|
|
1539
|
+
schema_invalid: "Done \xB7 repeated schema validation failures in review output",
|
|
1540
|
+
no_usable_reviewer_output: "Done \xB7 no usable review output"
|
|
1541
|
+
};
|
|
1542
|
+
const table = lang === "en" ? en : zh;
|
|
1543
|
+
if (typeof reason === "string" && Object.hasOwn(table, reason)) return table[reason];
|
|
1544
|
+
const text = typeof reason === "string" && reason !== "" ? reason : "";
|
|
1545
|
+
if (lang === "en") return text ? `Done \xB7 ${text}` : "Done";
|
|
1546
|
+
return text ? `\u5DF2\u7ED3\u675F \xB7 ${text}` : "\u5DF2\u7ED3\u675F";
|
|
1527
1547
|
}
|
|
1528
|
-
function buildCompletionSummary(report) {
|
|
1548
|
+
function buildCompletionSummary(report, lang = "zh") {
|
|
1529
1549
|
const conv = (
|
|
1530
1550
|
/** @type {Record<string, unknown>} */
|
|
1531
1551
|
report.convergence ?? {}
|
|
@@ -1534,33 +1554,74 @@ function buildCompletionSummary(report) {
|
|
|
1534
1554
|
const total = getTotalRounds(report);
|
|
1535
1555
|
const stats = severityStats(report);
|
|
1536
1556
|
const converged = conv.converged === true;
|
|
1537
|
-
const reason = converged ? "\u5DF2\u6536\u655B" : `\u5DF2\u8FBE\u6700\u5927\u8F6E\u6570 ${total}`;
|
|
1538
1557
|
const totalFindings = stats.critical + stats.high + stats.medium + stats.low;
|
|
1558
|
+
if (lang === "en") {
|
|
1559
|
+
const reason2 = converged ? "converged" : `reached the round limit ${total}`;
|
|
1560
|
+
return `iterate review complete \xB7 ${rounds}/${total} rounds \xB7 ${totalFindings} findings \xB7 ${reason2}`;
|
|
1561
|
+
}
|
|
1562
|
+
const reason = converged ? "\u5DF2\u6536\u655B" : `\u5DF2\u8FBE\u6700\u5927\u8F6E\u6570 ${total}`;
|
|
1539
1563
|
return `iterate \u8BC4\u5BA1\u5B8C\u6210 \xB7 ${rounds}/${total} \u8F6E \xB7 ${totalFindings} \u9879\u53D1\u73B0 \xB7 ${reason}`;
|
|
1540
1564
|
}
|
|
1541
1565
|
var CONFIG_EDIT_FIELDS = [
|
|
1542
|
-
{
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1566
|
+
{
|
|
1567
|
+
key: "goal",
|
|
1568
|
+
label: { zh: "\u76EE\u6807", en: "Goal" },
|
|
1569
|
+
hint: { zh: "\u4E00\u53E5\u8BDD\u63CF\u8FF0\u672C\u6B21\u8FED\u4EE3\u76EE\u6807\uFF08\u5B57\u7B26\u4E32\uFF09", en: "One-sentence description of this iteration's goal (a string)" }
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
key: "dimensions",
|
|
1573
|
+
label: { zh: "\u5BA1\u67E5\u7EF4\u5EA6", en: "Review dimensions" },
|
|
1574
|
+
hint: { zh: '\u6570\u7EC4\uFF0C\u5982 ["correctness","security"]', en: 'Array, e.g. ["correctness","security"]' }
|
|
1575
|
+
},
|
|
1576
|
+
{
|
|
1577
|
+
key: "max_rounds",
|
|
1578
|
+
label: { zh: "\u6700\u5927\u8F6E\u6570", en: "Max rounds" },
|
|
1579
|
+
hint: { zh: "\u6B63\u6574\u6570", en: "Positive integer" }
|
|
1580
|
+
},
|
|
1581
|
+
{
|
|
1582
|
+
key: "review.scope",
|
|
1583
|
+
label: { zh: "\u5BA1\u67E5\u8303\u56F4", en: "Review scope" },
|
|
1584
|
+
hint: { zh: '"full" \u6216 "changed-only"', en: '"full" or "changed-only"' }
|
|
1585
|
+
},
|
|
1586
|
+
{
|
|
1587
|
+
key: "reasoning_effort",
|
|
1588
|
+
label: { zh: "\u5BA1\u67E5\u63A8\u7406\u5F3A\u5EA6", en: "Review reasoning effort" },
|
|
1589
|
+
hint: { zh: '"low" / "medium" / "high"\uFF0C\u7F3A\u7701\u8DDF\u968F\u6A21\u578B\u9ED8\u8BA4', en: '"low" / "medium" / "high"; omitted = follow the model default' }
|
|
1590
|
+
},
|
|
1547
1591
|
{
|
|
1548
1592
|
key: "validation.commands",
|
|
1549
|
-
label: "\u9A8C\u8BC1\u547D\u4EE4",
|
|
1550
|
-
hint:
|
|
1593
|
+
label: { zh: "\u9A8C\u8BC1\u547D\u4EE4", en: "Validation commands" },
|
|
1594
|
+
hint: {
|
|
1595
|
+
zh: '\u6570\u7EC4\uFF0C\u6BCF\u9879\u662F\u4E00\u6761\u767D\u540D\u5355\u547D\u4EE4\u5B57\u7B26\u4E32\uFF0C\u5982 ["npm test","npm run lint"]\uFF1B\u6BCF\u8F6E\u9A8C\u8BC1\u9010\u6761\u6267\u884C\u3002\u7559\u7A7A\u6570\u7EC4 = \u4E0D\u9A8C\u8BC1\uFF08\u672C\u8F6E\u8FED\u4EE3\u4E0D\u53D7\u4EFB\u4F55\u6D4B\u8BD5\u4FDD\u62A4\uFF09',
|
|
1596
|
+
en: 'Array of allow-listed command strings, e.g. ["npm test","npm run lint"]; run one by one every round. An empty array = no verification (this iteration runs with no test protection)'
|
|
1597
|
+
}
|
|
1551
1598
|
},
|
|
1552
1599
|
{
|
|
1553
1600
|
key: "language",
|
|
1554
|
-
label: "\u754C\u9762\u8BED\u8A00",
|
|
1555
|
-
hint:
|
|
1601
|
+
label: { zh: "\u754C\u9762\u8BED\u8A00", en: "Interface language" },
|
|
1602
|
+
hint: {
|
|
1603
|
+
zh: '"zh" \u6216 "en"\uFF08\u9ED8\u8BA4 "en"\uFF09\uFF1A\u63A7\u5236\u5BA1\u6279\u7406\u7531\u3001\u9632\u5FA1\u4E8B\u4EF6\u6807\u7B7E\u7B49\u9762\u5411\u4EBA\u6587\u6848\u7684\u8BED\u8A00',
|
|
1604
|
+
en: '"zh" or "en" (default "en"): language of human-facing copy such as approval rationales and defense-event labels'
|
|
1605
|
+
}
|
|
1556
1606
|
},
|
|
1557
1607
|
{
|
|
1558
1608
|
key: "personalization.known_intentional",
|
|
1559
|
-
label: "\u5DF2\u77E5\u6709\u610F\u95EE\u9898",
|
|
1560
|
-
hint:
|
|
1609
|
+
label: { zh: "\u5DF2\u77E5\u6709\u610F\u95EE\u9898", en: "Known intentional issues" },
|
|
1610
|
+
hint: {
|
|
1611
|
+
zh: "\u6570\u7EC4\uFF0C\u6BCF\u9879 {file, line?, dimension, reason}\uFF1B\u547D\u4E2D\u6761\u76EE\u5728\u8BC4\u5BA1\u4E2D\u88AB\u8DF3\u8FC7\u3002\u53EF\u7528\u5206\u8BCA\u9762\u677F\u7684\u300C\u56DE\u8BFB\u5DF2\u5199\u5165\u6761\u76EE\u300D\u6838\u5BF9",
|
|
1612
|
+
en: `Array of {file, line?, dimension, reason}; findings hitting an entry are skipped in review. Verify via the triage panel's "read back written entries"`
|
|
1613
|
+
}
|
|
1614
|
+
},
|
|
1615
|
+
{
|
|
1616
|
+
key: "atomic.max_lines",
|
|
1617
|
+
label: { zh: "\u539F\u5B50\u4FEE\u590D\u4E0A\u9650\u884C\u6570", en: "Atomic fix line cap" },
|
|
1618
|
+
hint: { zh: "\u6B63\u6574\u6570", en: "Positive integer" }
|
|
1561
1619
|
},
|
|
1562
|
-
{
|
|
1563
|
-
|
|
1620
|
+
{
|
|
1621
|
+
key: "git.push_per_round",
|
|
1622
|
+
label: { zh: "\u6BCF\u8F6E\u63A8\u9001", en: "Push per round" },
|
|
1623
|
+
hint: { zh: "true / false", en: "true / false" }
|
|
1624
|
+
}
|
|
1564
1625
|
];
|
|
1565
1626
|
function buildConfigEditGuide() {
|
|
1566
1627
|
const lines = [
|
|
@@ -1568,12 +1629,13 @@ function buildConfigEditGuide() {
|
|
|
1568
1629
|
"---------------------",
|
|
1569
1630
|
"\u914D\u7F6E\u6587\u4EF6\uFF1A\u9879\u76EE\u6839\u76EE\u5F55 iterate.config.yaml\u3002",
|
|
1570
1631
|
"",
|
|
1571
|
-
"\u26A0 \u672A\u914D\u7F6E validation.commands \u65F6\u8FED\u4EE3\u4E0D\u88AB\u4FDD\u62A4\uFF1A\
|
|
1572
|
-
" \
|
|
1632
|
+
"\u26A0 \u672A\u914D\u7F6E validation.commands \u65F6\u8FED\u4EE3\u4E0D\u88AB\u4FDD\u62A4\uFF1A\u8BE5\u8F6E\u4F1A\u76F4\u63A5\u4E2D\u6B62\uFF08fail-closed\uFF09\uFF0C",
|
|
1633
|
+
" \u7A7A\u9A8C\u8BC1\u7ED3\u679C/\u9A8C\u8BC1\u672A\u6267\u884C\u7EDD\u4E0D\u5F53\u4F5C\u201C\u9A8C\u8BC1\u901A\u8FC7\u201D\uFF0C\u8FD0\u884C\u4EE5 converged_unverified /",
|
|
1634
|
+
" aborted_by_config \u6536\u5C3E\u800C\u4E0D\u662F plain converged\u3002\u7B2C\u4E00\u6B21\u542F\u52A8\u8FED\u4EE3\u524D\uFF0C",
|
|
1573
1635
|
" \u8BF7\u5148\u7ED9 validation.commands \u914D\u4E0A\u81F3\u5C11\u4E00\u6761\u771F\u5B9E\u547D\u4EE4\uFF08\u5982 npm test\uFF09\u3002",
|
|
1574
1636
|
"",
|
|
1575
1637
|
"\u53EF\u7F16\u8F91\u5B57\u6BB5\uFF1A",
|
|
1576
|
-
...CONFIG_EDIT_FIELDS.map((f) => `- ${f.key}\uFF08${f.label}\uFF09\uFF1A${f.hint}`),
|
|
1638
|
+
...CONFIG_EDIT_FIELDS.map((f) => `- ${f.key}\uFF08${f.label.zh}\uFF09\uFF1A${f.hint.zh}`),
|
|
1577
1639
|
"",
|
|
1578
1640
|
"\u8BA9\u6A21\u578B\u5E2E\u4F60\u6539\uFF1A",
|
|
1579
1641
|
'1. \u8C03\u7528 iterate_config({ operation: "read" }) \u67E5\u770B\u5F53\u524D\u914D\u7F6E\uFF1B',
|
|
@@ -1599,8 +1661,8 @@ function configFieldByKey(key) {
|
|
|
1599
1661
|
}
|
|
1600
1662
|
function buildConfigFieldInstruction(key, value) {
|
|
1601
1663
|
const field = configFieldByKey(key);
|
|
1602
|
-
const label = field ? field.label : key;
|
|
1603
|
-
const hint = field ? field.hint : "\u89C1 iterate.config.yaml";
|
|
1664
|
+
const label = field ? field.label.zh : key;
|
|
1665
|
+
const hint = field ? field.hint.zh : "\u89C1 iterate.config.yaml";
|
|
1604
1666
|
const updates = { [key]: value === void 0 ? CONFIG_VALUE_PLACEHOLDER : value };
|
|
1605
1667
|
const payload = buildConfigEditInstruction(updates);
|
|
1606
1668
|
return [
|
|
@@ -1612,6 +1674,71 @@ function buildConfigFieldInstruction(key, value) {
|
|
|
1612
1674
|
'\u5199\u5165\u524D\u8BF7\u5148 `operation: "read"` \u5C55\u793A\u5F53\u524D\u503C\uFF1B\u5199\u5165\u7531\u5DE5\u5177\u81EA\u52A8\u5907\u4EFD\uFF0C\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\u3002'
|
|
1613
1675
|
].join("\n");
|
|
1614
1676
|
}
|
|
1677
|
+
function buildConfigReadInstruction() {
|
|
1678
|
+
return `\u8BF7\u8C03\u7528 \`iterate_config\` \u56DE\u8BFB\u5F53\u524D\u751F\u6548\u914D\u7F6E\uFF0C\u5E76\u628A\u5B8C\u6574\u7ED3\u679C\uFF08\u542B config \u5B57\u6BB5\uFF09\u539F\u6837\u56DE\u663E\uFF0C\u4F9B\u6211\u6838\u5BF9\u754C\u9762\u4E0A\u5C55\u793A\u7684\u5F53\u524D\u503C\uFF1A
|
|
1679
|
+
|
|
1680
|
+
\`\`\`json
|
|
1681
|
+
${JSON.stringify({ operation: "read" }, null, 2)}
|
|
1682
|
+
\`\`\``;
|
|
1683
|
+
}
|
|
1684
|
+
var CONFIG_TOP_LEVEL_KEYS = [...new Set(CONFIG_EDIT_FIELDS.map((f) => f.key.split(".")[0]))];
|
|
1685
|
+
function isEffectiveConfig(v) {
|
|
1686
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return false;
|
|
1687
|
+
const rec = (
|
|
1688
|
+
/** @type {Record<string, unknown>} */
|
|
1689
|
+
v
|
|
1690
|
+
);
|
|
1691
|
+
let hits = 0;
|
|
1692
|
+
for (const key of CONFIG_TOP_LEVEL_KEYS) {
|
|
1693
|
+
if (Object.hasOwn(rec, key)) hits += 1;
|
|
1694
|
+
if (hits >= 3) return true;
|
|
1695
|
+
}
|
|
1696
|
+
return false;
|
|
1697
|
+
}
|
|
1698
|
+
function findConfigInResult(v) {
|
|
1699
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return null;
|
|
1700
|
+
const rec = (
|
|
1701
|
+
/** @type {Record<string, unknown>} */
|
|
1702
|
+
v
|
|
1703
|
+
);
|
|
1704
|
+
const cfg = safeGet(rec, "config");
|
|
1705
|
+
if (isEffectiveConfig(cfg)) return (
|
|
1706
|
+
/** @type {Record<string, unknown>} */
|
|
1707
|
+
cfg
|
|
1708
|
+
);
|
|
1709
|
+
return isEffectiveConfig(v) ? rec : null;
|
|
1710
|
+
}
|
|
1711
|
+
function scanSessionForConfigRead(session) {
|
|
1712
|
+
const node = latestToolResultNode(session, "iterate_config", findConfigInResult);
|
|
1713
|
+
if (node !== null) {
|
|
1714
|
+
const cfg = findConfigInResult(node);
|
|
1715
|
+
if (cfg) return cfg;
|
|
1716
|
+
}
|
|
1717
|
+
const loose = findFirstInObject(session, isEffectiveConfig);
|
|
1718
|
+
return loose ? (
|
|
1719
|
+
/** @type {Record<string, unknown>} */
|
|
1720
|
+
loose
|
|
1721
|
+
) : null;
|
|
1722
|
+
}
|
|
1723
|
+
function configValueByPath(config, key) {
|
|
1724
|
+
if (!config || typeof config !== "object" || typeof key !== "string" || !key) {
|
|
1725
|
+
return { resolved: false, value: void 0 };
|
|
1726
|
+
}
|
|
1727
|
+
let node = (
|
|
1728
|
+
/** @type {unknown} */
|
|
1729
|
+
config
|
|
1730
|
+
);
|
|
1731
|
+
for (const part of key.split(".")) {
|
|
1732
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return { resolved: false, value: void 0 };
|
|
1733
|
+
const rec = (
|
|
1734
|
+
/** @type {Record<string, unknown>} */
|
|
1735
|
+
node
|
|
1736
|
+
);
|
|
1737
|
+
if (!Object.hasOwn(rec, part)) return { resolved: false, value: void 0 };
|
|
1738
|
+
node = rec[part];
|
|
1739
|
+
}
|
|
1740
|
+
return { resolved: true, value: node };
|
|
1741
|
+
}
|
|
1615
1742
|
var VERDICT_SHORTCUTS = {
|
|
1616
1743
|
y: "keep",
|
|
1617
1744
|
Y: "keep",
|
|
@@ -1668,9 +1795,9 @@ function buildRuntimeStatusGuide() {
|
|
|
1668
1795
|
}
|
|
1669
1796
|
function filterLiveEntries(entries, type) {
|
|
1670
1797
|
const list = Array.isArray(entries) ? entries : [];
|
|
1671
|
-
const
|
|
1672
|
-
if (!
|
|
1673
|
-
return list.filter((e) => e && typeof e === "object" && String(e.type ?? "") ===
|
|
1798
|
+
const t2 = typeof type === "string" ? type.trim() : "";
|
|
1799
|
+
if (!t2) return list.slice();
|
|
1800
|
+
return list.filter((e) => e && typeof e === "object" && String(e.type ?? "") === t2);
|
|
1674
1801
|
}
|
|
1675
1802
|
function filterTimelineEntries(entries, opts) {
|
|
1676
1803
|
const list = Array.isArray(entries) ? entries : [];
|
|
@@ -1678,18 +1805,18 @@ function filterTimelineEntries(entries, opts) {
|
|
|
1678
1805
|
const type = typeof o.type === "string" ? o.type : "";
|
|
1679
1806
|
const round = typeof o.round === "string" ? o.round : "";
|
|
1680
1807
|
const q = typeof o.search === "string" ? o.search.trim().toLowerCase() : "";
|
|
1681
|
-
const filtered = list.filter((
|
|
1682
|
-
if (!
|
|
1683
|
-
if (type && String(
|
|
1684
|
-
if (round && String(
|
|
1808
|
+
const filtered = list.filter((t2) => {
|
|
1809
|
+
if (!t2 || typeof t2 !== "object") return false;
|
|
1810
|
+
if (type && String(t2.type ?? "") !== type) return false;
|
|
1811
|
+
if (round && String(t2.round ?? "") !== round) return false;
|
|
1685
1812
|
if (q) {
|
|
1686
1813
|
let dataText = "";
|
|
1687
1814
|
try {
|
|
1688
|
-
dataText = JSON.stringify(
|
|
1815
|
+
dataText = JSON.stringify(t2.data ?? {});
|
|
1689
1816
|
} catch {
|
|
1690
|
-
dataText =
|
|
1817
|
+
dataText = t2.data === void 0 ? "{}" : String(t2.data);
|
|
1691
1818
|
}
|
|
1692
|
-
const hay = [String(
|
|
1819
|
+
const hay = [String(t2.type ?? ""), String(t2.round ?? ""), dataText].join(" ").toLowerCase();
|
|
1693
1820
|
if (hay.indexOf(q) < 0) return false;
|
|
1694
1821
|
}
|
|
1695
1822
|
return true;
|
|
@@ -1721,7 +1848,7 @@ function pickExportExtras(extra) {
|
|
|
1721
1848
|
}
|
|
1722
1849
|
return out;
|
|
1723
1850
|
}
|
|
1724
|
-
function buildRoundComparison(report, validations) {
|
|
1851
|
+
function buildRoundComparison(report, validations, fixes) {
|
|
1725
1852
|
const rounds = /* @__PURE__ */ new Map();
|
|
1726
1853
|
const rowsFor = (round) => {
|
|
1727
1854
|
const n = Number(round);
|
|
@@ -1738,7 +1865,18 @@ function buildRoundComparison(report, validations) {
|
|
|
1738
1865
|
row.findings += 1;
|
|
1739
1866
|
const sev = typeof f.severity === "string" && f.severity ? f.severity : "unknown";
|
|
1740
1867
|
row.severities[sev] = (row.severities[sev] || 0) + 1;
|
|
1741
|
-
|
|
1868
|
+
}
|
|
1869
|
+
const fixList = Array.isArray(fixes) ? fixes : [];
|
|
1870
|
+
for (const fx of fixList) {
|
|
1871
|
+
if (!fx || typeof fx !== "object") continue;
|
|
1872
|
+
if (
|
|
1873
|
+
/** @type {Record<string, unknown>} */
|
|
1874
|
+
fx.success !== true
|
|
1875
|
+
) continue;
|
|
1876
|
+
rowsFor(
|
|
1877
|
+
/** @type {Record<string, unknown>} */
|
|
1878
|
+
fx.round
|
|
1879
|
+
).fixed += 1;
|
|
1742
1880
|
}
|
|
1743
1881
|
const vals = Array.isArray(validations) ? validations : [];
|
|
1744
1882
|
for (const v of vals) {
|
|
@@ -1753,15 +1891,15 @@ function buildRoundComparison(report, validations) {
|
|
|
1753
1891
|
return [...rounds.values()].filter((r) => r.round > 0).sort((a, b) => a.round - b.round).map((r) => ({ ...r, severities: { ...r.severities } }));
|
|
1754
1892
|
}
|
|
1755
1893
|
function dashboardRunState(transcript) {
|
|
1756
|
-
const
|
|
1757
|
-
if (!
|
|
1758
|
-
const rawRound = Number(
|
|
1894
|
+
const t2 = transcript && typeof transcript === "object" ? transcript : null;
|
|
1895
|
+
if (!t2) return { state: "empty", round: 0, phase: "", stoppedReason: "" };
|
|
1896
|
+
const rawRound = Number(t2.round);
|
|
1759
1897
|
const round = Number.isFinite(rawRound) && rawRound > 0 ? Math.floor(rawRound) : 1;
|
|
1760
|
-
const stoppedReason = typeof
|
|
1898
|
+
const stoppedReason = typeof t2.stoppedReason === "string" ? t2.stoppedReason.trim() : "";
|
|
1761
1899
|
return {
|
|
1762
|
-
state:
|
|
1900
|
+
state: t2.active === true ? "running" : "done",
|
|
1763
1901
|
round,
|
|
1764
|
-
phase: latestPhase(
|
|
1902
|
+
phase: latestPhase(t2.phases),
|
|
1765
1903
|
stoppedReason
|
|
1766
1904
|
};
|
|
1767
1905
|
}
|
|
@@ -1774,6 +1912,387 @@ function latestPhase(phases) {
|
|
|
1774
1912
|
return latest;
|
|
1775
1913
|
}
|
|
1776
1914
|
|
|
1915
|
+
// src/client/i18n.ts
|
|
1916
|
+
var LANG_STORAGE_KEY = "iterate-ui-lang";
|
|
1917
|
+
var LANG_NAMES = {
|
|
1918
|
+
zh: "\u7B80\u4F53\u4E2D\u6587",
|
|
1919
|
+
en: "English"
|
|
1920
|
+
};
|
|
1921
|
+
var EN = {
|
|
1922
|
+
// ── run state / dashboard ────────────────────────────────────────────────
|
|
1923
|
+
\u8FD0\u884C\u4E2D: "Running",
|
|
1924
|
+
\u5DF2\u7ED3\u675F: "Finished",
|
|
1925
|
+
"Round {a0}\uFF1A{a1} \u9879": "Round {a0}: {a1} findings",
|
|
1926
|
+
"\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\u8D8B\u52BF\uFF1A{a0}": "Findings per round: {a0}",
|
|
1927
|
+
\u5B8C\u6574\u8FED\u4EE3: "Full iteration",
|
|
1928
|
+
"\u590D\u5236\u542F\u52A8\u6307\u4EE4\uFF1A\u5B8C\u6574\u300C\u5BA1\u67E5 \u2192 \u4FEE\u590D \u2192 \u9A8C\u8BC1 \u2192 \u590D\u76D8\u300D\u95ED\u73AF\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u5373\u53EF\uFF09": 'Copy the start instruction: the full "review \u2192 fix \u2192 validate \u2192 debrief" loop (paste it to the model)',
|
|
1929
|
+
\u4EC5\u8BC4\u5BA1: "Review only",
|
|
1930
|
+
"\u590D\u5236\u542F\u52A8\u6307\u4EE4\uFF1A\u53EA\u5BA1\u67E5\u4E0D\u4FEE\u6539\uFF08dry-run\uFF09\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u5373\u53EF\uFF09": "Copy the start instruction: review without changing anything (dry-run) \u2014 paste it to the model",
|
|
1931
|
+
"\u7B2C {a0} \u8F6E\u8FDB\u884C\u4E2D{a1}\u2014\u2014\u9996\u8F6E\u6C47\u603B\u62A5\u544A\u843D\u5730\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u6536\u655B\u8FDB\u5EA6\u4E0E\u53D1\u73B0\u7EDF\u8BA1\u3002": "Round {a0} in progress{a1} \u2014 convergence progress and finding stats appear here once the first aggregated report lands.",
|
|
1932
|
+
" \xB7 \u9636\u6BB5\u300C{a0}\u300D": ' \xB7 phase "{a0}"',
|
|
1933
|
+
"\u4E0A\u6B21\u8FED\u4EE3\u5728\u7B2C {a0} \u8F6E\u540E\u505C\u6B62{a1}\uFF0C\u4F46\u6CA1\u6709\u53EF\u5C55\u793A\u7684\u6C47\u603B\u62A5\u544A\u3002": "The last iteration stopped at round {a0}{a1}, but there is no report to show.",
|
|
1934
|
+
"\u8FD0\u884C\u4E00\u6B21\u8BC4\u5BA1\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u6536\u655B\u8FDB\u5EA6\u4E0E\u53D1\u73B0\u7EDF\u8BA1\u3002": "Run a review and convergence progress and finding stats will show up here.",
|
|
1935
|
+
\u672C\u6B21\u8FED\u4EE3\u4ECE\u4E0A\u4E00\u6B21\u4E2D\u65AD\u7684\u65AD\u70B9\u7EE7\u7EED\u6267\u884C: "This run resumed from the previous interrupted checkpoint",
|
|
1936
|
+
"\u5DF2\u4E2D\u65AD\u6062\u590D \xD7{a0}": "Resumed \xD7{a0}",
|
|
1937
|
+
"\u4F1A\u8BDD\u4E2D\u68C0\u6D4B\u5230\u7528\u6237\u9644\u5E26\u7684\u56FE\u7247\uFF0C\u8BC4\u5BA1\u5C06\u4F5C\u4E3A\u89C6\u89C9\u8BC1\u636E\u53C2\u8003": "Images attached by the user were detected in this session; the review treats them as visual evidence",
|
|
1938
|
+
"\u9644\u4EF6\u56FE\u7247 {a0}": "Attached images {a0}",
|
|
1939
|
+
"\u5F53\u524D\u5904\u4E8E\u300C{a0}\u300D\u9636\u6BB5\uFF08\u8FD0\u884C\u4E2D\uFF09": 'Currently in the "{a0}" phase (running)',
|
|
1940
|
+
"\u6700\u8FD1\u4E00\u6B21\u6267\u884C\u505C\u7559\u5728\u300C{a0}\u300D\u9636\u6BB5\uFF08\u5DF2\u7ED3\u675F\uFF09": 'The last run stopped in the "{a0}" phase (finished)',
|
|
1941
|
+
"\u4EE3\u7801\u6A21\u5F0F\uFF1A\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6": "Code mode: edits files directly",
|
|
1942
|
+
"\u8FED\u4EE3\u6A21\u5F0F\uFF1A\u5BA1\u67E5-\u4FEE\u590D\u5FAA\u73AF": "Iterate mode: review \u2192 fix loop",
|
|
1943
|
+
"+{a0} \u66F4\u591A": "+{a0} more",
|
|
1944
|
+
"\u672C\u8F6E\u5DF2\u5E94\u7528\u7684\u539F\u5B50\u4FEE\u590D\u6570\uFF08\u6B63\u5E38\u6A21\u5F0F\uFF09": "Atomic fixes applied this round (normal mode)",
|
|
1945
|
+
"\u5BA1\u67E5\u5DF2\u6536\u655B\uFF1A\u6700\u540E\u4E00\u8F6E\u672A\u53D1\u73B0\u65B0\u95EE\u9898": "Review converged: the final round found nothing new",
|
|
1946
|
+
"\u2713 \u5DF2\u6536\u655B": "\u2713 Converged",
|
|
1947
|
+
"\u9996\u8F6E {a0} \u2192 \u672B\u8F6E {a1} \u9879\uFF0C\u964D\u5E45 {a2}%{a3}": "Round {a0} \u2192 round {a1} findings, down {a2}%{a3}",
|
|
1948
|
+
"\uFF0C\u5DF2\u6536\u655B": ", converged",
|
|
1949
|
+
"Iterate \xB7 \u6536\u655B\u7EDF\u8BA1": "Iterate \xB7 Convergence stats",
|
|
1950
|
+
\u6536\u8D77\u5386\u53F2: "Collapse history",
|
|
1951
|
+
"\u5386\u53F2 / \u8D8B\u52BF": "History / trend",
|
|
1952
|
+
\u8F6E\u6B21: "Round",
|
|
1953
|
+
\u53D1\u73B0: "Findings",
|
|
1954
|
+
\u8BF7\u7EE7\u7EED\u6267\u884C\u4E0B\u4E00\u8F6E\u8FED\u4EE3\u5BA1\u67E5: "Please continue with the next review round",
|
|
1955
|
+
"\u8BF7\u56DE\u6EDA\u4E0A\u4E00\u8F6E\u8FED\u4EE3\u7684\u4FEE\u590D\uFF1A\u8C03\u7528 `iterate_history` \u67E5\u770B\u672C\u8F6E fix id\uFF0C\u518D\u7528 `iterate_rollback` \u9010\u4E2A\u64A4\u9500\u8FD9\u4E9B\u4FEE\u590D": "Please roll back the fixes from the last round: call `iterate_history` for this round\u2019s fix ids, then `iterate_rollback` to undo them one by one",
|
|
1956
|
+
"y \u4FEE\u590D": "y fix",
|
|
1957
|
+
"\u4FDD\u7559\u8BE5 finding\uFF0C\u8FDB\u5165\u4FEE\u590D": "Keep this finding and move it to fixing",
|
|
1958
|
+
"n \u8DF3\u8FC7": "n skip",
|
|
1959
|
+
"\u8DF3\u8FC7\u8BE5 finding": "Skip this finding",
|
|
1960
|
+
"a \u5DF2\u77E5\u6709\u610F": "a known-intentional",
|
|
1961
|
+
"\u6807\u8BB0\u4E3A\u5DF2\u77E5\u6709\u610F\u5E76\u5199\u5165 known_intentional": "Mark as known-intentional and write it into known_intentional",
|
|
1962
|
+
// ── triage panel ─────────────────────────────────────────────────────────
|
|
1963
|
+
"Iterate \xB7 Findings \u5206\u8BCA ({a0}/{a1})": "Iterate \xB7 Findings triage ({a0}/{a1})",
|
|
1964
|
+
"y=\u4FEE\u590D \xB7 n=\u8DF3\u8FC7 \xB7 a=\u5DF2\u77E5\u6709\u610F \xB7 \u2191/\u2193 \u9009\u62E9": "y=fix \xB7 n=skip \xB7 a=known-intentional \xB7 \u2191/\u2193 to move",
|
|
1965
|
+
\u6309\u4E25\u91CD\u5EA6\u7B5B\u9009: "Filter by severity",
|
|
1966
|
+
\u5168\u90E8\u4E25\u91CD\u5EA6: "All severities",
|
|
1967
|
+
\u6309\u7EF4\u5EA6\u7B5B\u9009: "Filter by dimension",
|
|
1968
|
+
\u5168\u90E8\u7EF4\u5EA6: "All dimensions",
|
|
1969
|
+
"\u641C\u7D22\u6587\u4EF6 / \u6458\u8981\u2026": "Search file / summary\u2026",
|
|
1970
|
+
\u641C\u7D22\u6587\u4EF6\u6216\u6458\u8981: "Search file or summary",
|
|
1971
|
+
"\u6E05\u9664\u7B5B\u9009\uFF08\u663E\u793A {a0}\uFF09": "Clear filters (showing {a0})",
|
|
1972
|
+
"\u5171 {a0} \u9879": "{a0} in total",
|
|
1973
|
+
"\u6279\u91CF\u4F5C\u7528\u4E8E\uFF1A": "Batch applies to:",
|
|
1974
|
+
"\u6279\u91CF\u6309\u94AE\u4EC5\u4F5C\u7528\u4E8E\u5F53\u524D\u7B5B\u9009\u53EF\u89C1\u7684 findings": "Batch buttons only apply to the findings visible under the current filter",
|
|
1975
|
+
"\u53EF\u89C1 {a0}": "Visible {a0}",
|
|
1976
|
+
"\u6279\u91CF\u6309\u94AE\u4F5C\u7528\u4E8E\u5168\u90E8 findings": "Batch buttons apply to every finding",
|
|
1977
|
+
"\u5168\u90E8 {a0}": "All {a0}",
|
|
1978
|
+
"\u628A\u6240\u6709\u5224\u5B9A\u6062\u590D\u4E3A\u9ED8\u8BA4 y\uFF08\u4FEE\u590D\uFF09": "Reset every verdict to the default y (fix)",
|
|
1979
|
+
\u91CD\u7F6E: "Reset",
|
|
1980
|
+
"y {a0} \xB7 n {a1} \xB7 a {a2} \xB7 \u5F85\u5199\u56DE known_intentional\uFF1A{a3} \u6761": "y {a0} \xB7 n {a1} \xB7 a {a2} \xB7 pending known_intentional write-back: {a3}",
|
|
1981
|
+
"\u5F53\u524D\u6CA1\u6709\u6807\u8BB0\u4E3A\u300C\u5DF2\u77E5\u6709\u610F\u300D\u7684 finding": "No finding is marked known-intentional yet",
|
|
1982
|
+
"\u590D\u5236\u5E94\u7528\u6307\u4EE4\uFF1A\u7C98\u8D34\u540E\u6A21\u578B\u4F1A\u8C03\u7528 iterate_triage apply\uFF08\u6821\u9A8C\u3001\u53BB\u91CD\u3001\u5199\u5165\u524D\u5907\u4EFD\u3001\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\uFF09": "Copy the apply instruction: once pasted, the model calls iterate_triage apply (validates, dedupes, backs up before writing, rolls back on failure)",
|
|
1983
|
+
"\u751F\u6210\u5E94\u7528\u6307\u4EE4{a0}": "Generate apply instruction{a0}",
|
|
1984
|
+
"\u53EA\u590D\u5236 known_intentional YAML \u7247\u6BB5\uFF08\u9700\u81EA\u884C\u7C98\u5230 iterate.config.yaml \u7684 personalization.known_intentional \u4E0B\uFF09": "Copy only the known_intentional YAML fragment (you must paste it under personalization.known_intentional in iterate.config.yaml yourself)",
|
|
1985
|
+
"\u4EC5\u590D\u5236 YAML \u7247\u6BB5": "Copy YAML fragment only",
|
|
1986
|
+
"\u590D\u5236 iterate_triage list \u6307\u4EE4\uFF0C\u6838\u5BF9\u5DF2\u7ECF\u5199\u5165\u7684 known_intentional \u6761\u76EE\uFF08\u5199\u56DE\u95ED\u73AF\u786E\u8BA4\uFF09": "Copy the iterate_triage list instruction to check which known_intentional entries actually landed (write-back \u2192 readback)",
|
|
1987
|
+
\u56DE\u8BFB\u5DF2\u5199\u5165\u6761\u76EE: "Read back written entries",
|
|
1988
|
+
"\u5DF2\u590D\u5236\u5E94\u7528\u6307\u4EE4\u2014\u2014\u7C98\u8D34\u56DE\u4F1A\u8BDD\uFF0C\u6A21\u578B\u5C06\u8C03\u7528 iterate_triage apply \u5E76\u56DE\u62A5\u5199\u5165\u6761\u6570\u3002": "Apply instruction copied \u2014 paste it back into the chat and the model will call iterate_triage apply and report how many entries were written.",
|
|
1989
|
+
"\u5DF2\u590D\u5236\u56DE\u8BFB\u6307\u4EE4\u2014\u2014\u7C98\u8D34\u56DE\u4F1A\u8BDD\u53EF\u5217\u51FA\u5F53\u524D\u5DF2\u5199\u5165\u7684 known_intentional\u3002": "Readback instruction copied \u2014 paste it back to list the known_intentional entries currently written.",
|
|
1990
|
+
// ── command actions ──────────────────────────────────────────────────────
|
|
1991
|
+
\u6307\u6325\u64CD\u4F5C: "Command actions",
|
|
1992
|
+
"\u6279\u51C6\u67B6\u6784\u4FEE\u590D\uFF08\u590D\u5236\u6307\u4EE4\u6587\u672C\uFF1Aiterate_fix force:true\uFF0C\u542B\u5168\u90E8\u5FC5\u586B\u53C2\u6570\uFF09": "Approve the architectural fix (copies the instruction text: iterate_fix with force:true and every required argument)",
|
|
1993
|
+
\u6279\u51C6\u67B6\u6784\u4FEE\u590D: "Approve architectural fix",
|
|
1994
|
+
\u89E6\u53D1\u65B0\u4E00\u8F6E\u8FED\u4EE3\u5BA1\u67E5: "Trigger the next review round",
|
|
1995
|
+
\u89E6\u53D1\u65B0\u4E00\u8F6E: "New round",
|
|
1996
|
+
"\u6307\u6D3E\u5168\u90E8 {a0} \u4E2A findings \u4FEE\u590D\uFF08\u9010\u4E2A iterate_fix\uFF09": "Assign all {a0} findings for fixing (one iterate_fix each)",
|
|
1997
|
+
"\u6307\u6D3E\u5F53\u524D\u53EF\u89C1 {a0} \u4E2A findings \u4FEE\u590D\uFF08\u9010\u4E2A iterate_fix\uFF09": "Assign the {a0} visible findings for fixing (one iterate_fix each)",
|
|
1998
|
+
\u6307\u6D3E\u4FEE\u590D: "Assign fixes",
|
|
1999
|
+
\u56DE\u6EDA\u5230\u4E0A\u4E00\u4E2A\u68C0\u67E5\u70B9: "Roll back to the previous checkpoint",
|
|
2000
|
+
\u56DE\u6EDA\u68C0\u67E5\u70B9: "Rollback to checkpoint",
|
|
2001
|
+
// ── verdict banner / stats card ──────────────────────────────────────────
|
|
2002
|
+
\u62A5\u544A\u5DF2\u6279\u51C6: "Report approved",
|
|
2003
|
+
\u62A5\u544A\u9700\u4FEE\u8BA2: "Report needs revision",
|
|
2004
|
+
\u9879\u53D1\u73B0: " findings",
|
|
2005
|
+
\u8F6E: " rounds",
|
|
2006
|
+
\u9879\u5BA1\u67E5: " reviews",
|
|
2007
|
+
\u62A5\u544A\u901A\u8FC7\u5168\u90E8\u4E00\u81F4\u6027\u68C0\u67E5: "The report passed every consistency check",
|
|
2008
|
+
\u5904\u62A5\u544A\u7F3A\u9677: " report defects found",
|
|
2009
|
+
\u5DF2\u6536\u655B: "Converged",
|
|
2010
|
+
\u672A\u6536\u655B: "Not converged",
|
|
2011
|
+
"Round {a0} \u5B8C\u6210 \xB7 \u5DF2\u6536\u655B": "Round {a0} complete \xB7 converged",
|
|
2012
|
+
"Round {a0} \u5B8C\u6210": "Round {a0} complete",
|
|
2013
|
+
// ── settings ─────────────────────────────────────────────────────────────
|
|
2014
|
+
"\u5DF2\u6E05\u9664 {a0} \u6761": "Cleared {a0}",
|
|
2015
|
+
\u518D\u6B21\u70B9\u51FB\u4EE5\u786E\u8BA4\u6E05\u9664\u5168\u90E8\u5224\u5B9A: "Click again to confirm clearing every verdict",
|
|
2016
|
+
"\u6E05\u9664\u6240\u6709\u5206\u8BCA\u5224\u5B9A\u8BB0\u5F55\uFF08\u9700\u8981\u4E8C\u6B21\u786E\u8BA4\uFF09": "Clear every triage verdict (requires a second click)",
|
|
2017
|
+
"\u786E\u8BA4\u6E05\u9664\uFF1F": "Confirm clear?",
|
|
2018
|
+
\u6E05\u9664\u5206\u8BCA: "Clear triage",
|
|
2019
|
+
\u8FED\u4EE3\u4E3B\u9898: "Iterate theme",
|
|
2020
|
+
"\u542F\u7528\u6696\u7425\u73C0\u914D\u8272\u7684 iterate \u4E13\u5C5E\u76AE\u80A4\uFF0C\u8986\u76D6 dsh \u9ED8\u8BA4\u4E3B\u9898\u4EE4\u724C\u3002": "Enables the warm-amber iterate skin, overriding the default dsh theme tokens.",
|
|
2021
|
+
\u5DF2\u542F\u7528: "Enabled",
|
|
2022
|
+
\u5DF2\u5173\u95ED: "Off",
|
|
2023
|
+
"\u5F00\u5173 iterate \u4E3B\u9898": "Toggle the iterate theme",
|
|
2024
|
+
\u5206\u8BCA\u6301\u4E45\u5316: "Triage persistence",
|
|
2025
|
+
"y / n / a \u5224\u5B9A\u4FDD\u5B58\u5728\u672C\u5730\u6D4F\u89C8\u5668\uFF08localStorage\uFF09\uFF0C\u5237\u65B0\u4F1A\u8BDD\u540E\u4ECD\u4FDD\u7559\u3002": "y / n / a verdicts are stored in your browser (localStorage) and survive a session refresh.",
|
|
2026
|
+
\u672C\u5730\u4FDD\u5B58: "Stored locally",
|
|
2027
|
+
\u914D\u7F6E\u7BA1\u7406: "Configuration",
|
|
2028
|
+
"\u76EE\u6807 / \u7EF4\u5EA6 / \u6700\u5927\u8F6E\u6570 / validation.commands \u5199\u5728\u9879\u76EE\u7684 iterate.config.yaml\uFF0C\u590D\u5236\u6307\u5F15\u53EF\u8BA9\u6A21\u578B\u6309\u9700\u8C03\u6574\u3002": "Goal / dimensions / max rounds / validation.commands live in the project\u2019s iterate.config.yaml; copy the guide to let the model adjust them on demand.",
|
|
2029
|
+
\u590D\u5236\u6307\u5F15: "Copy guide",
|
|
2030
|
+
\u6536\u8D77: "Collapse",
|
|
2031
|
+
\u5C55\u5F00: "Expand",
|
|
2032
|
+
\u9009\u62E9\u8981\u4FEE\u6539\u7684\u914D\u7F6E\u5B57\u6BB5: "Pick the config field to edit",
|
|
2033
|
+
"\u751F\u6210\u5E76\u590D\u5236\u8BE5\u5B57\u6BB5\u7684 iterate_config \u5199\u5165\u6307\u4EE4\uFF08\u5199\u5165\u524D\u81EA\u52A8\u5907\u4EFD\u3001\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\uFF09": "Build and copy the iterate_config write instruction for this field (backs up first, rolls back on failure)",
|
|
2034
|
+
\u751F\u6210\u5199\u5165\u6307\u4EE4: "Build write instruction",
|
|
2035
|
+
"\u526A\u8D34\u677F\u88AB\u62D2\u7EDD\u2014\u2014\u6307\u4EE4\u5DF2\u5C55\u793A\u5728\u4E0B\u65B9\uFF0C\u8BF7\u624B\u52A8\u590D\u5236\u3002": "The clipboard write was refused \u2014 the instruction is shown below, copy it manually.",
|
|
2036
|
+
"\u5199\u5165\u6307\u4EE4\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\uFF09": "Write instruction (paste to the model)",
|
|
2037
|
+
"\u5C1A\u672A\u56DE\u8BFB\u5F53\u524D\u914D\u7F6E\u2014\u2014\u70B9\u300C\u56DE\u8BFB\u5F53\u524D\u914D\u7F6E\u300D\u590D\u5236\u6307\u4EE4\u53D1\u7ED9\u6A21\u578B\uFF0C\u56DE\u663E\u540E\u5373\u53EF\u5728\u6B64\u770B\u5230\u5F53\u524D\u503C\u3002": "No config read back yet \u2014 press \u201CRead back current config\u201D, send the instruction to the model, and the current value shows up here.",
|
|
2038
|
+
"\u5F53\u524D\u503C {a0}\uFF1A{a1}": "Current value for {a0}: {a1}",
|
|
2039
|
+
"\u56DE\u8BFB\u7ED3\u679C\u4E2D\u6CA1\u6709 {a0}\uFF08\u8BE5\u5B57\u6BB5\u53EF\u80FD\u5C1A\u672A\u5199\u5165\u914D\u7F6E\u6587\u4EF6\uFF09\u3002": "The read-back does not contain {a0} (it may not have been written to the config file yet).",
|
|
2040
|
+
'\u590D\u5236\u56DE\u8BFB\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u8C03\u7528 iterate_config(operation:"read") \u5E76\u56DE\u663E\u7ED3\u679C': 'Copy the read-back instruction: have the model call iterate_config(operation:"read") and echo the result',
|
|
2041
|
+
\u56DE\u8BFB\u5F53\u524D\u914D\u7F6E: "Read back current config",
|
|
2042
|
+
"\u56DE\u8BFB\u6307\u4EE4\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\uFF09": "Read-back instruction (paste to the model)",
|
|
2043
|
+
\u590D\u5236: "Copy",
|
|
2044
|
+
"iterate.config.yaml \u53EF\u7F16\u8F91\u5B57\u6BB5": "Editable iterate.config.yaml fields",
|
|
2045
|
+
"\u4E2D\u6587\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u7684\u5951\u7EA6\u6587\u6848\uFF09": "Chinese (contract text to paste to the model)",
|
|
2046
|
+
\u72B6\u6001\u6982\u89C8: "Runtime status",
|
|
2047
|
+
"\u67E5\u770B\u8FD0\u884C\u65F6\u4EA7\u7269\u5E03\u5C40\u4E0E\u6E05\u7406\u6307\u5F15\u3002iterate_status / iterate_history / iterate_prune \u5DE5\u5177\u7528\u4E8E\u67E5\u770B\u548C\u7BA1\u7406\u3002": "The runtime artifact layout and cleanup guide. Use iterate_status / iterate_history / iterate_prune to inspect and manage them.",
|
|
2048
|
+
\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167: "Pull disk snapshot",
|
|
2049
|
+
\u8FD0\u884C\u65F6\u5E03\u5C40\u4E0E\u6E05\u7406: "Runtime layout & cleanup",
|
|
2050
|
+
"\u4E3A\u6BCF\u6B21\u4EE3\u7801\u8BC4\u5BA1\u751F\u6210 Review \u62A5\u544A\u4E0E\u4EA4\u4E92\u5F0F\u5206\u8BCA\u89C6\u56FE\uFF0C\u4E13\u6CE8 AI \u81EA\u6211\u5BA1\u67E5\u4E0E\u4FEE\u6B63\u3002": "Generates a review report and an interactive triage view for every code review, focused on AI self-review and correction.",
|
|
2051
|
+
\u5C31\u7EEA: "Ready",
|
|
2052
|
+
"iterate \u8BBE\u7F6E": "iterate settings",
|
|
2053
|
+
"\u526A\u8D34\u677F\u5199\u5165\u88AB\u62D2\u7EDD\uFF0C\u590D\u5236\u672A\u751F\u6548\u2014\u2014\u8BF7\u5C55\u5F00\u5BF9\u5E94\u533A\u5757\u624B\u52A8\u9009\u4E2D\u6587\u672C\u590D\u5236\u3002": "The clipboard write was refused, so nothing was copied \u2014 expand the section and copy the text manually.",
|
|
2054
|
+
"\u754C\u9762\u8BED\u8A00": "Interface language",
|
|
2055
|
+
"\u9762\u677F\u6587\u6848\u7684\u663E\u793A\u8BED\u8A00\uFF1B\u590D\u5236\u7ED9\u6A21\u578B\u7684\u6307\u4EE4\u4FDD\u6301\u4E2D\u6587\uFF0C\u4E0E SKILL.md \u7684\u5DE5\u4F5C\u6D41\u5951\u7EA6\u4E00\u81F4\u3002": "The language of the panel copy. Instructions copied for the model stay Chinese \u2014 they mirror the SKILL.md workflow contract.",
|
|
2056
|
+
"\u5207\u6362\u754C\u9762\u8BED\u8A00": "Switch the interface language",
|
|
2057
|
+
// ── observatory shell / live feed ────────────────────────────────────────
|
|
2058
|
+
"{a0} \u9875 \xB7 \u6309 1-9/0 \u5207 Fn\u3001- \u56DE\u5B9E\u65F6\u6D41": "{a0} tabs \xB7 keys 1-9/0 switch Fn, - returns to the live feed",
|
|
2059
|
+
"\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u9010\u4E2A\u8C03\u7528 {a0} \u5E76\u56DE\u663E\u5B8C\u6574\u7ED3\u679C": "Copy the disk-snapshot instruction: have the model call {a0} and echo the full results",
|
|
2060
|
+
"iterate \u89C2\u6D4B\u53F0": "iterate observatory",
|
|
2061
|
+
"\u6682\u65E0\u8FD0\u884C\u65F6\u89C2\u6D4B\u6570\u636E \xB7 {a0}": "No runtime observations yet \xB7 {a0}",
|
|
2062
|
+
"\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u78C1\u76D8\u4E0A\u53EF\u80FD\u5DF2\u6709\u5386\u53F2\u4EA7\u7269\uFF0C\u56DE\u663E\u8FDB\u4F1A\u8BDD\u540E\u89C2\u6D4B\u53F0\u5373\u53EF\u586B\u5145": "Copy the disk-snapshot instruction: older artifacts may already be on disk; once echoed back into the chat the observatory fills up",
|
|
2063
|
+
"\u5B9E\u65F6\u6D3B\u52A8\u6D41\u4EC5\u5728\u8FD0\u884C\u671F\u95F4\u8BB0\u5F55\uFF0C\u5F53\u524D\u5DF2\u5B8C\u6210\u3002": "The live activity stream is only recorded while a run is active; this run has finished.",
|
|
2064
|
+
\u67E5\u770B\u5BA1\u67E5\u7EBF\u7A0B: "View review threads",
|
|
2065
|
+
\u67E5\u770B\u53D1\u73B0: "View findings",
|
|
2066
|
+
\u6682\u65E0\u5B9E\u65F6\u6D3B\u52A8: "No live activity yet",
|
|
2067
|
+
\u5B9E\u65F6\u6D3B\u52A8: "Live activity",
|
|
2068
|
+
\u6309\u6D3B\u52A8\u7C7B\u578B\u7B5B\u9009: "Filter by activity type",
|
|
2069
|
+
\u5168\u90E8\u6D3B\u52A8: "All activity",
|
|
2070
|
+
\u65E0\u5339\u914D\u7684\u5B9E\u65F6\u6D3B\u52A8: "No matching live activity",
|
|
2071
|
+
\u590D\u5236\u672A\u6210\u529F: "Copy failed",
|
|
2072
|
+
"\u526A\u8D34\u677F\u5199\u5165\u88AB\u62D2\u7EDD\uFF0C\u8BF7\u624B\u52A8\u9009\u4E2D\u4E0B\u65B9\u6307\u4EE4\u590D\u5236": "The clipboard write was refused \u2014 select the instruction below and copy it manually",
|
|
2073
|
+
\u5173\u95ED\u590D\u5236\u5931\u8D25\u63D0\u793A: "Dismiss the copy-failure notice",
|
|
2074
|
+
\u5173\u95ED: "Dismiss",
|
|
2075
|
+
"\u5C06\u89C2\u6D4B\u53F0\u5168\u90E8\u6570\u636E\u5BFC\u51FA\u4E3A JSON\uFF08\u4F18\u5148\u4E0B\u8F7D\uFF0C\u5931\u8D25\u5219\u590D\u5236\uFF09": "Export every observatory panel as JSON (downloads first, falls back to copying)",
|
|
2076
|
+
\u5DF2\u5BFC\u51FA: "Exported",
|
|
2077
|
+
"\u5BFC\u51FA JSON": "Export JSON",
|
|
2078
|
+
"\u89C2\u6D4B\u53F0\u9875\u7B7E\uFF08\u5FEB\u6377\u952E 1-9 / 0 / -\uFF09": "Observatory tabs (keys 1-9 / 0 / -)",
|
|
2079
|
+
"{a0}\uFF08\u6309 {a1} \u5207\u6362\uFF09": "{a0} (press {a1})",
|
|
2080
|
+
"{a0}\uFF08\u6309 - \u5207\u6362\uFF09": "{a0} (press -)",
|
|
2081
|
+
// ── F1 review threads ────────────────────────────────────────────────────
|
|
2082
|
+
\u6682\u65E0\u5BA1\u67E5\u7EBF\u7A0B: "No review threads yet",
|
|
2083
|
+
\u5BA1\u67E5\u7EBF\u7A0B: "Review threads",
|
|
2084
|
+
\u5C55\u5F00\u5168\u90E8\u7EBF\u7A0B: "Expand all threads",
|
|
2085
|
+
\u5168\u90E8\u5C55\u5F00: "Expand all",
|
|
2086
|
+
\u6536\u8D77\u5168\u90E8\u7EBF\u7A0B: "Collapse all threads",
|
|
2087
|
+
\u5168\u90E8\u6536\u8D77: "Collapse all",
|
|
2088
|
+
"{a0} \xB7 {a1} \u8F6E": "{a0} \xB7 {a1} rounds",
|
|
2089
|
+
\u81EA\u7531\u5207\u6362: "Mixed",
|
|
2090
|
+
\u672C\u8F6E\u65E0\u7EBF\u7A0B: "No threads in this round",
|
|
2091
|
+
\u672A\u547D\u540D\u7EF4\u5EA6: "Unnamed dimension",
|
|
2092
|
+
\u6536\u8D77\u7EBF\u7A0B: "Collapse thread",
|
|
2093
|
+
\u5C55\u5F00\u7EBF\u7A0B: "Expand thread",
|
|
2094
|
+
\u53D9\u8FF0: "Narration",
|
|
2095
|
+
"{a0} \u6761": "{a0}",
|
|
2096
|
+
\u65E0\u7EBF\u7D22\u53D9\u8FF0: "No narration recorded",
|
|
2097
|
+
\u8BFB\u53D6\u6587\u4EF6: "Files read",
|
|
2098
|
+
\u672C\u7EBF\u7A0B\u65E0\u53D1\u73B0: "This thread found nothing",
|
|
2099
|
+
"{a0} \u7EBF\u7A0B": "{a0} threads",
|
|
2100
|
+
// ── F2 convergence trend ─────────────────────────────────────────────────
|
|
2101
|
+
\u6682\u65E0\u6536\u655B\u6570\u636E: "No convergence data yet",
|
|
2102
|
+
\u5DF2\u4FEE\u590D: "Fixed",
|
|
2103
|
+
\u4E25\u91CD\u5EA6\u5206\u5E03: "Severity mix",
|
|
2104
|
+
\u9A8C\u8BC1: "Validation",
|
|
2105
|
+
"{a0}/{a1} \u901A\u8FC7{a2}": "{a0}/{a1} passed{a2}",
|
|
2106
|
+
"\uFF08\u542B\u5931\u8D25\uFF09": " (failures included)",
|
|
2107
|
+
"\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\uFF1A{a0}{a1}": "Findings per round: {a0}{a1}",
|
|
2108
|
+
" \xB7 \u5F53\u524D Round {a0}": " \xB7 current round {a0}",
|
|
2109
|
+
"\u8DE8\u8F6E\u5BF9\u6BD4\uFF08\u672C\u4F1A\u8BDD\uFF09": "Cross-round comparison (this session)",
|
|
2110
|
+
"\u8DE8\u4F1A\u8BDD\u5BF9\u6BD4\uFF1A\u672C\u8868\u53EA\u8986\u76D6\u5F53\u524D\u4F1A\u8BDD\uFF1B\u70B9\u300C\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u300D\u8BA9\u6A21\u578B\u56DE\u663E iterate_history\uFF0C\u518D\u7528\u300C\u5BFC\u51FA JSON\u300D\u5E76\u6392\u6BD4\u8F83\u4E24\u6B21\u8FD0\u884C": 'Cross-session comparison: this table only covers the current session \u2014 click "Pull disk snapshot" to have the model echo iterate_history, then use "Export JSON" to compare two runs side by side',
|
|
2111
|
+
// ── F3 finding locations ─────────────────────────────────────────────────
|
|
2112
|
+
\u6682\u65E0\u53D1\u73B0: "No findings",
|
|
2113
|
+
"{a0}\uFF08{a1} \u9879\uFF09": "{a0} ({a1} findings)",
|
|
2114
|
+
"\u641C\u7D22\u53D1\u73B0\u2026": "Search findings\u2026",
|
|
2115
|
+
\u641C\u7D22\u53D1\u73B0: "Search findings",
|
|
2116
|
+
\u6E05\u9664\u5168\u90E8\u7B5B\u9009: "Clear all filters",
|
|
2117
|
+
\u6E05\u9664\u7B5B\u9009: "Clear filters",
|
|
2118
|
+
\u5DF2\u786E\u8BA4: "Acknowledged",
|
|
2119
|
+
"\u590D\u5236\u8BE5 finding \u7684\u5EFA\u8BAE\u4FEE\u590D\u6587\u672C": "Copy this finding\u2019s suggested fix text",
|
|
2120
|
+
"\u590D\u5236 suggested_fix": "Copy suggested_fix",
|
|
2121
|
+
"\u590D\u5236 iterate_triage \u6307\u4EE4\u6587\u672C\uFF08\u6807\u8BB0\u4E3A known_intentional\uFF09": "Copy the iterate_triage instruction text (mark as known_intentional)",
|
|
2122
|
+
"\u590D\u5236 iterate_fix \u6307\u4EE4\u6587\u672C": "Copy the iterate_fix instruction text",
|
|
2123
|
+
\u590D\u5236\u4FEE\u590D\u6307\u4EE4: "Copy fix instruction",
|
|
2124
|
+
\u65E0\u5339\u914D\u7684\u53D1\u73B0: "No matching findings",
|
|
2125
|
+
// ── F4 fixes & rollback ──────────────────────────────────────────────────
|
|
2126
|
+
\u6682\u65E0\u4FEE\u590D\u8BB0\u5F55: "No fixes recorded yet",
|
|
2127
|
+
\u6210\u529F: "Success",
|
|
2128
|
+
\u5931\u8D25: "Failed",
|
|
2129
|
+
"\u590D\u5236 iterate_rollback \u6307\u4EE4\u6587\u672C": "Copy the iterate_rollback instruction text",
|
|
2130
|
+
\u56DE\u6EDA: "Rollback",
|
|
2131
|
+
// ── F5 checkpoint ────────────────────────────────────────────────────────
|
|
2132
|
+
\u65AD\u70B9: "Checkpoint",
|
|
2133
|
+
"\u590D\u5236 iterate_checkpoint resume \u6307\u4EE4\u6587\u672C\uFF08\u52A0\u8F7D\u65AD\u70B9\u5E76\u8BA1\u6570\u4E00\u6B21\u6062\u590D\uFF09": "Copy the iterate_checkpoint resume instruction (loads the checkpoint and counts one resume)",
|
|
2134
|
+
\u590D\u5236\u6062\u590D\u6307\u4EE4: "Copy resume instruction",
|
|
2135
|
+
"\u590D\u5236 iterate_checkpoint clear \u6307\u4EE4\u6587\u672C\uFF08\u6E05\u9664\u5F53\u524D\u65AD\u70B9\uFF0C\u4F9B\u91CD\u65B0\u5F00\u59CB\uFF09": "Copy the iterate_checkpoint clear instruction (drops the current checkpoint so you can start over)",
|
|
2136
|
+
\u6E05\u9664\u65AD\u70B9: "Clear checkpoint",
|
|
2137
|
+
// ── F6 run console ───────────────────────────────────────────────────────
|
|
2138
|
+
\u8BE2\u95EE\u7528\u6237: "Ask the user",
|
|
2139
|
+
\u62D2\u7EDD: "Deny",
|
|
2140
|
+
\u76F4\u63A5\u6267\u884C: "Run directly",
|
|
2141
|
+
"\u8BF7\u8C03\u7528 `iterate_transcript` \u5199\u5165 nudge \u6307\u4EE4\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_transcript` to write this nudge instruction:\n\n```json\n{a0}\n```",
|
|
2142
|
+
"\u8BF7\u8C03\u7528 `iterate_transcript` \u6E05\u9664\u5F53\u524D nudge\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_transcript` to clear the current nudge:\n\n```json\n{a0}\n```",
|
|
2143
|
+
\u901A\u8FC7: "Passed",
|
|
2144
|
+
\u5BFC\u81F4\u4E2D\u6B62: " aborted the run",
|
|
2145
|
+
\u9A8C\u8BC1\u7ED3\u679C: "Validation results",
|
|
2146
|
+
\u672C\u8F6E\u5C1A\u65E0\u9A8C\u8BC1\u8BB0\u5F55: "No validation was recorded in this round",
|
|
2147
|
+
"{a0} \u6761\u547D\u4EE4": "{a0} commands",
|
|
2148
|
+
\u56E0\u9A8C\u8BC1\u5931\u8D25\u4E2D\u6B62: "Stopped because validation failed",
|
|
2149
|
+
"\u5C1A\u672A\u62C9\u53D6\u9A8C\u8BC1\u7ED3\u679C\uFF1A\u6570\u636E\u5728\u78C1\u76D8 transcript \u91CC\uFF0C\u70B9\u300C\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u300D\u590D\u5236\u6307\u4EE4\u53D1\u56DE\u4F1A\u8BDD\u5373\u53EF\u56DE\u586B\u3002": 'Validation results have not been pulled yet: they live in the transcript on disk \u2014 click "Pull disk snapshot", paste the instruction back into the chat, and this panel fills.',
|
|
2150
|
+
\u8FD0\u884C\u63A7\u5236\u53F0: "Run console",
|
|
2151
|
+
"\u6279\u51C6\u7B56\u7565\uFF1A{a0}": "Approval policy: {a0}",
|
|
2152
|
+
"\u5F53\u524D nudge": "Current nudge",
|
|
2153
|
+
"\u590D\u5236\u6E05\u9664 nudge \u6307\u4EE4\uFF08\u5199 text:null\uFF09\uFF0C\u5728\u8FD0\u884C\u63A7\u5236\u53F0\u63D0\u793A\u6E05\u9664\u5DF2\u6301\u4E45\u5316\u7684 nudge": "Copy the clear-nudge instruction (writes text:null) to ask the model to drop the persisted nudge",
|
|
2154
|
+
\u5DF2\u590D\u5236\u6E05\u9664\u6307\u4EE4: "Clear instruction copied",
|
|
2155
|
+
\u590D\u5236\u6E05\u9664\u6307\u4EE4: "Copy clear instruction",
|
|
2156
|
+
"\u8D77\u8349\u4E00\u6761 nudge \u65B9\u5411\u6307\u4EE4\u2026": "Draft a nudge to steer the run\u2026",
|
|
2157
|
+
"nudge \u8349\u7A3F": "Nudge draft",
|
|
2158
|
+
"\u590D\u5236 iterate_transcript nudge \u6307\u4EE4\u6587\u672C": "Copy the iterate_transcript nudge instruction text",
|
|
2159
|
+
"\u590D\u5236 nudge \u6307\u4EE4": "Copy nudge instruction",
|
|
2160
|
+
// ── F7 decision timeline ─────────────────────────────────────────────────
|
|
2161
|
+
\u6682\u65E0\u65F6\u95F4\u7EBF\u6761\u76EE: "No timeline entries yet",
|
|
2162
|
+
\u65F6\u95F4\u7EBF: "Timeline",
|
|
2163
|
+
\u6309\u65F6\u95F4\u7EBF\u7C7B\u578B\u7B5B\u9009: "Filter the timeline by type",
|
|
2164
|
+
\u5168\u90E8\u7C7B\u578B: "All types",
|
|
2165
|
+
\u6309\u8F6E\u6B21\u7B5B\u9009: "Filter by round",
|
|
2166
|
+
\u5168\u90E8\u8F6E\u6B21: "All rounds",
|
|
2167
|
+
"\u641C\u7D22\u65F6\u95F4\u7EBF\u2026": "Search timeline\u2026",
|
|
2168
|
+
\u641C\u7D22\u65F6\u95F4\u7EBF: "Search timeline",
|
|
2169
|
+
\u6E05\u9664: "Clear",
|
|
2170
|
+
\u65E0\u5339\u914D\u7684\u65F6\u95F4\u7EBF\u6761\u76EE: "No matching timeline entries",
|
|
2171
|
+
// ── F8 quality gate ──────────────────────────────────────────────────────
|
|
2172
|
+
\u8D28\u91CF\u95E8\u7981: "Quality gate",
|
|
2173
|
+
"\u590D\u5236 iterate_quality_gate \u67E5\u8BE2\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09": "Copy the iterate_quality_gate query instruction (JSON arguments included)",
|
|
2174
|
+
\u67E5\u8BE2\u95E8\u7981: "Query gate",
|
|
2175
|
+
"\u590D\u5236 iterate_quality_gate clear \u6307\u4EE4\u6587\u672C\uFF08\u91CD\u7F6E\u9648\u65E7\u7684 FAIL \u8BC1\u4E66\uFF09": "Copy the iterate_quality_gate clear instruction (resets a stale FAIL certificate)",
|
|
2176
|
+
\u6E05\u9664\u95E8\u7981: "Clear gate",
|
|
2177
|
+
"\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u56DE\u663E .iterate/ \u4E0B\u7684\u95E8\u7981\u3001\u7ECF\u9A8C\u3001\u9632\u5FA1\u4E8B\u4EF6\u4E0E transcript": "Copy the disk-snapshot instruction: have the model echo the gate, experience, defense events and transcript artifacts under .iterate/",
|
|
2178
|
+
\u8D28\u91CF\u95E8\u7981\u89C6\u56FE: "Quality gate view",
|
|
2179
|
+
\u8D28\u91CF\u95E8\u7981\u8BC1\u4E66: "Quality gate certificate",
|
|
2180
|
+
"\u8D28\u91CF\u95E8\u7981\u663E\u793A\u5404\u7EF4\u5EA6\u6536\u655B\u5EA6\u3001\u9A8C\u8BC1\u901A\u8FC7\u7387\u548C\u6574\u4F53 PASS/FAIL \u72B6\u6001\u3002": "The quality gate shows per-dimension convergence, the validation pass rate and the overall PASS/FAIL state.",
|
|
2181
|
+
\u590D\u5236\u67E5\u8BE2\u6307\u4EE4: "Copy query instruction",
|
|
2182
|
+
\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4: "Copy disk-snapshot instruction",
|
|
2183
|
+
"\u6574\u4F53\u8BC4\u5206 {a0} \xB7 \u9A8C\u8BC1 {a1}/{a2} \xB7 \u901A\u8FC7\u7387 {a3}%": "Overall score {a0} \xB7 validation {a1}/{a2} \xB7 pass rate {a3}%",
|
|
2184
|
+
"{a0} \xB7 \u6536\u655B {a1}% \xB7 \u8BC4\u5206 {a2}": "{a0} \xB7 convergence {a1}% \xB7 score {a2}",
|
|
2185
|
+
"\u8BC4\u5206 {a0} \xB7 \u53D1\u73B0 {a1} \xB7 \u5DF2\u4FEE\u590D {a2}": "Score {a0} \xB7 findings {a1} \xB7 fixed {a2}",
|
|
2186
|
+
"\u5931\u8D25\u539F\u56E0\uFF1A{a0}": "Failure reason: {a0}",
|
|
2187
|
+
// ── F9 experience bank ───────────────────────────────────────────────────
|
|
2188
|
+
"\u8BF7\u8C03\u7528 `iterate_experience` \u641C\u7D22\u7ECF\u9A8C\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_experience` to search the experience bank:\n\n```json\n{a0}\n```",
|
|
2189
|
+
"\u8BF7\u8C03\u7528 `iterate_experience` \u91C7\u7EB3\u7ECF\u9A8C\u5E76\u5E94\u7528\u5DF2\u9A8C\u8BC1\u4FEE\u6CD5\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_experience` to adopt this entry and apply its verified fix:\n\n```json\n{a0}\n```",
|
|
2190
|
+
"\u547D\u4E2D {a0} \u6B21": "Hit {a0} times",
|
|
2191
|
+
"\u590D\u5236\u91C7\u7EB3\u6307\u4EE4\uFF08\u67E5\u8BE2\u8BE5\u7ECF\u9A8C\u5E76\u5E94\u7528\u5230\u5F53\u524D\u4E0A\u4E0B\u6587\uFF09": "Copy the adopt instruction (queries this entry and applies it to the current context)",
|
|
2192
|
+
\u91C7\u7EB3: "Adopt",
|
|
2193
|
+
\u7ECF\u9A8C\u94F6\u884C: "Experience bank",
|
|
2194
|
+
"\u4F1A\u8BDD\u5185 {a0} \u6761": "{a0} in this session",
|
|
2195
|
+
"\u590D\u5236\u5217\u51FA\u7ECF\u9A8C\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09": "Copy the list-experience instruction (JSON arguments included)",
|
|
2196
|
+
\u5217\u51FA\u7ECF\u9A8C: "List experiences",
|
|
2197
|
+
"\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u628A .iterate/ \u4E0B\u7684\u7ECF\u9A8C\u94F6\u884C\u7B49\u4EA7\u7269\u56DE\u663E\u8FDB\u4F1A\u8BDD": "Copy the disk-snapshot instruction: echo the experience bank and other .iterate/ artifacts back into the chat",
|
|
2198
|
+
"\u641C\u7D22\u7ECF\u9A8C\u6A21\u5F0F\u2026": "Search experience patterns\u2026",
|
|
2199
|
+
\u641C\u7D22\u7ECF\u9A8C: "Search experiences",
|
|
2200
|
+
\u590D\u5236\u641C\u7D22\u6307\u4EE4: "Copy search instruction",
|
|
2201
|
+
\u641C\u7D22: "Search",
|
|
2202
|
+
"\u6CA1\u6709\u5339\u914D\u300C": "No experience matches \u201C",
|
|
2203
|
+
"\u300D\u7684\u7ECF\u9A8C": "\u201D",
|
|
2204
|
+
"\u4E5F\u53EF\u70B9\u300C\u5217\u51FA\u7ECF\u9A8C\u300D\u67E5\u8BE2\u78C1\u76D8\u4E0A\u7684\u5168\u90E8\u5386\u53F2\u7ECF\u9A8C\u3002": 'You can also click "List experiences" to query every historical entry on disk.',
|
|
2205
|
+
// ── F10 defense events ───────────────────────────────────────────────────
|
|
2206
|
+
"\u8BF7\u8C03\u7528 `iterate_defense_events` \u67E5\u8BE2\u9632\u5FA1\u4E8B\u4EF6\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_defense_events` to query defense events:\n\n```json\n{a0}\n```",
|
|
2207
|
+
\u524D\u7F6E\u6821\u9A8C\u5931\u8D25: "Precondition failed",
|
|
2208
|
+
\u4E0D\u53D8\u91CF\u8FDD\u53CD: "Invariant violated",
|
|
2209
|
+
\u5047\u8BBE\u88AB\u8BC1\u4F2A: "Assumption falsified",
|
|
2210
|
+
\u9632\u5FA1\u4E8B\u4EF6: "Defense events",
|
|
2211
|
+
"\u4F1A\u8BDD\u5185 {a0} \u6761 / \u7D2F\u8BA1 {a1}": "{a0} in this session / {a1} total",
|
|
2212
|
+
"\u590D\u5236\u5217\u51FA\u9632\u5FA1\u4E8B\u4EF6\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09": "Copy the list-defense-events instruction (JSON arguments included)",
|
|
2213
|
+
\u5217\u51FA\u4E8B\u4EF6: "List events",
|
|
2214
|
+
"\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u628A .iterate/ \u4E0B\u7684\u9632\u5FA1\u4E8B\u4EF6\u6D41\u56DE\u663E\u8FDB\u4F1A\u8BDD": "Copy the disk-snapshot instruction: echo the defense event stream under .iterate/ back into the chat",
|
|
2215
|
+
"\u8BF7\u8C03\u7528 `iterate_defense_events` \u67E5\u8BE2\u4E8B\u4EF6\u7EDF\u8BA1\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_defense_events` for the event counts:\n\n```json\n{a0}\n```",
|
|
2216
|
+
"\u590D\u5236\u7EDF\u8BA1\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09": "Copy the counts instruction (JSON arguments included)",
|
|
2217
|
+
\u7EDF\u8BA1: "Counts",
|
|
2218
|
+
"\u8BF7\u8C03\u7528 `iterate_defense_events` \u6E05\u9664\u9632\u5FA1\u4E8B\u4EF6\u6D41\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_defense_events` to clear the defense event stream:\n\n```json\n{a0}\n```",
|
|
2219
|
+
"\u590D\u5236 iterate_defense_events clear \u6307\u4EE4\u6587\u672C\uFF08\u91CD\u7F6E\u9648\u65E7\u4E8B\u4EF6\u6D41\uFF09": "Copy the iterate_defense_events clear instruction (resets a stale event stream)",
|
|
2220
|
+
\u6E05\u9664\u4E8B\u4EF6: "Clear events",
|
|
2221
|
+
\u9632\u5FA1\u4E8B\u4EF6\u6D41: "Defense event stream",
|
|
2222
|
+
"\u9632\u5FA1\u4E8B\u4EF6\u5305\u62EC\uFF1A\u524D\u7F6E\u6821\u9A8C\u5931\u8D25\u3001\u56DE\u6EDA\u3001\u4E0D\u53D8\u91CF\u8FDD\u53CD\u3001\u5047\u8BBE\u88AB\u8BC1\u4F2A\u3002": "Defense events cover failed preconditions, rollbacks, violated invariants and falsified assumptions.",
|
|
2223
|
+
\u6309\u4E8B\u4EF6\u7C7B\u578B\u7B5B\u9009: "Filter by event type",
|
|
2224
|
+
\u6E05\u9664\u7C7B\u578B\u7B5B\u9009: "Clear type filter",
|
|
2225
|
+
"\u9632\u7EBF\uFF1A{a0}": "Defense: {a0}",
|
|
2226
|
+
\u6CA1\u6709\u5339\u914D\u8BE5\u7C7B\u578B\u7684\u9632\u5FA1\u4E8B\u4EF6: "No defense events of this type",
|
|
2227
|
+
// ── copy feedback ────────────────────────────────────────────────────────
|
|
2228
|
+
\u5DF2\u590D\u5236: "Copied",
|
|
2229
|
+
\u590D\u5236\u5931\u8D25: "Copy failed"
|
|
2230
|
+
};
|
|
2231
|
+
var current = "zh";
|
|
2232
|
+
var storage = null;
|
|
2233
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
2234
|
+
function getLang() {
|
|
2235
|
+
return current;
|
|
2236
|
+
}
|
|
2237
|
+
function setLang(next, persist = true) {
|
|
2238
|
+
if (next !== current) {
|
|
2239
|
+
current = next;
|
|
2240
|
+
if (persist) {
|
|
2241
|
+
try {
|
|
2242
|
+
storage?.set(LANG_STORAGE_KEY, next);
|
|
2243
|
+
} catch {
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
for (const listener of [...listeners]) {
|
|
2247
|
+
try {
|
|
2248
|
+
listener(next);
|
|
2249
|
+
} catch {
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
function subscribeLang(listener) {
|
|
2255
|
+
listeners.add(listener);
|
|
2256
|
+
return () => {
|
|
2257
|
+
listeners.delete(listener);
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
function configureLangStorage(next) {
|
|
2261
|
+
storage = next;
|
|
2262
|
+
}
|
|
2263
|
+
function detectLang(stored, navigatorLanguage) {
|
|
2264
|
+
if (stored === "en" || stored === "zh") return stored;
|
|
2265
|
+
return /^zh\b/i.test(navigatorLanguage ?? "") ? "zh" : "en";
|
|
2266
|
+
}
|
|
2267
|
+
function t(key) {
|
|
2268
|
+
return current === "en" ? EN[key] ?? key : key;
|
|
2269
|
+
}
|
|
2270
|
+
function tf(template, params = {}) {
|
|
2271
|
+
const translated = current === "en" ? EN[template] ?? template : template;
|
|
2272
|
+
return translated.replace(
|
|
2273
|
+
/\{([A-Za-z0-9_]+)\}/g,
|
|
2274
|
+
(whole, name2) => Object.prototype.hasOwnProperty.call(params, name2) ? String(params[name2]) : whole
|
|
2275
|
+
);
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
// src/client/session-cache.ts
|
|
2279
|
+
var current2 = null;
|
|
2280
|
+
var listeners2 = /* @__PURE__ */ new Set();
|
|
2281
|
+
function noteSession(session) {
|
|
2282
|
+
if (Object.is(session, current2)) return;
|
|
2283
|
+
current2 = session;
|
|
2284
|
+
for (const listener of [...listeners2]) listener();
|
|
2285
|
+
}
|
|
2286
|
+
function readSession() {
|
|
2287
|
+
return current2;
|
|
2288
|
+
}
|
|
2289
|
+
function subscribeSession(listener) {
|
|
2290
|
+
listeners2.add(listener);
|
|
2291
|
+
return () => {
|
|
2292
|
+
listeners2.delete(listener);
|
|
2293
|
+
};
|
|
2294
|
+
}
|
|
2295
|
+
|
|
1777
2296
|
// src/client/index.ts
|
|
1778
2297
|
var React = require("react");
|
|
1779
2298
|
var name = "iterate-plugin";
|
|
@@ -2106,12 +2625,12 @@ function createStorage() {
|
|
|
2106
2625
|
}
|
|
2107
2626
|
}
|
|
2108
2627
|
function removeStorageByPrefix(prefix) {
|
|
2109
|
-
if (!
|
|
2628
|
+
if (!storage2) return 0;
|
|
2110
2629
|
let removed = 0;
|
|
2111
2630
|
try {
|
|
2112
|
-
for (const key of
|
|
2631
|
+
for (const key of storage2.keys()) {
|
|
2113
2632
|
if (key.startsWith(prefix)) {
|
|
2114
|
-
|
|
2633
|
+
storage2.remove(key);
|
|
2115
2634
|
removed++;
|
|
2116
2635
|
}
|
|
2117
2636
|
}
|
|
@@ -2120,6 +2639,20 @@ function removeStorageByPrefix(prefix) {
|
|
|
2120
2639
|
}
|
|
2121
2640
|
return removed;
|
|
2122
2641
|
}
|
|
2642
|
+
function useLang() {
|
|
2643
|
+
const [lang, set] = React.useState(getLang());
|
|
2644
|
+
React.useEffect(() => subscribeLang(set), []);
|
|
2645
|
+
return lang;
|
|
2646
|
+
}
|
|
2647
|
+
function useSessionSnapshot() {
|
|
2648
|
+
return React.useSyncExternalStore(subscribeSession, readSession, readSession);
|
|
2649
|
+
}
|
|
2650
|
+
function withLang(Component) {
|
|
2651
|
+
return function LangScoped(props) {
|
|
2652
|
+
useLang();
|
|
2653
|
+
return React.createElement(Component, props);
|
|
2654
|
+
};
|
|
2655
|
+
}
|
|
2123
2656
|
function copyText(text) {
|
|
2124
2657
|
if (typeof navigator !== "undefined" && navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
|
|
2125
2658
|
return navigator.clipboard.writeText(text).then(
|
|
@@ -2157,10 +2690,23 @@ function severityColor(severity) {
|
|
|
2157
2690
|
function severityLabel(severity) {
|
|
2158
2691
|
return SEVERITY_LABEL[coerceSeverity(severity)];
|
|
2159
2692
|
}
|
|
2693
|
+
function formatConfigValue(value) {
|
|
2694
|
+
if (typeof value === "string") return value;
|
|
2695
|
+
if (value === void 0) return "\u2014";
|
|
2696
|
+
if (value === null) return "null";
|
|
2697
|
+
try {
|
|
2698
|
+
return JSON.stringify(value) ?? String(value);
|
|
2699
|
+
} catch {
|
|
2700
|
+
return String(value);
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
function parenthesize(label) {
|
|
2704
|
+
return getLang() === "en" ? ` (${label})` : `\uFF08${label}\uFF09`;
|
|
2705
|
+
}
|
|
2160
2706
|
function runStatusText(live, manifest) {
|
|
2161
|
-
if (live) return "\u8FD0\u884C\u4E2D";
|
|
2707
|
+
if (live) return t("\u8FD0\u884C\u4E2D");
|
|
2162
2708
|
const reason = manifest && typeof manifest.stoppedReason === "string" ? manifest.stoppedReason : "";
|
|
2163
|
-
return reason ? stoppedReasonLabel(reason) : "\u5DF2\u7ED3\u675F";
|
|
2709
|
+
return reason ? stoppedReasonLabel(reason, getLang()) : t("\u5DF2\u7ED3\u675F");
|
|
2164
2710
|
}
|
|
2165
2711
|
function readSlots(ctx) {
|
|
2166
2712
|
if (ctx && ctx.slots && typeof ctx.slots.inject === "function" && typeof ctx.slots.register === "function") {
|
|
@@ -2184,7 +2730,7 @@ function readTheme(ctx) {
|
|
|
2184
2730
|
}
|
|
2185
2731
|
var slotsSvc = void 0;
|
|
2186
2732
|
var themeSvc = void 0;
|
|
2187
|
-
var
|
|
2733
|
+
var storage2 = null;
|
|
2188
2734
|
var themeDisposer = null;
|
|
2189
2735
|
var themeListener = null;
|
|
2190
2736
|
var themeEnabled = true;
|
|
@@ -2235,7 +2781,7 @@ function registerThemeListener(ctx) {
|
|
|
2235
2781
|
}
|
|
2236
2782
|
function setThemeEnabled(enabled) {
|
|
2237
2783
|
themeEnabled = enabled;
|
|
2238
|
-
if (
|
|
2784
|
+
if (storage2) storage2.set(THEME_STORAGE_KEY, enabled ? "1" : "0");
|
|
2239
2785
|
if (enabled) applyThemeSkin();
|
|
2240
2786
|
else clearThemeSkin();
|
|
2241
2787
|
}
|
|
@@ -2252,7 +2798,7 @@ function TrendChart({ points }) {
|
|
|
2252
2798
|
key: p.round,
|
|
2253
2799
|
className: "iterate-trend-bar",
|
|
2254
2800
|
"data-hot": p.count > 0 ? "" : void 0,
|
|
2255
|
-
title: `Round
|
|
2801
|
+
title: tf(`Round {a0}\uFF1A{a1} \u9879`, { a0: p.round, a1: p.count }),
|
|
2256
2802
|
style: { height: `${Math.max(4, Math.round(p.count / max * 24))}px` }
|
|
2257
2803
|
})
|
|
2258
2804
|
);
|
|
@@ -2260,7 +2806,7 @@ function TrendChart({ points }) {
|
|
|
2260
2806
|
return React.createElement("div", {
|
|
2261
2807
|
className: "iterate-trend",
|
|
2262
2808
|
role: "img",
|
|
2263
|
-
"aria-label": `\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\u8D8B\u52BF\uFF1A
|
|
2809
|
+
"aria-label": tf(`\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\u8D8B\u52BF\uFF1A{a0}`, { a0: summary })
|
|
2264
2810
|
}, ...bars);
|
|
2265
2811
|
}
|
|
2266
2812
|
function StartIterationButton() {
|
|
@@ -2269,7 +2815,7 @@ function StartIterationButton() {
|
|
|
2269
2815
|
const timers = React.useRef([]);
|
|
2270
2816
|
React.useEffect(
|
|
2271
2817
|
() => () => {
|
|
2272
|
-
for (const
|
|
2818
|
+
for (const t2 of timers.current) clearTimeout(t2);
|
|
2273
2819
|
timers.current = [];
|
|
2274
2820
|
},
|
|
2275
2821
|
[]
|
|
@@ -2279,8 +2825,8 @@ function StartIterationButton() {
|
|
|
2279
2825
|
if (ok) {
|
|
2280
2826
|
setFailed(null);
|
|
2281
2827
|
setCopied(key);
|
|
2282
|
-
const
|
|
2283
|
-
timers.current.push(
|
|
2828
|
+
const t2 = window.setTimeout(() => setCopied((cur) => cur === key ? null : cur), 1600);
|
|
2829
|
+
timers.current.push(t2);
|
|
2284
2830
|
} else {
|
|
2285
2831
|
setCopied(null);
|
|
2286
2832
|
setFailed(key);
|
|
@@ -2295,21 +2841,32 @@ function StartIterationButton() {
|
|
|
2295
2841
|
"data-failed": failed === key ? "" : void 0,
|
|
2296
2842
|
onClick: () => copy(key, command),
|
|
2297
2843
|
title
|
|
2298
|
-
}, copied === key ? "\u5DF2\u590D\u5236" : failed === key ? "\u590D\u5236\u5931\u8D25" : label);
|
|
2844
|
+
}, copied === key ? t("\u5DF2\u590D\u5236") : failed === key ? t("\u590D\u5236\u5931\u8D25") : label);
|
|
2299
2845
|
return React.createElement(
|
|
2300
2846
|
"span",
|
|
2301
2847
|
{ className: "iterate-dashboard-launch" },
|
|
2302
|
-
btn("start-full", "\u5B8C\u6574\u8FED\u4EE3", START_INSTRUCTIONS.full, true, "\u590D\u5236\u542F\u52A8\u6307\u4EE4\uFF1A\u5B8C\u6574\u300C\u5BA1\u67E5 \u2192 \u4FEE\u590D \u2192 \u9A8C\u8BC1 \u2192 \u590D\u76D8\u300D\u95ED\u73AF\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u5373\u53EF\uFF09"),
|
|
2303
|
-
btn("start-review", "\u4EC5\u8BC4\u5BA1", START_INSTRUCTIONS.reviewOnly, false, "\u590D\u5236\u542F\u52A8\u6307\u4EE4\uFF1A\u53EA\u5BA1\u67E5\u4E0D\u4FEE\u6539\uFF08dry-run\uFF09\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u5373\u53EF\uFF09")
|
|
2848
|
+
btn("start-full", t("\u5B8C\u6574\u8FED\u4EE3"), START_INSTRUCTIONS.full, true, t("\u590D\u5236\u542F\u52A8\u6307\u4EE4\uFF1A\u5B8C\u6574\u300C\u5BA1\u67E5 \u2192 \u4FEE\u590D \u2192 \u9A8C\u8BC1 \u2192 \u590D\u76D8\u300D\u95ED\u73AF\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u5373\u53EF\uFF09")),
|
|
2849
|
+
btn("start-review", t("\u4EC5\u8BC4\u5BA1"), START_INSTRUCTIONS.reviewOnly, false, t("\u590D\u5236\u542F\u52A8\u6307\u4EE4\uFF1A\u53EA\u5BA1\u67E5\u4E0D\u4FEE\u6539\uFF08dry-run\uFF09\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u5373\u53EF\uFF09"))
|
|
2304
2850
|
);
|
|
2305
2851
|
}
|
|
2306
2852
|
function ConvergenceDashboard(props) {
|
|
2307
2853
|
const [pulseKey, setPulseKey] = React.useState(0);
|
|
2308
2854
|
const session = props && props.session ? props.session : null;
|
|
2855
|
+
React.useEffect(() => {
|
|
2856
|
+
noteSession(session);
|
|
2857
|
+
}, [session]);
|
|
2309
2858
|
const report = latestReport(session);
|
|
2859
|
+
const pulseSeenRef = React.useRef(null);
|
|
2310
2860
|
React.useEffect(() => {
|
|
2311
2861
|
if (!report) return;
|
|
2312
2862
|
const cur = getCurrentRound(report);
|
|
2863
|
+
const key = hashReport(report) + ":" + String(cur);
|
|
2864
|
+
if (pulseSeenRef.current === null) {
|
|
2865
|
+
pulseSeenRef.current = key;
|
|
2866
|
+
return;
|
|
2867
|
+
}
|
|
2868
|
+
if (pulseSeenRef.current === key) return;
|
|
2869
|
+
pulseSeenRef.current = key;
|
|
2313
2870
|
const conv = report.convergence;
|
|
2314
2871
|
emitRoundPulse(cur, conv?.converged === true);
|
|
2315
2872
|
setPulseKey((k) => k + 1);
|
|
@@ -2320,11 +2877,11 @@ function ConvergenceDashboard(props) {
|
|
|
2320
2877
|
return React.createElement(
|
|
2321
2878
|
"div",
|
|
2322
2879
|
{ "data-iterate-root": "", "data-iterate": "dashboard", "data-run-state": "running", className: "iterate-dashboard iterate-dashboard-empty" },
|
|
2323
|
-
React.createElement("span", { className: "iterate-round-badge", "data-live": "" }, "\u8FD0\u884C\u4E2D"),
|
|
2880
|
+
React.createElement("span", { className: "iterate-round-badge", "data-live": "" }, t("\u8FD0\u884C\u4E2D")),
|
|
2324
2881
|
React.createElement(
|
|
2325
2882
|
"span",
|
|
2326
2883
|
{ className: "iterate-empty-hint" },
|
|
2327
|
-
`\u7B2C
|
|
2884
|
+
tf(`\u7B2C {a0} \u8F6E\u8FDB\u884C\u4E2D{a1}\u2014\u2014\u9996\u8F6E\u6C47\u603B\u62A5\u544A\u843D\u5730\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u6536\u655B\u8FDB\u5EA6\u4E0E\u53D1\u73B0\u7EDF\u8BA1\u3002`, { a0: String(runState.round), a1: runState.phase ? tf(` \xB7 \u9636\u6BB5\u300C{a0}\u300D`, { a0: runState.phase }) : "" })
|
|
2328
2885
|
)
|
|
2329
2886
|
// No start button while a run is active.
|
|
2330
2887
|
);
|
|
@@ -2333,11 +2890,11 @@ function ConvergenceDashboard(props) {
|
|
|
2333
2890
|
return React.createElement(
|
|
2334
2891
|
"div",
|
|
2335
2892
|
{ "data-iterate-root": "", "data-iterate": "dashboard", "data-run-state": "done", className: "iterate-dashboard iterate-dashboard-empty" },
|
|
2336
|
-
React.createElement("span", { className: "iterate-round-badge" }, "\u5DF2\u7ED3\u675F"),
|
|
2893
|
+
React.createElement("span", { className: "iterate-round-badge" }, t("\u5DF2\u7ED3\u675F")),
|
|
2337
2894
|
React.createElement(
|
|
2338
2895
|
"span",
|
|
2339
2896
|
{ className: "iterate-empty-hint" },
|
|
2340
|
-
`\u4E0A\u6B21\u8FED\u4EE3\u5728\u7B2C
|
|
2897
|
+
tf(`\u4E0A\u6B21\u8FED\u4EE3\u5728\u7B2C {a0} \u8F6E\u540E\u505C\u6B62{a1}\uFF0C\u4F46\u6CA1\u6709\u53EF\u5C55\u793A\u7684\u6C47\u603B\u62A5\u544A\u3002`, { a0: String(runState.round), a1: runState.stoppedReason ? parenthesize(stoppedReasonLabel(runState.stoppedReason, getLang())) : "" })
|
|
2341
2898
|
),
|
|
2342
2899
|
React.createElement(StartIterationButton, null)
|
|
2343
2900
|
);
|
|
@@ -2346,7 +2903,7 @@ function ConvergenceDashboard(props) {
|
|
|
2346
2903
|
"div",
|
|
2347
2904
|
{ "data-iterate-root": "", "data-iterate": "dashboard", "data-run-state": "empty", className: "iterate-dashboard iterate-dashboard-empty" },
|
|
2348
2905
|
React.createElement("span", { className: "iterate-round-badge" }, "iterate"),
|
|
2349
|
-
React.createElement("span", { className: "iterate-empty-hint" }, "\u8FD0\u884C\u4E00\u6B21\u8BC4\u5BA1\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u6536\u655B\u8FDB\u5EA6\u4E0E\u53D1\u73B0\u7EDF\u8BA1\u3002"),
|
|
2906
|
+
React.createElement("span", { className: "iterate-empty-hint" }, t("\u8FD0\u884C\u4E00\u6B21\u8BC4\u5BA1\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u6536\u655B\u8FDB\u5EA6\u4E0E\u53D1\u73B0\u7EDF\u8BA1\u3002")),
|
|
2350
2907
|
React.createElement(StartIterationButton, null)
|
|
2351
2908
|
);
|
|
2352
2909
|
}
|
|
@@ -2361,20 +2918,20 @@ function ConvergenceDashboard(props) {
|
|
|
2361
2918
|
const resumeChip = resumeCount > 0 ? React.createElement("span", {
|
|
2362
2919
|
className: "iterate-chip-resume",
|
|
2363
2920
|
key: "resume",
|
|
2364
|
-
title: "\u672C\u6B21\u8FED\u4EE3\u4ECE\u4E0A\u4E00\u6B21\u4E2D\u65AD\u7684\u65AD\u70B9\u7EE7\u7EED\u6267\u884C"
|
|
2365
|
-
}, `\u5DF2\u4E2D\u65AD\u6062\u590D \xD7
|
|
2921
|
+
title: t("\u672C\u6B21\u8FED\u4EE3\u4ECE\u4E0A\u4E00\u6B21\u4E2D\u65AD\u7684\u65AD\u70B9\u7EE7\u7EED\u6267\u884C")
|
|
2922
|
+
}, tf(`\u5DF2\u4E2D\u65AD\u6062\u590D \xD7{a0}`, { a0: String(resumeCount) })) : null;
|
|
2366
2923
|
const imageChip = imageCount > 0 ? React.createElement("span", {
|
|
2367
2924
|
className: "iterate-chip-images",
|
|
2368
2925
|
key: "images",
|
|
2369
|
-
title: "\u4F1A\u8BDD\u4E2D\u68C0\u6D4B\u5230\u7528\u6237\u9644\u5E26\u7684\u56FE\u7247\uFF0C\u8BC4\u5BA1\u5C06\u4F5C\u4E3A\u89C6\u89C9\u8BC1\u636E\u53C2\u8003"
|
|
2370
|
-
}, `\u9644\u4EF6\u56FE\u7247
|
|
2926
|
+
title: t("\u4F1A\u8BDD\u4E2D\u68C0\u6D4B\u5230\u7528\u6237\u9644\u5E26\u7684\u56FE\u7247\uFF0C\u8BC4\u5BA1\u5C06\u4F5C\u4E3A\u89C6\u89C9\u8BC1\u636E\u53C2\u8003")
|
|
2927
|
+
}, tf(`\u9644\u4EF6\u56FE\u7247 {a0}`, { a0: String(imageCount) })) : null;
|
|
2371
2928
|
const transcript = latestTranscript(session);
|
|
2372
2929
|
const phase = transcript ? latestPhase(transcript.phases) : "";
|
|
2373
2930
|
const liveChip = transcript && phase ? React.createElement("span", {
|
|
2374
2931
|
className: "iterate-chip-phase",
|
|
2375
2932
|
key: "phase",
|
|
2376
2933
|
"data-live": transcript.active === true ? "" : void 0,
|
|
2377
|
-
title: transcript.active === true ? `\u5F53\u524D\u5904\u4E8E\u300C
|
|
2934
|
+
title: transcript.active === true ? tf(`\u5F53\u524D\u5904\u4E8E\u300C{a0}\u300D\u9636\u6BB5\uFF08\u8FD0\u884C\u4E2D\uFF09`, { a0: phase }) : tf(`\u6700\u8FD1\u4E00\u6B21\u6267\u884C\u505C\u7559\u5728\u300C{a0}\u300D\u9636\u6BB5\uFF08\u5DF2\u7ED3\u675F\uFF09`, { a0: phase })
|
|
2378
2935
|
}, `${phase} \xB7 ${runStatusText(transcript.active === true, transcript)}`) : null;
|
|
2379
2936
|
const taskModeTranscript = latestTranscript(session);
|
|
2380
2937
|
const taskMode = taskModeTranscript?.taskMode;
|
|
@@ -2382,7 +2939,7 @@ function ConvergenceDashboard(props) {
|
|
|
2382
2939
|
className: "iterate-chip-taskmode",
|
|
2383
2940
|
key: "taskmode",
|
|
2384
2941
|
"data-mode": taskMode,
|
|
2385
|
-
title: taskMode === "code" ? "\u4EE3\u7801\u6A21\u5F0F\uFF1A\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6" : "\u8FED\u4EE3\u6A21\u5F0F\uFF1A\u5BA1\u67E5-\u4FEE\u590D\u5FAA\u73AF"
|
|
2942
|
+
title: taskMode === "code" ? t("\u4EE3\u7801\u6A21\u5F0F\uFF1A\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6") : t("\u8FED\u4EE3\u6A21\u5F0F\uFF1A\u5BA1\u67E5-\u4FEE\u590D\u5FAA\u73AF")
|
|
2386
2943
|
}, taskMode === "code" ? "\u25CF code" : "\u25CF iterate") : null;
|
|
2387
2944
|
const dimNames = Object.keys(dims);
|
|
2388
2945
|
const dimBadges = dimNames.slice(0, 6).map(
|
|
@@ -2398,7 +2955,7 @@ function ConvergenceDashboard(props) {
|
|
|
2398
2955
|
React.createElement(
|
|
2399
2956
|
"span",
|
|
2400
2957
|
{ key: "+more", className: "iterate-dim-badge iterate-dim-more", title: dimNames.slice(6).join(", ") },
|
|
2401
|
-
|
|
2958
|
+
tf(`+{a0} \u66F4\u591A`, { a0: overflow })
|
|
2402
2959
|
)
|
|
2403
2960
|
);
|
|
2404
2961
|
}
|
|
@@ -2409,14 +2966,14 @@ function ConvergenceDashboard(props) {
|
|
|
2409
2966
|
const fixBadge = fixCount !== null ? React.createElement("span", {
|
|
2410
2967
|
className: "iterate-metric",
|
|
2411
2968
|
key: "fixes",
|
|
2412
|
-
title: "\u672C\u8F6E\u5DF2\u5E94\u7528\u7684\u539F\u5B50\u4FEE\u590D\u6570\uFF08\u6B63\u5E38\u6A21\u5F0F\uFF09"
|
|
2969
|
+
title: t("\u672C\u8F6E\u5DF2\u5E94\u7528\u7684\u539F\u5B50\u4FEE\u590D\u6570\uFF08\u6B63\u5E38\u6A21\u5F0F\uFF09")
|
|
2413
2970
|
}, `${String(fixCount)} fixes`) : null;
|
|
2414
2971
|
const converged = report.convergence && report.convergence.converged === true;
|
|
2415
2972
|
const convChip = converged ? React.createElement("span", {
|
|
2416
2973
|
className: "iterate-chip-resume",
|
|
2417
2974
|
key: "converged",
|
|
2418
|
-
title: "\u5BA1\u67E5\u5DF2\u6536\u655B\uFF1A\u6700\u540E\u4E00\u8F6E\u672A\u53D1\u73B0\u65B0\u95EE\u9898"
|
|
2419
|
-
}, "\u2713 \u5DF2\u6536\u655B") : null;
|
|
2975
|
+
title: t("\u5BA1\u67E5\u5DF2\u6536\u655B\uFF1A\u6700\u540E\u4E00\u8F6E\u672A\u53D1\u73B0\u65B0\u95EE\u9898")
|
|
2976
|
+
}, t("\u2713 \u5DF2\u6536\u655B")) : null;
|
|
2420
2977
|
const sevMetric = (key, label) => React.createElement(
|
|
2421
2978
|
"span",
|
|
2422
2979
|
{ className: "iterate-metric", key, title: label },
|
|
@@ -2485,19 +3042,19 @@ function StatsCard(props) {
|
|
|
2485
3042
|
React.createElement("td", { style: { color: SEVERITY_COLOR.low } }, String(h.low))
|
|
2486
3043
|
)
|
|
2487
3044
|
);
|
|
2488
|
-
const trendLine = `\u9996\u8F6E
|
|
2489
|
-
const completion = buildCompletionSummary(report);
|
|
3045
|
+
const trendLine = tf(`\u9996\u8F6E {a0} \u2192 \u672B\u8F6E {a1} \u9879\uFF0C\u964D\u5E45 {a2}%{a3}`, { a0: trend.firstRound, a1: trend.lastRound, a2: trend.reductionPercent, a3: trend.converged ? t("\uFF0C\u5DF2\u6536\u655B") : "" });
|
|
3046
|
+
const completion = buildCompletionSummary(report, getLang());
|
|
2490
3047
|
return React.createElement(
|
|
2491
3048
|
"div",
|
|
2492
3049
|
{ "data-iterate-root": "", "data-iterate": "stats", className: "iterate-stats" },
|
|
2493
3050
|
React.createElement(
|
|
2494
3051
|
"div",
|
|
2495
3052
|
{ className: "iterate-triage-head" },
|
|
2496
|
-
React.createElement("span", {}, "Iterate \xB7 \u6536\u655B\u7EDF\u8BA1"),
|
|
3053
|
+
React.createElement("span", {}, t("Iterate \xB7 \u6536\u655B\u7EDF\u8BA1")),
|
|
2497
3054
|
React.createElement(
|
|
2498
3055
|
"span",
|
|
2499
3056
|
{ className: "iterate-triage-hint" },
|
|
2500
|
-
`Round ${getCurrentRound(report)}/${getTotalRounds(report)} \xB7 ${total} findings \xB7 ${report.convergence && report.convergence.converged ? "\u5DF2\u6536\u655B" : "\u672A\u6536\u655B"}`
|
|
3057
|
+
`Round ${getCurrentRound(report)}/${getTotalRounds(report)} \xB7 ${total} findings \xB7 ${report.convergence && report.convergence.converged ? t("\u5DF2\u6536\u655B") : t("\u672A\u6536\u655B")}`
|
|
2501
3058
|
)
|
|
2502
3059
|
),
|
|
2503
3060
|
React.createElement("div", { className: "iterate-stats-grid" }, ...rows),
|
|
@@ -2510,7 +3067,7 @@ function StatsCard(props) {
|
|
|
2510
3067
|
className: "iterate-btn",
|
|
2511
3068
|
"data-primary": showHistory ? "" : void 0,
|
|
2512
3069
|
onClick: () => setShowHistory((v) => !v)
|
|
2513
|
-
}, showHistory ? "\u6536\u8D77\u5386\u53F2" : "\u5386\u53F2 / \u8D8B\u52BF")
|
|
3070
|
+
}, showHistory ? t("\u6536\u8D77\u5386\u53F2") : t("\u5386\u53F2 / \u8D8B\u52BF"))
|
|
2514
3071
|
),
|
|
2515
3072
|
showHistory ? React.createElement(
|
|
2516
3073
|
"div",
|
|
@@ -2524,8 +3081,8 @@ function StatsCard(props) {
|
|
|
2524
3081
|
React.createElement(
|
|
2525
3082
|
"tr",
|
|
2526
3083
|
{},
|
|
2527
|
-
React.createElement("th", {}, "\u8F6E\u6B21"),
|
|
2528
|
-
React.createElement("th", {}, "\u53D1\u73B0"),
|
|
3084
|
+
React.createElement("th", {}, t("\u8F6E\u6B21")),
|
|
3085
|
+
React.createElement("th", {}, t("\u53D1\u73B0")),
|
|
2529
3086
|
React.createElement("th", { style: { color: SEVERITY_COLOR.critical } }, "CRIT"),
|
|
2530
3087
|
React.createElement("th", { style: { color: SEVERITY_COLOR.high } }, "HIGH"),
|
|
2531
3088
|
React.createElement("th", { style: { color: SEVERITY_COLOR.medium } }, "MED"),
|
|
@@ -2548,7 +3105,7 @@ function TriagePanel(props) {
|
|
|
2548
3105
|
const storageKey = TRIAGE_STORAGE_PREFIX + hashReport(report);
|
|
2549
3106
|
const [verdicts, setVerdicts] = React.useState(() => {
|
|
2550
3107
|
const initial = buildTriageState(report);
|
|
2551
|
-
const saved =
|
|
3108
|
+
const saved = storage2 ? storage2.get(storageKey) : null;
|
|
2552
3109
|
if (saved) {
|
|
2553
3110
|
try {
|
|
2554
3111
|
const parsed = JSON.parse(saved);
|
|
@@ -2573,7 +3130,7 @@ function TriagePanel(props) {
|
|
|
2573
3130
|
const [cmdFailed, setCmdFailed] = React.useState(null);
|
|
2574
3131
|
const cmdTimers = React.useRef([]);
|
|
2575
3132
|
React.useEffect(() => () => {
|
|
2576
|
-
for (const
|
|
3133
|
+
for (const t2 of cmdTimers.current) clearTimeout(t2);
|
|
2577
3134
|
cmdTimers.current = [];
|
|
2578
3135
|
}, []);
|
|
2579
3136
|
const copyWithFeedback = (text, key) => {
|
|
@@ -2590,7 +3147,7 @@ function TriagePanel(props) {
|
|
|
2590
3147
|
});
|
|
2591
3148
|
};
|
|
2592
3149
|
React.useEffect(() => {
|
|
2593
|
-
if (
|
|
3150
|
+
if (storage2) storage2.set(storageKey, JSON.stringify(verdicts));
|
|
2594
3151
|
}, [storageKey, verdicts]);
|
|
2595
3152
|
const setVerdict = (index, verdict) => {
|
|
2596
3153
|
setVerdicts((prev) => ({ ...prev, [String(index)]: verdict }));
|
|
@@ -2612,15 +3169,16 @@ function TriagePanel(props) {
|
|
|
2612
3169
|
setVerdicts((prev) => setAllVerdicts(prev, "keep"));
|
|
2613
3170
|
setSelectAll(false);
|
|
2614
3171
|
};
|
|
3172
|
+
const rootRef = React.useRef(null);
|
|
2615
3173
|
React.useEffect(() => {
|
|
2616
3174
|
const doc = typeof document !== "undefined" ? document : null;
|
|
2617
3175
|
if (!doc) return;
|
|
2618
3176
|
const onKeyDown = (ev) => {
|
|
2619
3177
|
if (ev.metaKey || ev.ctrlKey || ev.altKey) return;
|
|
2620
|
-
const
|
|
2621
|
-
if (
|
|
2622
|
-
if (
|
|
2623
|
-
const rootEl =
|
|
3178
|
+
const t2 = ev.target;
|
|
3179
|
+
if (t2 && (t2.tagName === "INPUT" || t2.tagName === "TEXTAREA" || t2.tagName === "SELECT")) return;
|
|
3180
|
+
if (t2 && typeof t2.isContentEditable === "boolean" && t2.isContentEditable) return;
|
|
3181
|
+
const rootEl = rootRef.current;
|
|
2624
3182
|
if (!rootEl) return;
|
|
2625
3183
|
const activeEl = doc.activeElement;
|
|
2626
3184
|
if (!activeEl) return;
|
|
@@ -2658,11 +3216,11 @@ function TriagePanel(props) {
|
|
|
2658
3216
|
copyWithFeedback(cmd, "approve-arch");
|
|
2659
3217
|
};
|
|
2660
3218
|
const doTriggerNewRound = () => {
|
|
2661
|
-
const cmd = "\u8BF7\u7EE7\u7EED\u6267\u884C\u4E0B\u4E00\u8F6E\u8FED\u4EE3\u5BA1\u67E5";
|
|
3219
|
+
const cmd = t("\u8BF7\u7EE7\u7EED\u6267\u884C\u4E0B\u4E00\u8F6E\u8FED\u4EE3\u5BA1\u67E5");
|
|
2662
3220
|
copyWithFeedback(cmd, "new-round");
|
|
2663
3221
|
};
|
|
2664
3222
|
const doRollbackToCheckpoint = () => {
|
|
2665
|
-
const cmd = "\u8BF7\u56DE\u6EDA\u4E0A\u4E00\u8F6E\u8FED\u4EE3\u7684\u4FEE\u590D\uFF1A\u8C03\u7528 `iterate_history` \u67E5\u770B\u672C\u8F6E fix id\uFF0C\u518D\u7528 `iterate_rollback` \u9010\u4E2A\u64A4\u9500\u8FD9\u4E9B\u4FEE\u590D";
|
|
3223
|
+
const cmd = t("\u8BF7\u56DE\u6EDA\u4E0A\u4E00\u8F6E\u8FED\u4EE3\u7684\u4FEE\u590D\uFF1A\u8C03\u7528 `iterate_history` \u67E5\u770B\u672C\u8F6E fix id\uFF0C\u518D\u7528 `iterate_rollback` \u9010\u4E2A\u64A4\u9500\u8FD9\u4E9B\u4FEE\u590D");
|
|
2666
3224
|
copyWithFeedback(cmd, "rollback");
|
|
2667
3225
|
};
|
|
2668
3226
|
const doAssignFindings = () => {
|
|
@@ -2750,36 +3308,36 @@ function TriagePanel(props) {
|
|
|
2750
3308
|
React.createElement(
|
|
2751
3309
|
"div",
|
|
2752
3310
|
{ className: "iterate-finding-actions" },
|
|
2753
|
-
btn("y \u4FEE\u590D", "keep", "\u4FDD\u7559\u8BE5 finding\uFF0C\u8FDB\u5165\u4FEE\u590D"),
|
|
2754
|
-
btn("n \u8DF3\u8FC7", "skip", "\u8DF3\u8FC7\u8BE5 finding"),
|
|
2755
|
-
btn("a \u5DF2\u77E5\u6709\u610F", "ignore", "\u6807\u8BB0\u4E3A\u5DF2\u77E5\u6709\u610F\u5E76\u5199\u5165 known_intentional")
|
|
3311
|
+
btn(t("y \u4FEE\u590D"), "keep", t("\u4FDD\u7559\u8BE5 finding\uFF0C\u8FDB\u5165\u4FEE\u590D")),
|
|
3312
|
+
btn(t("n \u8DF3\u8FC7"), "skip", t("\u8DF3\u8FC7\u8BE5 finding")),
|
|
3313
|
+
btn(t("a \u5DF2\u77E5\u6709\u610F"), "ignore", t("\u6807\u8BB0\u4E3A\u5DF2\u77E5\u6709\u610F\u5E76\u5199\u5165 known_intentional"))
|
|
2756
3314
|
)
|
|
2757
3315
|
);
|
|
2758
3316
|
});
|
|
2759
3317
|
return React.createElement(
|
|
2760
3318
|
"div",
|
|
2761
|
-
{ "data-iterate-root": "", "data-iterate": "triage", className: "iterate-triage" },
|
|
3319
|
+
{ ref: rootRef, "data-iterate-root": "", "data-iterate": "triage", className: "iterate-triage" },
|
|
2762
3320
|
React.createElement(
|
|
2763
3321
|
"div",
|
|
2764
3322
|
{ className: "iterate-triage-head" },
|
|
2765
|
-
React.createElement("span", { role: "heading", "aria-level": 3 }, `Iterate \xB7 Findings \u5206\u8BCA (
|
|
2766
|
-
React.createElement("span", { className: "iterate-triage-hint" }, "y=\u4FEE\u590D \xB7 n=\u8DF3\u8FC7 \xB7 a=\u5DF2\u77E5\u6709\u610F \xB7 \u2191/\u2193 \u9009\u62E9")
|
|
3323
|
+
React.createElement("span", { role: "heading", "aria-level": 3 }, tf(`Iterate \xB7 Findings \u5206\u8BCA ({a0}/{a1})`, { a0: filtered.length, a1: findings.length })),
|
|
3324
|
+
React.createElement("span", { className: "iterate-triage-hint" }, t("y=\u4FEE\u590D \xB7 n=\u8DF3\u8FC7 \xB7 a=\u5DF2\u77E5\u6709\u610F \xB7 \u2191/\u2193 \u9009\u62E9"))
|
|
2767
3325
|
),
|
|
2768
3326
|
React.createElement(
|
|
2769
3327
|
"div",
|
|
2770
3328
|
{ className: "iterate-filter" },
|
|
2771
3329
|
React.createElement(
|
|
2772
3330
|
"select",
|
|
2773
|
-
{ className: "iterate-filter-select", value: filter.severities[0] || "", onChange: (e) => setSeverityFilter(e.target.value), "aria-label": "\u6309\u4E25\u91CD\u5EA6\u7B5B\u9009" },
|
|
2774
|
-
React.createElement("option", { value: "" }, "\u5168\u90E8\u4E25\u91CD\u5EA6"),
|
|
3331
|
+
{ className: "iterate-filter-select", value: filter.severities[0] || "", onChange: (e) => setSeverityFilter(e.target.value), "aria-label": t("\u6309\u4E25\u91CD\u5EA6\u7B5B\u9009") },
|
|
3332
|
+
React.createElement("option", { value: "" }, t("\u5168\u90E8\u4E25\u91CD\u5EA6")),
|
|
2775
3333
|
...options.severities.map(
|
|
2776
3334
|
(s) => React.createElement("option", { key: s.value, value: s.value }, `${severityLabel(s.value)} (${s.count})`)
|
|
2777
3335
|
)
|
|
2778
3336
|
),
|
|
2779
3337
|
React.createElement(
|
|
2780
3338
|
"select",
|
|
2781
|
-
{ className: "iterate-filter-select", value: filter.dimensions[0] || "", onChange: (e) => setDimensionFilter(e.target.value), "aria-label": "\u6309\u7EF4\u5EA6\u7B5B\u9009" },
|
|
2782
|
-
React.createElement("option", { value: "" }, "\u5168\u90E8\u7EF4\u5EA6"),
|
|
3339
|
+
{ className: "iterate-filter-select", value: filter.dimensions[0] || "", onChange: (e) => setDimensionFilter(e.target.value), "aria-label": t("\u6309\u7EF4\u5EA6\u7B5B\u9009") },
|
|
3340
|
+
React.createElement("option", { value: "" }, t("\u5168\u90E8\u7EF4\u5EA6")),
|
|
2783
3341
|
...options.dimensions.map(
|
|
2784
3342
|
(d) => React.createElement("option", { key: d.value, value: d.value }, `${d.value} (${d.count})`)
|
|
2785
3343
|
)
|
|
@@ -2787,41 +3345,41 @@ function TriagePanel(props) {
|
|
|
2787
3345
|
React.createElement("input", {
|
|
2788
3346
|
className: "iterate-filter-search",
|
|
2789
3347
|
type: "search",
|
|
2790
|
-
placeholder: "\u641C\u7D22\u6587\u4EF6 / \u6458\u8981\u2026",
|
|
2791
|
-
"aria-label": "\u641C\u7D22\u6587\u4EF6\u6216\u6458\u8981",
|
|
3348
|
+
placeholder: t("\u641C\u7D22\u6587\u4EF6 / \u6458\u8981\u2026"),
|
|
3349
|
+
"aria-label": t("\u641C\u7D22\u6587\u4EF6\u6216\u6458\u8981"),
|
|
2792
3350
|
value: filter.search,
|
|
2793
3351
|
onChange: (e) => setSearchFilter(e.target.value)
|
|
2794
3352
|
}),
|
|
2795
3353
|
React.createElement(
|
|
2796
3354
|
"span",
|
|
2797
3355
|
{ className: "iterate-filter-count" },
|
|
2798
|
-
isFilterActive ? React.createElement("button", { className: "iterate-batch-btn", onClick: clearFilter }, `\u6E05\u9664\u7B5B\u9009\uFF08\u663E\u793A
|
|
3356
|
+
isFilterActive ? React.createElement("button", { className: "iterate-batch-btn", onClick: clearFilter }, tf(`\u6E05\u9664\u7B5B\u9009\uFF08\u663E\u793A {a0}\uFF09`, { a0: filtered.length })) : tf(`\u5171 {a0} \u9879`, { a0: findings.length })
|
|
2799
3357
|
)
|
|
2800
3358
|
),
|
|
2801
3359
|
React.createElement(
|
|
2802
3360
|
"div",
|
|
2803
3361
|
{ className: "iterate-batch" },
|
|
2804
|
-
React.createElement("span", { className: "iterate-batch-label" }, "\u6279\u91CF\u4F5C\u7528\u4E8E\uFF1A"),
|
|
3362
|
+
React.createElement("span", { className: "iterate-batch-label" }, t("\u6279\u91CF\u4F5C\u7528\u4E8E\uFF1A")),
|
|
2805
3363
|
React.createElement("button", {
|
|
2806
3364
|
className: selectAll ? "iterate-batch-btn iterate-batch-scope" : "iterate-batch-btn iterate-batch-scope iterate-batch-scope-on",
|
|
2807
3365
|
onClick: () => setSelectAll(false),
|
|
2808
|
-
title: "\u6279\u91CF\u6309\u94AE\u4EC5\u4F5C\u7528\u4E8E\u5F53\u524D\u7B5B\u9009\u53EF\u89C1\u7684 findings"
|
|
2809
|
-
}, `\u53EF\u89C1
|
|
3366
|
+
title: t("\u6279\u91CF\u6309\u94AE\u4EC5\u4F5C\u7528\u4E8E\u5F53\u524D\u7B5B\u9009\u53EF\u89C1\u7684 findings")
|
|
3367
|
+
}, tf(`\u53EF\u89C1 {a0}`, { a0: indices.length })),
|
|
2810
3368
|
React.createElement("button", {
|
|
2811
3369
|
className: selectAll ? "iterate-batch-btn iterate-batch-scope iterate-batch-scope-on" : "iterate-batch-btn iterate-batch-scope",
|
|
2812
3370
|
onClick: () => setSelectAll(true),
|
|
2813
|
-
title: "\u6279\u91CF\u6309\u94AE\u4F5C\u7528\u4E8E\u5168\u90E8 findings"
|
|
2814
|
-
}, `\u5168\u90E8
|
|
3371
|
+
title: t("\u6279\u91CF\u6309\u94AE\u4F5C\u7528\u4E8E\u5168\u90E8 findings")
|
|
3372
|
+
}, tf(`\u5168\u90E8 {a0}`, { a0: allIndices.length })),
|
|
2815
3373
|
React.createElement("button", { className: "iterate-batch-btn", onClick: () => applyBatch("keep") }, "y"),
|
|
2816
3374
|
React.createElement("button", { className: "iterate-batch-btn", onClick: () => applyBatch("skip") }, "n"),
|
|
2817
3375
|
React.createElement("button", { className: "iterate-batch-btn", onClick: () => applyBatch("ignore") }, "a"),
|
|
2818
|
-
React.createElement("button", { className: "iterate-batch-btn", onClick: doResetVerdicts, title: "\u628A\u6240\u6709\u5224\u5B9A\u6062\u590D\u4E3A\u9ED8\u8BA4 y\uFF08\u4FEE\u590D\uFF09" }, "\u91CD\u7F6E")
|
|
3376
|
+
React.createElement("button", { className: "iterate-batch-btn", onClick: doResetVerdicts, title: t("\u628A\u6240\u6709\u5224\u5B9A\u6062\u590D\u4E3A\u9ED8\u8BA4 y\uFF08\u4FEE\u590D\uFF09") }, t("\u91CD\u7F6E"))
|
|
2819
3377
|
),
|
|
2820
3378
|
...rows,
|
|
2821
3379
|
React.createElement(
|
|
2822
3380
|
"div",
|
|
2823
3381
|
{ className: "iterate-triage-foot" },
|
|
2824
|
-
React.createElement("span", {}, `y
|
|
3382
|
+
React.createElement("span", {}, tf(`y {a0} \xB7 n {a1} \xB7 a {a2} \xB7 \u5F85\u5199\u56DE known_intentional\uFF1A{a3} \u6761`, { a0: counts.keep, a1: counts.skip, a2: counts.ignore, a3: ignoredCount })),
|
|
2825
3383
|
React.createElement(
|
|
2826
3384
|
"span",
|
|
2827
3385
|
{ style: { display: "flex", gap: 6, flexWrap: "wrap" } },
|
|
@@ -2834,31 +3392,31 @@ function TriagePanel(props) {
|
|
|
2834
3392
|
"data-failed": cmdFailed === "apply" ? "" : void 0,
|
|
2835
3393
|
onClick: doBuildInstruction,
|
|
2836
3394
|
disabled: ignoredCount === 0,
|
|
2837
|
-
title: ignoredCount === 0 ? "\u5F53\u524D\u6CA1\u6709\u6807\u8BB0\u4E3A\u300C\u5DF2\u77E5\u6709\u610F\u300D\u7684 finding" : "\u590D\u5236\u5E94\u7528\u6307\u4EE4\uFF1A\u7C98\u8D34\u540E\u6A21\u578B\u4F1A\u8C03\u7528 iterate_triage apply\uFF08\u6821\u9A8C\u3001\u53BB\u91CD\u3001\u5199\u5165\u524D\u5907\u4EFD\u3001\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\uFF09"
|
|
2838
|
-
}, cmdCopied === "apply" ? "\u5DF2\u590D\u5236" : cmdFailed === "apply" ? "\u590D\u5236\u5931\u8D25" : `\u751F\u6210\u5E94\u7528\u6307\u4EE4
|
|
3395
|
+
title: ignoredCount === 0 ? t("\u5F53\u524D\u6CA1\u6709\u6807\u8BB0\u4E3A\u300C\u5DF2\u77E5\u6709\u610F\u300D\u7684 finding") : t("\u590D\u5236\u5E94\u7528\u6307\u4EE4\uFF1A\u7C98\u8D34\u540E\u6A21\u578B\u4F1A\u8C03\u7528 iterate_triage apply\uFF08\u6821\u9A8C\u3001\u53BB\u91CD\u3001\u5199\u5165\u524D\u5907\u4EFD\u3001\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\uFF09")
|
|
3396
|
+
}, cmdCopied === "apply" ? t("\u5DF2\u590D\u5236") : cmdFailed === "apply" ? t("\u590D\u5236\u5931\u8D25") : tf(`\u751F\u6210\u5E94\u7528\u6307\u4EE4{a0}`, { a0: ignoredCount > 0 ? `\uFF08${ignoredCount}\uFF09` : "" })),
|
|
2839
3397
|
React.createElement("button", {
|
|
2840
3398
|
className: "iterate-btn",
|
|
2841
3399
|
"data-copied": copied ? "" : void 0,
|
|
2842
3400
|
"data-failed": cmdFailed === "yaml" ? "" : void 0,
|
|
2843
3401
|
onClick: doCopyYaml,
|
|
2844
3402
|
disabled: ignoredCount === 0,
|
|
2845
|
-
title: ignoredCount === 0 ? "\u5F53\u524D\u6CA1\u6709\u6807\u8BB0\u4E3A\u300C\u5DF2\u77E5\u6709\u610F\u300D\u7684 finding" : "\u53EA\u590D\u5236 known_intentional YAML \u7247\u6BB5\uFF08\u9700\u81EA\u884C\u7C98\u5230 iterate.config.yaml \u7684 personalization.known_intentional \u4E0B\uFF09"
|
|
2846
|
-
}, copied ? "\u5DF2\u590D\u5236" : cmdFailed === "yaml" ? "\u590D\u5236\u5931\u8D25" : "\u4EC5\u590D\u5236 YAML \u7247\u6BB5"),
|
|
3403
|
+
title: ignoredCount === 0 ? t("\u5F53\u524D\u6CA1\u6709\u6807\u8BB0\u4E3A\u300C\u5DF2\u77E5\u6709\u610F\u300D\u7684 finding") : t("\u53EA\u590D\u5236 known_intentional YAML \u7247\u6BB5\uFF08\u9700\u81EA\u884C\u7C98\u5230 iterate.config.yaml \u7684 personalization.known_intentional \u4E0B\uFF09")
|
|
3404
|
+
}, copied ? t("\u5DF2\u590D\u5236") : cmdFailed === "yaml" ? t("\u590D\u5236\u5931\u8D25") : t("\u4EC5\u590D\u5236 YAML \u7247\u6BB5")),
|
|
2847
3405
|
React.createElement("button", {
|
|
2848
3406
|
className: "iterate-btn",
|
|
2849
3407
|
"data-copied": cmdCopied === "readback" ? "" : void 0,
|
|
2850
3408
|
"data-failed": cmdFailed === "readback" ? "" : void 0,
|
|
2851
3409
|
onClick: doReadback,
|
|
2852
|
-
title: "\u590D\u5236 iterate_triage list \u6307\u4EE4\uFF0C\u6838\u5BF9\u5DF2\u7ECF\u5199\u5165\u7684 known_intentional \u6761\u76EE\uFF08\u5199\u56DE\u95ED\u73AF\u786E\u8BA4\uFF09"
|
|
2853
|
-
}, cmdCopied === "readback" ? "\u5DF2\u590D\u5236" : cmdFailed === "readback" ? "\u590D\u5236\u5931\u8D25" : "\u56DE\u8BFB\u5DF2\u5199\u5165\u6761\u76EE")
|
|
3410
|
+
title: t("\u590D\u5236 iterate_triage list \u6307\u4EE4\uFF0C\u6838\u5BF9\u5DF2\u7ECF\u5199\u5165\u7684 known_intentional \u6761\u76EE\uFF08\u5199\u56DE\u95ED\u73AF\u786E\u8BA4\uFF09")
|
|
3411
|
+
}, cmdCopied === "readback" ? t("\u5DF2\u590D\u5236") : cmdFailed === "readback" ? t("\u590D\u5236\u5931\u8D25") : t("\u56DE\u8BFB\u5DF2\u5199\u5165\u6761\u76EE"))
|
|
2854
3412
|
),
|
|
2855
|
-
cmdCopied === "apply" ? React.createElement("span", { className: "iterate-triage-hint", role: "status" }, "\u5DF2\u590D\u5236\u5E94\u7528\u6307\u4EE4\u2014\u2014\u7C98\u8D34\u56DE\u4F1A\u8BDD\uFF0C\u6A21\u578B\u5C06\u8C03\u7528 iterate_triage apply \u5E76\u56DE\u62A5\u5199\u5165\u6761\u6570\u3002") : cmdCopied === "readback" ? React.createElement("span", { className: "iterate-triage-hint", role: "status" }, "\u5DF2\u590D\u5236\u56DE\u8BFB\u6307\u4EE4\u2014\u2014\u7C98\u8D34\u56DE\u4F1A\u8BDD\u53EF\u5217\u51FA\u5F53\u524D\u5DF2\u5199\u5165\u7684 known_intentional\u3002") : null
|
|
3413
|
+
cmdCopied === "apply" ? React.createElement("span", { className: "iterate-triage-hint", role: "status" }, t("\u5DF2\u590D\u5236\u5E94\u7528\u6307\u4EE4\u2014\u2014\u7C98\u8D34\u56DE\u4F1A\u8BDD\uFF0C\u6A21\u578B\u5C06\u8C03\u7528 iterate_triage apply \u5E76\u56DE\u62A5\u5199\u5165\u6761\u6570\u3002")) : cmdCopied === "readback" ? React.createElement("span", { className: "iterate-triage-hint", role: "status" }, t("\u5DF2\u590D\u5236\u56DE\u8BFB\u6307\u4EE4\u2014\u2014\u7C98\u8D34\u56DE\u4F1A\u8BDD\u53EF\u5217\u51FA\u5F53\u524D\u5DF2\u5199\u5165\u7684 known_intentional\u3002")) : null
|
|
2856
3414
|
),
|
|
2857
3415
|
// v3.0: Command buttons for native actions (§8)
|
|
2858
3416
|
React.createElement(
|
|
2859
3417
|
"div",
|
|
2860
3418
|
{ className: "iterate-triage-foot", style: { borderTop: "1px solid var(--dsw-alias-border-l1)" } },
|
|
2861
|
-
React.createElement("span", { style: { fontSize: 11, fontWeight: 600, color: "var(--dsw-alias-label-secondary)" } }, "\u6307\u6325\u64CD\u4F5C"),
|
|
3419
|
+
React.createElement("span", { style: { fontSize: 11, fontWeight: 600, color: "var(--dsw-alias-label-secondary)" } }, t("\u6307\u6325\u64CD\u4F5C")),
|
|
2862
3420
|
React.createElement(
|
|
2863
3421
|
"span",
|
|
2864
3422
|
{ style: { display: "flex", gap: 6, flexWrap: "wrap" } },
|
|
@@ -2868,15 +3426,15 @@ function TriagePanel(props) {
|
|
|
2868
3426
|
"data-copied": cmdCopied === "approve-arch" ? "" : void 0,
|
|
2869
3427
|
"data-failed": cmdFailed === "approve-arch" ? "" : void 0,
|
|
2870
3428
|
onClick: doApproveArchitecturalFix,
|
|
2871
|
-
title: "\u6279\u51C6\u67B6\u6784\u4FEE\u590D\uFF08\u590D\u5236\u6307\u4EE4\u6587\u672C\uFF1Aiterate_fix force:true\uFF0C\u542B\u5168\u90E8\u5FC5\u586B\u53C2\u6570\uFF09"
|
|
2872
|
-
}, cmdCopied === "approve-arch" ? "\u5DF2\u590D\u5236" : cmdFailed === "approve-arch" ? "\u590D\u5236\u5931\u8D25" : "\u6279\u51C6\u67B6\u6784\u4FEE\u590D"),
|
|
3429
|
+
title: t("\u6279\u51C6\u67B6\u6784\u4FEE\u590D\uFF08\u590D\u5236\u6307\u4EE4\u6587\u672C\uFF1Aiterate_fix force:true\uFF0C\u542B\u5168\u90E8\u5FC5\u586B\u53C2\u6570\uFF09")
|
|
3430
|
+
}, cmdCopied === "approve-arch" ? t("\u5DF2\u590D\u5236") : cmdFailed === "approve-arch" ? t("\u590D\u5236\u5931\u8D25") : t("\u6279\u51C6\u67B6\u6784\u4FEE\u590D")),
|
|
2873
3431
|
React.createElement("button", {
|
|
2874
3432
|
className: "iterate-cmd",
|
|
2875
3433
|
"data-copied": cmdCopied === "new-round" ? "" : void 0,
|
|
2876
3434
|
"data-failed": cmdFailed === "new-round" ? "" : void 0,
|
|
2877
3435
|
onClick: doTriggerNewRound,
|
|
2878
|
-
title: "\u89E6\u53D1\u65B0\u4E00\u8F6E\u8FED\u4EE3\u5BA1\u67E5"
|
|
2879
|
-
}, cmdCopied === "new-round" ? "\u5DF2\u590D\u5236" : cmdFailed === "new-round" ? "\u590D\u5236\u5931\u8D25" : "\u89E6\u53D1\u65B0\u4E00\u8F6E"),
|
|
3436
|
+
title: t("\u89E6\u53D1\u65B0\u4E00\u8F6E\u8FED\u4EE3\u5BA1\u67E5")
|
|
3437
|
+
}, cmdCopied === "new-round" ? t("\u5DF2\u590D\u5236") : cmdFailed === "new-round" ? t("\u590D\u5236\u5931\u8D25") : t("\u89E6\u53D1\u65B0\u4E00\u8F6E")),
|
|
2880
3438
|
React.createElement("button", {
|
|
2881
3439
|
className: "iterate-cmd",
|
|
2882
3440
|
"data-primary": "",
|
|
@@ -2884,16 +3442,16 @@ function TriagePanel(props) {
|
|
|
2884
3442
|
"data-failed": cmdFailed === "assign" ? "" : void 0,
|
|
2885
3443
|
onClick: doAssignFindings,
|
|
2886
3444
|
disabled: (selectAll ? allIndices : indices).length === 0,
|
|
2887
|
-
title: selectAll ? `\u6307\u6D3E\u5168\u90E8
|
|
2888
|
-
}, cmdCopied === "assign" ? "\u5DF2\u590D\u5236" : cmdFailed === "assign" ? "\u590D\u5236\u5931\u8D25" : "\u6307\u6D3E\u4FEE\u590D"),
|
|
3445
|
+
title: selectAll ? tf(`\u6307\u6D3E\u5168\u90E8 {a0} \u4E2A findings \u4FEE\u590D\uFF08\u9010\u4E2A iterate_fix\uFF09`, { a0: allIndices.length }) : tf(`\u6307\u6D3E\u5F53\u524D\u53EF\u89C1 {a0} \u4E2A findings \u4FEE\u590D\uFF08\u9010\u4E2A iterate_fix\uFF09`, { a0: indices.length })
|
|
3446
|
+
}, cmdCopied === "assign" ? t("\u5DF2\u590D\u5236") : cmdFailed === "assign" ? t("\u590D\u5236\u5931\u8D25") : t("\u6307\u6D3E\u4FEE\u590D")),
|
|
2889
3447
|
React.createElement("button", {
|
|
2890
3448
|
className: "iterate-cmd",
|
|
2891
3449
|
"data-danger": "",
|
|
2892
3450
|
"data-copied": cmdCopied === "rollback" ? "" : void 0,
|
|
2893
3451
|
"data-failed": cmdFailed === "rollback" ? "" : void 0,
|
|
2894
3452
|
onClick: doRollbackToCheckpoint,
|
|
2895
|
-
title: "\u56DE\u6EDA\u5230\u4E0A\u4E00\u4E2A\u68C0\u67E5\u70B9"
|
|
2896
|
-
}, cmdCopied === "rollback" ? "\u5DF2\u590D\u5236" : cmdFailed === "rollback" ? "\u590D\u5236\u5931\u8D25" : "\u56DE\u6EDA\u68C0\u67E5\u70B9")
|
|
3453
|
+
title: t("\u56DE\u6EDA\u5230\u4E0A\u4E00\u4E2A\u68C0\u67E5\u70B9")
|
|
3454
|
+
}, cmdCopied === "rollback" ? t("\u5DF2\u590D\u5236") : cmdFailed === "rollback" ? t("\u590D\u5236\u5931\u8D25") : t("\u56DE\u6EDA\u68C0\u67E5\u70B9"))
|
|
2897
3455
|
)
|
|
2898
3456
|
),
|
|
2899
3457
|
payload ? React.createElement("div", { className: "iterate-payload" }, payload) : null
|
|
@@ -2916,16 +3474,16 @@ function VerdictBanner(props) {
|
|
|
2916
3474
|
React.createElement(
|
|
2917
3475
|
"span",
|
|
2918
3476
|
{ className: "iterate-verdict-tag", "data-ok": ok ? "" : void 0, "data-warn": ok ? void 0 : "" },
|
|
2919
|
-
ok ? "\u62A5\u544A\u5DF2\u6279\u51C6" : "\u62A5\u544A\u9700\u4FEE\u8BA2"
|
|
3477
|
+
ok ? t("\u62A5\u544A\u5DF2\u6279\u51C6") : t("\u62A5\u544A\u9700\u4FEE\u8BA2")
|
|
2920
3478
|
),
|
|
2921
3479
|
React.createElement(
|
|
2922
3480
|
"span",
|
|
2923
3481
|
{ className: "iterate-verdict-detail" },
|
|
2924
|
-
item(verdict.totalFindings, "\u9879\u53D1\u73B0"),
|
|
2925
|
-
item(verdict.totalRounds, "\u8F6E"),
|
|
2926
|
-
item(verdict.checksRun, "\u9879\u5BA1\u67E5"),
|
|
2927
|
-
ok ? phrase("\u62A5\u544A\u901A\u8FC7\u5168\u90E8\u4E00\u81F4\u6027\u68C0\u67E5") : item(verdict.reportIssues, "\u5904\u62A5\u544A\u7F3A\u9677"),
|
|
2928
|
-
phrase(verdict.converged ? "\u5DF2\u6536\u655B" : "\u672A\u6536\u655B")
|
|
3482
|
+
item(verdict.totalFindings, t("\u9879\u53D1\u73B0")),
|
|
3483
|
+
item(verdict.totalRounds, t("\u8F6E")),
|
|
3484
|
+
item(verdict.checksRun, t("\u9879\u5BA1\u67E5")),
|
|
3485
|
+
ok ? phrase(t("\u62A5\u544A\u901A\u8FC7\u5168\u90E8\u4E00\u81F4\u6027\u68C0\u67E5")) : item(verdict.reportIssues, t("\u5904\u62A5\u544A\u7F3A\u9677")),
|
|
3486
|
+
phrase(verdict.converged ? t("\u5DF2\u6536\u655B") : t("\u672A\u6536\u655B"))
|
|
2929
3487
|
)
|
|
2930
3488
|
);
|
|
2931
3489
|
}
|
|
@@ -2967,7 +3525,7 @@ function ProgressCapsule() {
|
|
|
2967
3525
|
const listener = (payload) => {
|
|
2968
3526
|
const converged = payload && payload.converged === true;
|
|
2969
3527
|
const round = payload && typeof payload.round === "number" ? payload.round : "?";
|
|
2970
|
-
setInfo({ text: converged ? `Round
|
|
3528
|
+
setInfo({ text: converged ? tf(`Round {a0} \u5B8C\u6210 \xB7 \u5DF2\u6536\u655B`, { a0: round }) : tf(`Round {a0} \u5B8C\u6210`, { a0: round }), ok: converged });
|
|
2971
3529
|
if (timer) clearTimeout(timer);
|
|
2972
3530
|
timer = setTimeout(() => setInfo(null), converged ? 3600 : 2400);
|
|
2973
3531
|
};
|
|
@@ -2987,7 +3545,10 @@ function ProgressCapsule() {
|
|
|
2987
3545
|
"aria-live": "polite"
|
|
2988
3546
|
}, info.text);
|
|
2989
3547
|
}
|
|
2990
|
-
function SettingsPanel(
|
|
3548
|
+
function SettingsPanel(props) {
|
|
3549
|
+
const lang = useLang();
|
|
3550
|
+
const cachedSession = useSessionSnapshot();
|
|
3551
|
+
const session = props && props.session ? props.session : cachedSession;
|
|
2991
3552
|
const [enabled, setEnabled] = React.useState(themeEnabled);
|
|
2992
3553
|
const [guideCopied, setGuideCopied] = React.useState(false);
|
|
2993
3554
|
const [statusCopied, setStatusCopied] = React.useState(false);
|
|
@@ -3001,13 +3562,19 @@ function SettingsPanel(_props) {
|
|
|
3001
3562
|
const [cfgCopied, setCfgCopied] = React.useState(false);
|
|
3002
3563
|
const [cfgFailed, setCfgFailed] = React.useState(false);
|
|
3003
3564
|
const [cfgText, setCfgText] = React.useState(null);
|
|
3565
|
+
const [cfgReadCopied, setCfgReadCopied] = React.useState(false);
|
|
3566
|
+
const [cfgReadText, setCfgReadText] = React.useState(null);
|
|
3004
3567
|
const [pullCopied, setPullCopied] = React.useState(false);
|
|
3005
3568
|
const [copyError, setCopyError] = React.useState(null);
|
|
3006
3569
|
const guide = buildConfigEditGuide();
|
|
3007
3570
|
const statusGuide = buildRuntimeStatusGuide();
|
|
3571
|
+
const cfgMeta = configFieldByKey(cfgField);
|
|
3572
|
+
const cfgRead = scanSessionForConfigRead(session);
|
|
3573
|
+
const cfgCurrent = cfgRead ? configValueByPath(cfgRead, cfgField) : null;
|
|
3574
|
+
const cfgFieldLabel = cfgMeta ? cfgMeta.label[getLang()] : cfgField;
|
|
3008
3575
|
const cfgTimers = React.useRef([]);
|
|
3009
3576
|
React.useEffect(() => () => {
|
|
3010
|
-
for (const
|
|
3577
|
+
for (const t2 of cfgTimers.current) clearTimeout(t2);
|
|
3011
3578
|
cfgTimers.current = [];
|
|
3012
3579
|
}, []);
|
|
3013
3580
|
const toggleTheme = () => {
|
|
@@ -3016,7 +3583,7 @@ function SettingsPanel(_props) {
|
|
|
3016
3583
|
setThemeEnabled(next);
|
|
3017
3584
|
};
|
|
3018
3585
|
const flashCopied = (slot) => {
|
|
3019
|
-
const setter = slot === "guide" ? setGuideCopied : slot === "status" ? setStatusCopied : setPullCopied;
|
|
3586
|
+
const setter = slot === "guide" ? setGuideCopied : slot === "status" ? setStatusCopied : slot === "pull" ? setPullCopied : setCfgReadCopied;
|
|
3020
3587
|
setter(true);
|
|
3021
3588
|
cfgTimers.current.push(window.setTimeout(() => setter(false), 1600));
|
|
3022
3589
|
};
|
|
@@ -3036,6 +3603,17 @@ function SettingsPanel(_props) {
|
|
|
3036
3603
|
if (!ok) return;
|
|
3037
3604
|
});
|
|
3038
3605
|
};
|
|
3606
|
+
const doCopyConfigRead = () => {
|
|
3607
|
+
const text = buildConfigReadInstruction();
|
|
3608
|
+
copyText(text).then((ok) => {
|
|
3609
|
+
setCopyError(ok ? null : "cfgRead");
|
|
3610
|
+
if (ok) {
|
|
3611
|
+
flashCopied("cfgRead");
|
|
3612
|
+
return;
|
|
3613
|
+
}
|
|
3614
|
+
setCfgReadText(text);
|
|
3615
|
+
});
|
|
3616
|
+
};
|
|
3039
3617
|
const requestClear = () => {
|
|
3040
3618
|
if (confirming) {
|
|
3041
3619
|
const count = removeStorageByPrefix(TRIAGE_STORAGE_PREFIX);
|
|
@@ -3047,13 +3625,13 @@ function SettingsPanel(_props) {
|
|
|
3047
3625
|
setConfirming(true);
|
|
3048
3626
|
cfgTimers.current.push(window.setTimeout(() => setConfirming(false), 3e3));
|
|
3049
3627
|
};
|
|
3050
|
-
const clearButton = clearedInfo !== null ? React.createElement("button", { className: "iterate-btn", "data-copied": "", disabled: true }, `\u5DF2\u6E05\u9664
|
|
3628
|
+
const clearButton = clearedInfo !== null ? React.createElement("button", { className: "iterate-btn", "data-copied": "", disabled: true }, tf(`\u5DF2\u6E05\u9664 {a0} \u6761`, { a0: clearedInfo })) : React.createElement("button", {
|
|
3051
3629
|
className: "iterate-btn",
|
|
3052
3630
|
"data-danger": confirming ? void 0 : "",
|
|
3053
3631
|
"data-confirm": confirming ? "" : void 0,
|
|
3054
3632
|
onClick: requestClear,
|
|
3055
|
-
title: confirming ? "\u518D\u6B21\u70B9\u51FB\u4EE5\u786E\u8BA4\u6E05\u9664\u5168\u90E8\u5224\u5B9A" : "\u6E05\u9664\u6240\u6709\u5206\u8BCA\u5224\u5B9A\u8BB0\u5F55\uFF08\u9700\u8981\u4E8C\u6B21\u786E\u8BA4\uFF09"
|
|
3056
|
-
}, confirming ? "\u786E\u8BA4\u6E05\u9664\uFF1F" : "\u6E05\u9664\u5206\u8BCA");
|
|
3633
|
+
title: confirming ? t("\u518D\u6B21\u70B9\u51FB\u4EE5\u786E\u8BA4\u6E05\u9664\u5168\u90E8\u5224\u5B9A") : t("\u6E05\u9664\u6240\u6709\u5206\u8BCA\u5224\u5B9A\u8BB0\u5F55\uFF08\u9700\u8981\u4E8C\u6B21\u786E\u8BA4\uFF09")
|
|
3634
|
+
}, confirming ? t("\u786E\u8BA4\u6E05\u9664\uFF1F") : t("\u6E05\u9664\u5206\u8BCA"));
|
|
3057
3635
|
const themeCard = React.createElement(
|
|
3058
3636
|
"div",
|
|
3059
3637
|
{ key: "theme", className: "iterate-scard" },
|
|
@@ -3063,17 +3641,17 @@ function SettingsPanel(_props) {
|
|
|
3063
3641
|
React.createElement(
|
|
3064
3642
|
"div",
|
|
3065
3643
|
{},
|
|
3066
|
-
React.createElement("div", { className: "iterate-settings-title" }, "\u8FED\u4EE3\u4E3B\u9898"),
|
|
3067
|
-
React.createElement("div", { className: "iterate-settings-desc" }, "\u542F\u7528\u6696\u7425\u73C0\u914D\u8272\u7684 iterate \u4E13\u5C5E\u76AE\u80A4\uFF0C\u8986\u76D6 dsh \u9ED8\u8BA4\u4E3B\u9898\u4EE4\u724C\u3002")
|
|
3644
|
+
React.createElement("div", { className: "iterate-settings-title" }, t("\u8FED\u4EE3\u4E3B\u9898")),
|
|
3645
|
+
React.createElement("div", { className: "iterate-settings-desc" }, t("\u542F\u7528\u6696\u7425\u73C0\u914D\u8272\u7684 iterate \u4E13\u5C5E\u76AE\u80A4\uFF0C\u8986\u76D6 dsh \u9ED8\u8BA4\u4E3B\u9898\u4EE4\u724C\u3002"))
|
|
3068
3646
|
),
|
|
3069
3647
|
React.createElement(
|
|
3070
3648
|
"div",
|
|
3071
3649
|
{ className: "iterate-scard-actions" },
|
|
3072
|
-
React.createElement("span", { className: "iterate-chip", "data-ok": enabled ? "" : void 0 }, enabled ? "\u5DF2\u542F\u7528" : "\u5DF2\u5173\u95ED"),
|
|
3650
|
+
React.createElement("span", { className: "iterate-chip", "data-ok": enabled ? "" : void 0 }, enabled ? t("\u5DF2\u542F\u7528") : t("\u5DF2\u5173\u95ED")),
|
|
3073
3651
|
React.createElement("button", {
|
|
3074
3652
|
role: "switch",
|
|
3075
3653
|
"aria-checked": enabled,
|
|
3076
|
-
"aria-label": "\u5F00\u5173 iterate \u4E3B\u9898",
|
|
3654
|
+
"aria-label": t("\u5F00\u5173 iterate \u4E3B\u9898"),
|
|
3077
3655
|
className: "iterate-switch",
|
|
3078
3656
|
"data-on": enabled ? "" : void 0,
|
|
3079
3657
|
onClick: toggleTheme
|
|
@@ -3081,6 +3659,37 @@ function SettingsPanel(_props) {
|
|
|
3081
3659
|
)
|
|
3082
3660
|
)
|
|
3083
3661
|
);
|
|
3662
|
+
const langCard = React.createElement(
|
|
3663
|
+
"div",
|
|
3664
|
+
{ key: "lang", className: "iterate-scard" },
|
|
3665
|
+
React.createElement(
|
|
3666
|
+
"div",
|
|
3667
|
+
{ className: "iterate-scard-head" },
|
|
3668
|
+
React.createElement(
|
|
3669
|
+
"div",
|
|
3670
|
+
{},
|
|
3671
|
+
React.createElement("div", { className: "iterate-settings-title" }, t("\u754C\u9762\u8BED\u8A00")),
|
|
3672
|
+
React.createElement(
|
|
3673
|
+
"div",
|
|
3674
|
+
{ className: "iterate-settings-desc" },
|
|
3675
|
+
t("\u9762\u677F\u6587\u6848\u7684\u663E\u793A\u8BED\u8A00\uFF1B\u590D\u5236\u7ED9\u6A21\u578B\u7684\u6307\u4EE4\u4FDD\u6301\u4E2D\u6587\uFF0C\u4E0E SKILL.md \u7684\u5DE5\u4F5C\u6D41\u5951\u7EA6\u4E00\u81F4\u3002")
|
|
3676
|
+
)
|
|
3677
|
+
),
|
|
3678
|
+
React.createElement(
|
|
3679
|
+
"div",
|
|
3680
|
+
{ className: "iterate-scard-actions" },
|
|
3681
|
+
React.createElement("span", { className: "iterate-chip", "data-ok": "" }, LANG_NAMES[lang]),
|
|
3682
|
+
React.createElement("button", {
|
|
3683
|
+
role: "switch",
|
|
3684
|
+
"aria-checked": lang === "en",
|
|
3685
|
+
"aria-label": t("\u5207\u6362\u754C\u9762\u8BED\u8A00"),
|
|
3686
|
+
className: "iterate-switch",
|
|
3687
|
+
"data-on": lang === "en" ? "" : void 0,
|
|
3688
|
+
onClick: () => setLang(lang === "en" ? "zh" : "en")
|
|
3689
|
+
}, React.createElement("span", { className: "iterate-switch-knob" }))
|
|
3690
|
+
)
|
|
3691
|
+
)
|
|
3692
|
+
);
|
|
3084
3693
|
const dataCard = React.createElement(
|
|
3085
3694
|
"div",
|
|
3086
3695
|
{ key: "data", className: "iterate-scard" },
|
|
@@ -3090,13 +3699,13 @@ function SettingsPanel(_props) {
|
|
|
3090
3699
|
React.createElement(
|
|
3091
3700
|
"div",
|
|
3092
3701
|
{},
|
|
3093
|
-
React.createElement("div", { className: "iterate-settings-title" }, "\u5206\u8BCA\u6301\u4E45\u5316"),
|
|
3094
|
-
React.createElement("div", { className: "iterate-settings-desc" }, "y / n / a \u5224\u5B9A\u4FDD\u5B58\u5728\u672C\u5730\u6D4F\u89C8\u5668\uFF08localStorage\uFF09\uFF0C\u5237\u65B0\u4F1A\u8BDD\u540E\u4ECD\u4FDD\u7559\u3002")
|
|
3702
|
+
React.createElement("div", { className: "iterate-settings-title" }, t("\u5206\u8BCA\u6301\u4E45\u5316")),
|
|
3703
|
+
React.createElement("div", { className: "iterate-settings-desc" }, t("y / n / a \u5224\u5B9A\u4FDD\u5B58\u5728\u672C\u5730\u6D4F\u89C8\u5668\uFF08localStorage\uFF09\uFF0C\u5237\u65B0\u4F1A\u8BDD\u540E\u4ECD\u4FDD\u7559\u3002"))
|
|
3095
3704
|
),
|
|
3096
3705
|
React.createElement(
|
|
3097
3706
|
"div",
|
|
3098
3707
|
{ className: "iterate-scard-actions" },
|
|
3099
|
-
React.createElement("span", { className: "iterate-chip" }, "\u672C\u5730\u4FDD\u5B58"),
|
|
3708
|
+
React.createElement("span", { className: "iterate-chip" }, t("\u672C\u5730\u4FDD\u5B58")),
|
|
3100
3709
|
clearButton
|
|
3101
3710
|
)
|
|
3102
3711
|
)
|
|
@@ -3110,14 +3719,14 @@ function SettingsPanel(_props) {
|
|
|
3110
3719
|
React.createElement(
|
|
3111
3720
|
"div",
|
|
3112
3721
|
{},
|
|
3113
|
-
React.createElement("div", { className: "iterate-settings-title" }, "\u914D\u7F6E\u7BA1\u7406"),
|
|
3114
|
-
React.createElement("div", { className: "iterate-settings-desc" }, "\u76EE\u6807 / \u7EF4\u5EA6 / \u6700\u5927\u8F6E\u6570 / validation.commands \u5199\u5728\u9879\u76EE\u7684 iterate.config.yaml\uFF0C\u590D\u5236\u6307\u5F15\u53EF\u8BA9\u6A21\u578B\u6309\u9700\u8C03\u6574\u3002")
|
|
3722
|
+
React.createElement("div", { className: "iterate-settings-title" }, t("\u914D\u7F6E\u7BA1\u7406")),
|
|
3723
|
+
React.createElement("div", { className: "iterate-settings-desc" }, t("\u76EE\u6807 / \u7EF4\u5EA6 / \u6700\u5927\u8F6E\u6570 / validation.commands \u5199\u5728\u9879\u76EE\u7684 iterate.config.yaml\uFF0C\u590D\u5236\u6307\u5F15\u53EF\u8BA9\u6A21\u578B\u6309\u9700\u8C03\u6574\u3002"))
|
|
3115
3724
|
),
|
|
3116
3725
|
React.createElement(
|
|
3117
3726
|
"div",
|
|
3118
3727
|
{ className: "iterate-scard-actions" },
|
|
3119
|
-
React.createElement("button", { className: "iterate-btn", "data-primary": "", "data-copied": guideCopied ? "" : void 0, onClick: () => doCopy(guide, "guide") }, guideCopied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u6307\u5F15"),
|
|
3120
|
-
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => setShowGuide((v) => !v) }, showGuide ? "\u6536\u8D77" : "\u5C55\u5F00")
|
|
3728
|
+
React.createElement("button", { className: "iterate-btn", "data-primary": "", "data-copied": guideCopied ? "" : void 0, onClick: () => doCopy(guide, "guide") }, guideCopied ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236\u6307\u5F15")),
|
|
3729
|
+
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => setShowGuide((v) => !v) }, showGuide ? t("\u6536\u8D77") : t("\u5C55\u5F00"))
|
|
3121
3730
|
)
|
|
3122
3731
|
),
|
|
3123
3732
|
// Gap #8: wire up the previously-dead `buildConfigFieldInstruction` — pick
|
|
@@ -3131,17 +3740,17 @@ function SettingsPanel(_props) {
|
|
|
3131
3740
|
{
|
|
3132
3741
|
className: "iterate-filter-select",
|
|
3133
3742
|
value: cfgField,
|
|
3134
|
-
"aria-label": "\u9009\u62E9\u8981\u4FEE\u6539\u7684\u914D\u7F6E\u5B57\u6BB5",
|
|
3743
|
+
"aria-label": t("\u9009\u62E9\u8981\u4FEE\u6539\u7684\u914D\u7F6E\u5B57\u6BB5"),
|
|
3135
3744
|
onChange: (e) => setCfgField(e.target.value)
|
|
3136
3745
|
},
|
|
3137
3746
|
...CONFIG_EDIT_FIELDS.map(
|
|
3138
|
-
(f) => React.createElement("option", { key: f.key, value: f.key }, `${f.label}\uFF08${f.key}\uFF09`)
|
|
3747
|
+
(f) => React.createElement("option", { key: f.key, value: f.key }, getLang() === "en" ? `${f.label.en} (${f.key})` : `${f.label.zh}\uFF08${f.key}\uFF09`)
|
|
3139
3748
|
)
|
|
3140
3749
|
),
|
|
3141
3750
|
React.createElement(
|
|
3142
3751
|
"span",
|
|
3143
3752
|
{ className: "iterate-settings-desc" },
|
|
3144
|
-
|
|
3753
|
+
cfgMeta?.hint[getLang()] ?? ""
|
|
3145
3754
|
),
|
|
3146
3755
|
React.createElement("button", {
|
|
3147
3756
|
className: "iterate-btn",
|
|
@@ -3149,18 +3758,48 @@ function SettingsPanel(_props) {
|
|
|
3149
3758
|
"data-copied": cfgCopied ? "" : void 0,
|
|
3150
3759
|
"data-failed": cfgFailed ? "" : void 0,
|
|
3151
3760
|
onClick: doBuildConfigInstruction,
|
|
3152
|
-
title: "\u751F\u6210\u5E76\u590D\u5236\u8BE5\u5B57\u6BB5\u7684 iterate_config \u5199\u5165\u6307\u4EE4\uFF08\u5199\u5165\u524D\u81EA\u52A8\u5907\u4EFD\u3001\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\uFF09"
|
|
3153
|
-
}, cfgCopied ? "\u5DF2\u590D\u5236" : cfgFailed ? "\u590D\u5236\u5931\u8D25" : "\u751F\u6210\u5199\u5165\u6307\u4EE4"),
|
|
3154
|
-
cfgFailed ? React.createElement("span", { className: "iterate-settings-desc" }, "\u526A\u8D34\u677F\u88AB\u62D2\u7EDD\u2014\u2014\u6307\u4EE4\u5DF2\u5C55\u793A\u5728\u4E0B\u65B9\uFF0C\u8BF7\u624B\u52A8\u590D\u5236\u3002") : null
|
|
3761
|
+
title: t("\u751F\u6210\u5E76\u590D\u5236\u8BE5\u5B57\u6BB5\u7684 iterate_config \u5199\u5165\u6307\u4EE4\uFF08\u5199\u5165\u524D\u81EA\u52A8\u5907\u4EFD\u3001\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\uFF09")
|
|
3762
|
+
}, cfgCopied ? t("\u5DF2\u590D\u5236") : cfgFailed ? t("\u590D\u5236\u5931\u8D25") : t("\u751F\u6210\u5199\u5165\u6307\u4EE4")),
|
|
3763
|
+
cfgFailed ? React.createElement("span", { className: "iterate-settings-desc" }, t("\u526A\u8D34\u677F\u88AB\u62D2\u7EDD\u2014\u2014\u6307\u4EE4\u5DF2\u5C55\u793A\u5728\u4E0B\u65B9\uFF0C\u8BF7\u624B\u52A8\u590D\u5236\u3002")) : null
|
|
3155
3764
|
),
|
|
3765
|
+
// Gap #1 (3.7.0): close the write -> read-back loop. Three honest states:
|
|
3766
|
+
// nothing echoed yet, the value the model read back, or a read-back that
|
|
3767
|
+
// never contained this field (defaults are in play — say so, don't blank).
|
|
3768
|
+
React.createElement(
|
|
3769
|
+
"div",
|
|
3770
|
+
{ className: "iterate-guide-bar", style: { marginTop: 8, flexWrap: "wrap" } },
|
|
3771
|
+
React.createElement(
|
|
3772
|
+
"span",
|
|
3773
|
+
{ className: "iterate-settings-desc" },
|
|
3774
|
+
!cfgRead ? t("\u5C1A\u672A\u56DE\u8BFB\u5F53\u524D\u914D\u7F6E\u2014\u2014\u70B9\u300C\u56DE\u8BFB\u5F53\u524D\u914D\u7F6E\u300D\u590D\u5236\u6307\u4EE4\u53D1\u7ED9\u6A21\u578B\uFF0C\u56DE\u663E\u540E\u5373\u53EF\u5728\u6B64\u770B\u5230\u5F53\u524D\u503C\u3002") : cfgCurrent && cfgCurrent.resolved ? tf("\u5F53\u524D\u503C {a0}\uFF1A{a1}", { a0: cfgFieldLabel, a1: formatConfigValue(cfgCurrent.value) }) : tf("\u56DE\u8BFB\u7ED3\u679C\u4E2D\u6CA1\u6709 {a0}\uFF08\u8BE5\u5B57\u6BB5\u53EF\u80FD\u5C1A\u672A\u5199\u5165\u914D\u7F6E\u6587\u4EF6\uFF09\u3002", { a0: cfgFieldLabel })
|
|
3775
|
+
),
|
|
3776
|
+
React.createElement("button", {
|
|
3777
|
+
className: "iterate-btn",
|
|
3778
|
+
"data-primary": "",
|
|
3779
|
+
"data-copied": cfgReadCopied ? "" : void 0,
|
|
3780
|
+
onClick: doCopyConfigRead,
|
|
3781
|
+
title: t('\u590D\u5236\u56DE\u8BFB\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u8C03\u7528 iterate_config(operation:"read") \u5E76\u56DE\u663E\u7ED3\u679C')
|
|
3782
|
+
}, cfgReadCopied ? t("\u5DF2\u590D\u5236") : t("\u56DE\u8BFB\u5F53\u524D\u914D\u7F6E"))
|
|
3783
|
+
),
|
|
3784
|
+
cfgReadText ? React.createElement(
|
|
3785
|
+
"div",
|
|
3786
|
+
{ className: "iterate-guide" },
|
|
3787
|
+
React.createElement(
|
|
3788
|
+
"div",
|
|
3789
|
+
{ className: "iterate-guide-bar" },
|
|
3790
|
+
React.createElement("span", {}, t("\u56DE\u8BFB\u6307\u4EE4\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\uFF09")),
|
|
3791
|
+
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => doCopy(cfgReadText, "cfgRead") }, t("\u590D\u5236"))
|
|
3792
|
+
),
|
|
3793
|
+
React.createElement("div", { className: "iterate-guide-body" }, cfgReadText)
|
|
3794
|
+
) : null,
|
|
3156
3795
|
cfgText ? React.createElement(
|
|
3157
3796
|
"div",
|
|
3158
3797
|
{ className: "iterate-guide" },
|
|
3159
3798
|
React.createElement(
|
|
3160
3799
|
"div",
|
|
3161
3800
|
{ className: "iterate-guide-bar" },
|
|
3162
|
-
React.createElement("span", {}, "\u5199\u5165\u6307\u4EE4\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\uFF09"),
|
|
3163
|
-
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => doCopy(cfgText, "guide") }, "\u590D\u5236")
|
|
3801
|
+
React.createElement("span", {}, t("\u5199\u5165\u6307\u4EE4\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\uFF09")),
|
|
3802
|
+
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => doCopy(cfgText, "guide") }, t("\u590D\u5236"))
|
|
3164
3803
|
),
|
|
3165
3804
|
React.createElement("div", { className: "iterate-guide-body" }, cfgText)
|
|
3166
3805
|
) : null,
|
|
@@ -3170,8 +3809,9 @@ function SettingsPanel(_props) {
|
|
|
3170
3809
|
React.createElement(
|
|
3171
3810
|
"div",
|
|
3172
3811
|
{ className: "iterate-guide-bar" },
|
|
3173
|
-
React.createElement("span", {}, "iterate.config.yaml \u53EF\u7F16\u8F91\u5B57\u6BB5"),
|
|
3174
|
-
React.createElement("
|
|
3812
|
+
React.createElement("span", {}, t("iterate.config.yaml \u53EF\u7F16\u8F91\u5B57\u6BB5")),
|
|
3813
|
+
React.createElement("span", { className: "iterate-chip" }, t("\u4E2D\u6587\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u7684\u5951\u7EA6\u6587\u6848\uFF09")),
|
|
3814
|
+
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => doCopy(guide, "guide") }, t("\u590D\u5236"))
|
|
3175
3815
|
),
|
|
3176
3816
|
React.createElement("div", { className: "iterate-guide-body" }, guide)
|
|
3177
3817
|
) : null
|
|
@@ -3185,13 +3825,13 @@ function SettingsPanel(_props) {
|
|
|
3185
3825
|
React.createElement(
|
|
3186
3826
|
"div",
|
|
3187
3827
|
{},
|
|
3188
|
-
React.createElement("div", { className: "iterate-settings-title" }, "\u72B6\u6001\u6982\u89C8"),
|
|
3189
|
-
React.createElement("div", { className: "iterate-settings-desc" }, "\u67E5\u770B\u8FD0\u884C\u65F6\u4EA7\u7269\u5E03\u5C40\u4E0E\u6E05\u7406\u6307\u5F15\u3002iterate_status / iterate_history / iterate_prune \u5DE5\u5177\u7528\u4E8E\u67E5\u770B\u548C\u7BA1\u7406\u3002")
|
|
3828
|
+
React.createElement("div", { className: "iterate-settings-title" }, t("\u72B6\u6001\u6982\u89C8")),
|
|
3829
|
+
React.createElement("div", { className: "iterate-settings-desc" }, t("\u67E5\u770B\u8FD0\u884C\u65F6\u4EA7\u7269\u5E03\u5C40\u4E0E\u6E05\u7406\u6307\u5F15\u3002iterate_status / iterate_history / iterate_prune \u5DE5\u5177\u7528\u4E8E\u67E5\u770B\u548C\u7BA1\u7406\u3002"))
|
|
3190
3830
|
),
|
|
3191
3831
|
React.createElement(
|
|
3192
3832
|
"div",
|
|
3193
3833
|
{ className: "iterate-scard-actions" },
|
|
3194
|
-
React.createElement("button", { className: "iterate-btn", "data-primary": "", "data-copied": statusCopied ? "" : void 0, onClick: () => doCopy(statusGuide, "status") }, statusCopied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u6307\u5F15"),
|
|
3834
|
+
React.createElement("button", { className: "iterate-btn", "data-primary": "", "data-copied": statusCopied ? "" : void 0, onClick: () => doCopy(statusGuide, "status") }, statusCopied ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236\u6307\u5F15")),
|
|
3195
3835
|
// Gap #4: RUNTIME_ARTIFACTS stops being documentation-only — this
|
|
3196
3836
|
// copies the instruction that makes the model echo those artifacts
|
|
3197
3837
|
// into the session so the observatory panels can read them.
|
|
@@ -3199,8 +3839,8 @@ function SettingsPanel(_props) {
|
|
|
3199
3839
|
className: "iterate-btn",
|
|
3200
3840
|
"data-copied": pullCopied ? "" : void 0,
|
|
3201
3841
|
onClick: () => doCopy(buildDiskSnapshotInstruction(), "pull")
|
|
3202
|
-
}, pullCopied ? "\u5DF2\u590D\u5236" : "\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167"),
|
|
3203
|
-
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => setShowStatus((v) => !v) }, showStatus ? "\u6536\u8D77" : "\u5C55\u5F00")
|
|
3842
|
+
}, pullCopied ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167")),
|
|
3843
|
+
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => setShowStatus((v) => !v) }, showStatus ? t("\u6536\u8D77") : t("\u5C55\u5F00"))
|
|
3204
3844
|
)
|
|
3205
3845
|
),
|
|
3206
3846
|
showStatus ? React.createElement(
|
|
@@ -3209,8 +3849,9 @@ function SettingsPanel(_props) {
|
|
|
3209
3849
|
React.createElement(
|
|
3210
3850
|
"div",
|
|
3211
3851
|
{ className: "iterate-guide-bar" },
|
|
3212
|
-
React.createElement("span", {}, "\u8FD0\u884C\u65F6\u5E03\u5C40\u4E0E\u6E05\u7406"),
|
|
3213
|
-
React.createElement("
|
|
3852
|
+
React.createElement("span", {}, t("\u8FD0\u884C\u65F6\u5E03\u5C40\u4E0E\u6E05\u7406")),
|
|
3853
|
+
React.createElement("span", { className: "iterate-chip" }, t("\u4E2D\u6587\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u7684\u5951\u7EA6\u6587\u6848\uFF09")),
|
|
3854
|
+
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => doCopy(statusGuide, "status") }, t("\u590D\u5236"))
|
|
3214
3855
|
),
|
|
3215
3856
|
React.createElement("div", { className: "iterate-guide-body" }, statusGuide)
|
|
3216
3857
|
) : null
|
|
@@ -3225,20 +3866,20 @@ function SettingsPanel(_props) {
|
|
|
3225
3866
|
"div",
|
|
3226
3867
|
{},
|
|
3227
3868
|
React.createElement("div", { className: "iterate-settings-title" }, "iterate"),
|
|
3228
|
-
React.createElement("div", { className: "iterate-settings-desc" }, "\u4E3A\u6BCF\u6B21\u4EE3\u7801\u8BC4\u5BA1\u751F\u6210 Review \u62A5\u544A\u4E0E\u4EA4\u4E92\u5F0F\u5206\u8BCA\u89C6\u56FE\uFF0C\u4E13\u6CE8 AI \u81EA\u6211\u5BA1\u67E5\u4E0E\u4FEE\u6B63\u3002")
|
|
3869
|
+
React.createElement("div", { className: "iterate-settings-desc" }, t("\u4E3A\u6BCF\u6B21\u4EE3\u7801\u8BC4\u5BA1\u751F\u6210 Review \u62A5\u544A\u4E0E\u4EA4\u4E92\u5F0F\u5206\u8BCA\u89C6\u56FE\uFF0C\u4E13\u6CE8 AI \u81EA\u6211\u5BA1\u67E5\u4E0E\u4FEE\u6B63\u3002"))
|
|
3229
3870
|
),
|
|
3230
3871
|
React.createElement(
|
|
3231
3872
|
"span",
|
|
3232
3873
|
{ className: "iterate-pill" },
|
|
3233
3874
|
React.createElement("span", { className: "iterate-pill-dot" }),
|
|
3234
|
-
"\u5C31\u7EEA"
|
|
3875
|
+
t("\u5C31\u7EEA")
|
|
3235
3876
|
)
|
|
3236
3877
|
)
|
|
3237
3878
|
);
|
|
3238
3879
|
return React.createElement(
|
|
3239
3880
|
"div",
|
|
3240
3881
|
{ "data-iterate-root": "", "data-iterate": "settings", className: "iterate-settings" },
|
|
3241
|
-
React.createElement("div", { className: "iterate-settings-title", style: { fontSize: 15, fontWeight: 700 } }, "iterate \u8BBE\u7F6E"),
|
|
3882
|
+
React.createElement("div", { className: "iterate-settings-title", style: { fontSize: 15, fontWeight: 700 } }, t("iterate \u8BBE\u7F6E")),
|
|
3242
3883
|
banner,
|
|
3243
3884
|
copyError ? React.createElement(
|
|
3244
3885
|
"div",
|
|
@@ -3246,9 +3887,10 @@ function SettingsPanel(_props) {
|
|
|
3246
3887
|
React.createElement(
|
|
3247
3888
|
"div",
|
|
3248
3889
|
{ className: "iterate-settings-desc" },
|
|
3249
|
-
"\u526A\u8D34\u677F\u5199\u5165\u88AB\u62D2\u7EDD\uFF0C\u590D\u5236\u672A\u751F\u6548\u2014\u2014\u8BF7\u5C55\u5F00\u5BF9\u5E94\u533A\u5757\u624B\u52A8\u9009\u4E2D\u6587\u672C\u590D\u5236\u3002"
|
|
3890
|
+
t("\u526A\u8D34\u677F\u5199\u5165\u88AB\u62D2\u7EDD\uFF0C\u590D\u5236\u672A\u751F\u6548\u2014\u2014\u8BF7\u5C55\u5F00\u5BF9\u5E94\u533A\u5757\u624B\u52A8\u9009\u4E2D\u6587\u672C\u590D\u5236\u3002")
|
|
3250
3891
|
)
|
|
3251
3892
|
) : null,
|
|
3893
|
+
langCard,
|
|
3252
3894
|
themeCard,
|
|
3253
3895
|
dataCard,
|
|
3254
3896
|
guideCard,
|
|
@@ -3256,18 +3898,18 @@ function SettingsPanel(_props) {
|
|
|
3256
3898
|
);
|
|
3257
3899
|
}
|
|
3258
3900
|
var OBS_TABS = [
|
|
3259
|
-
{ key: "live", label: "\u5B9E\u65F6\u6D3B\u52A8\u6D41", hotkey: "" },
|
|
3260
|
-
{ key: "f1", label: "\u5BA1\u67E5\u7EBF\u7A0B", hotkey: "1" },
|
|
3261
|
-
{ key: "f2", label: "\u6536\u655B\u8D8B\u52BF", hotkey: "2" },
|
|
3262
|
-
{ key: "f3", label: "\u53D1\u73B0\u5B9A\u4F4D", hotkey: "3" },
|
|
3263
|
-
{ key: "f4", label: "\u4FEE\u590D\u4E0E\u56DE\u6EDA", hotkey: "4" },
|
|
3264
|
-
{ key: "f5", label: "\u65AD\u70B9\u6062\u590D", hotkey: "5" },
|
|
3265
|
-
{ key: "f6", label: "\u8FD0\u884C\u63A7\u5236\u53F0", hotkey: "6" },
|
|
3266
|
-
{ key: "f7", label: "\u51B3\u7B56\u65F6\u95F4\u7EBF", hotkey: "7" },
|
|
3901
|
+
{ key: "live", label: { zh: "\u5B9E\u65F6\u6D3B\u52A8\u6D41", en: "Live activity" }, hotkey: "" },
|
|
3902
|
+
{ key: "f1", label: { zh: "\u5BA1\u67E5\u7EBF\u7A0B", en: "Review threads" }, hotkey: "1" },
|
|
3903
|
+
{ key: "f2", label: { zh: "\u6536\u655B\u8D8B\u52BF", en: "Convergence trend" }, hotkey: "2" },
|
|
3904
|
+
{ key: "f3", label: { zh: "\u53D1\u73B0\u5B9A\u4F4D", en: "Finding locations" }, hotkey: "3" },
|
|
3905
|
+
{ key: "f4", label: { zh: "\u4FEE\u590D\u4E0E\u56DE\u6EDA", en: "Fixes & rollback" }, hotkey: "4" },
|
|
3906
|
+
{ key: "f5", label: { zh: "\u65AD\u70B9\u6062\u590D", en: "Checkpoint resume" }, hotkey: "5" },
|
|
3907
|
+
{ key: "f6", label: { zh: "\u8FD0\u884C\u63A7\u5236\u53F0", en: "Run console" }, hotkey: "6" },
|
|
3908
|
+
{ key: "f7", label: { zh: "\u51B3\u7B56\u65F6\u95F4\u7EBF", en: "Decision timeline" }, hotkey: "7" },
|
|
3267
3909
|
// v3.0: Quality Command Center tabs
|
|
3268
|
-
{ key: "f8", label: "\u8D28\u91CF\u95E8\u7981", hotkey: "8" },
|
|
3269
|
-
{ key: "f9", label: "\u7ECF\u9A8C\u94F6\u884C", hotkey: "9" },
|
|
3270
|
-
{ key: "f10", label: "\u9632\u5FA1\u4E8B\u4EF6", hotkey: "0" }
|
|
3910
|
+
{ key: "f8", label: { zh: "\u8D28\u91CF\u95E8\u7981", en: "Quality gate" }, hotkey: "8" },
|
|
3911
|
+
{ key: "f9", label: { zh: "\u7ECF\u9A8C\u94F6\u884C", en: "Experience bank" }, hotkey: "9" },
|
|
3912
|
+
{ key: "f10", label: { zh: "\u9632\u5FA1\u4E8B\u4EF6", en: "Defense events" }, hotkey: "0" }
|
|
3271
3913
|
];
|
|
3272
3914
|
var OBS_HOTKEY_TABS = {
|
|
3273
3915
|
1: "f1",
|
|
@@ -3282,23 +3924,26 @@ var OBS_HOTKEY_TABS = {
|
|
|
3282
3924
|
0: "f10",
|
|
3283
3925
|
"-": "live"
|
|
3284
3926
|
};
|
|
3285
|
-
function obsTabLabel(
|
|
3286
|
-
|
|
3287
|
-
|
|
3927
|
+
function obsTabLabel(t2) {
|
|
3928
|
+
const label = t2.label[getLang()];
|
|
3929
|
+
if (t2.key === "live") return label;
|
|
3930
|
+
return `${t2.key.toUpperCase()} ${label}`;
|
|
3931
|
+
}
|
|
3932
|
+
function obsHotkeyHint() {
|
|
3933
|
+
return getLang() === "en" ? `${String(OBS_TABS.length)} tabs \xB7 keys 1-9/0 switch Fn, - returns to the live feed` : tf(`{a0} \u9875 \xB7 \u6309 1-9/0 \u5207 Fn\u3001- \u56DE\u5B9E\u65F6\u6D41`, { a0: String(OBS_TABS.length) });
|
|
3288
3934
|
}
|
|
3289
|
-
var OBS_HOTKEY_HINT = `${String(OBS_TABS.length)} \u9875 \xB7 \u6309 1-9/0 \u5207 Fn\u3001- \u56DE\u5B9E\u65F6\u6D41`;
|
|
3290
3935
|
var OBS_LIVE_META = {
|
|
3291
|
-
read: { label: "\u9605\u8BFB", color: "var(--dsw-alias-brand-primary)" },
|
|
3292
|
-
fix: { label: "\u4FEE\u590D", color: "var(--dsw-alias-state-success-primary)" },
|
|
3293
|
-
rollback: { label: "\u56DE\u6EDA", color: "var(--dsw-alias-state-error-primary)" },
|
|
3294
|
-
diff: { label: "\u5DEE\u5F02", color: "var(--dsw-alias-state-warn-primary)" },
|
|
3295
|
-
review: { label: "\u5BA1\u67E5", color: "var(--dsw-alias-brand-primary)" },
|
|
3296
|
-
triage: { label: "\u5206\u6D41", color: "var(--dsw-alias-state-warn-primary)" },
|
|
3297
|
-
checkpoint: { label: "\u65AD\u70B9", color: "var(--dsw-alias-brand-primary)" },
|
|
3298
|
-
validate: { label: "\u6821\u9A8C", color: "var(--dsw-alias-state-success-primary)" },
|
|
3299
|
-
log: { label: "\u8BB0\u5F55", color: "var(--dsw-alias-label-secondary)" },
|
|
3300
|
-
prune: { label: "\u6E05\u7406", color: "var(--dsw-alias-state-warn-primary)" },
|
|
3301
|
-
info: { label: "\u4FE1\u606F", color: "var(--dsw-alias-label-secondary)" }
|
|
3936
|
+
read: { label: { zh: "\u9605\u8BFB", en: "Read" }, color: "var(--dsw-alias-brand-primary)" },
|
|
3937
|
+
fix: { label: { zh: "\u4FEE\u590D", en: "Fix" }, color: "var(--dsw-alias-state-success-primary)" },
|
|
3938
|
+
rollback: { label: { zh: "\u56DE\u6EDA", en: "Rollback" }, color: "var(--dsw-alias-state-error-primary)" },
|
|
3939
|
+
diff: { label: { zh: "\u5DEE\u5F02", en: "Diff" }, color: "var(--dsw-alias-state-warn-primary)" },
|
|
3940
|
+
review: { label: { zh: "\u5BA1\u67E5", en: "Review" }, color: "var(--dsw-alias-brand-primary)" },
|
|
3941
|
+
triage: { label: { zh: "\u5206\u6D41", en: "Triage" }, color: "var(--dsw-alias-state-warn-primary)" },
|
|
3942
|
+
checkpoint: { label: { zh: "\u65AD\u70B9", en: "Checkpoint" }, color: "var(--dsw-alias-brand-primary)" },
|
|
3943
|
+
validate: { label: { zh: "\u6821\u9A8C", en: "Validate" }, color: "var(--dsw-alias-state-success-primary)" },
|
|
3944
|
+
log: { label: { zh: "\u8BB0\u5F55", en: "Log" }, color: "var(--dsw-alias-label-secondary)" },
|
|
3945
|
+
prune: { label: { zh: "\u6E05\u7406", en: "Prune" }, color: "var(--dsw-alias-state-warn-primary)" },
|
|
3946
|
+
info: { label: { zh: "\u4FE1\u606F", en: "Info" }, color: "var(--dsw-alias-label-secondary)" }
|
|
3302
3947
|
};
|
|
3303
3948
|
var OBS_LIVE_FALLBACK_COLOR = "var(--dsw-alias-label-secondary)";
|
|
3304
3949
|
function formatObsLiveTime(ts) {
|
|
@@ -3340,6 +3985,9 @@ function buildObsFixInstruction(f) {
|
|
|
3340
3985
|
}
|
|
3341
3986
|
function ObservatoryPanel(props) {
|
|
3342
3987
|
const session = props && props.session ? props.session : null;
|
|
3988
|
+
React.useEffect(() => {
|
|
3989
|
+
noteSession(session);
|
|
3990
|
+
}, [session]);
|
|
3343
3991
|
const manifest = latestTranscript(session);
|
|
3344
3992
|
const qualityGate = latestQualityGate(session);
|
|
3345
3993
|
const experienceBank = latestExperienceBank(session);
|
|
@@ -3369,9 +4017,9 @@ function ObservatoryPanel(props) {
|
|
|
3369
4017
|
if (!doc || !open) return;
|
|
3370
4018
|
const onKeyDown = (ev) => {
|
|
3371
4019
|
if (ev.metaKey || ev.ctrlKey || ev.altKey || ev.shiftKey) return;
|
|
3372
|
-
const
|
|
3373
|
-
if (
|
|
3374
|
-
if (
|
|
4020
|
+
const t2 = ev.target;
|
|
4021
|
+
if (t2 && (t2.tagName === "INPUT" || t2.tagName === "TEXTAREA" || t2.tagName === "SELECT")) return;
|
|
4022
|
+
if (t2 && typeof t2.isContentEditable === "boolean" && t2.isContentEditable) return;
|
|
3375
4023
|
const rootEl = doc.querySelector('[data-iterate="obs"]');
|
|
3376
4024
|
if (!rootEl) return;
|
|
3377
4025
|
const activeEl = doc.activeElement;
|
|
@@ -3399,7 +4047,7 @@ function ObservatoryPanel(props) {
|
|
|
3399
4047
|
});
|
|
3400
4048
|
};
|
|
3401
4049
|
const pullInstruction = buildDiskSnapshotInstruction();
|
|
3402
|
-
const pullTitle = `\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u9010\u4E2A\u8C03\u7528
|
|
4050
|
+
const pullTitle = tf(`\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u9010\u4E2A\u8C03\u7528 {a0} \u5E76\u56DE\u663E\u5B8C\u6574\u7ED3\u679C`, { a0: DISK_SNAPSHOT_SOURCES.map((s) => s.tool).join(" / ") });
|
|
3403
4051
|
const toggleThread = (key) => {
|
|
3404
4052
|
setThreadMode("auto");
|
|
3405
4053
|
setExpandedThreads((prev) => {
|
|
@@ -3451,8 +4099,8 @@ function ObservatoryPanel(props) {
|
|
|
3451
4099
|
React.createElement(
|
|
3452
4100
|
"div",
|
|
3453
4101
|
{ className: "iterate-obs-head", "data-closed": "" },
|
|
3454
|
-
React.createElement("span", { className: "iterate-obs-title" }, "iterate \u89C2\u6D4B\u53F0"),
|
|
3455
|
-
React.createElement("span", { className: "iterate-obs-head-meta" }, `\u6682\u65E0\u8FD0\u884C\u65F6\u89C2\u6D4B\u6570\u636E \xB7
|
|
4102
|
+
React.createElement("span", { className: "iterate-obs-title" }, t("iterate \u89C2\u6D4B\u53F0")),
|
|
4103
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, tf(`\u6682\u65E0\u8FD0\u884C\u65F6\u89C2\u6D4B\u6570\u636E \xB7 {a0}`, { a0: obsHotkeyHint() })),
|
|
3456
4104
|
React.createElement("button", {
|
|
3457
4105
|
className: "iterate-btn",
|
|
3458
4106
|
"data-copied": copiedKey === "pull-snapshot" ? "" : void 0,
|
|
@@ -3460,8 +4108,8 @@ function ObservatoryPanel(props) {
|
|
|
3460
4108
|
e.stopPropagation();
|
|
3461
4109
|
copyInstruction("pull-snapshot", pullInstruction);
|
|
3462
4110
|
},
|
|
3463
|
-
title: "\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u78C1\u76D8\u4E0A\u53EF\u80FD\u5DF2\u6709\u5386\u53F2\u4EA7\u7269\uFF0C\u56DE\u663E\u8FDB\u4F1A\u8BDD\u540E\u89C2\u6D4B\u53F0\u5373\u53EF\u586B\u5145"
|
|
3464
|
-
}, copiedKey === "pull-snapshot" ? "\u5DF2\u590D\u5236" : "\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167")
|
|
4111
|
+
title: t("\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u78C1\u76D8\u4E0A\u53EF\u80FD\u5DF2\u6709\u5386\u53F2\u4EA7\u7269\uFF0C\u56DE\u663E\u8FDB\u4F1A\u8BDD\u540E\u89C2\u6D4B\u53F0\u5373\u53EF\u586B\u5145")
|
|
4112
|
+
}, copiedKey === "pull-snapshot" ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167"))
|
|
3465
4113
|
)
|
|
3466
4114
|
);
|
|
3467
4115
|
}
|
|
@@ -3483,7 +4131,7 @@ function ObservatoryPanel(props) {
|
|
|
3483
4131
|
const renderThreads = () => {
|
|
3484
4132
|
const rounds = manifest.rounds || [];
|
|
3485
4133
|
if (rounds.length === 0) {
|
|
3486
|
-
return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u5BA1\u67E5\u7EBF\u7A0B");
|
|
4134
|
+
return React.createElement("div", { className: "iterate-obs-empty" }, t("\u6682\u65E0\u5BA1\u67E5\u7EBF\u7A0B"));
|
|
3487
4135
|
}
|
|
3488
4136
|
return React.createElement(
|
|
3489
4137
|
"div",
|
|
@@ -3491,34 +4139,34 @@ function ObservatoryPanel(props) {
|
|
|
3491
4139
|
React.createElement(
|
|
3492
4140
|
"div",
|
|
3493
4141
|
{ className: "iterate-obs-bar", style: { marginBottom: 8, flexWrap: "wrap" } },
|
|
3494
|
-
React.createElement("b", {}, "\u5BA1\u67E5\u7EBF\u7A0B"),
|
|
3495
|
-
React.createElement("button", { className: "iterate-btn", onClick: () => setAllThreads("all"), title: "\u5C55\u5F00\u5168\u90E8\u7EBF\u7A0B" }, "\u5168\u90E8\u5C55\u5F00"),
|
|
3496
|
-
React.createElement("button", { className: "iterate-btn", onClick: () => setAllThreads("none"), title: "\u6536\u8D77\u5168\u90E8\u7EBF\u7A0B" }, "\u5168\u90E8\u6536\u8D77"),
|
|
4142
|
+
React.createElement("b", {}, t("\u5BA1\u67E5\u7EBF\u7A0B")),
|
|
4143
|
+
React.createElement("button", { className: "iterate-btn", onClick: () => setAllThreads("all"), title: t("\u5C55\u5F00\u5168\u90E8\u7EBF\u7A0B") }, t("\u5168\u90E8\u5C55\u5F00")),
|
|
4144
|
+
React.createElement("button", { className: "iterate-btn", onClick: () => setAllThreads("none"), title: t("\u6536\u8D77\u5168\u90E8\u7EBF\u7A0B") }, t("\u5168\u90E8\u6536\u8D77")),
|
|
3497
4145
|
React.createElement(
|
|
3498
4146
|
"span",
|
|
3499
4147
|
{ className: "iterate-filter-count" },
|
|
3500
|
-
|
|
4148
|
+
tf(`{a0} \xB7 {a1} \u8F6E`, { a0: threadMode === "all" ? t("\u5168\u90E8\u5C55\u5F00") : threadMode === "none" ? t("\u5168\u90E8\u6536\u8D77") : t("\u81EA\u7531\u5207\u6362"), a1: rounds.length })
|
|
3501
4149
|
)
|
|
3502
4150
|
),
|
|
3503
4151
|
...rounds.map((r, ri) => {
|
|
3504
4152
|
const threads = r.threads || [];
|
|
3505
|
-
const fCount = threads.reduce((sum,
|
|
3506
|
-
const threadBlocks = threads.length === 0 ? [React.createElement("div", { key: "none", className: "iterate-obs-empty" }, "\u672C\u8F6E\u65E0\u7EBF\u7A0B")] : threads.map((
|
|
4153
|
+
const fCount = threads.reduce((sum, t2) => sum + (t2.findings ? t2.findings.length : 0), 0);
|
|
4154
|
+
const threadBlocks = threads.length === 0 ? [React.createElement("div", { key: "none", className: "iterate-obs-empty" }, t("\u672C\u8F6E\u65E0\u7EBF\u7A0B"))] : threads.map((th, ti) => {
|
|
3507
4155
|
const key = `${ri}-${ti}`;
|
|
3508
4156
|
const expanded = isThreadExpanded(key);
|
|
3509
|
-
const dim =
|
|
3510
|
-
const tFindings =
|
|
3511
|
-
const files =
|
|
3512
|
-
const msgs =
|
|
4157
|
+
const dim = th.dimension || t("\u672A\u547D\u540D\u7EF4\u5EA6");
|
|
4158
|
+
const tFindings = th.findings || [];
|
|
4159
|
+
const files = th.readFiles || [];
|
|
4160
|
+
const msgs = th.messages || [];
|
|
3513
4161
|
return React.createElement(
|
|
3514
4162
|
"div",
|
|
3515
4163
|
{ key, className: "iterate-obs-block" },
|
|
3516
4164
|
React.createElement(
|
|
3517
4165
|
"div",
|
|
3518
|
-
{ className: "iterate-obs-block-head", "data-click": "", onClick: () => toggleThread(key), title: expanded ? "\u6536\u8D77\u7EBF\u7A0B" : "\u5C55\u5F00\u7EBF\u7A0B" },
|
|
4166
|
+
{ className: "iterate-obs-block-head", "data-click": "", onClick: () => toggleThread(key), title: expanded ? t("\u6536\u8D77\u7EBF\u7A0B") : t("\u5C55\u5F00\u7EBF\u7A0B") },
|
|
3519
4167
|
React.createElement("span", {}, expanded ? "\u2212" : "+"),
|
|
3520
4168
|
React.createElement("b", {}, dim),
|
|
3521
|
-
React.createElement("span", { className: "iterate-obs-chip" }, `attempt ${typeof
|
|
4169
|
+
React.createElement("span", { className: "iterate-obs-chip" }, `attempt ${typeof th.attempt === "number" ? th.attempt : "?"}`),
|
|
3522
4170
|
React.createElement("span", { className: "iterate-obs-chip" }, `${tFindings.length} findings`)
|
|
3523
4171
|
),
|
|
3524
4172
|
expanded ? React.createElement(
|
|
@@ -3527,15 +4175,15 @@ function ObservatoryPanel(props) {
|
|
|
3527
4175
|
React.createElement(
|
|
3528
4176
|
"div",
|
|
3529
4177
|
{ className: "iterate-obs-bar" },
|
|
3530
|
-
React.createElement("b", {}, "\u53D9\u8FF0"),
|
|
3531
|
-
React.createElement("span", { className: "iterate-obs-head-meta" },
|
|
4178
|
+
React.createElement("b", {}, t("\u53D9\u8FF0")),
|
|
4179
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, tf(`{a0} \u6761`, { a0: msgs.length }))
|
|
3532
4180
|
),
|
|
3533
|
-
...msgs.length === 0 ? [React.createElement("div", { key: "msg-none", className: "iterate-obs-empty" }, "\u65E0\u7EBF\u7D22\u53D9\u8FF0")] : msgs.map((m, mi) => React.createElement("div", { key: mi, className: "iterate-obs-msg" }, String(m))),
|
|
4181
|
+
...msgs.length === 0 ? [React.createElement("div", { key: "msg-none", className: "iterate-obs-empty" }, t("\u65E0\u7EBF\u7D22\u53D9\u8FF0"))] : msgs.map((m, mi) => React.createElement("div", { key: mi, className: "iterate-obs-msg" }, String(m))),
|
|
3534
4182
|
...files.length > 0 ? [
|
|
3535
4183
|
React.createElement(
|
|
3536
4184
|
"div",
|
|
3537
4185
|
{ key: "files-h", className: "iterate-obs-bar", style: { marginTop: 6 } },
|
|
3538
|
-
React.createElement("b", {}, "\u8BFB\u53D6\u6587\u4EF6")
|
|
4186
|
+
React.createElement("b", {}, t("\u8BFB\u53D6\u6587\u4EF6"))
|
|
3539
4187
|
),
|
|
3540
4188
|
...files.map(
|
|
3541
4189
|
(f, fi) => React.createElement("div", { key: `f${fi}`, className: "iterate-obs-file" }, String(f))
|
|
@@ -3544,9 +4192,9 @@ function ObservatoryPanel(props) {
|
|
|
3544
4192
|
React.createElement(
|
|
3545
4193
|
"div",
|
|
3546
4194
|
{ className: "iterate-obs-bar", style: { marginTop: 6 } },
|
|
3547
|
-
React.createElement("b", {}, "\u53D1\u73B0")
|
|
4195
|
+
React.createElement("b", {}, t("\u53D1\u73B0"))
|
|
3548
4196
|
),
|
|
3549
|
-
...tFindings.length === 0 ? [React.createElement("div", { key: "finding-none", className: "iterate-obs-empty" }, "\u672C\u7EBF\u7A0B\u65E0\u53D1\u73B0")] : tFindings.map((f, fi) => threadFindingPill(f, `tf${fi}`))
|
|
4197
|
+
...tFindings.length === 0 ? [React.createElement("div", { key: "finding-none", className: "iterate-obs-empty" }, t("\u672C\u7EBF\u7A0B\u65E0\u53D1\u73B0"))] : tFindings.map((f, fi) => threadFindingPill(f, `tf${fi}`))
|
|
3550
4198
|
) : null
|
|
3551
4199
|
);
|
|
3552
4200
|
});
|
|
@@ -3557,7 +4205,7 @@ function ObservatoryPanel(props) {
|
|
|
3557
4205
|
"div",
|
|
3558
4206
|
{ className: "iterate-obs-block-head" },
|
|
3559
4207
|
React.createElement("b", {}, `Round ${typeof r.round === "number" ? r.round : ri + 1}`),
|
|
3560
|
-
React.createElement("span", { className: "iterate-obs-chip" },
|
|
4208
|
+
React.createElement("span", { className: "iterate-obs-chip" }, tf(`{a0} \u7EBF\u7A0B`, { a0: threads.length })),
|
|
3561
4209
|
React.createElement("span", { className: "iterate-obs-head-meta" }, `${fCount} findings`)
|
|
3562
4210
|
),
|
|
3563
4211
|
React.createElement("div", { className: "iterate-obs-block-body" }, ...threadBlocks)
|
|
@@ -3568,11 +4216,11 @@ function ObservatoryPanel(props) {
|
|
|
3568
4216
|
const renderTrend = () => {
|
|
3569
4217
|
const conv = manifest.convergence || [];
|
|
3570
4218
|
if (conv.length === 0) {
|
|
3571
|
-
return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u6536\u655B\u6570\u636E");
|
|
4219
|
+
return React.createElement("div", { className: "iterate-obs-empty" }, t("\u6682\u65E0\u6536\u655B\u6570\u636E"));
|
|
3572
4220
|
}
|
|
3573
4221
|
const points = conv.map((n, i) => ({ round: i + 1, count: n }));
|
|
3574
4222
|
const report = latestReport(session);
|
|
3575
|
-
const comparison = buildRoundComparison(report, manifest.validations || []);
|
|
4223
|
+
const comparison = buildRoundComparison(report, manifest.validations || [], manifest.fixes || []);
|
|
3576
4224
|
const comparisonTable = comparison.length === 0 ? null : React.createElement(
|
|
3577
4225
|
"table",
|
|
3578
4226
|
{ className: "iterate-obs-table" },
|
|
@@ -3582,11 +4230,11 @@ function ObservatoryPanel(props) {
|
|
|
3582
4230
|
React.createElement(
|
|
3583
4231
|
"tr",
|
|
3584
4232
|
{},
|
|
3585
|
-
React.createElement("th", {}, "\u8F6E\u6B21"),
|
|
3586
|
-
React.createElement("th", {}, "\u53D1\u73B0"),
|
|
3587
|
-
React.createElement("th", {}, "\u5DF2\u4FEE\u590D"),
|
|
3588
|
-
React.createElement("th", {}, "\u4E25\u91CD\u5EA6\u5206\u5E03"),
|
|
3589
|
-
React.createElement("th", {}, "\u9A8C\u8BC1")
|
|
4233
|
+
React.createElement("th", {}, t("\u8F6E\u6B21")),
|
|
4234
|
+
React.createElement("th", {}, t("\u53D1\u73B0")),
|
|
4235
|
+
React.createElement("th", {}, t("\u5DF2\u4FEE\u590D")),
|
|
4236
|
+
React.createElement("th", {}, t("\u4E25\u91CD\u5EA6\u5206\u5E03")),
|
|
4237
|
+
React.createElement("th", {}, t("\u9A8C\u8BC1"))
|
|
3590
4238
|
)
|
|
3591
4239
|
),
|
|
3592
4240
|
React.createElement(
|
|
@@ -3595,7 +4243,7 @@ function ObservatoryPanel(props) {
|
|
|
3595
4243
|
...comparison.map((row) => {
|
|
3596
4244
|
const sevText = Object.entries(row.severities).map(([k, v]) => `${k}:${v}`).join(" \xB7 ");
|
|
3597
4245
|
const anyFailed = row.validations.some((v) => v.allowed !== true);
|
|
3598
|
-
const valText = row.validations.length === 0 ? "\u2014" :
|
|
4246
|
+
const valText = row.validations.length === 0 ? "\u2014" : tf(`{a0}/{a1} \u901A\u8FC7{a2}`, { a0: row.validations.filter((v) => v.allowed === true).length, a1: row.validations.length, a2: anyFailed ? t("\uFF08\u542B\u5931\u8D25\uFF09") : "" });
|
|
3599
4247
|
return React.createElement(
|
|
3600
4248
|
"tr",
|
|
3601
4249
|
{ key: `cmp-${row.round}` },
|
|
@@ -3617,7 +4265,7 @@ function ObservatoryPanel(props) {
|
|
|
3617
4265
|
React.createElement(
|
|
3618
4266
|
"div",
|
|
3619
4267
|
{ className: "iterate-obs-bar", style: { marginTop: 6 } },
|
|
3620
|
-
`\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\uFF1A
|
|
4268
|
+
tf(`\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\uFF1A{a0}{a1}`, { a0: conv.join(" \u2192 "), a1: typeof manifest.round === "number" ? tf(` \xB7 \u5F53\u524D Round {a0}`, { a0: manifest.round }) : "" })
|
|
3621
4269
|
),
|
|
3622
4270
|
comparisonTable ? React.createElement(
|
|
3623
4271
|
"div",
|
|
@@ -3625,11 +4273,11 @@ function ObservatoryPanel(props) {
|
|
|
3625
4273
|
React.createElement(
|
|
3626
4274
|
"div",
|
|
3627
4275
|
{ className: "iterate-obs-block-head" },
|
|
3628
|
-
React.createElement("span", {}, "\u8DE8\u8F6E\u5BF9\u6BD4\uFF08\u672C\u4F1A\u8BDD\uFF09"),
|
|
4276
|
+
React.createElement("span", {}, t("\u8DE8\u8F6E\u5BF9\u6BD4\uFF08\u672C\u4F1A\u8BDD\uFF09")),
|
|
3629
4277
|
React.createElement(
|
|
3630
4278
|
"span",
|
|
3631
4279
|
{ className: "iterate-obs-head-meta" },
|
|
3632
|
-
"\u8DE8\u4F1A\u8BDD\u5BF9\u6BD4\uFF1A\u672C\u8868\u53EA\u8986\u76D6\u5F53\u524D\u4F1A\u8BDD\uFF1B\u70B9\u300C\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u300D\u8BA9\u6A21\u578B\u56DE\u663E iterate_history\uFF0C\u518D\u7528\u300C\u5BFC\u51FA JSON\u300D\u5E76\u6392\u6BD4\u8F83\u4E24\u6B21\u8FD0\u884C"
|
|
4280
|
+
t("\u8DE8\u4F1A\u8BDD\u5BF9\u6BD4\uFF1A\u672C\u8868\u53EA\u8986\u76D6\u5F53\u524D\u4F1A\u8BDD\uFF1B\u70B9\u300C\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u300D\u8BA9\u6A21\u578B\u56DE\u663E iterate_history\uFF0C\u518D\u7528\u300C\u5BFC\u51FA JSON\u300D\u5E76\u6392\u6BD4\u8F83\u4E24\u6B21\u8FD0\u884C")
|
|
3633
4281
|
)
|
|
3634
4282
|
),
|
|
3635
4283
|
React.createElement("div", { className: "iterate-obs-block-body" }, comparisonTable)
|
|
@@ -3639,7 +4287,7 @@ function ObservatoryPanel(props) {
|
|
|
3639
4287
|
const renderFindings = () => {
|
|
3640
4288
|
const findings = manifest.findings || [];
|
|
3641
4289
|
if (findings.length === 0) {
|
|
3642
|
-
return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u53D1\u73B0");
|
|
4290
|
+
return React.createElement("div", { className: "iterate-obs-empty" }, t("\u6682\u65E0\u53D1\u73B0"));
|
|
3643
4291
|
}
|
|
3644
4292
|
const opts = buildFilterOptions(findings);
|
|
3645
4293
|
const { filtered } = filterFindingsWithIndices(findings, f3Filter);
|
|
@@ -3653,13 +4301,13 @@ function ObservatoryPanel(props) {
|
|
|
3653
4301
|
const filterBar = React.createElement(
|
|
3654
4302
|
"div",
|
|
3655
4303
|
{ className: "iterate-obs-bar", style: { marginBottom: 8, flexWrap: "wrap" } },
|
|
3656
|
-
React.createElement("b", {}, "\u53D1\u73B0"),
|
|
4304
|
+
React.createElement("b", {}, t("\u53D1\u73B0")),
|
|
3657
4305
|
...opts.severities.map(
|
|
3658
4306
|
(s) => React.createElement("button", {
|
|
3659
4307
|
key: `sev-${s.value}`,
|
|
3660
4308
|
className: "iterate-filter-chip",
|
|
3661
4309
|
"data-active": f3Filter.severities.includes(s.value) ? "" : void 0,
|
|
3662
|
-
title:
|
|
4310
|
+
title: tf(`{a0}\uFF08{a1} \u9879\uFF09`, { a0: s.value, a1: s.count }),
|
|
3663
4311
|
onClick: () => toggleSeverity(s.value)
|
|
3664
4312
|
}, `${s.value} ${s.count}`)
|
|
3665
4313
|
),
|
|
@@ -3668,13 +4316,13 @@ function ObservatoryPanel(props) {
|
|
|
3668
4316
|
{
|
|
3669
4317
|
className: "iterate-filter-select",
|
|
3670
4318
|
value: f3Filter.dimensions[0] || "",
|
|
3671
|
-
"aria-label": "\u6309\u7EF4\u5EA6\u7B5B\u9009",
|
|
4319
|
+
"aria-label": t("\u6309\u7EF4\u5EA6\u7B5B\u9009"),
|
|
3672
4320
|
onChange: (e) => {
|
|
3673
4321
|
const v = e.target.value;
|
|
3674
4322
|
setF3Filter((prev) => ({ ...prev, dimensions: v ? [v] : [] }));
|
|
3675
4323
|
}
|
|
3676
4324
|
},
|
|
3677
|
-
React.createElement("option", { value: "" }, "\u5168\u90E8\u7EF4\u5EA6"),
|
|
4325
|
+
React.createElement("option", { value: "" }, t("\u5168\u90E8\u7EF4\u5EA6")),
|
|
3678
4326
|
...opts.dimensions.map(
|
|
3679
4327
|
(d) => React.createElement("option", { key: d.value, value: d.value }, `${d.value} (${d.count})`)
|
|
3680
4328
|
)
|
|
@@ -3682,16 +4330,16 @@ function ObservatoryPanel(props) {
|
|
|
3682
4330
|
React.createElement("input", {
|
|
3683
4331
|
className: "iterate-filter-search",
|
|
3684
4332
|
type: "search",
|
|
3685
|
-
placeholder: "\u641C\u7D22\u53D1\u73B0\u2026",
|
|
3686
|
-
"aria-label": "\u641C\u7D22\u53D1\u73B0",
|
|
4333
|
+
placeholder: t("\u641C\u7D22\u53D1\u73B0\u2026"),
|
|
4334
|
+
"aria-label": t("\u641C\u7D22\u53D1\u73B0"),
|
|
3687
4335
|
value: f3Filter.search,
|
|
3688
4336
|
onChange: (e) => setF3Filter((prev) => ({ ...prev, search: e.target.value }))
|
|
3689
4337
|
}),
|
|
3690
4338
|
filterActive ? React.createElement("button", {
|
|
3691
4339
|
className: "iterate-filter-clear",
|
|
3692
4340
|
onClick: () => setF3Filter({ severities: [], dimensions: [], search: "" }),
|
|
3693
|
-
title: "\u6E05\u9664\u5168\u90E8\u7B5B\u9009"
|
|
3694
|
-
}, "\u6E05\u9664\u7B5B\u9009") : null,
|
|
4341
|
+
title: t("\u6E05\u9664\u5168\u90E8\u7B5B\u9009")
|
|
4342
|
+
}, t("\u6E05\u9664\u7B5B\u9009")) : null,
|
|
3695
4343
|
React.createElement("span", { className: "iterate-filter-count" }, `${filtered.length}/${findings.length}`)
|
|
3696
4344
|
);
|
|
3697
4345
|
const findingBlocks = filtered.map((f, i) => {
|
|
@@ -3716,7 +4364,7 @@ function ObservatoryPanel(props) {
|
|
|
3716
4364
|
React.createElement("span", {}, severityLabel(finding.severity)),
|
|
3717
4365
|
React.createElement("span", { className: "iterate-obs-file" }, loc),
|
|
3718
4366
|
React.createElement("span", {}, String(finding.dimension || "")),
|
|
3719
|
-
finding.acknowledged === true ? React.createElement("span", { className: "iterate-obs-chip" }, "\u5DF2\u786E\u8BA4") : null
|
|
4367
|
+
finding.acknowledged === true ? React.createElement("span", { className: "iterate-obs-chip" }, t("\u5DF2\u786E\u8BA4")) : null
|
|
3720
4368
|
),
|
|
3721
4369
|
React.createElement(
|
|
3722
4370
|
"div",
|
|
@@ -3729,21 +4377,21 @@ function ObservatoryPanel(props) {
|
|
|
3729
4377
|
className: "iterate-btn",
|
|
3730
4378
|
"data-copied": copiedKey === `${k}-fix` ? "" : void 0,
|
|
3731
4379
|
onClick: () => copyInstruction(`${k}-fix`, String(finding.suggested_fix || "")),
|
|
3732
|
-
title: "\u590D\u5236\u8BE5 finding \u7684\u5EFA\u8BAE\u4FEE\u590D\u6587\u672C"
|
|
3733
|
-
}, copiedKey === `${k}-fix` ? "\u5DF2\u590D\u5236" : "\u590D\u5236 suggested_fix"),
|
|
4380
|
+
title: t("\u590D\u5236\u8BE5 finding \u7684\u5EFA\u8BAE\u4FEE\u590D\u6587\u672C")
|
|
4381
|
+
}, copiedKey === `${k}-fix` ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236 suggested_fix")),
|
|
3734
4382
|
React.createElement("button", {
|
|
3735
4383
|
className: "iterate-btn",
|
|
3736
4384
|
"data-copied": copiedKey === `${k}-known` ? "" : void 0,
|
|
3737
4385
|
onClick: () => copyInstruction(`${k}-known`, knownInstruction),
|
|
3738
|
-
title: "\u590D\u5236 iterate_triage \u6307\u4EE4\u6587\u672C\uFF08\u6807\u8BB0\u4E3A known_intentional\uFF09"
|
|
3739
|
-
}, copiedKey === `${k}-known` ? "\u5DF2\u590D\u5236" : "known_intentional"),
|
|
4386
|
+
title: t("\u590D\u5236 iterate_triage \u6307\u4EE4\u6587\u672C\uFF08\u6807\u8BB0\u4E3A known_intentional\uFF09")
|
|
4387
|
+
}, copiedKey === `${k}-known` ? t("\u5DF2\u590D\u5236") : "known_intentional"),
|
|
3740
4388
|
React.createElement("button", {
|
|
3741
4389
|
className: "iterate-btn",
|
|
3742
4390
|
"data-primary": "",
|
|
3743
4391
|
"data-copied": copiedKey === `${k}-repair` ? "" : void 0,
|
|
3744
4392
|
onClick: () => copyInstruction(`${k}-repair`, fixInstruction),
|
|
3745
|
-
title: "\u590D\u5236 iterate_fix \u6307\u4EE4\u6587\u672C"
|
|
3746
|
-
}, copiedKey === `${k}-repair` ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u4FEE\u590D\u6307\u4EE4")
|
|
4393
|
+
title: t("\u590D\u5236 iterate_fix \u6307\u4EE4\u6587\u672C")
|
|
4394
|
+
}, copiedKey === `${k}-repair` ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236\u4FEE\u590D\u6307\u4EE4"))
|
|
3747
4395
|
)
|
|
3748
4396
|
)
|
|
3749
4397
|
);
|
|
@@ -3753,7 +4401,7 @@ function ObservatoryPanel(props) {
|
|
|
3753
4401
|
"div",
|
|
3754
4402
|
{},
|
|
3755
4403
|
filterBar,
|
|
3756
|
-
React.createElement("div", { className: "iterate-obs-empty" }, "\u65E0\u5339\u914D\u7684\u53D1\u73B0")
|
|
4404
|
+
React.createElement("div", { className: "iterate-obs-empty" }, t("\u65E0\u5339\u914D\u7684\u53D1\u73B0"))
|
|
3757
4405
|
);
|
|
3758
4406
|
}
|
|
3759
4407
|
return React.createElement("div", {}, filterBar, ...findingBlocks);
|
|
@@ -3761,7 +4409,7 @@ function ObservatoryPanel(props) {
|
|
|
3761
4409
|
const renderFixes = () => {
|
|
3762
4410
|
const fixes = manifest.fixes || [];
|
|
3763
4411
|
if (fixes.length === 0) {
|
|
3764
|
-
return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u4FEE\u590D\u8BB0\u5F55");
|
|
4412
|
+
return React.createElement("div", { className: "iterate-obs-empty" }, t("\u6682\u65E0\u4FEE\u590D\u8BB0\u5F55"));
|
|
3765
4413
|
}
|
|
3766
4414
|
return React.createElement("div", {}, ...fixes.map((f, i) => {
|
|
3767
4415
|
const k = `f4-${i}`;
|
|
@@ -3777,7 +4425,7 @@ function ObservatoryPanel(props) {
|
|
|
3777
4425
|
{ className: "iterate-obs-block-head" },
|
|
3778
4426
|
React.createElement("b", {}, id),
|
|
3779
4427
|
React.createElement("span", { className: "iterate-obs-file" }, String(f.file || "?")),
|
|
3780
|
-
React.createElement("span", { className: "iterate-obs-chip" }, f.success === true ? "\u6210\u529F" : "\u5931\u8D25"),
|
|
4428
|
+
React.createElement("span", { className: "iterate-obs-chip" }, f.success === true ? t("\u6210\u529F") : t("\u5931\u8D25")),
|
|
3781
4429
|
typeof f.round === "number" ? React.createElement("span", { className: "iterate-obs-head-meta" }, `Round ${f.round}`) : null
|
|
3782
4430
|
),
|
|
3783
4431
|
React.createElement(
|
|
@@ -3794,8 +4442,8 @@ function ObservatoryPanel(props) {
|
|
|
3794
4442
|
"data-danger": "",
|
|
3795
4443
|
"data-copied": copiedKey === `${k}-rb` ? "" : void 0,
|
|
3796
4444
|
onClick: () => copyInstruction(`${k}-rb`, rollbackText),
|
|
3797
|
-
title: "\u590D\u5236 iterate_rollback \u6307\u4EE4\u6587\u672C"
|
|
3798
|
-
}, copiedKey === `${k}-rb` ? "\u5DF2\u590D\u5236" : "\u56DE\u6EDA")
|
|
4445
|
+
title: t("\u590D\u5236 iterate_rollback \u6307\u4EE4\u6587\u672C")
|
|
4446
|
+
}, copiedKey === `${k}-rb` ? t("\u5DF2\u590D\u5236") : t("\u56DE\u6EDA"))
|
|
3799
4447
|
)
|
|
3800
4448
|
)
|
|
3801
4449
|
);
|
|
@@ -3807,7 +4455,7 @@ function ObservatoryPanel(props) {
|
|
|
3807
4455
|
return React.createElement(
|
|
3808
4456
|
"div",
|
|
3809
4457
|
{ className: "iterate-obs-empty" },
|
|
3810
|
-
diskEmptyStateText(toolCalledInSession(session, "iterate_transcript"), "\u65AD\u70B9")
|
|
4458
|
+
diskEmptyStateText(toolCalledInSession(session, "iterate_transcript"), t("\u65AD\u70B9"), getLang())
|
|
3811
4459
|
);
|
|
3812
4460
|
}
|
|
3813
4461
|
const resumeText = buildCheckpointResumeInstruction();
|
|
@@ -3819,7 +4467,7 @@ function ObservatoryPanel(props) {
|
|
|
3819
4467
|
React.createElement(
|
|
3820
4468
|
"div",
|
|
3821
4469
|
{ className: "iterate-obs-block-head" },
|
|
3822
|
-
React.createElement("b", {}, "\u65AD\u70B9"),
|
|
4470
|
+
React.createElement("b", {}, t("\u65AD\u70B9")),
|
|
3823
4471
|
React.createElement("span", { className: "iterate-obs-head-meta" }, String(cp.updatedAt || ""))
|
|
3824
4472
|
),
|
|
3825
4473
|
React.createElement(
|
|
@@ -3842,36 +4490,36 @@ function ObservatoryPanel(props) {
|
|
|
3842
4490
|
"data-primary": "",
|
|
3843
4491
|
"data-copied": copiedKey === "cp-resume" ? "" : void 0,
|
|
3844
4492
|
onClick: () => copyInstruction("cp-resume", resumeText),
|
|
3845
|
-
title: "\u590D\u5236 iterate_checkpoint resume \u6307\u4EE4\u6587\u672C\uFF08\u52A0\u8F7D\u65AD\u70B9\u5E76\u8BA1\u6570\u4E00\u6B21\u6062\u590D\uFF09"
|
|
3846
|
-
}, copiedKey === "cp-resume" ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u6062\u590D\u6307\u4EE4"),
|
|
4493
|
+
title: t("\u590D\u5236 iterate_checkpoint resume \u6307\u4EE4\u6587\u672C\uFF08\u52A0\u8F7D\u65AD\u70B9\u5E76\u8BA1\u6570\u4E00\u6B21\u6062\u590D\uFF09")
|
|
4494
|
+
}, copiedKey === "cp-resume" ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236\u6062\u590D\u6307\u4EE4")),
|
|
3847
4495
|
React.createElement("button", {
|
|
3848
4496
|
className: "iterate-btn",
|
|
3849
4497
|
"data-danger": "",
|
|
3850
4498
|
"data-copied": copiedKey === "cp-clear" ? "" : void 0,
|
|
3851
4499
|
onClick: () => copyInstruction("cp-clear", clearText),
|
|
3852
|
-
title: "\u590D\u5236 iterate_checkpoint clear \u6307\u4EE4\u6587\u672C\uFF08\u6E05\u9664\u5F53\u524D\u65AD\u70B9\uFF0C\u4F9B\u91CD\u65B0\u5F00\u59CB\uFF09"
|
|
3853
|
-
}, copiedKey === "cp-clear" ? "\u5DF2\u590D\u5236" : "\u6E05\u9664\u65AD\u70B9")
|
|
4500
|
+
title: t("\u590D\u5236 iterate_checkpoint clear \u6307\u4EE4\u6587\u672C\uFF08\u6E05\u9664\u5F53\u524D\u65AD\u70B9\uFF0C\u4F9B\u91CD\u65B0\u5F00\u59CB\uFF09")
|
|
4501
|
+
}, copiedKey === "cp-clear" ? t("\u5DF2\u590D\u5236") : t("\u6E05\u9664\u65AD\u70B9"))
|
|
3854
4502
|
)
|
|
3855
4503
|
)
|
|
3856
4504
|
);
|
|
3857
4505
|
};
|
|
3858
4506
|
const renderConsole = () => {
|
|
3859
4507
|
const approval = manifest.approval || { policy: "ask" };
|
|
3860
|
-
const policyLabelMap = { ask: "\u8BE2\u95EE\u7528\u6237", deny: "\u62D2\u7EDD", allow: "\u76F4\u63A5\u6267\u884C" };
|
|
4508
|
+
const policyLabelMap = { ask: t("\u8BE2\u95EE\u7528\u6237"), deny: t("\u62D2\u7EDD"), allow: t("\u76F4\u63A5\u6267\u884C") };
|
|
3861
4509
|
const policyLabel = policyLabelMap[String(approval.policy || "ask")] || String(approval.policy || "ask");
|
|
3862
4510
|
const nudge = manifest.nudge || null;
|
|
3863
4511
|
const present = Boolean(nudge && nudge.text);
|
|
3864
4512
|
const activeNudgeText = nudge && nudge.text ? nudge.text : "";
|
|
3865
|
-
const nudgeInstruction = `\u8BF7\u8C03\u7528 \`iterate_transcript\` \u5199\u5165 nudge \u6307\u4EE4\uFF1A
|
|
4513
|
+
const nudgeInstruction = tf(`\u8BF7\u8C03\u7528 \`iterate_transcript\` \u5199\u5165 nudge \u6307\u4EE4\uFF1A
|
|
3866
4514
|
|
|
3867
4515
|
\`\`\`json
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
const activeClearInstruction = `\u8BF7\u8C03\u7528 \`iterate_transcript\` \u6E05\u9664\u5F53\u524D nudge\uFF1A
|
|
4516
|
+
{a0}
|
|
4517
|
+
\`\`\``, { a0: JSON.stringify({ operation: "nudge", text: nudgeText }, null, 2) });
|
|
4518
|
+
const activeClearInstruction = tf(`\u8BF7\u8C03\u7528 \`iterate_transcript\` \u6E05\u9664\u5F53\u524D nudge\uFF1A
|
|
3871
4519
|
|
|
3872
4520
|
\`\`\`json
|
|
3873
|
-
|
|
3874
|
-
|
|
4521
|
+
{a0}
|
|
4522
|
+
\`\`\``, { a0: JSON.stringify({ operation: "nudge", text: null }, null, 2) });
|
|
3875
4523
|
const validations = Array.isArray(manifest.validations) ? manifest.validations : [];
|
|
3876
4524
|
const abortedByValidation = String(manifest.stoppedReason || "") === "aborted_by_validation";
|
|
3877
4525
|
const validationRows = validations.map((v, i) => {
|
|
@@ -3893,10 +4541,10 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3893
4541
|
React.createElement(
|
|
3894
4542
|
"span",
|
|
3895
4543
|
{ className: "iterate-obs-chip", "data-status": allowed ? "pass" : "fail" },
|
|
3896
|
-
allowed ? "\u901A\u8FC7" : "\u5931\u8D25"
|
|
4544
|
+
allowed ? t("\u901A\u8FC7") : t("\u5931\u8D25")
|
|
3897
4545
|
),
|
|
3898
4546
|
React.createElement("span", { className: "iterate-obs-head-meta" }, exit),
|
|
3899
|
-
highlight ? React.createElement("span", { className: "iterate-obs-chip", "data-status": "fail" }, "\u5BFC\u81F4\u4E2D\u6B62") : null
|
|
4547
|
+
highlight ? React.createElement("span", { className: "iterate-obs-chip", "data-status": "fail" }, t("\u5BFC\u81F4\u4E2D\u6B62")) : null
|
|
3900
4548
|
);
|
|
3901
4549
|
});
|
|
3902
4550
|
const validationsBlock = React.createElement(
|
|
@@ -3905,9 +4553,9 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3905
4553
|
React.createElement(
|
|
3906
4554
|
"div",
|
|
3907
4555
|
{ className: "iterate-obs-block-head" },
|
|
3908
|
-
React.createElement("span", {}, "\u9A8C\u8BC1\u7ED3\u679C"),
|
|
3909
|
-
validations.length === 0 ? React.createElement("span", { className: "iterate-obs-head-meta" }, "\u672C\u8F6E\u5C1A\u65E0\u9A8C\u8BC1\u8BB0\u5F55") : React.createElement("span", { className: "iterate-obs-head-meta" },
|
|
3910
|
-
abortedByValidation ? React.createElement("span", { className: "iterate-obs-badge", "data-status": "fail" }, "\u56E0\u9A8C\u8BC1\u5931\u8D25\u4E2D\u6B62") : null
|
|
4556
|
+
React.createElement("span", {}, t("\u9A8C\u8BC1\u7ED3\u679C")),
|
|
4557
|
+
validations.length === 0 ? React.createElement("span", { className: "iterate-obs-head-meta" }, t("\u672C\u8F6E\u5C1A\u65E0\u9A8C\u8BC1\u8BB0\u5F55")) : React.createElement("span", { className: "iterate-obs-head-meta" }, tf(`{a0} \u6761\u547D\u4EE4`, { a0: validations.length })),
|
|
4558
|
+
abortedByValidation ? React.createElement("span", { className: "iterate-obs-badge", "data-status": "fail" }, t("\u56E0\u9A8C\u8BC1\u5931\u8D25\u4E2D\u6B62")) : null
|
|
3911
4559
|
),
|
|
3912
4560
|
React.createElement(
|
|
3913
4561
|
"div",
|
|
@@ -3915,7 +4563,7 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3915
4563
|
validations.length === 0 ? React.createElement(
|
|
3916
4564
|
"div",
|
|
3917
4565
|
{ className: "iterate-obs-empty" },
|
|
3918
|
-
"\u5C1A\u672A\u62C9\u53D6\u9A8C\u8BC1\u7ED3\u679C\uFF1A\u6570\u636E\u5728\u78C1\u76D8 transcript \u91CC\uFF0C\u70B9\u300C\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u300D\u590D\u5236\u6307\u4EE4\u53D1\u56DE\u4F1A\u8BDD\u5373\u53EF\u56DE\u586B\u3002"
|
|
4566
|
+
t("\u5C1A\u672A\u62C9\u53D6\u9A8C\u8BC1\u7ED3\u679C\uFF1A\u6570\u636E\u5728\u78C1\u76D8 transcript \u91CC\uFF0C\u70B9\u300C\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u300D\u590D\u5236\u6307\u4EE4\u53D1\u56DE\u4F1A\u8BDD\u5373\u53EF\u56DE\u586B\u3002")
|
|
3919
4567
|
) : React.createElement(React.Fragment, null, ...validationRows)
|
|
3920
4568
|
)
|
|
3921
4569
|
);
|
|
@@ -3925,9 +4573,9 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3925
4573
|
React.createElement(
|
|
3926
4574
|
"div",
|
|
3927
4575
|
{ className: "iterate-obs-block-head" },
|
|
3928
|
-
React.createElement("span", {}, "\u8FD0\u884C\u63A7\u5236\u53F0"),
|
|
4576
|
+
React.createElement("span", {}, t("\u8FD0\u884C\u63A7\u5236\u53F0")),
|
|
3929
4577
|
React.createElement("span", { className: "iterate-obs-badge", "data-live": live ? "" : void 0 }, runStatusText(live, manifest)),
|
|
3930
|
-
React.createElement("span", { className: "iterate-obs-head-meta" }, `\u6279\u51C6\u7B56\u7565\uFF1A
|
|
4578
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, tf(`\u6279\u51C6\u7B56\u7565\uFF1A{a0}`, { a0: policyLabel }))
|
|
3931
4579
|
),
|
|
3932
4580
|
React.createElement(
|
|
3933
4581
|
"div",
|
|
@@ -3935,21 +4583,21 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3935
4583
|
present ? React.createElement(
|
|
3936
4584
|
"div",
|
|
3937
4585
|
{ className: "iterate-obs-bar", style: { marginBottom: 6 } },
|
|
3938
|
-
React.createElement("b", {}, "\u5F53\u524D nudge"),
|
|
4586
|
+
React.createElement("b", {}, t("\u5F53\u524D nudge")),
|
|
3939
4587
|
React.createElement("span", { className: "iterate-obs-msg" }, activeNudgeText),
|
|
3940
4588
|
React.createElement("button", {
|
|
3941
4589
|
className: "iterate-btn",
|
|
3942
4590
|
"data-copied": copiedKey === "nudge-clear" ? "" : void 0,
|
|
3943
4591
|
onClick: () => copyInstruction("nudge-clear", activeClearInstruction),
|
|
3944
|
-
title: "\u590D\u5236\u6E05\u9664 nudge \u6307\u4EE4\uFF08\u5199 text:null\uFF09\uFF0C\u5728\u8FD0\u884C\u63A7\u5236\u53F0\u63D0\u793A\u6E05\u9664\u5DF2\u6301\u4E45\u5316\u7684 nudge"
|
|
3945
|
-
}, copiedKey === "nudge-clear" ? "\u5DF2\u590D\u5236\u6E05\u9664\u6307\u4EE4" : "\u590D\u5236\u6E05\u9664\u6307\u4EE4")
|
|
4592
|
+
title: t("\u590D\u5236\u6E05\u9664 nudge \u6307\u4EE4\uFF08\u5199 text:null\uFF09\uFF0C\u5728\u8FD0\u884C\u63A7\u5236\u53F0\u63D0\u793A\u6E05\u9664\u5DF2\u6301\u4E45\u5316\u7684 nudge")
|
|
4593
|
+
}, copiedKey === "nudge-clear" ? t("\u5DF2\u590D\u5236\u6E05\u9664\u6307\u4EE4") : t("\u590D\u5236\u6E05\u9664\u6307\u4EE4"))
|
|
3946
4594
|
) : null,
|
|
3947
4595
|
React.createElement("textarea", {
|
|
3948
4596
|
className: "iterate-obs-input iterate-obs-mono",
|
|
3949
4597
|
rows: 3,
|
|
3950
|
-
placeholder: "\u8D77\u8349\u4E00\u6761 nudge \u65B9\u5411\u6307\u4EE4\u2026",
|
|
4598
|
+
placeholder: t("\u8D77\u8349\u4E00\u6761 nudge \u65B9\u5411\u6307\u4EE4\u2026"),
|
|
3951
4599
|
value: nudgeText,
|
|
3952
|
-
"aria-label": "nudge \u8349\u7A3F",
|
|
4600
|
+
"aria-label": t("nudge \u8349\u7A3F"),
|
|
3953
4601
|
onChange: (e) => setNudgeText(e.target.value)
|
|
3954
4602
|
}),
|
|
3955
4603
|
React.createElement(
|
|
@@ -3961,8 +4609,8 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3961
4609
|
"data-copied": copiedKey === "nudge" ? "" : void 0,
|
|
3962
4610
|
disabled: !nudgeText,
|
|
3963
4611
|
onClick: () => copyInstruction("nudge", nudgeInstruction),
|
|
3964
|
-
title: "\u590D\u5236 iterate_transcript nudge \u6307\u4EE4\u6587\u672C"
|
|
3965
|
-
}, copiedKey === "nudge" ? "\u5DF2\u590D\u5236" : "\u590D\u5236 nudge \u6307\u4EE4")
|
|
4612
|
+
title: t("\u590D\u5236 iterate_transcript nudge \u6307\u4EE4\u6587\u672C")
|
|
4613
|
+
}, copiedKey === "nudge" ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236 nudge \u6307\u4EE4"))
|
|
3966
4614
|
),
|
|
3967
4615
|
validationsBlock
|
|
3968
4616
|
)
|
|
@@ -3971,11 +4619,11 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3971
4619
|
const renderTimeline = () => {
|
|
3972
4620
|
const entries = manifest.timeline || [];
|
|
3973
4621
|
if (entries.length === 0) {
|
|
3974
|
-
return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u65F6\u95F4\u7EBF\u6761\u76EE");
|
|
4622
|
+
return React.createElement("div", { className: "iterate-obs-empty" }, t("\u6682\u65E0\u65F6\u95F4\u7EBF\u6761\u76EE"));
|
|
3975
4623
|
}
|
|
3976
|
-
const types = Array.from(new Set(entries.map((
|
|
4624
|
+
const types = Array.from(new Set(entries.map((t2) => String(t2.type || "unknown")).filter(Boolean))).sort();
|
|
3977
4625
|
const rounds = Array.from(
|
|
3978
|
-
new Set(entries.map((
|
|
4626
|
+
new Set(entries.map((t2) => typeof t2.round === "number" ? String(t2.round) : "").filter(Boolean))
|
|
3979
4627
|
).sort((a, b) => Number(a) - Number(b));
|
|
3980
4628
|
const filtered = filterTimelineEntries(entries, {
|
|
3981
4629
|
type: timelineType,
|
|
@@ -3989,14 +4637,14 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3989
4637
|
setTimelineSearch("");
|
|
3990
4638
|
};
|
|
3991
4639
|
const sorted = filtered.slice();
|
|
3992
|
-
const rows = sorted.map((
|
|
4640
|
+
const rows = sorted.map((t2, i) => {
|
|
3993
4641
|
const k = `f7-${i}`;
|
|
3994
4642
|
let dataText = "";
|
|
3995
|
-
if (
|
|
4643
|
+
if (t2.data && typeof t2.data === "object") {
|
|
3996
4644
|
try {
|
|
3997
|
-
dataText = JSON.stringify(
|
|
4645
|
+
dataText = JSON.stringify(t2.data);
|
|
3998
4646
|
} catch {
|
|
3999
|
-
dataText = String(
|
|
4647
|
+
dataText = String(t2.data);
|
|
4000
4648
|
}
|
|
4001
4649
|
}
|
|
4002
4650
|
return React.createElement(
|
|
@@ -4005,11 +4653,11 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
4005
4653
|
React.createElement(
|
|
4006
4654
|
"div",
|
|
4007
4655
|
{ className: "iterate-obs-bar", style: { width: "100%" } },
|
|
4008
|
-
React.createElement("span", { className: "iterate-obs-chip" }, String(
|
|
4656
|
+
React.createElement("span", { className: "iterate-obs-chip" }, String(t2.type || "?")),
|
|
4009
4657
|
React.createElement(
|
|
4010
4658
|
"span",
|
|
4011
4659
|
{ className: "iterate-obs-head-meta" },
|
|
4012
|
-
`${typeof
|
|
4660
|
+
`${typeof t2.round === "number" ? `Round ${t2.round} \xB7 ` : ""}${String(t2.timestamp || "")}`
|
|
4013
4661
|
)
|
|
4014
4662
|
),
|
|
4015
4663
|
dataText ? React.createElement("div", { className: "iterate-obs-code" }, dataText) : null
|
|
@@ -4018,42 +4666,42 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
4018
4666
|
const filterBar = React.createElement(
|
|
4019
4667
|
"div",
|
|
4020
4668
|
{ className: "iterate-obs-bar", style: { marginBottom: 8 } },
|
|
4021
|
-
React.createElement("b", {}, "\u65F6\u95F4\u7EBF"),
|
|
4669
|
+
React.createElement("b", {}, t("\u65F6\u95F4\u7EBF")),
|
|
4022
4670
|
React.createElement(
|
|
4023
4671
|
"select",
|
|
4024
4672
|
{
|
|
4025
4673
|
className: "iterate-filter-select",
|
|
4026
4674
|
value: timelineType,
|
|
4027
|
-
"aria-label": "\u6309\u65F6\u95F4\u7EBF\u7C7B\u578B\u7B5B\u9009",
|
|
4675
|
+
"aria-label": t("\u6309\u65F6\u95F4\u7EBF\u7C7B\u578B\u7B5B\u9009"),
|
|
4028
4676
|
onChange: (e) => setTimelineType(e.target.value)
|
|
4029
4677
|
},
|
|
4030
|
-
React.createElement("option", { value: "" }, "\u5168\u90E8\u7C7B\u578B"),
|
|
4031
|
-
...types.map((
|
|
4678
|
+
React.createElement("option", { value: "" }, t("\u5168\u90E8\u7C7B\u578B")),
|
|
4679
|
+
...types.map((t2) => React.createElement("option", { key: t2, value: t2 }, t2))
|
|
4032
4680
|
),
|
|
4033
4681
|
React.createElement(
|
|
4034
4682
|
"select",
|
|
4035
4683
|
{
|
|
4036
4684
|
className: "iterate-filter-select",
|
|
4037
4685
|
value: timelineRound,
|
|
4038
|
-
"aria-label": "\u6309\u8F6E\u6B21\u7B5B\u9009",
|
|
4686
|
+
"aria-label": t("\u6309\u8F6E\u6B21\u7B5B\u9009"),
|
|
4039
4687
|
onChange: (e) => setTimelineRound(e.target.value)
|
|
4040
4688
|
},
|
|
4041
|
-
React.createElement("option", { value: "" }, "\u5168\u90E8\u8F6E\u6B21"),
|
|
4689
|
+
React.createElement("option", { value: "" }, t("\u5168\u90E8\u8F6E\u6B21")),
|
|
4042
4690
|
...rounds.map((r) => React.createElement("option", { key: r, value: r }, `Round ${r}`))
|
|
4043
4691
|
),
|
|
4044
4692
|
React.createElement("input", {
|
|
4045
4693
|
className: "iterate-filter-search",
|
|
4046
4694
|
type: "search",
|
|
4047
|
-
placeholder: "\u641C\u7D22\u65F6\u95F4\u7EBF\u2026",
|
|
4048
|
-
"aria-label": "\u641C\u7D22\u65F6\u95F4\u7EBF",
|
|
4695
|
+
placeholder: t("\u641C\u7D22\u65F6\u95F4\u7EBF\u2026"),
|
|
4696
|
+
"aria-label": t("\u641C\u7D22\u65F6\u95F4\u7EBF"),
|
|
4049
4697
|
value: timelineSearch,
|
|
4050
4698
|
onChange: (e) => setTimelineSearch(e.target.value)
|
|
4051
4699
|
}),
|
|
4052
4700
|
filterActive ? React.createElement("button", {
|
|
4053
4701
|
className: "iterate-filter-clear",
|
|
4054
4702
|
onClick: clearTimelineFilter,
|
|
4055
|
-
title: "\u6E05\u9664\u5168\u90E8\u7B5B\u9009"
|
|
4056
|
-
}, "\u6E05\u9664") : null,
|
|
4703
|
+
title: t("\u6E05\u9664\u5168\u90E8\u7B5B\u9009")
|
|
4704
|
+
}, t("\u6E05\u9664")) : null,
|
|
4057
4705
|
React.createElement("span", { className: "iterate-filter-count" }, `${filtered.length}/${entries.length}`)
|
|
4058
4706
|
);
|
|
4059
4707
|
if (rows.length === 0) {
|
|
@@ -4061,7 +4709,7 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
4061
4709
|
"div",
|
|
4062
4710
|
{},
|
|
4063
4711
|
filterBar,
|
|
4064
|
-
React.createElement("div", { className: "iterate-obs-empty" }, "\u65E0\u5339\u914D\u7684\u65F6\u95F4\u7EBF\u6761\u76EE")
|
|
4712
|
+
React.createElement("div", { className: "iterate-obs-empty" }, t("\u65E0\u5339\u914D\u7684\u65F6\u95F4\u7EBF\u6761\u76EE"))
|
|
4065
4713
|
);
|
|
4066
4714
|
}
|
|
4067
4715
|
return React.createElement("div", {}, filterBar, ...rows);
|
|
@@ -4079,29 +4727,29 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
4079
4727
|
const headerBar = React.createElement(
|
|
4080
4728
|
"div",
|
|
4081
4729
|
{ className: "iterate-obs-bar", style: { marginBottom: 8 } },
|
|
4082
|
-
React.createElement("b", {}, "\u8D28\u91CF\u95E8\u7981"),
|
|
4730
|
+
React.createElement("b", {}, t("\u8D28\u91CF\u95E8\u7981")),
|
|
4083
4731
|
React.createElement("button", {
|
|
4084
4732
|
className: "iterate-btn",
|
|
4085
4733
|
"data-primary": "",
|
|
4086
4734
|
"data-copied": copiedKey === "qgate" ? "" : void 0,
|
|
4087
4735
|
onClick: () => copyInstruction("qgate", gateInstruction),
|
|
4088
|
-
title: "\u590D\u5236 iterate_quality_gate \u67E5\u8BE2\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09"
|
|
4089
|
-
}, copiedKey === "qgate" ? "\u5DF2\u590D\u5236" : "\u67E5\u8BE2\u95E8\u7981"),
|
|
4736
|
+
title: t("\u590D\u5236 iterate_quality_gate \u67E5\u8BE2\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09")
|
|
4737
|
+
}, copiedKey === "qgate" ? t("\u5DF2\u590D\u5236") : t("\u67E5\u8BE2\u95E8\u7981")),
|
|
4090
4738
|
React.createElement("button", {
|
|
4091
4739
|
className: "iterate-btn",
|
|
4092
4740
|
"data-danger": "",
|
|
4093
4741
|
"data-copied": copiedKey === "qgate-clear" ? "" : void 0,
|
|
4094
4742
|
onClick: () => copyInstruction("qgate-clear", gateClearInstruction),
|
|
4095
|
-
title: "\u590D\u5236 iterate_quality_gate clear \u6307\u4EE4\u6587\u672C\uFF08\u91CD\u7F6E\u9648\u65E7\u7684 FAIL \u8BC1\u4E66\uFF09"
|
|
4096
|
-
}, copiedKey === "qgate-clear" ? "\u5DF2\u590D\u5236" : "\u6E05\u9664\u95E8\u7981"),
|
|
4743
|
+
title: t("\u590D\u5236 iterate_quality_gate clear \u6307\u4EE4\u6587\u672C\uFF08\u91CD\u7F6E\u9648\u65E7\u7684 FAIL \u8BC1\u4E66\uFF09")
|
|
4744
|
+
}, copiedKey === "qgate-clear" ? t("\u5DF2\u590D\u5236") : t("\u6E05\u9664\u95E8\u7981")),
|
|
4097
4745
|
// Gap #4: the browser cannot read `.iterate/` directly — one click
|
|
4098
4746
|
// copies the pull instruction that makes the model echo it into the stream.
|
|
4099
4747
|
React.createElement("button", {
|
|
4100
4748
|
className: "iterate-btn",
|
|
4101
4749
|
"data-copied": copiedKey === "disk-snapshot" ? "" : void 0,
|
|
4102
4750
|
onClick: () => copyInstruction("disk-snapshot", pullInstruction),
|
|
4103
|
-
title: "\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u56DE\u663E .iterate/ \u4E0B\u7684\u95E8\u7981\u3001\u7ECF\u9A8C\u3001\u9632\u5FA1\u4E8B\u4EF6\u4E0E transcript"
|
|
4104
|
-
}, copiedKey === "disk-snapshot" ? "\u5DF2\u590D\u5236" : "\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167")
|
|
4751
|
+
title: t("\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u56DE\u663E .iterate/ \u4E0B\u7684\u95E8\u7981\u3001\u7ECF\u9A8C\u3001\u9632\u5FA1\u4E8B\u4EF6\u4E0E transcript")
|
|
4752
|
+
}, copiedKey === "disk-snapshot" ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167"))
|
|
4105
4753
|
);
|
|
4106
4754
|
if (!hasGate) {
|
|
4107
4755
|
return React.createElement(
|
|
@@ -4114,7 +4762,7 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
4114
4762
|
React.createElement(
|
|
4115
4763
|
"div",
|
|
4116
4764
|
{ className: "iterate-obs-block-head" },
|
|
4117
|
-
React.createElement("span", {}, "\u8D28\u91CF\u95E8\u7981\u89C6\u56FE"),
|
|
4765
|
+
React.createElement("span", {}, t("\u8D28\u91CF\u95E8\u7981\u89C6\u56FE")),
|
|
4118
4766
|
React.createElement("span", { className: "iterate-obs-badge" }, "v3.0")
|
|
4119
4767
|
),
|
|
4120
4768
|
React.createElement(
|
|
@@ -4125,12 +4773,12 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
4125
4773
|
{ className: "iterate-obs-msg" },
|
|
4126
4774
|
// Gap #4: distinguish "we pulled it and it is empty" from "we
|
|
4127
4775
|
// never asked the model to read the disk".
|
|
4128
|
-
diskEmptyStateText(pulled, "\u8D28\u91CF\u95E8\u7981\u8BC1\u4E66")
|
|
4776
|
+
diskEmptyStateText(pulled, t("\u8D28\u91CF\u95E8\u7981\u8BC1\u4E66"), getLang())
|
|
4129
4777
|
),
|
|
4130
4778
|
React.createElement(
|
|
4131
4779
|
"div",
|
|
4132
4780
|
{ className: "iterate-obs-msg", style: { marginTop: 6 } },
|
|
4133
|
-
"\u8D28\u91CF\u95E8\u7981\u663E\u793A\u5404\u7EF4\u5EA6\u6536\u655B\u5EA6\u3001\u9A8C\u8BC1\u901A\u8FC7\u7387\u548C\u6574\u4F53 PASS/FAIL \u72B6\u6001\u3002"
|
|
4781
|
+
t("\u8D28\u91CF\u95E8\u7981\u663E\u793A\u5404\u7EF4\u5EA6\u6536\u655B\u5EA6\u3001\u9A8C\u8BC1\u901A\u8FC7\u7387\u548C\u6574\u4F53 PASS/FAIL \u72B6\u6001\u3002")
|
|
4134
4782
|
),
|
|
4135
4783
|
React.createElement(
|
|
4136
4784
|
"div",
|
|
@@ -4139,14 +4787,14 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
4139
4787
|
className: "iterate-btn",
|
|
4140
4788
|
"data-copied": copiedKey === "qgate-instr" ? "" : void 0,
|
|
4141
4789
|
onClick: () => copyInstruction("qgate-instr", gateInstruction),
|
|
4142
|
-
title: "\u590D\u5236\u67E5\u8BE2\u6307\u4EE4"
|
|
4143
|
-
}, copiedKey === "qgate-instr" ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u67E5\u8BE2\u6307\u4EE4"),
|
|
4790
|
+
title: t("\u590D\u5236\u67E5\u8BE2\u6307\u4EE4")
|
|
4791
|
+
}, copiedKey === "qgate-instr" ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236\u67E5\u8BE2\u6307\u4EE4")),
|
|
4144
4792
|
React.createElement("button", {
|
|
4145
4793
|
className: "iterate-btn",
|
|
4146
4794
|
"data-copied": copiedKey === "disk-snapshot-2" ? "" : void 0,
|
|
4147
4795
|
onClick: () => copyInstruction("disk-snapshot-2", pullInstruction),
|
|
4148
|
-
title: "\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4"
|
|
4149
|
-
}, copiedKey === "disk-snapshot-2" ? "\u5DF2\u590D\u5236" : "\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167")
|
|
4796
|
+
title: t("\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4")
|
|
4797
|
+
}, copiedKey === "disk-snapshot-2" ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167"))
|
|
4150
4798
|
)
|
|
4151
4799
|
)
|
|
4152
4800
|
)
|
|
@@ -4166,7 +4814,7 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
4166
4814
|
React.createElement(
|
|
4167
4815
|
"span",
|
|
4168
4816
|
{ className: "iterate-obs-head-meta" },
|
|
4169
|
-
`\u6574\u4F53\u8BC4\u5206
|
|
4817
|
+
tf(`\u6574\u4F53\u8BC4\u5206 {a0} \xB7 \u9A8C\u8BC1 {a1}/{a2} \xB7 \u901A\u8FC7\u7387 {a3}%`, { a0: num(gate && gate.overallScore), a1: num(gate && gate.passedChecks), a2: num(gate && gate.totalChecks), a3: num(gate && gate.verificationPassRate) })
|
|
4170
4818
|
)
|
|
4171
4819
|
),
|
|
4172
4820
|
React.createElement(
|
|
@@ -4179,7 +4827,7 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
4179
4827
|
(d, i) => React.createElement(
|
|
4180
4828
|
"span",
|
|
4181
4829
|
{ key: `gdim-${i}`, className: "iterate-obs-chip" },
|
|
4182
|
-
|
|
4830
|
+
tf(`{a0} \xB7 \u6536\u655B {a1}% \xB7 \u8BC4\u5206 {a2}`, { a0: String(d.dimension || "?"), a1: num(d.convergenceRate), a2: num(d.score) })
|
|
4183
4831
|
)
|
|
4184
4832
|
)
|
|
4185
4833
|
) : null,
|
|
@@ -4201,33 +4849,33 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
4201
4849
|
style: { width: `${fill}%` }
|
|
4202
4850
|
})
|
|
4203
4851
|
),
|
|
4204
|
-
React.createElement("span", {}, `\u8BC4\u5206
|
|
4852
|
+
React.createElement("span", {}, tf(`\u8BC4\u5206 {a0} \xB7 \u53D1\u73B0 {a1} \xB7 \u5DF2\u4FEE\u590D {a2}`, { a0: num(d.score), a1: num(d.findingsCount), a2: num(d.fixedCount) }))
|
|
4205
4853
|
);
|
|
4206
4854
|
})
|
|
4207
4855
|
),
|
|
4208
|
-
gate && gate.failReason ? React.createElement("div", { className: "iterate-obs-msg", style: { marginTop: 6 } }, `\u5931\u8D25\u539F\u56E0\uFF1A
|
|
4856
|
+
gate && gate.failReason ? React.createElement("div", { className: "iterate-obs-msg", style: { marginTop: 6 } }, tf(`\u5931\u8D25\u539F\u56E0\uFF1A{a0}`, { a0: String(gate.failReason) })) : null
|
|
4209
4857
|
)
|
|
4210
4858
|
)
|
|
4211
4859
|
);
|
|
4212
4860
|
};
|
|
4213
4861
|
const renderExperienceBank = () => {
|
|
4214
|
-
const expInstruction = expSearch ? `\u8BF7\u8C03\u7528 \`iterate_experience\` \u641C\u7D22\u7ECF\u9A8C\uFF1A
|
|
4862
|
+
const expInstruction = expSearch ? tf(`\u8BF7\u8C03\u7528 \`iterate_experience\` \u641C\u7D22\u7ECF\u9A8C\uFF1A
|
|
4215
4863
|
|
|
4216
4864
|
\`\`\`json
|
|
4217
|
-
|
|
4218
|
-
|
|
4865
|
+
{a0}
|
|
4866
|
+
\`\`\``, { a0: JSON.stringify({ operation: "search", query: expSearch }, null, 2) }) : buildExperienceListInstruction();
|
|
4219
4867
|
const expEntries = experienceBank && experienceBank.entries ? experienceBank.entries : [];
|
|
4220
4868
|
const query = expSearch.trim().toLowerCase();
|
|
4221
4869
|
const visible = query ? expEntries.filter(
|
|
4222
|
-
(e) => [e.pattern, e.description, e.id, e.dimension, e.severity, ...e.tags || []].filter(Boolean).some((
|
|
4870
|
+
(e) => [e.pattern, e.description, e.id, e.dimension, e.severity, ...e.tags || []].filter(Boolean).some((t2) => String(t2).toLowerCase().includes(query))
|
|
4223
4871
|
) : expEntries;
|
|
4224
4872
|
const pulled = toolCalledInSession(session, "iterate_experience");
|
|
4225
4873
|
const resultRows = visible.map((e, i) => {
|
|
4226
|
-
const adoptInstruction = `\u8BF7\u8C03\u7528 \`iterate_experience\` \u91C7\u7EB3\u7ECF\u9A8C\u5E76\u5E94\u7528\u5DF2\u9A8C\u8BC1\u4FEE\u6CD5\uFF1A
|
|
4874
|
+
const adoptInstruction = tf(`\u8BF7\u8C03\u7528 \`iterate_experience\` \u91C7\u7EB3\u7ECF\u9A8C\u5E76\u5E94\u7528\u5DF2\u9A8C\u8BC1\u4FEE\u6CD5\uFF1A
|
|
4227
4875
|
|
|
4228
4876
|
\`\`\`json
|
|
4229
|
-
|
|
4230
|
-
|
|
4877
|
+
{a0}
|
|
4878
|
+
\`\`\``, { a0: JSON.stringify({ operation: "get", id: e.id }, null, 2) });
|
|
4231
4879
|
return React.createElement(
|
|
4232
4880
|
"div",
|
|
4233
4881
|
{ key: `exp-${i}`, className: "iterate-obs-block" },
|
|
@@ -4236,7 +4884,7 @@ ${JSON.stringify({ operation: "get", id: e.id }, null, 2)}
|
|
|
4236
4884
|
{ className: "iterate-obs-block-head" },
|
|
4237
4885
|
React.createElement("span", { className: "iterate-obs-chip" }, String(e.dimension || "")),
|
|
4238
4886
|
React.createElement("span", {}, String(e.pattern || "?")),
|
|
4239
|
-
React.createElement("span", { className: "iterate-exp-hit" }, `\u547D\u4E2D
|
|
4887
|
+
React.createElement("span", { className: "iterate-exp-hit" }, tf(`\u547D\u4E2D {a0} \u6B21`, { a0: e.hitCount ?? 0 }))
|
|
4240
4888
|
),
|
|
4241
4889
|
React.createElement(
|
|
4242
4890
|
"div",
|
|
@@ -4250,8 +4898,8 @@ ${JSON.stringify({ operation: "get", id: e.id }, null, 2)}
|
|
|
4250
4898
|
"data-primary": "",
|
|
4251
4899
|
"data-copied": copiedKey === `exp-adopt-${i}` ? "" : void 0,
|
|
4252
4900
|
onClick: () => copyInstruction(`exp-adopt-${i}`, adoptInstruction),
|
|
4253
|
-
title: "\u590D\u5236\u91C7\u7EB3\u6307\u4EE4\uFF08\u67E5\u8BE2\u8BE5\u7ECF\u9A8C\u5E76\u5E94\u7528\u5230\u5F53\u524D\u4E0A\u4E0B\u6587\uFF09"
|
|
4254
|
-
}, copiedKey === `exp-adopt-${i}` ? "\u5DF2\u590D\u5236" : "\u91C7\u7EB3") : null
|
|
4901
|
+
title: t("\u590D\u5236\u91C7\u7EB3\u6307\u4EE4\uFF08\u67E5\u8BE2\u8BE5\u7ECF\u9A8C\u5E76\u5E94\u7528\u5230\u5F53\u524D\u4E0A\u4E0B\u6587\uFF09")
|
|
4902
|
+
}, copiedKey === `exp-adopt-${i}` ? t("\u5DF2\u590D\u5236") : t("\u91C7\u7EB3")) : null
|
|
4255
4903
|
)
|
|
4256
4904
|
)
|
|
4257
4905
|
);
|
|
@@ -4262,21 +4910,21 @@ ${JSON.stringify({ operation: "get", id: e.id }, null, 2)}
|
|
|
4262
4910
|
React.createElement(
|
|
4263
4911
|
"div",
|
|
4264
4912
|
{ className: "iterate-obs-bar", style: { marginBottom: 8 } },
|
|
4265
|
-
React.createElement("b", {}, "\u7ECF\u9A8C\u94F6\u884C"),
|
|
4266
|
-
React.createElement("span", { className: "iterate-obs-head-meta" }, `\u4F1A\u8BDD\u5185
|
|
4913
|
+
React.createElement("b", {}, t("\u7ECF\u9A8C\u94F6\u884C")),
|
|
4914
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, tf(`\u4F1A\u8BDD\u5185 {a0} \u6761`, { a0: expEntries.length })),
|
|
4267
4915
|
React.createElement("button", {
|
|
4268
4916
|
className: "iterate-btn",
|
|
4269
4917
|
"data-primary": "",
|
|
4270
4918
|
"data-copied": copiedKey === "exp-list" ? "" : void 0,
|
|
4271
4919
|
onClick: () => copyInstruction("exp-list", buildExperienceListInstruction()),
|
|
4272
|
-
title: "\u590D\u5236\u5217\u51FA\u7ECF\u9A8C\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09"
|
|
4273
|
-
}, copiedKey === "exp-list" ? "\u5DF2\u590D\u5236" : "\u5217\u51FA\u7ECF\u9A8C"),
|
|
4920
|
+
title: t("\u590D\u5236\u5217\u51FA\u7ECF\u9A8C\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09")
|
|
4921
|
+
}, copiedKey === "exp-list" ? t("\u5DF2\u590D\u5236") : t("\u5217\u51FA\u7ECF\u9A8C")),
|
|
4274
4922
|
React.createElement("button", {
|
|
4275
4923
|
className: "iterate-btn",
|
|
4276
4924
|
"data-copied": copiedKey === "exp-snapshot" ? "" : void 0,
|
|
4277
4925
|
onClick: () => copyInstruction("exp-snapshot", pullInstruction),
|
|
4278
|
-
title: "\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u628A .iterate/ \u4E0B\u7684\u7ECF\u9A8C\u94F6\u884C\u7B49\u4EA7\u7269\u56DE\u663E\u8FDB\u4F1A\u8BDD"
|
|
4279
|
-
}, copiedKey === "exp-snapshot" ? "\u5DF2\u590D\u5236" : "\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167")
|
|
4926
|
+
title: t("\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u628A .iterate/ \u4E0B\u7684\u7ECF\u9A8C\u94F6\u884C\u7B49\u4EA7\u7269\u56DE\u663E\u8FDB\u4F1A\u8BDD")
|
|
4927
|
+
}, copiedKey === "exp-snapshot" ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167"))
|
|
4280
4928
|
),
|
|
4281
4929
|
React.createElement(
|
|
4282
4930
|
"div",
|
|
@@ -4284,8 +4932,8 @@ ${JSON.stringify({ operation: "get", id: e.id }, null, 2)}
|
|
|
4284
4932
|
React.createElement("input", {
|
|
4285
4933
|
className: "iterate-filter-search",
|
|
4286
4934
|
type: "search",
|
|
4287
|
-
placeholder: "\u641C\u7D22\u7ECF\u9A8C\u6A21\u5F0F\u2026",
|
|
4288
|
-
"aria-label": "\u641C\u7D22\u7ECF\u9A8C",
|
|
4935
|
+
placeholder: t("\u641C\u7D22\u7ECF\u9A8C\u6A21\u5F0F\u2026"),
|
|
4936
|
+
"aria-label": t("\u641C\u7D22\u7ECF\u9A8C"),
|
|
4289
4937
|
value: expSearch,
|
|
4290
4938
|
onChange: (e) => setExpSearch(e.target.value)
|
|
4291
4939
|
}),
|
|
@@ -4294,36 +4942,36 @@ ${JSON.stringify({ operation: "get", id: e.id }, null, 2)}
|
|
|
4294
4942
|
"data-copied": copiedKey === "exp-search" ? "" : void 0,
|
|
4295
4943
|
disabled: !expSearch,
|
|
4296
4944
|
onClick: () => copyInstruction("exp-search", expInstruction),
|
|
4297
|
-
title: "\u590D\u5236\u641C\u7D22\u6307\u4EE4"
|
|
4298
|
-
}, copiedKey === "exp-search" ? "\u5DF2\u590D\u5236" : "\u641C\u7D22")
|
|
4945
|
+
title: t("\u590D\u5236\u641C\u7D22\u6307\u4EE4")
|
|
4946
|
+
}, copiedKey === "exp-search" ? t("\u5DF2\u590D\u5236") : t("\u641C\u7D22"))
|
|
4299
4947
|
),
|
|
4300
|
-
resultRows.length > 0 ? React.createElement("div", {}, ...resultRows) : expEntries.length > 0 ? React.createElement("div", { className: "iterate-obs-empty" }, "\u6CA1\u6709\u5339\u914D\u300C" + expSearch + "\u300D\u7684\u7ECF\u9A8C") : React.createElement(
|
|
4948
|
+
resultRows.length > 0 ? React.createElement("div", {}, ...resultRows) : expEntries.length > 0 ? React.createElement("div", { className: "iterate-obs-empty" }, t("\u6CA1\u6709\u5339\u914D\u300C") + expSearch + t("\u300D\u7684\u7ECF\u9A8C")) : React.createElement(
|
|
4301
4949
|
"div",
|
|
4302
4950
|
{},
|
|
4303
4951
|
// Gap #4: separate "pulled and empty" from "never pulled".
|
|
4304
|
-
React.createElement("div", { className: "iterate-obs-empty" }, diskEmptyStateText(pulled, "\u7ECF\u9A8C\u94F6\u884C")),
|
|
4305
|
-
React.createElement("div", { className: "iterate-obs-empty" }, "\u4E5F\u53EF\u70B9\u300C\u5217\u51FA\u7ECF\u9A8C\u300D\u67E5\u8BE2\u78C1\u76D8\u4E0A\u7684\u5168\u90E8\u5386\u53F2\u7ECF\u9A8C\u3002")
|
|
4952
|
+
React.createElement("div", { className: "iterate-obs-empty" }, diskEmptyStateText(pulled, t("\u7ECF\u9A8C\u94F6\u884C"), getLang())),
|
|
4953
|
+
React.createElement("div", { className: "iterate-obs-empty" }, t("\u4E5F\u53EF\u70B9\u300C\u5217\u51FA\u7ECF\u9A8C\u300D\u67E5\u8BE2\u78C1\u76D8\u4E0A\u7684\u5168\u90E8\u5386\u53F2\u7ECF\u9A8C\u3002"))
|
|
4306
4954
|
)
|
|
4307
4955
|
);
|
|
4308
4956
|
};
|
|
4309
4957
|
const renderDefenseEvents = () => {
|
|
4310
|
-
const defenseInstruction = defenseFilter ? `\u8BF7\u8C03\u7528 \`iterate_defense_events\` \u67E5\u8BE2\u9632\u5FA1\u4E8B\u4EF6\uFF1A
|
|
4958
|
+
const defenseInstruction = defenseFilter ? tf(`\u8BF7\u8C03\u7528 \`iterate_defense_events\` \u67E5\u8BE2\u9632\u5FA1\u4E8B\u4EF6\uFF1A
|
|
4311
4959
|
|
|
4312
4960
|
\`\`\`json
|
|
4313
|
-
|
|
4314
|
-
|
|
4961
|
+
{a0}
|
|
4962
|
+
\`\`\``, { a0: JSON.stringify({ operation: "list", type: defenseFilter || void 0 }, null, 2) }) : buildDefenseEventsListInstruction();
|
|
4315
4963
|
const typeOptions = [
|
|
4316
|
-
{ value: "", label: "\u5168\u90E8\u7C7B\u578B" },
|
|
4317
|
-
{ value: "precondition_failed", label: "\u524D\u7F6E\u6821\u9A8C\u5931\u8D25" },
|
|
4318
|
-
{ value: "rollback", label: "\u56DE\u6EDA" },
|
|
4319
|
-
{ value: "invariant_violated", label: "\u4E0D\u53D8\u91CF\u8FDD\u53CD" },
|
|
4320
|
-
{ value: "assumption_falsified", label: "\u5047\u8BBE\u88AB\u8BC1\u4F2A" }
|
|
4964
|
+
{ value: "", label: t("\u5168\u90E8\u7C7B\u578B") },
|
|
4965
|
+
{ value: "precondition_failed", label: t("\u524D\u7F6E\u6821\u9A8C\u5931\u8D25") },
|
|
4966
|
+
{ value: "rollback", label: t("\u56DE\u6EDA") },
|
|
4967
|
+
{ value: "invariant_violated", label: t("\u4E0D\u53D8\u91CF\u8FDD\u53CD") },
|
|
4968
|
+
{ value: "assumption_falsified", label: t("\u5047\u8BBE\u88AB\u8BC1\u4F2A") }
|
|
4321
4969
|
];
|
|
4322
4970
|
const TYPE_LABEL = {
|
|
4323
|
-
precondition_failed: "\u524D\u7F6E\u6821\u9A8C\u5931\u8D25",
|
|
4324
|
-
rollback: "\u56DE\u6EDA",
|
|
4325
|
-
invariant_violated: "\u4E0D\u53D8\u91CF\u8FDD\u53CD",
|
|
4326
|
-
assumption_falsified: "\u5047\u8BBE\u88AB\u8BC1\u4F2A"
|
|
4971
|
+
precondition_failed: t("\u524D\u7F6E\u6821\u9A8C\u5931\u8D25"),
|
|
4972
|
+
rollback: t("\u56DE\u6EDA"),
|
|
4973
|
+
invariant_violated: t("\u4E0D\u53D8\u91CF\u8FDD\u53CD"),
|
|
4974
|
+
assumption_falsified: t("\u5047\u8BBE\u88AB\u8BC1\u4F2A")
|
|
4327
4975
|
};
|
|
4328
4976
|
const counts = defenseEvents && defenseEvents.counts || {};
|
|
4329
4977
|
const allEvents = defenseEvents && defenseEvents.events ? defenseEvents.events : [];
|
|
@@ -4332,42 +4980,42 @@ ${JSON.stringify({ operation: "list", type: defenseFilter || void 0 }, null, 2)}
|
|
|
4332
4980
|
const headerBar = React.createElement(
|
|
4333
4981
|
"div",
|
|
4334
4982
|
{ className: "iterate-obs-bar", style: { marginBottom: 8 } },
|
|
4335
|
-
React.createElement("b", {}, "\u9632\u5FA1\u4E8B\u4EF6"),
|
|
4336
|
-
React.createElement("span", { className: "iterate-obs-head-meta" }, `\u4F1A\u8BDD\u5185
|
|
4983
|
+
React.createElement("b", {}, t("\u9632\u5FA1\u4E8B\u4EF6")),
|
|
4984
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, tf(`\u4F1A\u8BDD\u5185 {a0} \u6761 / \u7D2F\u8BA1 {a1}`, { a0: allEvents.length, a1: totalCount })),
|
|
4337
4985
|
React.createElement("button", {
|
|
4338
4986
|
className: "iterate-btn",
|
|
4339
4987
|
"data-primary": "",
|
|
4340
4988
|
"data-copied": copiedKey === "defense-list" ? "" : void 0,
|
|
4341
4989
|
onClick: () => copyInstruction("defense-list", buildDefenseEventsListInstruction()),
|
|
4342
|
-
title: "\u590D\u5236\u5217\u51FA\u9632\u5FA1\u4E8B\u4EF6\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09"
|
|
4343
|
-
}, copiedKey === "defense-list" ? "\u5DF2\u590D\u5236" : "\u5217\u51FA\u4E8B\u4EF6"),
|
|
4990
|
+
title: t("\u590D\u5236\u5217\u51FA\u9632\u5FA1\u4E8B\u4EF6\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09")
|
|
4991
|
+
}, copiedKey === "defense-list" ? t("\u5DF2\u590D\u5236") : t("\u5217\u51FA\u4E8B\u4EF6")),
|
|
4344
4992
|
React.createElement("button", {
|
|
4345
4993
|
className: "iterate-btn",
|
|
4346
4994
|
"data-copied": copiedKey === "defense-snapshot" ? "" : void 0,
|
|
4347
4995
|
onClick: () => copyInstruction("defense-snapshot", pullInstruction),
|
|
4348
|
-
title: "\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u628A .iterate/ \u4E0B\u7684\u9632\u5FA1\u4E8B\u4EF6\u6D41\u56DE\u663E\u8FDB\u4F1A\u8BDD"
|
|
4349
|
-
}, copiedKey === "defense-snapshot" ? "\u5DF2\u590D\u5236" : "\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167"),
|
|
4996
|
+
title: t("\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u628A .iterate/ \u4E0B\u7684\u9632\u5FA1\u4E8B\u4EF6\u6D41\u56DE\u663E\u8FDB\u4F1A\u8BDD")
|
|
4997
|
+
}, copiedKey === "defense-snapshot" ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167")),
|
|
4350
4998
|
React.createElement("button", {
|
|
4351
4999
|
className: "iterate-btn",
|
|
4352
5000
|
"data-copied": copiedKey === "defense-counts" ? "" : void 0,
|
|
4353
|
-
onClick: () => copyInstruction("defense-counts", `\u8BF7\u8C03\u7528 \`iterate_defense_events\` \u67E5\u8BE2\u4E8B\u4EF6\u7EDF\u8BA1\uFF1A
|
|
5001
|
+
onClick: () => copyInstruction("defense-counts", tf(`\u8BF7\u8C03\u7528 \`iterate_defense_events\` \u67E5\u8BE2\u4E8B\u4EF6\u7EDF\u8BA1\uFF1A
|
|
4354
5002
|
|
|
4355
5003
|
\`\`\`json
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
title: "\u590D\u5236\u7EDF\u8BA1\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09"
|
|
4359
|
-
}, copiedKey === "defense-counts" ? "\u5DF2\u590D\u5236" : "\u7EDF\u8BA1"),
|
|
5004
|
+
{a0}
|
|
5005
|
+
\`\`\``, { a0: JSON.stringify({ operation: "counts" }, null, 2) })),
|
|
5006
|
+
title: t("\u590D\u5236\u7EDF\u8BA1\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09")
|
|
5007
|
+
}, copiedKey === "defense-counts" ? t("\u5DF2\u590D\u5236") : t("\u7EDF\u8BA1")),
|
|
4360
5008
|
React.createElement("button", {
|
|
4361
5009
|
className: "iterate-btn",
|
|
4362
5010
|
"data-danger": "",
|
|
4363
5011
|
"data-copied": copiedKey === "defense-clear" ? "" : void 0,
|
|
4364
|
-
onClick: () => copyInstruction("defense-clear", `\u8BF7\u8C03\u7528 \`iterate_defense_events\` \u6E05\u9664\u9632\u5FA1\u4E8B\u4EF6\u6D41\uFF1A
|
|
5012
|
+
onClick: () => copyInstruction("defense-clear", tf(`\u8BF7\u8C03\u7528 \`iterate_defense_events\` \u6E05\u9664\u9632\u5FA1\u4E8B\u4EF6\u6D41\uFF1A
|
|
4365
5013
|
|
|
4366
5014
|
\`\`\`json
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
title: "\u590D\u5236 iterate_defense_events clear \u6307\u4EE4\u6587\u672C\uFF08\u91CD\u7F6E\u9648\u65E7\u4E8B\u4EF6\u6D41\uFF09"
|
|
4370
|
-
}, copiedKey === "defense-clear" ? "\u5DF2\u590D\u5236" : "\u6E05\u9664\u4E8B\u4EF6")
|
|
5015
|
+
{a0}
|
|
5016
|
+
\`\`\``, { a0: JSON.stringify({ operation: "clear" }, null, 2) })),
|
|
5017
|
+
title: t("\u590D\u5236 iterate_defense_events clear \u6307\u4EE4\u6587\u672C\uFF08\u91CD\u7F6E\u9648\u65E7\u4E8B\u4EF6\u6D41\uFF09")
|
|
5018
|
+
}, copiedKey === "defense-clear" ? t("\u5DF2\u590D\u5236") : t("\u6E05\u9664\u4E8B\u4EF6"))
|
|
4371
5019
|
);
|
|
4372
5020
|
if (allEvents.length === 0 && totalCount === 0) {
|
|
4373
5021
|
return React.createElement(
|
|
@@ -4380,7 +5028,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4380
5028
|
React.createElement(
|
|
4381
5029
|
"div",
|
|
4382
5030
|
{ className: "iterate-obs-block-head" },
|
|
4383
|
-
React.createElement("span", {}, "\u9632\u5FA1\u4E8B\u4EF6\u6D41"),
|
|
5031
|
+
React.createElement("span", {}, t("\u9632\u5FA1\u4E8B\u4EF6\u6D41")),
|
|
4384
5032
|
React.createElement("span", { className: "iterate-obs-badge" }, "v3.0")
|
|
4385
5033
|
),
|
|
4386
5034
|
React.createElement(
|
|
@@ -4389,12 +5037,12 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4389
5037
|
React.createElement(
|
|
4390
5038
|
"div",
|
|
4391
5039
|
{ className: "iterate-obs-msg" },
|
|
4392
|
-
diskEmptyStateText(toolCalledInSession(session, "iterate_defense_events"), "\u9632\u5FA1\u4E8B\u4EF6\u6D41")
|
|
5040
|
+
diskEmptyStateText(toolCalledInSession(session, "iterate_defense_events"), t("\u9632\u5FA1\u4E8B\u4EF6\u6D41"), getLang())
|
|
4393
5041
|
),
|
|
4394
5042
|
React.createElement(
|
|
4395
5043
|
"div",
|
|
4396
5044
|
{ className: "iterate-obs-msg", style: { marginTop: 6 } },
|
|
4397
|
-
"\u9632\u5FA1\u4E8B\u4EF6\u5305\u62EC\uFF1A\u524D\u7F6E\u6821\u9A8C\u5931\u8D25\u3001\u56DE\u6EDA\u3001\u4E0D\u53D8\u91CF\u8FDD\u53CD\u3001\u5047\u8BBE\u88AB\u8BC1\u4F2A\u3002"
|
|
5045
|
+
t("\u9632\u5FA1\u4E8B\u4EF6\u5305\u62EC\uFF1A\u524D\u7F6E\u6821\u9A8C\u5931\u8D25\u3001\u56DE\u6EDA\u3001\u4E0D\u53D8\u91CF\u8FDD\u53CD\u3001\u5047\u8BBE\u88AB\u8BC1\u4F2A\u3002")
|
|
4398
5046
|
),
|
|
4399
5047
|
React.createElement(
|
|
4400
5048
|
"div",
|
|
@@ -4403,8 +5051,8 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4403
5051
|
className: "iterate-btn",
|
|
4404
5052
|
"data-copied": copiedKey === "defense-instr" ? "" : void 0,
|
|
4405
5053
|
onClick: () => copyInstruction("defense-instr", defenseInstruction),
|
|
4406
|
-
title: "\u590D\u5236\u67E5\u8BE2\u6307\u4EE4"
|
|
4407
|
-
}, copiedKey === "defense-instr" ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u67E5\u8BE2\u6307\u4EE4")
|
|
5054
|
+
title: t("\u590D\u5236\u67E5\u8BE2\u6307\u4EE4")
|
|
5055
|
+
}, copiedKey === "defense-instr" ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236\u67E5\u8BE2\u6307\u4EE4"))
|
|
4408
5056
|
)
|
|
4409
5057
|
)
|
|
4410
5058
|
)
|
|
@@ -4418,7 +5066,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4418
5066
|
{
|
|
4419
5067
|
className: "iterate-filter-select",
|
|
4420
5068
|
value: defenseFilter,
|
|
4421
|
-
"aria-label": "\u6309\u4E8B\u4EF6\u7C7B\u578B\u7B5B\u9009",
|
|
5069
|
+
"aria-label": t("\u6309\u4E8B\u4EF6\u7C7B\u578B\u7B5B\u9009"),
|
|
4422
5070
|
onChange: (e) => setDefenseFilter(e.target.value)
|
|
4423
5071
|
},
|
|
4424
5072
|
...typeOptions.map((opt) => React.createElement("option", { key: opt.value, value: opt.value }, opt.label))
|
|
@@ -4426,8 +5074,8 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4426
5074
|
defenseFilter ? React.createElement("button", {
|
|
4427
5075
|
className: "iterate-filter-clear",
|
|
4428
5076
|
onClick: () => setDefenseFilter(""),
|
|
4429
|
-
title: "\u6E05\u9664\u7C7B\u578B\u7B5B\u9009"
|
|
4430
|
-
}, "\u6E05\u9664") : null
|
|
5077
|
+
title: t("\u6E05\u9664\u7C7B\u578B\u7B5B\u9009")
|
|
5078
|
+
}, t("\u6E05\u9664")) : null
|
|
4431
5079
|
);
|
|
4432
5080
|
let eventRows = [];
|
|
4433
5081
|
if (visibleEvents.length > 0) {
|
|
@@ -4448,7 +5096,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4448
5096
|
"div",
|
|
4449
5097
|
{ className: "iterate-obs-block-body" },
|
|
4450
5098
|
React.createElement("div", { className: "iterate-obs-msg" }, String(e.description || "")),
|
|
4451
|
-
e.defense ? React.createElement("div", { className: "iterate-obs-msg", style: { marginTop: 4 } }, `\u9632\u7EBF\uFF1A
|
|
5099
|
+
e.defense ? React.createElement("div", { className: "iterate-obs-msg", style: { marginTop: 4 } }, tf(`\u9632\u7EBF\uFF1A{a0}`, { a0: String(e.defense) })) : null
|
|
4452
5100
|
)
|
|
4453
5101
|
);
|
|
4454
5102
|
});
|
|
@@ -4469,7 +5117,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4469
5117
|
)
|
|
4470
5118
|
)
|
|
4471
5119
|
),
|
|
4472
|
-
eventRows.length > 0 ? React.createElement("div", {}, ...eventRows) : React.createElement("div", { className: "iterate-obs-empty" }, "\u6CA1\u6709\u5339\u914D\u8BE5\u7C7B\u578B\u7684\u9632\u5FA1\u4E8B\u4EF6")
|
|
5120
|
+
eventRows.length > 0 ? React.createElement("div", {}, ...eventRows) : React.createElement("div", { className: "iterate-obs-empty" }, t("\u6CA1\u6709\u5339\u914D\u8BE5\u7C7B\u578B\u7684\u9632\u5FA1\u4E8B\u4EF6"))
|
|
4473
5121
|
);
|
|
4474
5122
|
};
|
|
4475
5123
|
const renderLive = () => {
|
|
@@ -4481,29 +5129,31 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4481
5129
|
return React.createElement(
|
|
4482
5130
|
"div",
|
|
4483
5131
|
{ className: "iterate-obs-empty" },
|
|
4484
|
-
React.createElement("div", {}, "\u5B9E\u65F6\u6D3B\u52A8\u6D41\u4EC5\u5728\u8FD0\u884C\u671F\u95F4\u8BB0\u5F55\uFF0C\u5F53\u524D\u5DF2\u5B8C\u6210\u3002"),
|
|
5132
|
+
React.createElement("div", {}, t("\u5B9E\u65F6\u6D3B\u52A8\u6D41\u4EC5\u5728\u8FD0\u884C\u671F\u95F4\u8BB0\u5F55\uFF0C\u5F53\u524D\u5DF2\u5B8C\u6210\u3002")),
|
|
4485
5133
|
React.createElement(
|
|
4486
5134
|
"div",
|
|
4487
5135
|
{ className: "iterate-obs-bar", style: { marginTop: 8 } },
|
|
4488
|
-
React.createElement("button", { className: "iterate-btn", onClick: () => setTab("f1") }, "\u67E5\u770B\u5BA1\u67E5\u7EBF\u7A0B"),
|
|
4489
|
-
React.createElement("button", { className: "iterate-btn", onClick: () => setTab("f3") }, "\u67E5\u770B\u53D1\u73B0")
|
|
5136
|
+
React.createElement("button", { className: "iterate-btn", onClick: () => setTab("f1") }, t("\u67E5\u770B\u5BA1\u67E5\u7EBF\u7A0B")),
|
|
5137
|
+
React.createElement("button", { className: "iterate-btn", onClick: () => setTab("f3") }, t("\u67E5\u770B\u53D1\u73B0"))
|
|
4490
5138
|
)
|
|
4491
5139
|
);
|
|
4492
5140
|
}
|
|
4493
|
-
return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u5B9E\u65F6\u6D3B\u52A8");
|
|
5141
|
+
return React.createElement("div", { className: "iterate-obs-empty" }, t("\u6682\u65E0\u5B9E\u65F6\u6D3B\u52A8"));
|
|
4494
5142
|
}
|
|
4495
5143
|
const types = Array.from(new Set(liveEntries.map((e) => String(e.type || "unknown")).filter(Boolean))).sort();
|
|
4496
5144
|
const filtered = filterLiveEntries(liveEntries, liveType);
|
|
4497
5145
|
const rows = filtered.map((e, i) => {
|
|
4498
5146
|
const type = String(e.type || "unknown");
|
|
4499
|
-
const meta = OBS_LIVE_META[type]
|
|
5147
|
+
const meta = OBS_LIVE_META[type];
|
|
5148
|
+
const typeLabel = meta ? meta.label[getLang()] : String(e.type || "?");
|
|
5149
|
+
const typeColor = meta ? meta.color : OBS_LIVE_FALLBACK_COLOR;
|
|
4500
5150
|
const tool = String(e.tool || "");
|
|
4501
5151
|
const target = String(e.target || "");
|
|
4502
5152
|
const time = formatObsLiveTime(String(e.ts || ""));
|
|
4503
5153
|
return React.createElement(
|
|
4504
5154
|
"div",
|
|
4505
5155
|
{ key: `live-${i}`, className: "iterate-obs-row" },
|
|
4506
|
-
React.createElement("span", { className: "iterate-obs-livebadge", style: { color:
|
|
5156
|
+
React.createElement("span", { className: "iterate-obs-livebadge", style: { color: typeColor } }, typeLabel),
|
|
4507
5157
|
tool ? React.createElement("span", { className: "iterate-obs-chip" }, tool) : null,
|
|
4508
5158
|
React.createElement("span", { className: "iterate-obs-msg" }, target || "\u2014"),
|
|
4509
5159
|
React.createElement("span", { className: "iterate-obs-head-meta" }, time)
|
|
@@ -4512,23 +5162,23 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4512
5162
|
const filterBar = React.createElement(
|
|
4513
5163
|
"div",
|
|
4514
5164
|
{ className: "iterate-obs-bar", style: { marginBottom: 8 } },
|
|
4515
|
-
React.createElement("b", {}, "\u5B9E\u65F6\u6D3B\u52A8"),
|
|
5165
|
+
React.createElement("b", {}, t("\u5B9E\u65F6\u6D3B\u52A8")),
|
|
4516
5166
|
React.createElement(
|
|
4517
5167
|
"select",
|
|
4518
5168
|
{
|
|
4519
5169
|
className: "iterate-filter-select",
|
|
4520
5170
|
value: liveType,
|
|
4521
|
-
"aria-label": "\u6309\u6D3B\u52A8\u7C7B\u578B\u7B5B\u9009",
|
|
5171
|
+
"aria-label": t("\u6309\u6D3B\u52A8\u7C7B\u578B\u7B5B\u9009"),
|
|
4522
5172
|
onChange: (e) => setLiveType(e.target.value)
|
|
4523
5173
|
},
|
|
4524
|
-
React.createElement("option", { value: "" }, "\u5168\u90E8\u6D3B\u52A8"),
|
|
4525
|
-
...types.map((
|
|
5174
|
+
React.createElement("option", { value: "" }, t("\u5168\u90E8\u6D3B\u52A8")),
|
|
5175
|
+
...types.map((t2) => React.createElement("option", { key: t2, value: t2 }, t2))
|
|
4526
5176
|
),
|
|
4527
5177
|
liveType ? React.createElement("button", {
|
|
4528
5178
|
className: "iterate-filter-clear",
|
|
4529
5179
|
onClick: () => setLiveType(""),
|
|
4530
|
-
title: "\u6E05\u9664\u7C7B\u578B\u7B5B\u9009"
|
|
4531
|
-
}, "\u6E05\u9664") : null,
|
|
5180
|
+
title: t("\u6E05\u9664\u7C7B\u578B\u7B5B\u9009")
|
|
5181
|
+
}, t("\u6E05\u9664")) : null,
|
|
4532
5182
|
React.createElement("span", { className: "iterate-filter-count" }, `${filtered.length}/${liveEntries.length}`)
|
|
4533
5183
|
);
|
|
4534
5184
|
if (rows.length === 0) {
|
|
@@ -4536,7 +5186,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4536
5186
|
"div",
|
|
4537
5187
|
{},
|
|
4538
5188
|
filterBar,
|
|
4539
|
-
React.createElement("div", { className: "iterate-obs-empty" }, "\u65E0\u5339\u914D\u7684\u5B9E\u65F6\u6D3B\u52A8")
|
|
5189
|
+
React.createElement("div", { className: "iterate-obs-empty" }, t("\u65E0\u5339\u914D\u7684\u5B9E\u65F6\u6D3B\u52A8"))
|
|
4540
5190
|
);
|
|
4541
5191
|
}
|
|
4542
5192
|
return React.createElement("div", {}, filterBar, ...rows);
|
|
@@ -4576,14 +5226,14 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4576
5226
|
React.createElement(
|
|
4577
5227
|
"div",
|
|
4578
5228
|
{ className: "iterate-obs-head", "data-closed": open ? void 0 : "", onClick: () => setOpen((v) => !v) },
|
|
4579
|
-
React.createElement("span", { className: "iterate-obs-title" }, "iterate \u89C2\u6D4B\u53F0"),
|
|
5229
|
+
React.createElement("span", { className: "iterate-obs-title" }, t("iterate \u89C2\u6D4B\u53F0")),
|
|
4580
5230
|
React.createElement("span", { className: "iterate-obs-badge", "data-live": live ? "" : void 0 }, runStatusText(live, manifest)),
|
|
4581
5231
|
React.createElement("span", { className: "iterate-obs-head-meta" }, headMeta || "runtime"),
|
|
4582
5232
|
// Gap #14: make the tab system discoverable without expanding it.
|
|
4583
5233
|
React.createElement("span", {
|
|
4584
5234
|
className: "iterate-obs-head-meta",
|
|
4585
|
-
title:
|
|
4586
|
-
},
|
|
5235
|
+
title: obsHotkeyHint()
|
|
5236
|
+
}, obsHotkeyHint()),
|
|
4587
5237
|
React.createElement("button", {
|
|
4588
5238
|
className: "iterate-btn",
|
|
4589
5239
|
"data-ghost": "",
|
|
@@ -4591,7 +5241,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4591
5241
|
e.stopPropagation();
|
|
4592
5242
|
setOpen((v) => !v);
|
|
4593
5243
|
}
|
|
4594
|
-
}, open ? "\u6536\u8D77" : "\u5C55\u5F00")
|
|
5244
|
+
}, open ? t("\u6536\u8D77") : t("\u5C55\u5F00"))
|
|
4595
5245
|
),
|
|
4596
5246
|
open ? React.createElement(
|
|
4597
5247
|
"div",
|
|
@@ -4602,14 +5252,14 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4602
5252
|
React.createElement(
|
|
4603
5253
|
"div",
|
|
4604
5254
|
{ className: "iterate-obs-block-head" },
|
|
4605
|
-
React.createElement("b", {}, "\u590D\u5236\u672A\u6210\u529F"),
|
|
4606
|
-
React.createElement("span", { className: "iterate-obs-head-meta" }, "\u526A\u8D34\u677F\u5199\u5165\u88AB\u62D2\u7EDD\uFF0C\u8BF7\u624B\u52A8\u9009\u4E2D\u4E0B\u65B9\u6307\u4EE4\u590D\u5236"),
|
|
5255
|
+
React.createElement("b", {}, t("\u590D\u5236\u672A\u6210\u529F")),
|
|
5256
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, t("\u526A\u8D34\u677F\u5199\u5165\u88AB\u62D2\u7EDD\uFF0C\u8BF7\u624B\u52A8\u9009\u4E2D\u4E0B\u65B9\u6307\u4EE4\u590D\u5236")),
|
|
4607
5257
|
React.createElement("button", {
|
|
4608
5258
|
className: "iterate-btn",
|
|
4609
5259
|
"data-ghost": "",
|
|
4610
|
-
"aria-label": "\u5173\u95ED\u590D\u5236\u5931\u8D25\u63D0\u793A",
|
|
5260
|
+
"aria-label": t("\u5173\u95ED\u590D\u5236\u5931\u8D25\u63D0\u793A"),
|
|
4611
5261
|
onClick: () => setCopyFailText(null)
|
|
4612
|
-
}, "\u5173\u95ED")
|
|
5262
|
+
}, t("\u5173\u95ED"))
|
|
4613
5263
|
),
|
|
4614
5264
|
React.createElement("div", { className: "iterate-obs-code" }, copyFailText)
|
|
4615
5265
|
) : null,
|
|
@@ -4626,8 +5276,8 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4626
5276
|
"data-primary": "",
|
|
4627
5277
|
"data-copied": copiedKey === "export" ? "" : void 0,
|
|
4628
5278
|
onClick: exportObservatory,
|
|
4629
|
-
title: "\u5C06\u89C2\u6D4B\u53F0\u5168\u90E8\u6570\u636E\u5BFC\u51FA\u4E3A JSON\uFF08\u4F18\u5148\u4E0B\u8F7D\uFF0C\u5931\u8D25\u5219\u590D\u5236\uFF09"
|
|
4630
|
-
}, copiedKey === "export" ? "\u5DF2\u5BFC\u51FA" : "\u5BFC\u51FA JSON"),
|
|
5279
|
+
title: t("\u5C06\u89C2\u6D4B\u53F0\u5168\u90E8\u6570\u636E\u5BFC\u51FA\u4E3A JSON\uFF08\u4F18\u5148\u4E0B\u8F7D\uFF0C\u5931\u8D25\u5219\u590D\u5236\uFF09")
|
|
5280
|
+
}, copiedKey === "export" ? t("\u5DF2\u5BFC\u51FA") : t("\u5BFC\u51FA JSON")),
|
|
4631
5281
|
// Gap #4: `.iterate/` artifacts are unreachable from the browser;
|
|
4632
5282
|
// this copies the one instruction that makes the model echo them
|
|
4633
5283
|
// into the session stream (which is what every panel scans).
|
|
@@ -4636,21 +5286,21 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
4636
5286
|
"data-copied": copiedKey === "pull-snapshot" ? "" : void 0,
|
|
4637
5287
|
onClick: () => copyInstruction("pull-snapshot", pullInstruction),
|
|
4638
5288
|
title: pullTitle
|
|
4639
|
-
}, copiedKey === "pull-snapshot" ? "\u5DF2\u590D\u5236" : "\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167")
|
|
5289
|
+
}, copiedKey === "pull-snapshot" ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167"))
|
|
4640
5290
|
),
|
|
4641
5291
|
React.createElement(
|
|
4642
5292
|
"div",
|
|
4643
|
-
{ className: "iterate-obs-tabs", role: "tablist", "aria-label": "\u89C2\u6D4B\u53F0\u9875\u7B7E\uFF08\u5FEB\u6377\u952E 1-9 / 0 / -\uFF09" },
|
|
5293
|
+
{ className: "iterate-obs-tabs", role: "tablist", "aria-label": t("\u89C2\u6D4B\u53F0\u9875\u7B7E\uFF08\u5FEB\u6377\u952E 1-9 / 0 / -\uFF09") },
|
|
4644
5294
|
...OBS_TABS.map(
|
|
4645
|
-
(
|
|
4646
|
-
key:
|
|
5295
|
+
(t2) => React.createElement("button", {
|
|
5296
|
+
key: t2.key,
|
|
4647
5297
|
className: "iterate-obs-tab",
|
|
4648
5298
|
role: "tab",
|
|
4649
|
-
"aria-selected": tab ===
|
|
4650
|
-
"data-active": tab ===
|
|
4651
|
-
onClick: () => setTab(
|
|
4652
|
-
title:
|
|
4653
|
-
}, obsTabLabel(
|
|
5299
|
+
"aria-selected": tab === t2.key,
|
|
5300
|
+
"data-active": tab === t2.key ? "" : void 0,
|
|
5301
|
+
onClick: () => setTab(t2.key),
|
|
5302
|
+
title: t2.hotkey ? tf(`{a0}\uFF08\u6309 {a1} \u5207\u6362\uFF09`, { a0: obsTabLabel(t2), a1: t2.hotkey }) : tf(`{a0}\uFF08\u6309 - \u5207\u6362\uFF09`, { a0: obsTabLabel(t2) })
|
|
5303
|
+
}, obsTabLabel(t2))
|
|
4654
5304
|
)
|
|
4655
5305
|
),
|
|
4656
5306
|
React.createElement("div", { className: "iterate-obs-body" }, renderBody())
|
|
@@ -4663,11 +5313,21 @@ function selectTurnTail(owner) {
|
|
|
4663
5313
|
if (scanSessionForRunSummary(owner.turn)) return { matched: true };
|
|
4664
5314
|
return null;
|
|
4665
5315
|
}
|
|
5316
|
+
var LangDashboard = withLang(ConvergenceDashboard);
|
|
5317
|
+
var LangObservatory = withLang(ObservatoryPanel);
|
|
5318
|
+
var LangTurnTail = withLang(TurnTailEntry);
|
|
5319
|
+
var LangProgress = withLang(ProgressCapsule);
|
|
5320
|
+
var LangSettings = withLang(SettingsPanel);
|
|
4666
5321
|
function apply(ctx) {
|
|
4667
5322
|
slotsSvc = readSlots(ctx);
|
|
4668
5323
|
themeSvc = readTheme(ctx);
|
|
4669
|
-
|
|
4670
|
-
|
|
5324
|
+
storage2 = createStorage();
|
|
5325
|
+
configureLangStorage(storage2);
|
|
5326
|
+
setLang(
|
|
5327
|
+
detectLang(storage2.get(LANG_STORAGE_KEY), typeof navigator !== "undefined" ? navigator.language : ""),
|
|
5328
|
+
false
|
|
5329
|
+
);
|
|
5330
|
+
const savedTheme = storage2.get(THEME_STORAGE_KEY);
|
|
4671
5331
|
themeEnabled = savedTheme === null ? true : savedTheme === "1";
|
|
4672
5332
|
if (themeEnabled) applyThemeSkin();
|
|
4673
5333
|
if (typeof document !== "undefined" && document.createElement && document.head) {
|
|
@@ -4697,35 +5357,35 @@ function apply(ctx) {
|
|
|
4697
5357
|
"conversation.input.dock",
|
|
4698
5358
|
() => slotsSvc?.register(
|
|
4699
5359
|
{ name: "conversation.input.dock", id: "iterate-dashboard", order: 90 },
|
|
4700
|
-
(props) => React.createElement(
|
|
5360
|
+
(props) => React.createElement(LangDashboard, props)
|
|
4701
5361
|
)
|
|
4702
5362
|
);
|
|
4703
5363
|
slotsSvc.inject(
|
|
4704
5364
|
"conversation.input.dock",
|
|
4705
5365
|
() => slotsSvc?.register(
|
|
4706
5366
|
{ name: "conversation.input.dock", id: "iterate-observatory", order: 91 },
|
|
4707
|
-
(props) => React.createElement(
|
|
5367
|
+
(props) => React.createElement(LangObservatory, props)
|
|
4708
5368
|
)
|
|
4709
5369
|
);
|
|
4710
5370
|
slotsSvc.inject(
|
|
4711
5371
|
"conversation.chat.turnTail",
|
|
4712
5372
|
() => slotsSvc?.register(
|
|
4713
5373
|
{ name: "conversation.chat.turnTail", id: "iterate-turn-tail", select: selectTurnTail },
|
|
4714
|
-
(props) => React.createElement(
|
|
5374
|
+
(props) => React.createElement(LangTurnTail, props)
|
|
4715
5375
|
)
|
|
4716
5376
|
);
|
|
4717
5377
|
slotsSvc.inject(
|
|
4718
5378
|
"shell.overlay",
|
|
4719
5379
|
() => slotsSvc?.register(
|
|
4720
5380
|
{ name: "shell.overlay", id: "iterate-progress", order: 0 },
|
|
4721
|
-
(props) => React.createElement(
|
|
5381
|
+
(props) => React.createElement(LangProgress, props)
|
|
4722
5382
|
)
|
|
4723
5383
|
);
|
|
4724
5384
|
slotsSvc.inject(
|
|
4725
5385
|
"settings.section",
|
|
4726
5386
|
() => slotsSvc?.register(
|
|
4727
5387
|
{ name: "settings.section", id: "iterate-settings", order: 30, label: () => "iterate" },
|
|
4728
|
-
(props) => React.createElement(
|
|
5388
|
+
(props) => React.createElement(LangSettings, props)
|
|
4729
5389
|
)
|
|
4730
5390
|
);
|
|
4731
5391
|
}
|