dsh-plugin-teamflow 0.1.8 → 0.2.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/CHANGELOG.md +252 -159
- package/README.en.md +63 -57
- package/README.md +51 -74
- package/lib/client.js +1123 -291
- package/lib/descriptors.mjs +14 -1
- package/lib/host.mjs +4893 -696
- package/lib/store.mjs +77 -6
- package/package.json +134 -134
package/lib/client.js
CHANGED
|
@@ -29,11 +29,15 @@ window.__ModuleLoader__.load({
|
|
|
29
29
|
let react = require("react");
|
|
30
30
|
react = __toESM(react, 1);
|
|
31
31
|
//#region descriptors.ts
|
|
32
|
+
/** 恒等 parse:接受任意 JSON 值,原样返回。 */
|
|
33
|
+
const JSON_SCHEMA = { parse: (value) => value };
|
|
32
34
|
/** 统一 strict codec(本插件所有参数/结果均为自由 JSON)。 */
|
|
33
35
|
const strict = {
|
|
34
36
|
mode: "strict",
|
|
35
37
|
typeSymbol: "dsh-plugin-teamflow/types#Json",
|
|
36
|
-
schema:
|
|
38
|
+
schema: JSON_SCHEMA,
|
|
39
|
+
/** 首次跨边界使用时物化 schema(宿主按需调用,只调一次并缓存)。 */
|
|
40
|
+
create: () => JSON_SCHEMA
|
|
37
41
|
};
|
|
38
42
|
const p = (name) => ({
|
|
39
43
|
name,
|
|
@@ -51,6 +55,15 @@ window.__ModuleLoader__.load({
|
|
|
51
55
|
parameters: [],
|
|
52
56
|
result: strict
|
|
53
57
|
},
|
|
58
|
+
{
|
|
59
|
+
id: "dsh-plugin-teamflow#teamflow/setLocale",
|
|
60
|
+
service: "teamflow",
|
|
61
|
+
namespace: "teamflow",
|
|
62
|
+
method: "setLocale",
|
|
63
|
+
invocation: { kind: "direct" },
|
|
64
|
+
parameters: [p("locale")],
|
|
65
|
+
result: strict
|
|
66
|
+
},
|
|
54
67
|
{
|
|
55
68
|
id: "dsh-plugin-teamflow#teamflow/list",
|
|
56
69
|
service: "teamflow",
|
|
@@ -287,7 +300,60 @@ window.__ModuleLoader__.load({
|
|
|
287
300
|
* 会话内工作台(index.tsx)与全局面板(panel.tsx)共用:**只放无状态纯展示件**,
|
|
288
301
|
* 不放任何 Remote 调用或会话上下文逻辑——两边数据来源不同(sessionId / productKey),
|
|
289
302
|
* 展示语言必须一致。
|
|
303
|
+
*
|
|
304
|
+
* 双语(v0.1.9):文案统一走宿主 locale 服务(机制说明见 client/locales.ts)。
|
|
305
|
+
* 组件里能拿到注入的 `t`,但**词表/格式化/折叠件是纯函数**,拿不到 prop,
|
|
306
|
+
* 故由 `apply()` 调 `setTranslator()` 注入模块级翻译函数:`bind()` 每次调用都读当前语言,
|
|
307
|
+
* 且宿主在切语言时会重渲染每个 slot outlet(ui-renderer `useLocaleRevision`),
|
|
308
|
+
* 因此模块级函数不会持有过期语言。
|
|
309
|
+
*/
|
|
310
|
+
/** 翻译函数(默认恒等:未注入时显示 key 本身,便于发现漏注册)。 */
|
|
311
|
+
let translate = (key) => key;
|
|
312
|
+
/** 当前语言 id 的读取器(默认 en)。 */
|
|
313
|
+
let localeIdOf = () => "en";
|
|
314
|
+
/**
|
|
315
|
+
* 注入翻译函数与语言读取器(apply 时调用一次)。
|
|
316
|
+
* @param fn - `ctx.locale.bind(NS)` 的返回值(调用时读当前语言)。
|
|
317
|
+
* @param idOf - 返回当前语言 id 的函数(如 `() => ctx.locale.getSnapshot().active`)。
|
|
290
318
|
*/
|
|
319
|
+
function setTranslator(fn, idOf) {
|
|
320
|
+
if (typeof fn === "function") translate = fn;
|
|
321
|
+
if (typeof idOf === "function") localeIdOf = idOf;
|
|
322
|
+
}
|
|
323
|
+
/** 翻译(`{name}` 占位符由宿主替换)。 */
|
|
324
|
+
const t = (key, params) => translate(key, params);
|
|
325
|
+
/** 当前语言的 BCP 47 标签(时间格式化用;未知语言回退 en)。 */
|
|
326
|
+
function localeTag() {
|
|
327
|
+
let id = "en";
|
|
328
|
+
try {
|
|
329
|
+
id = String(localeIdOf() || "en");
|
|
330
|
+
} catch (e) {}
|
|
331
|
+
return id === "zh" ? "zh-CN" : id;
|
|
332
|
+
}
|
|
333
|
+
/** 词表查表:命中返回译文,未命中回退原始值(未知状态/角色等)。 */
|
|
334
|
+
function vocab(prefix, raw) {
|
|
335
|
+
const key = `${prefix}.${raw}`;
|
|
336
|
+
const hit = t(key);
|
|
337
|
+
return hit === key ? String(raw === null || raw === void 0 ? "" : raw) : hit;
|
|
338
|
+
}
|
|
339
|
+
const stText = (s) => vocab("status", s);
|
|
340
|
+
const runStatusText = (s) => vocab("runStatus", s);
|
|
341
|
+
/**
|
|
342
|
+
* **阶段**状态文案:只有 `cancelled` 与 backlog 词表分道。
|
|
343
|
+
*
|
|
344
|
+
* `stText` 是 backlog 卡片词表(`status.cancelled`/`closed`/`verified` 都译「已关闭」),阶段渲染若直接复用它,
|
|
345
|
+
* 被中断的阶段会显示成「已关闭」(en:Closed)——2026-09-16 中断功能实测截图里,同一屏 run 行写「已取消」、
|
|
346
|
+
* 阶段节点写「已关闭」。阶段是被**中止**而不是被关闭,故单独取词;其余阶段状态仍走同一张表(不开两套词表)。
|
|
347
|
+
*/
|
|
348
|
+
const stageStatusText = (s) => s === "cancelled" ? t("stageStatus.cancelled") : stText(s);
|
|
349
|
+
const kindTitle = (k) => vocab("kind", k);
|
|
350
|
+
const roleName = (r) => vocab("role", r);
|
|
351
|
+
/** 角色 chip(带图标;未知角色回退「⚙️ <raw>」)。 */
|
|
352
|
+
function roleChip(r) {
|
|
353
|
+
const key = `roleChip.${r}`;
|
|
354
|
+
const hit = t(key);
|
|
355
|
+
return hit === key ? `⚙️ ${String(r)}` : hit;
|
|
356
|
+
}
|
|
291
357
|
const T = {
|
|
292
358
|
bg: "var(--dsw-alias-bg-base)",
|
|
293
359
|
layer1: "var(--dsw-alias-bg-layer-1)",
|
|
@@ -301,30 +367,7 @@ window.__ModuleLoader__.load({
|
|
|
301
367
|
success: "var(--dsw-alias-state-success-primary)",
|
|
302
368
|
warn: "var(--dsw-alias-state-warn-primary)"
|
|
303
369
|
};
|
|
304
|
-
|
|
305
|
-
created: "立项",
|
|
306
|
-
"in-progress": "进行中",
|
|
307
|
-
"pending-acceptance": "待验收",
|
|
308
|
-
accepted: "已验收",
|
|
309
|
-
closed: "已关闭",
|
|
310
|
-
pending: "待办",
|
|
311
|
-
running: "开发中",
|
|
312
|
-
testable: "待测试",
|
|
313
|
-
testing: "测试中",
|
|
314
|
-
rework: "打回",
|
|
315
|
-
"needs-human": "需人工",
|
|
316
|
-
cancelled: "已关闭",
|
|
317
|
-
open: "待认领",
|
|
318
|
-
claimed: "处理中",
|
|
319
|
-
fixed: "已修复待验",
|
|
320
|
-
verified: "已关闭",
|
|
321
|
-
reopened: "重开",
|
|
322
|
-
done: "已完成",
|
|
323
|
-
completed: "已完成",
|
|
324
|
-
failed: "失败",
|
|
325
|
-
interrupted: "已中断",
|
|
326
|
-
superseded: "已取代"
|
|
327
|
-
};
|
|
370
|
+
/** 状态色表(与语言无关的纯视觉映射)。 */
|
|
328
371
|
const STATUS_COLOR = {
|
|
329
372
|
created: T.text2,
|
|
330
373
|
pending: T.text2,
|
|
@@ -349,7 +392,7 @@ window.__ModuleLoader__.load({
|
|
|
349
392
|
interrupted: T.warn,
|
|
350
393
|
superseded: T.text2
|
|
351
394
|
};
|
|
352
|
-
/** 阶段英文键 →
|
|
395
|
+
/** 阶段英文键 → 图标(2026-09-06 英文化:journal.phase 为英文键,展示名统一走词表——换语言即换表)。 */
|
|
353
396
|
const PHASE_ICON = {
|
|
354
397
|
prd: "📋",
|
|
355
398
|
design: "🎨",
|
|
@@ -359,17 +402,6 @@ window.__ModuleLoader__.load({
|
|
|
359
402
|
qa: "🧪",
|
|
360
403
|
acceptance: "✅"
|
|
361
404
|
};
|
|
362
|
-
const PHASE_NAME = {
|
|
363
|
-
prd: "PRD 产品需求",
|
|
364
|
-
design: "UI/UX 设计",
|
|
365
|
-
scaffold: "架构规划",
|
|
366
|
-
tech: "技术方案",
|
|
367
|
-
dev: "开发",
|
|
368
|
-
qa: "QA 测试",
|
|
369
|
-
acceptance: "产品验收"
|
|
370
|
-
};
|
|
371
|
-
const phaseNameOf = (p) => PHASE_NAME[p] || p || "—";
|
|
372
|
-
const phaseIconOf = (p) => PHASE_ICON[p] || "⚙️";
|
|
373
405
|
/** phase 归一:英文键直通;存量中文映射(防御性——新数据全英文)。 */
|
|
374
406
|
const phaseKeyOf = (p) => ({
|
|
375
407
|
"PRD 产品需求": "prd",
|
|
@@ -380,15 +412,22 @@ window.__ModuleLoader__.load({
|
|
|
380
412
|
"QA 测试": "qa",
|
|
381
413
|
"产品验收": "acceptance"
|
|
382
414
|
})[p] || String(p || "");
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
415
|
+
const phaseNameOf = (p) => vocab("phase", phaseKeyOf(p));
|
|
416
|
+
const phaseIconOf = (p) => PHASE_ICON[phaseKeyOf(p)] || "⚙️";
|
|
417
|
+
/**
|
|
418
|
+
* 阶段的展示名(双语安全的取法)。
|
|
419
|
+
*
|
|
420
|
+
* journal 里 `stage.label` 是**持久化中文**(teams.json 配置 + 历史数据),而每个阶段都带
|
|
421
|
+
* 英文 `phase` 键——所以展示时:任务级阶段(dev 子卡,`taskKey` 有值)保留任务名(LLM 数据,
|
|
422
|
+
* 不该翻译),其余阶段用 `phase` 键查当前语言词表。**只影响展示,不动数据**:
|
|
423
|
+
* `__taskKey`/`taskKeyOf` 的任务聚合身份仍走 label 清理(聚合语义不能随语言变)。
|
|
424
|
+
*/
|
|
425
|
+
function stageLabelOf(s) {
|
|
426
|
+
const raw = String(s && s.label || "");
|
|
427
|
+
if (s && s.taskKey) return raw || String(s.taskKey);
|
|
428
|
+
if (s && phaseKeyOf(s.phase) === "dev" && raw) return raw;
|
|
429
|
+
return (s && s.phase ? phaseNameOf(s.phase) : "") || raw;
|
|
430
|
+
}
|
|
392
431
|
const COLUMNS = {
|
|
393
432
|
req: [
|
|
394
433
|
"created",
|
|
@@ -418,11 +457,6 @@ window.__ModuleLoader__.load({
|
|
|
418
457
|
"needs-human"
|
|
419
458
|
]
|
|
420
459
|
};
|
|
421
|
-
const KIND_TITLE = {
|
|
422
|
-
req: "需求",
|
|
423
|
-
task: "任务",
|
|
424
|
-
bug: "缺陷"
|
|
425
|
-
};
|
|
426
460
|
const h = react.default.createElement;
|
|
427
461
|
const MONO = "ui-monospace, SFMono-Regular, Consolas, \"Cascadia Mono\", monospace";
|
|
428
462
|
const SANS = "-apple-system, BlinkMacSystemFont, \"Segoe UI\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif";
|
|
@@ -461,9 +495,9 @@ window.__ModuleLoader__.load({
|
|
|
461
495
|
function FoldableText({ text, charLimit = 280, lineLimit = 5, style }) {
|
|
462
496
|
const [open, setOpen] = react.default.useState(false);
|
|
463
497
|
if (!text) return null;
|
|
464
|
-
const
|
|
465
|
-
const lines =
|
|
466
|
-
const compact = lines.length <= lineLimit &&
|
|
498
|
+
const s = String(text);
|
|
499
|
+
const lines = s.split("\n");
|
|
500
|
+
const compact = lines.length <= lineLimit && s.length <= charLimit;
|
|
467
501
|
const body = (txt) => h("div", { style: {
|
|
468
502
|
fontSize: 11.5,
|
|
469
503
|
color: T.text,
|
|
@@ -472,10 +506,10 @@ window.__ModuleLoader__.load({
|
|
|
472
506
|
wordBreak: "break-word",
|
|
473
507
|
...style || {}
|
|
474
508
|
} }, txt);
|
|
475
|
-
if (compact) return body(
|
|
476
|
-
if (open) return h("div", null, body(
|
|
509
|
+
if (compact) return body(s);
|
|
510
|
+
if (open) return h("div", null, body(s), h("button", {
|
|
477
511
|
onClick: () => setOpen(false),
|
|
478
|
-
title: "
|
|
512
|
+
title: t("common.collapseFull"),
|
|
479
513
|
style: {
|
|
480
514
|
marginTop: 3,
|
|
481
515
|
font: "inherit",
|
|
@@ -487,12 +521,12 @@ window.__ModuleLoader__.load({
|
|
|
487
521
|
padding: 0,
|
|
488
522
|
cursor: "pointer"
|
|
489
523
|
}
|
|
490
|
-
}, "
|
|
491
|
-
const pre = lines.length > lineLimit ? lines.slice(0, lineLimit).join("\n") :
|
|
492
|
-
const more = lines.length > lineLimit ?
|
|
524
|
+
}, t("common.collapse")));
|
|
525
|
+
const pre = lines.length > lineLimit ? lines.slice(0, lineLimit).join("\n") : s.slice(0, charLimit);
|
|
526
|
+
const more = lines.length > lineLimit ? t("common.moreLines", { n: lines.length - lineLimit }) : "…";
|
|
493
527
|
return h("div", null, body(pre), h("button", {
|
|
494
528
|
onClick: () => setOpen(true),
|
|
495
|
-
title: "
|
|
529
|
+
title: t("common.clickToExpand"),
|
|
496
530
|
style: {
|
|
497
531
|
marginTop: 3,
|
|
498
532
|
font: "inherit",
|
|
@@ -504,11 +538,75 @@ window.__ModuleLoader__.load({
|
|
|
504
538
|
padding: 0,
|
|
505
539
|
cursor: "pointer"
|
|
506
540
|
}
|
|
507
|
-
},
|
|
541
|
+
}, `${t("common.expandFull")}${more}`));
|
|
508
542
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
543
|
+
/**
|
|
544
|
+
* 中断运行按钮(**两段式内联确认**):首次点击进入待确认态,3 秒内再点一次才真正执行。
|
|
545
|
+
*
|
|
546
|
+
* 为什么不自造对话框:客户端全目录 grep `confirm|dialog` 0 命中——目前没有宿主确认能力依赖,
|
|
547
|
+
* 内联两段式不引新依赖、也不改宿主能力面,且天然贴合「误点一个跑了几十分钟的 run」的防护诉求。
|
|
548
|
+
*
|
|
549
|
+
* 数据面由调用方给(`onConfirm(runId)`):会话内工作台与全局面板/右栏共用同一个 `teamflow/cancel`
|
|
550
|
+
* 方法,本件只负责待确认窗口、忙碌态与**点击不冒泡**(面板 run 行整行可点开详情)。
|
|
551
|
+
* `onConfirm` **必须自行消化错误**(它由调用方 try/catch 并落到可见的 err 提示);本件兜底只记 console,
|
|
552
|
+
* 不让点击处理里出现未处理的 rejection。
|
|
553
|
+
*/
|
|
554
|
+
function CancelButton({ runId, label, title, onConfirm, style }) {
|
|
555
|
+
const [arm, setArm] = react.default.useState(false);
|
|
556
|
+
const [busy, setBusy] = react.default.useState(false);
|
|
557
|
+
react.default.useEffect(() => {
|
|
558
|
+
if (!arm) return void 0;
|
|
559
|
+
const timer = setTimeout(() => setArm(false), 3e3);
|
|
560
|
+
return () => clearTimeout(timer);
|
|
561
|
+
}, [arm]);
|
|
562
|
+
const onClick = async (e) => {
|
|
563
|
+
if (e && typeof e.stopPropagation === "function") e.stopPropagation();
|
|
564
|
+
if (busy) return;
|
|
565
|
+
if (!arm) {
|
|
566
|
+
setArm(true);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
setArm(false);
|
|
570
|
+
setBusy(true);
|
|
571
|
+
try {
|
|
572
|
+
await onConfirm(runId);
|
|
573
|
+
} catch (err) {
|
|
574
|
+
console.warn("[teamflow] cancel failed", err);
|
|
575
|
+
} finally {
|
|
576
|
+
setBusy(false);
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
const tone = busy ? {
|
|
580
|
+
opacity: .6,
|
|
581
|
+
cursor: "default"
|
|
582
|
+
} : arm ? {
|
|
583
|
+
background: T.error,
|
|
584
|
+
color: "#fff",
|
|
585
|
+
borderColor: T.error
|
|
586
|
+
} : null;
|
|
587
|
+
return h("button", {
|
|
588
|
+
onClick,
|
|
589
|
+
disabled: busy,
|
|
590
|
+
title: title || void 0,
|
|
591
|
+
style: {
|
|
592
|
+
font: "inherit",
|
|
593
|
+
fontSize: 12,
|
|
594
|
+
padding: "4px 12px",
|
|
595
|
+
borderRadius: 8,
|
|
596
|
+
cursor: "pointer",
|
|
597
|
+
border: `1px solid ${T.error}`,
|
|
598
|
+
background: "transparent",
|
|
599
|
+
color: T.error,
|
|
600
|
+
fontWeight: 600,
|
|
601
|
+
transition: "background .12s ease, color .12s ease",
|
|
602
|
+
...style || {},
|
|
603
|
+
...tone || {}
|
|
604
|
+
}
|
|
605
|
+
}, busy ? t("cancel.busy") : arm ? t("cancel.arm") : label || t("cancel.btn"));
|
|
606
|
+
}
|
|
607
|
+
function fmtTime(tm) {
|
|
608
|
+
if (!tm) return "—";
|
|
609
|
+
const d = new Date(tm);
|
|
512
610
|
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}:${String(d.getSeconds()).padStart(2, "0")}`;
|
|
513
611
|
}
|
|
514
612
|
function fmtDur(a, b) {
|
|
@@ -545,9 +643,15 @@ window.__ModuleLoader__.load({
|
|
|
545
643
|
if (s.usage) {
|
|
546
644
|
const u = s.usage;
|
|
547
645
|
const hit = hitRate(u);
|
|
548
|
-
return
|
|
646
|
+
return t("token.usageLine", {
|
|
647
|
+
input: k(u.input),
|
|
648
|
+
cacheRead: k(u.cacheRead),
|
|
649
|
+
cacheWrite: k(u.cacheWrite),
|
|
650
|
+
output: k(u.output),
|
|
651
|
+
calls: u.calls
|
|
652
|
+
}) + (hit !== null ? t("token.usageHit", { hit }) : "");
|
|
549
653
|
}
|
|
550
|
-
return "
|
|
654
|
+
return t("token.usageMissing");
|
|
551
655
|
}
|
|
552
656
|
/** 节点卡主 token 行:官方口径 —— 输入(未命中)/输入(命中)/输出 + 缓存命中率。 */
|
|
553
657
|
function stageUsageLine(s) {
|
|
@@ -558,16 +662,6 @@ window.__ModuleLoader__.load({
|
|
|
558
662
|
}
|
|
559
663
|
return null;
|
|
560
664
|
}
|
|
561
|
-
const ROLE_NAME = {
|
|
562
|
-
pm: "产品",
|
|
563
|
-
design: "设计",
|
|
564
|
-
arch: "架构",
|
|
565
|
-
tech: "方案",
|
|
566
|
-
dev: "开发",
|
|
567
|
-
qa: "测试",
|
|
568
|
-
acceptance: "验收",
|
|
569
|
-
other: "其他"
|
|
570
|
-
};
|
|
571
665
|
const roleUsage = (u) => {
|
|
572
666
|
if (!u) return "";
|
|
573
667
|
const hit = hitRate(u);
|
|
@@ -576,11 +670,11 @@ window.__ModuleLoader__.load({
|
|
|
576
670
|
/** 任务卡按角色累计的真实 token 摘要(官方口径:未命中/命中输入 + 输出 + 命中率)。 */
|
|
577
671
|
function byRoleLine(task) {
|
|
578
672
|
const roles = task && task.byRole || {};
|
|
579
|
-
return Object.keys(roles).filter((k) => roles[k] && roles[k].input + roles[k].output + roles[k].cacheRead + roles[k].cacheWrite > 0).map((k) => `${
|
|
673
|
+
return Object.keys(roles).filter((k) => roles[k] && roles[k].input + roles[k].output + roles[k].cacheRead + roles[k].cacheWrite > 0).map((k) => `${roleName(k)} ${roleUsage(roles[k])}`).join(" · ");
|
|
580
674
|
}
|
|
581
675
|
/** 多阶段 usage 汇总(官方口径)。 */
|
|
582
676
|
function totalUsage(stages) {
|
|
583
|
-
const
|
|
677
|
+
const sum = {
|
|
584
678
|
input: 0,
|
|
585
679
|
cacheRead: 0,
|
|
586
680
|
cacheWrite: 0,
|
|
@@ -590,17 +684,575 @@ window.__ModuleLoader__.load({
|
|
|
590
684
|
for (const s of stages || []) {
|
|
591
685
|
const u = s && s.usage;
|
|
592
686
|
if (!u) continue;
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
687
|
+
sum.input += u.input || 0;
|
|
688
|
+
sum.cacheRead += u.cacheRead || 0;
|
|
689
|
+
sum.cacheWrite += u.cacheWrite || 0;
|
|
690
|
+
sum.output += u.output || 0;
|
|
691
|
+
sum.calls += u.calls || 0;
|
|
598
692
|
}
|
|
599
|
-
return
|
|
693
|
+
return sum;
|
|
600
694
|
}
|
|
601
|
-
const stText = (s) => STATUS_TEXT[s] || s;
|
|
602
695
|
const stColor = (s) => STATUS_COLOR[s] || T.text2;
|
|
603
696
|
//#endregion
|
|
697
|
+
//#region client/locales.ts
|
|
698
|
+
/**
|
|
699
|
+
* dsh-plugin-teamflow — 客户端文案词典(zh / en)。
|
|
700
|
+
*
|
|
701
|
+
* 机制:走宿主 `ctx.locale`(@deepseek-ai/dsh-client-locale),不自建 i18n:
|
|
702
|
+
* - `apply()` 里 `ctx.locale.register(NS, { zh, en })` 注册两个词典;
|
|
703
|
+
* - `ctx.locale.bind(NS)` 取翻译函数(调用时读当前语言,故可常驻模块变量);
|
|
704
|
+
* - slot 注册项声明 `locale: NS`,切语言时宿主重渲染每个 outlet
|
|
705
|
+
* (ui-renderer `useLocaleRevision`),面板/tab 名称用 thunk `() => t(...)`。
|
|
706
|
+
*
|
|
707
|
+
* 约定:
|
|
708
|
+
* - key 扁平点号命名,按区域前缀分组(common/status/phase/kind/role/stage/…);
|
|
709
|
+
* - 占位符用 `{name}`(宿主 `translate` 的替换语法);
|
|
710
|
+
* - **zh 与 en 的 key 集合必须完全一致**(en 是兜底语言,缺 key 会直接显示 key 本身;
|
|
711
|
+
* 由 test/smoke.js 断言守门);
|
|
712
|
+
* - 数据面文案(LLM 产出的标题/摘要、teams.json 的团队名、journal 里既有中文 label)不在此表,
|
|
713
|
+
* 属 host/产物语言层,见 AGENTS §5「客户端面」锚点。
|
|
714
|
+
*/
|
|
715
|
+
/** 词典命名空间(宿主 LocaleRuntime 的单占位命名空间)。 */
|
|
716
|
+
const NS = "teamflow";
|
|
717
|
+
/** 中文词典(key 为单一事实来源,en 必须与之同形)。 */
|
|
718
|
+
const zh = {
|
|
719
|
+
"common.close": "关闭",
|
|
720
|
+
"common.loading": "加载中…",
|
|
721
|
+
"common.retry": "重试",
|
|
722
|
+
"common.collapse": "收起",
|
|
723
|
+
"common.collapseFull": "收起全文",
|
|
724
|
+
"common.clickToExpand": "点击查看全文",
|
|
725
|
+
"common.expandFull": "展开全文",
|
|
726
|
+
"common.moreLines": "… +{n} 行",
|
|
727
|
+
"common.none": "无",
|
|
728
|
+
"common.noneDash": "(暂无)",
|
|
729
|
+
"common.noSummary": "(无摘要)",
|
|
730
|
+
"common.failed": "失败",
|
|
731
|
+
"common.running": "进行中",
|
|
732
|
+
"common.needsHuman": "需人工介入",
|
|
733
|
+
"common.unknownError": "未知错误",
|
|
734
|
+
"common.gotIt": "知道了",
|
|
735
|
+
"common.remoteNotReady": "remote 未就绪",
|
|
736
|
+
"common.remoteCallFailed": "{what} 调用失败:{detail}",
|
|
737
|
+
"common.remoteEmptyResult": "{what} 返回空结果(ok=true 但无 value;原始信封={raw})",
|
|
738
|
+
"common.calls": "{n} 次",
|
|
739
|
+
"common.callsCount": "{n} 次调用",
|
|
740
|
+
"common.noUsage": "无 token 数据",
|
|
741
|
+
"status.created": "立项",
|
|
742
|
+
"status.in-progress": "进行中",
|
|
743
|
+
"status.pending-acceptance": "待验收",
|
|
744
|
+
"status.accepted": "已验收",
|
|
745
|
+
"status.closed": "已关闭",
|
|
746
|
+
"status.pending": "待办",
|
|
747
|
+
"status.running": "开发中",
|
|
748
|
+
"status.testable": "待测试",
|
|
749
|
+
"status.testing": "测试中",
|
|
750
|
+
"status.rework": "打回",
|
|
751
|
+
"status.needs-human": "需人工",
|
|
752
|
+
"status.cancelled": "已关闭",
|
|
753
|
+
"status.open": "待认领",
|
|
754
|
+
"status.claimed": "处理中",
|
|
755
|
+
"status.fixed": "已修复待验",
|
|
756
|
+
"status.verified": "已关闭",
|
|
757
|
+
"status.reopened": "重开",
|
|
758
|
+
"status.done": "已完成",
|
|
759
|
+
"status.completed": "已完成",
|
|
760
|
+
"status.failed": "失败",
|
|
761
|
+
"status.interrupted": "已中断",
|
|
762
|
+
"status.superseded": "已取代",
|
|
763
|
+
"runStatus.pending": "等待中",
|
|
764
|
+
"runStatus.running": "进行中",
|
|
765
|
+
"runStatus.completed": "已完成",
|
|
766
|
+
"runStatus.failed": "失败",
|
|
767
|
+
"runStatus.cancelled": "已取消",
|
|
768
|
+
"runStatus.interrupted": "已中断",
|
|
769
|
+
"runStatus.superseded": "已取代",
|
|
770
|
+
"stageStatus.cancelled": "已中止",
|
|
771
|
+
"phase.prd": "PRD 产品需求",
|
|
772
|
+
"phase.design": "UI/UX 设计",
|
|
773
|
+
"phase.scaffold": "架构规划",
|
|
774
|
+
"phase.tech": "技术方案",
|
|
775
|
+
"phase.dev": "开发",
|
|
776
|
+
"phase.qa": "QA 测试",
|
|
777
|
+
"phase.acceptance": "产品验收",
|
|
778
|
+
"kind.req": "需求",
|
|
779
|
+
"kind.task": "任务",
|
|
780
|
+
"kind.bug": "缺陷",
|
|
781
|
+
"role.pm": "产品",
|
|
782
|
+
"role.design": "设计",
|
|
783
|
+
"role.arch": "架构",
|
|
784
|
+
"role.tech": "方案",
|
|
785
|
+
"role.dev": "开发",
|
|
786
|
+
"role.qa": "测试",
|
|
787
|
+
"role.acceptance": "验收",
|
|
788
|
+
"role.other": "其他",
|
|
789
|
+
"roleChip.pm": "📌 产品",
|
|
790
|
+
"roleChip.design": "🎨 设计",
|
|
791
|
+
"roleChip.arch": "🏗 架构",
|
|
792
|
+
"roleChip.tech": "📐 方案",
|
|
793
|
+
"roleChip.dev": "👨💻 开发",
|
|
794
|
+
"roleChip.qa": "🧪 QA",
|
|
795
|
+
"roleChip.acceptance": "✅ 验收",
|
|
796
|
+
"token.officialTitle": "TOKEN · 官方口径",
|
|
797
|
+
"token.usageLine": "输入(未命中) {input} / 输入(命中) {cacheRead} / 写缓存 {cacheWrite} / 输出 {output} · {calls} 次调用",
|
|
798
|
+
"token.usageHit": " · 缓存命中 {hit}%",
|
|
799
|
+
"token.usageMissing": "无 usage 明细",
|
|
800
|
+
"token.inputMiss": "输入(未命中) {v}",
|
|
801
|
+
"token.inputHit": "输入(命中) {v}",
|
|
802
|
+
"token.cacheWrite": "写缓存 {v}",
|
|
803
|
+
"token.output": "输出 {v}",
|
|
804
|
+
"token.total": "合计 {v}",
|
|
805
|
+
"token.allStagesTip": "输入(未命中)/输入(命中)/输出 全部阶段合计",
|
|
806
|
+
"token.stageLine": "阶段 token:{v}",
|
|
807
|
+
"stage.cardTip": "{label} —— 点击查看阶段详情",
|
|
808
|
+
"stage.retryTip": "重试 {n} 次(共 {m} 次尝试)",
|
|
809
|
+
"stage.detailTitle": "阶段详情",
|
|
810
|
+
"stage.summaryFallback": "(该 run 未保存完整正文,展示摘要)\n\n{summary}",
|
|
811
|
+
"stage.loadFailed": "⚠ 加载失败:{err}",
|
|
812
|
+
"stage.noOutput": "(无产物正文)",
|
|
813
|
+
"stage.attemptHistory": "↻ 尝试历史({n} 次 · 点击查看该次详情)",
|
|
814
|
+
"stage.attemptDone": "✅ 成功",
|
|
815
|
+
"stage.attemptFailed": "❌ {outcome}",
|
|
816
|
+
"stage.attemptRunning": "⏳ 进行中",
|
|
817
|
+
"stage.childJumpBtn": "🎬 跳转子代理会话",
|
|
818
|
+
"stage.childJumpTip": "跳转到该阶段子代理会话(完整推理与工具调用轨迹);跳转后请切换「对话」tab 查看",
|
|
819
|
+
"stage.childNoneTip": "该阶段无可用子代理会话",
|
|
820
|
+
"stage.childCrossSessionTip": "该子代理由会话 {sid} 发起,跨会话跳转暂不支持——请打开其发起会话的团队工作台查看",
|
|
821
|
+
"stage.childCrossSessionNote": "跨会话暂不支持:该子代理由会话 {sid} 发起。如需查看轨迹,请打开其发起会话的团队工作台。",
|
|
822
|
+
"stage.childJumpNote": "跳转成功后,请切「对话」tab 查看该子代理的完整会话轨迹",
|
|
823
|
+
"stage.evidenceTitle": "🔬 验证证据",
|
|
824
|
+
"stage.evidenceMissing": "(缺失——契约未兑现,host 已记警告;可与该 run 的命令日志对照:run 期间在 logs/teamflow/<runId>/,结束后由 host 归档到 $DSH_HOME/teamflow/<workspace>/logs/<runId>/)",
|
|
825
|
+
"stage.evidenceMissingShort": "(缺失——契约未兑现,host 已记警告)",
|
|
826
|
+
"stage.artifactsTitle": "📄 阶段性产物",
|
|
827
|
+
"pipeline.empty": "暂无运行中的流水线——让模型调用 teamflow_start,或在上方输入需求",
|
|
828
|
+
"pipeline.noNodes": "流水线还没有开始执行节点",
|
|
829
|
+
"pipeline.zoomOut": "缩小",
|
|
830
|
+
"pipeline.zoomIn": "放大",
|
|
831
|
+
"pipeline.fitCanvas": "适应画布",
|
|
832
|
+
"pipeline.canvasHint": "✥ 拖动画布 · 滚轮缩放",
|
|
833
|
+
"workbench.title": "团队工作台",
|
|
834
|
+
"workbench.running": "流水线运行中",
|
|
835
|
+
"workbench.idle": "空闲",
|
|
836
|
+
"workbench.refresh": "🔄 刷新",
|
|
837
|
+
"workbench.resumeTip": "断点续跑 {id}\n当前状态:{status};跳过已完成阶段,从第一个未完成阶段重跑",
|
|
838
|
+
"workbench.resuming": "续跑中…",
|
|
839
|
+
"workbench.resumeBtn": "↻ 从断点重跑 #{id}",
|
|
840
|
+
"workbench.loadFailed": "⚠ {err}(确认已安装 dsh-plugin-teamflow 且 web 已重启)",
|
|
841
|
+
"workbench.needsHumanBanner": "⚠ {n} 项需人工介入",
|
|
842
|
+
"workbench.handle": "处理 {id}",
|
|
843
|
+
"workbench.manualReason": "人工处理",
|
|
844
|
+
"workbench.tabPipeline": "🔄 流水线",
|
|
845
|
+
"workbench.tabBoard": "📋 Backlog 看板",
|
|
846
|
+
"workbench.workspaceTip": "当前工作区(workspace 级隔离):{path}",
|
|
847
|
+
"workbench.noWorkspace": "未连接工作区",
|
|
848
|
+
"workbench.history": "历史",
|
|
849
|
+
"workbench.openRightBarTip": "在右侧栏打开该 run 详情(与任务夹产物并排看)",
|
|
850
|
+
"workbench.openRightBarBtn": "⇥ 右栏打开",
|
|
851
|
+
"cancel.btn": "⏹ 中断",
|
|
852
|
+
"cancel.btnWithId": "⏹ 中断 #{id}",
|
|
853
|
+
"cancel.arm": "确认中断?",
|
|
854
|
+
"cancel.busy": "中断中…",
|
|
855
|
+
"cancel.sent": "已请求中断,等待当前阶段收尾…",
|
|
856
|
+
"cancel.tip": "中断 {id}\n立即中止当前阶段子代理;已完成阶段保留,之后可用「从断点重跑」继续;取消的 run 不产生提交",
|
|
857
|
+
"cancel.failed": "中断未生效:该流水线已不在运行中(可能刚结束)",
|
|
858
|
+
"board.empty": "backlog 为空(还没有流水线运行过)",
|
|
859
|
+
"board.dragReason": "看板拖拽流转",
|
|
860
|
+
"board.cardTip": "{id} · {status}{summary}(点击查看详情)",
|
|
861
|
+
"board.assignDevTip": "dev 分配\n{who}",
|
|
862
|
+
"board.assignQaTip": "qa 分配\n{who}",
|
|
863
|
+
"board.acceptTip": "验收/汇报人\n{who}",
|
|
864
|
+
"board.subtaskCount": "📦 {n} 子卡",
|
|
865
|
+
"board.retries": "重试 {n}",
|
|
866
|
+
"item.overview": "概览",
|
|
867
|
+
"item.acceptRow": "✅ 验收",
|
|
868
|
+
"item.retryRow": "↻ 重试",
|
|
869
|
+
"item.humanRow": "⚠ 人工介入",
|
|
870
|
+
"item.updatedAt": "更新于",
|
|
871
|
+
"item.runSection": "运行",
|
|
872
|
+
"item.runBtn": "▶ 流水线",
|
|
873
|
+
"item.jumpRunTip": "跳转到该需求的流水线视图 #{id}",
|
|
874
|
+
"item.requirement": "需求原文",
|
|
875
|
+
"item.noRequirement": "(无原文)",
|
|
876
|
+
"item.runDocs": "任务夹",
|
|
877
|
+
"item.previewTip": "在右侧栏预览 {path}",
|
|
878
|
+
"item.subtasks": "关联子卡({n})",
|
|
879
|
+
"item.bugs": "关联缺陷({n})",
|
|
880
|
+
"item.timeline": "流转时间线({n})",
|
|
881
|
+
"team.currentTip": "当前团队:{name}(点击切换)",
|
|
882
|
+
"team.pick": "选择团队",
|
|
883
|
+
"team.label": "团队",
|
|
884
|
+
"team.none": "无团队(直接对话)",
|
|
885
|
+
"team.noneNote": "不走 teamflow,模型直接工作",
|
|
886
|
+
"panel.title": "🏭 团队工作台",
|
|
887
|
+
"panel.subtitle": "全局面板 · 按产品线",
|
|
888
|
+
"panel.currentSession": "当前会话 {sid}",
|
|
889
|
+
"panel.noSession": "无当前会话",
|
|
890
|
+
"panel.reload": "刷新数据",
|
|
891
|
+
"panel.backToChat": "回到对话",
|
|
892
|
+
"panel.backToChatTip": "回到对话(再点侧边栏图标即可切回本面板)",
|
|
893
|
+
"panel.railTitle": "产品线 · {n}",
|
|
894
|
+
"panel.rescanTip": "重新扫描 $DSH_HOME/teamflow",
|
|
895
|
+
"panel.refreshing": "刷新中",
|
|
896
|
+
"panel.refresh": "刷新",
|
|
897
|
+
"panel.noProducts": "还没有产品线。在某个工作区跑过一次流水线后,这里会出现对应产品线($DSH_HOME/teamflow/<key>)。",
|
|
898
|
+
"panel.activeRuns": "运行 {n}",
|
|
899
|
+
"panel.updated": "更新 {time}",
|
|
900
|
+
"panel.reading": "读取中…",
|
|
901
|
+
"panel.verdict": "验收 {v}",
|
|
902
|
+
"panel.pickProduct": "选择左侧产品线查看 backlog 与 run(首次进入默认选最近更新的产品线)。",
|
|
903
|
+
"panel.loadingView": "读取产品线数据中…",
|
|
904
|
+
"panel.runChip": "run {n}",
|
|
905
|
+
"panel.activeChip": "活跃 {n}",
|
|
906
|
+
"panel.verdictChip": "验收 {v}",
|
|
907
|
+
"panel.tabRuns": "🚀 流水线 run · {n}",
|
|
908
|
+
"panel.tabBacklog": "📋 Backlog · {n}",
|
|
909
|
+
"panel.pinnedActive": "已置顶进行中 {n}",
|
|
910
|
+
"panel.showRecent": "只看最近 {n} 条",
|
|
911
|
+
"panel.showAll": "展开全部 {n} 条",
|
|
912
|
+
"panel.clearRunFilterTip": "清除 run 状态筛选",
|
|
913
|
+
"panel.filtered": "筛选中 {sel} 项 · 显示 {shown}/{total} × 清除",
|
|
914
|
+
"panel.emptyRunFilter": "该筛选下没有 run。",
|
|
915
|
+
"panel.runHint": "点一行看详情浮层;「去会话右栏」= 跳到该 run 的发起会话并在其右侧栏打开(与任务夹产物并排)",
|
|
916
|
+
"panel.boardHint": "点状态徽章可筛选(多选);终态卡片默认收起,筛选时自动显示",
|
|
917
|
+
"panel.runDetail": "run 详情",
|
|
918
|
+
"panel.goOwnerSession": "去发起会话",
|
|
919
|
+
"panel.goOwnerSessionTip": "跳到该 run 的发起会话,并在那个会话的右侧栏打开(与任务夹产物并排看)",
|
|
920
|
+
"panel.remoteProductsUnavailable": "remote.products 不可用(插件未挂载或版本过旧)",
|
|
921
|
+
"panel.hintNoAddress": "{label}:host 未生成可打开的地址(可能缺少会话上下文)",
|
|
922
|
+
"panel.hintRightbarFailed": "右侧栏打开失败(宿主只在对话视图挂载它):{label}——已在本面板内联显示",
|
|
923
|
+
"panel.hintSessionGone": "发起会话 {sid}… 不在会话列表里(可能已被清理)——已在本面板展示详情",
|
|
924
|
+
"panel.hintSwitchedNoRightbar": "已切到发起会话,但右栏没打开({label})——可在该会话里用「⇥ 右栏打开」重试",
|
|
925
|
+
"panel.hintInlineSuffix": "——已在本面板内联显示",
|
|
926
|
+
"runList.empty": "该产品线还没有 run 记录。",
|
|
927
|
+
"runList.running": "进行中",
|
|
928
|
+
"runList.noRequirement": "(无需求描述)",
|
|
929
|
+
"runList.stageProgress": "阶段 {done}/{total}",
|
|
930
|
+
"runList.openRightBar": "去会话右栏",
|
|
931
|
+
"runList.openRightBarTip": "跳到该 run 的发起会话,并在那个会话的右侧栏打开详情(右侧栏是会话级的:挂到无关会话上没有意义)",
|
|
932
|
+
"runList.callsSuffix": " · {n} 次",
|
|
933
|
+
"panelBoard.empty": "backlog 为空(该产品线还没有立项卡片)。",
|
|
934
|
+
"panelBoard.groupCount": "{kind} · {n}",
|
|
935
|
+
"panelBoard.activeCount": "活动 {n}",
|
|
936
|
+
"panelBoard.clearFilterTip": "清除本组筛选",
|
|
937
|
+
"panelBoard.collapseDone": "收起已完成 {n}",
|
|
938
|
+
"panelBoard.expandDone": "已完成 {n} ▸",
|
|
939
|
+
"panelBoard.collapseDoneTip": "收起已完成/已关闭卡片",
|
|
940
|
+
"panelBoard.expandDoneTip": "展开已完成/已关闭卡片",
|
|
941
|
+
"panelBoard.emptyFiltered": "该筛选下没有卡片。",
|
|
942
|
+
"panelBoard.filterChipOn": "点击取消该状态筛选",
|
|
943
|
+
"panelBoard.filterChipOff": "点击只看该状态(可多选)",
|
|
944
|
+
"panelItem.row.req": "需求",
|
|
945
|
+
"panelItem.row.owner": "负责人",
|
|
946
|
+
"panelItem.row.dev": "开发",
|
|
947
|
+
"panelItem.row.qa": "测试",
|
|
948
|
+
"panelItem.row.accept": "验收",
|
|
949
|
+
"panelItem.row.retries": "重试",
|
|
950
|
+
"panelItem.row.runDocs": "任务夹",
|
|
951
|
+
"panelItem.defectSection": "缺陷详情(QA 报告导入)",
|
|
952
|
+
"panelItem.row.defectId": "缺陷编号",
|
|
953
|
+
"panelItem.row.severity": "严重级",
|
|
954
|
+
"panelItem.row.module": "功能模块",
|
|
955
|
+
"panelItem.row.reproduce": "复现步骤",
|
|
956
|
+
"panelItem.row.expected": "期望行为",
|
|
957
|
+
"panelItem.row.actual": "实际行为",
|
|
958
|
+
"panelItem.row.defectAc": "关联验收项",
|
|
959
|
+
"panelItem.row.defectCheck": "检测命令",
|
|
960
|
+
"panelItem.row.defectCriterion": "通过判据",
|
|
961
|
+
"panelItem.noDefectDetail": "(该卡无缺陷描述:登记时 QA 报告只给了三要素,或为旧版本登记的卡;细节见关联 run 的 QA-REPORT.md)",
|
|
962
|
+
"panelItem.spec": "规格",
|
|
963
|
+
"panelItem.summary": "结论摘要",
|
|
964
|
+
"panelItem.artifacts": "任务夹产物 · {n}",
|
|
965
|
+
"panelItem.artifactTip": "{address}\n(跳到产物所属会话后在该会话右侧栏打开)",
|
|
966
|
+
"panelItem.noArtifacts": "该条目没有可预览的任务夹产物(或缺少会话上下文,无法生成文件地址)。",
|
|
967
|
+
"panelItem.subtasks": "子卡 · {n}",
|
|
968
|
+
"panelItem.bugs": "关联缺陷 · {n}",
|
|
969
|
+
"panelItem.events": "流转时间线 · 最近 {n} 条",
|
|
970
|
+
"detail.runMissing": "未找到该 run(可能已被清理,或地址已过期)。",
|
|
971
|
+
"detail.subagents": "子代理 {n}",
|
|
972
|
+
"detail.stages": "阶段 · {n}",
|
|
973
|
+
"detail.stageFailed": "阶段详情读取失败:{err}",
|
|
974
|
+
"detail.stageTitle": "阶段 #{seq} 详情",
|
|
975
|
+
"detail.output": "阶段产出",
|
|
976
|
+
"detail.attempts": "同任务尝试 · {n}",
|
|
977
|
+
"detail.logs": "日志 · 最近 {n} 条",
|
|
978
|
+
"detail.endedRunning": "进行中",
|
|
979
|
+
"detail.subagentChip": "子代理 {id}",
|
|
980
|
+
"tab.resolveFailed": "无法解析 run 地址:{address}",
|
|
981
|
+
"tab.readingAddress": "读取 tab 地址中…",
|
|
982
|
+
"tab.noHook": "宿主 tab 信息钩子不可用(useTabInfo 缺失)",
|
|
983
|
+
"tab.remoteUnavailable": "remote 不可用(插件未挂载或版本过旧)",
|
|
984
|
+
"tab.readingRun": "读取 run 详情中…"
|
|
985
|
+
};
|
|
986
|
+
/** 英文词典(key 集合必须与 zh 完全一致)。 */
|
|
987
|
+
const en = {
|
|
988
|
+
"common.close": "Close",
|
|
989
|
+
"common.loading": "Loading…",
|
|
990
|
+
"common.retry": "Retry",
|
|
991
|
+
"common.collapse": "Collapse",
|
|
992
|
+
"common.collapseFull": "Collapse",
|
|
993
|
+
"common.clickToExpand": "Click to expand",
|
|
994
|
+
"common.expandFull": "Show more",
|
|
995
|
+
"common.moreLines": "… +{n} lines",
|
|
996
|
+
"common.none": "none",
|
|
997
|
+
"common.noneDash": "(none)",
|
|
998
|
+
"common.noSummary": "(no summary)",
|
|
999
|
+
"common.failed": "Failed",
|
|
1000
|
+
"common.running": "Running",
|
|
1001
|
+
"common.needsHuman": "needs human intervention",
|
|
1002
|
+
"common.unknownError": "unknown error",
|
|
1003
|
+
"common.gotIt": "Got it",
|
|
1004
|
+
"common.remoteNotReady": "remote not ready",
|
|
1005
|
+
"common.remoteCallFailed": "{what} call failed: {detail}",
|
|
1006
|
+
"common.remoteEmptyResult": "{what} returned an empty result (ok=true but no value; raw envelope={raw})",
|
|
1007
|
+
"common.calls": "{n} calls",
|
|
1008
|
+
"common.callsCount": "{n} calls",
|
|
1009
|
+
"common.noUsage": "no token data",
|
|
1010
|
+
"status.created": "Created",
|
|
1011
|
+
"status.in-progress": "In progress",
|
|
1012
|
+
"status.pending-acceptance": "Pending acceptance",
|
|
1013
|
+
"status.accepted": "Accepted",
|
|
1014
|
+
"status.closed": "Closed",
|
|
1015
|
+
"status.pending": "Todo",
|
|
1016
|
+
"status.running": "Developing",
|
|
1017
|
+
"status.testable": "Ready for test",
|
|
1018
|
+
"status.testing": "Testing",
|
|
1019
|
+
"status.rework": "Rework",
|
|
1020
|
+
"status.needs-human": "Needs human",
|
|
1021
|
+
"status.cancelled": "Closed",
|
|
1022
|
+
"status.open": "Unclaimed",
|
|
1023
|
+
"status.claimed": "In progress",
|
|
1024
|
+
"status.fixed": "Fixed (pending verify)",
|
|
1025
|
+
"status.verified": "Closed",
|
|
1026
|
+
"status.reopened": "Reopened",
|
|
1027
|
+
"status.done": "Done",
|
|
1028
|
+
"status.completed": "Completed",
|
|
1029
|
+
"status.failed": "Failed",
|
|
1030
|
+
"status.interrupted": "Interrupted",
|
|
1031
|
+
"status.superseded": "Superseded",
|
|
1032
|
+
"runStatus.pending": "Queued",
|
|
1033
|
+
"runStatus.running": "Running",
|
|
1034
|
+
"runStatus.completed": "Completed",
|
|
1035
|
+
"runStatus.failed": "Failed",
|
|
1036
|
+
"runStatus.cancelled": "Cancelled",
|
|
1037
|
+
"runStatus.interrupted": "Interrupted",
|
|
1038
|
+
"runStatus.superseded": "Superseded",
|
|
1039
|
+
"stageStatus.cancelled": "Stopped",
|
|
1040
|
+
"phase.prd": "PRD",
|
|
1041
|
+
"phase.design": "UI/UX design",
|
|
1042
|
+
"phase.scaffold": "Architecture",
|
|
1043
|
+
"phase.tech": "Technical design",
|
|
1044
|
+
"phase.dev": "Development",
|
|
1045
|
+
"phase.qa": "QA testing",
|
|
1046
|
+
"phase.acceptance": "Acceptance",
|
|
1047
|
+
"kind.req": "Requirements",
|
|
1048
|
+
"kind.task": "Tasks",
|
|
1049
|
+
"kind.bug": "Defects",
|
|
1050
|
+
"role.pm": "Product",
|
|
1051
|
+
"role.design": "Design",
|
|
1052
|
+
"role.arch": "Architecture",
|
|
1053
|
+
"role.tech": "Tech design",
|
|
1054
|
+
"role.dev": "Dev",
|
|
1055
|
+
"role.qa": "QA",
|
|
1056
|
+
"role.acceptance": "Acceptance",
|
|
1057
|
+
"role.other": "Other",
|
|
1058
|
+
"roleChip.pm": "📌 Product",
|
|
1059
|
+
"roleChip.design": "🎨 Design",
|
|
1060
|
+
"roleChip.arch": "🏗 Architecture",
|
|
1061
|
+
"roleChip.tech": "📐 Tech design",
|
|
1062
|
+
"roleChip.dev": "👨💻 Dev",
|
|
1063
|
+
"roleChip.qa": "🧪 QA",
|
|
1064
|
+
"roleChip.acceptance": "✅ Acceptance",
|
|
1065
|
+
"token.officialTitle": "TOKEN · official metering",
|
|
1066
|
+
"token.usageLine": "Input (miss) {input} / Input (hit) {cacheRead} / Cache write {cacheWrite} / Output {output} · {calls} calls",
|
|
1067
|
+
"token.usageHit": " · cache hit {hit}%",
|
|
1068
|
+
"token.usageMissing": "no usage detail",
|
|
1069
|
+
"token.inputMiss": "Input (miss) {v}",
|
|
1070
|
+
"token.inputHit": "Input (hit) {v}",
|
|
1071
|
+
"token.cacheWrite": "Cache write {v}",
|
|
1072
|
+
"token.output": "Output {v}",
|
|
1073
|
+
"token.total": "Total {v}",
|
|
1074
|
+
"token.allStagesTip": "Input (miss) / Input (hit) / Output totals across all stages",
|
|
1075
|
+
"token.stageLine": "Stage tokens: {v}",
|
|
1076
|
+
"stage.cardTip": "{label} — click for stage detail",
|
|
1077
|
+
"stage.retryTip": "Retried {n}× ({m} attempts in total)",
|
|
1078
|
+
"stage.detailTitle": "Stage detail",
|
|
1079
|
+
"stage.summaryFallback": "(Full text was not saved for this run — showing the summary)\n\n{summary}",
|
|
1080
|
+
"stage.loadFailed": "⚠ Load failed: {err}",
|
|
1081
|
+
"stage.noOutput": "(no output text)",
|
|
1082
|
+
"stage.attemptHistory": "↻ Attempt history ({n} · click to inspect one)",
|
|
1083
|
+
"stage.attemptDone": "✅ OK",
|
|
1084
|
+
"stage.attemptFailed": "❌ {outcome}",
|
|
1085
|
+
"stage.attemptRunning": "⏳ Running",
|
|
1086
|
+
"stage.childJumpBtn": "🎬 Open subagent session",
|
|
1087
|
+
"stage.childJumpTip": "Open this stage's subagent session (full reasoning and tool-call trail); then switch to the \"Chat\" tab",
|
|
1088
|
+
"stage.childNoneTip": "No subagent session is available for this stage",
|
|
1089
|
+
"stage.childCrossSessionTip": "This subagent was started by session {sid}; cross-session navigation is not supported yet — open the Team Workbench in its originating session",
|
|
1090
|
+
"stage.childCrossSessionNote": "Cross-session navigation is not supported yet: this subagent was started by session {sid}. Open the Team Workbench in its originating session to see the trail.",
|
|
1091
|
+
"stage.childJumpNote": "After jumping, switch to the \"Chat\" tab to see this subagent's full session trail",
|
|
1092
|
+
"stage.evidenceTitle": "🔬 Verification evidence",
|
|
1093
|
+
"stage.evidenceMissing": "(missing — the contract was not honored and the host logged a warning; cross-check with this run's command logs: logs/teamflow/<runId>/ while the run is live, archived by the host to $DSH_HOME/teamflow/<workspace>/logs/<runId>/ afterwards)",
|
|
1094
|
+
"stage.evidenceMissingShort": "(missing — the contract was not honored and the host logged a warning)",
|
|
1095
|
+
"stage.artifactsTitle": "📄 Stage artifacts",
|
|
1096
|
+
"pipeline.empty": "No active pipeline — ask the model to call teamflow_start, or type a requirement above",
|
|
1097
|
+
"pipeline.noNodes": "The pipeline has not started any stage node yet",
|
|
1098
|
+
"pipeline.zoomOut": "Zoom out",
|
|
1099
|
+
"pipeline.zoomIn": "Zoom in",
|
|
1100
|
+
"pipeline.fitCanvas": "Fit canvas",
|
|
1101
|
+
"pipeline.canvasHint": "✥ Drag to pan · scroll to zoom",
|
|
1102
|
+
"workbench.title": "Team Workbench",
|
|
1103
|
+
"workbench.running": "Pipeline running",
|
|
1104
|
+
"workbench.idle": "Idle",
|
|
1105
|
+
"workbench.refresh": "🔄 Refresh",
|
|
1106
|
+
"workbench.resumeTip": "Resume {id}\nCurrent status: {status}; completed stages are skipped and the run restarts from the first unfinished stage",
|
|
1107
|
+
"workbench.resuming": "Resuming…",
|
|
1108
|
+
"workbench.resumeBtn": "↻ Resume from #{id}",
|
|
1109
|
+
"workbench.loadFailed": "⚠ {err} (make sure dsh-plugin-teamflow is installed and the web host was restarted)",
|
|
1110
|
+
"workbench.needsHumanBanner": "⚠ {n} item(s) need human intervention",
|
|
1111
|
+
"workbench.handle": "Handle {id}",
|
|
1112
|
+
"workbench.manualReason": "manual handling",
|
|
1113
|
+
"workbench.tabPipeline": "🔄 Pipeline",
|
|
1114
|
+
"workbench.tabBoard": "📋 Backlog board",
|
|
1115
|
+
"workbench.workspaceTip": "Current workspace (workspace-level isolation): {path}",
|
|
1116
|
+
"workbench.noWorkspace": "no workspace connected",
|
|
1117
|
+
"workbench.history": "History",
|
|
1118
|
+
"workbench.openRightBarTip": "Open this run's detail in the right sidebar (side by side with task-folder artifacts)",
|
|
1119
|
+
"workbench.openRightBarBtn": "⇥ Open in right bar",
|
|
1120
|
+
"cancel.btn": "⏹ Stop",
|
|
1121
|
+
"cancel.btnWithId": "⏹ Stop #{id}",
|
|
1122
|
+
"cancel.arm": "Confirm stop?",
|
|
1123
|
+
"cancel.busy": "Stopping…",
|
|
1124
|
+
"cancel.sent": "Stop requested — waiting for the current stage to wind down…",
|
|
1125
|
+
"cancel.tip": "Stop {id}\nAborts the subagent of the current stage immediately; finished stages are kept and can be resumed from the checkpoint later; a cancelled run never commits",
|
|
1126
|
+
"cancel.failed": "Stop had no effect: the pipeline is no longer running (it may have just finished)",
|
|
1127
|
+
"board.empty": "Backlog is empty (no pipeline has run yet)",
|
|
1128
|
+
"board.dragReason": "kanban drag transition",
|
|
1129
|
+
"board.cardTip": "{id} · {status}{summary} (click for details)",
|
|
1130
|
+
"board.assignDevTip": "dev assignee\n{who}",
|
|
1131
|
+
"board.assignQaTip": "qa assignee\n{who}",
|
|
1132
|
+
"board.acceptTip": "acceptance/report owner\n{who}",
|
|
1133
|
+
"board.subtaskCount": "📦 {n} subtasks",
|
|
1134
|
+
"board.retries": "retries {n}",
|
|
1135
|
+
"item.overview": "Overview",
|
|
1136
|
+
"item.acceptRow": "✅ Acceptance",
|
|
1137
|
+
"item.retryRow": "↻ Retries",
|
|
1138
|
+
"item.humanRow": "⚠ Human intervention",
|
|
1139
|
+
"item.updatedAt": "Updated",
|
|
1140
|
+
"item.runSection": "Run",
|
|
1141
|
+
"item.runBtn": "▶ Pipeline",
|
|
1142
|
+
"item.jumpRunTip": "Open this requirement's pipeline view #{id}",
|
|
1143
|
+
"item.requirement": "Raw requirement",
|
|
1144
|
+
"item.noRequirement": "(no raw text)",
|
|
1145
|
+
"item.runDocs": "Task folder",
|
|
1146
|
+
"item.previewTip": "Preview {path} in the right sidebar",
|
|
1147
|
+
"item.subtasks": "Linked subtasks ({n})",
|
|
1148
|
+
"item.bugs": "Linked defects ({n})",
|
|
1149
|
+
"item.timeline": "Transition timeline ({n})",
|
|
1150
|
+
"team.currentTip": "Current team: {name} (click to switch)",
|
|
1151
|
+
"team.pick": "Select team",
|
|
1152
|
+
"team.label": "Team",
|
|
1153
|
+
"team.none": "No team (chat directly)",
|
|
1154
|
+
"team.noneNote": "teamflow is not used; the model works directly",
|
|
1155
|
+
"panel.title": "🏭 Team Workbench",
|
|
1156
|
+
"panel.subtitle": "Global panel · by product line",
|
|
1157
|
+
"panel.currentSession": "Session {sid}",
|
|
1158
|
+
"panel.noSession": "No active session",
|
|
1159
|
+
"panel.reload": "Reload data",
|
|
1160
|
+
"panel.backToChat": "Back to chat",
|
|
1161
|
+
"panel.backToChatTip": "Back to chat (click the sidebar icon again to return to this panel)",
|
|
1162
|
+
"panel.railTitle": "Product lines · {n}",
|
|
1163
|
+
"panel.rescanTip": "Rescan $DSH_HOME/teamflow",
|
|
1164
|
+
"panel.refreshing": "Refreshing",
|
|
1165
|
+
"panel.refresh": "Refresh",
|
|
1166
|
+
"panel.noProducts": "No product lines yet. Once a pipeline has run in a workspace, its product line shows up here ($DSH_HOME/teamflow/<key>).",
|
|
1167
|
+
"panel.activeRuns": "{n} running",
|
|
1168
|
+
"panel.updated": "updated {time}",
|
|
1169
|
+
"panel.reading": "Loading…",
|
|
1170
|
+
"panel.verdict": "acceptance {v}",
|
|
1171
|
+
"panel.pickProduct": "Pick a product line on the left to see its backlog and runs (the most recently updated one is selected on first entry).",
|
|
1172
|
+
"panel.loadingView": "Loading product-line data…",
|
|
1173
|
+
"panel.runChip": "runs {n}",
|
|
1174
|
+
"panel.activeChip": "active {n}",
|
|
1175
|
+
"panel.verdictChip": "acceptance {v}",
|
|
1176
|
+
"panel.tabRuns": "🚀 Pipeline runs · {n}",
|
|
1177
|
+
"panel.tabBacklog": "📋 Backlog · {n}",
|
|
1178
|
+
"panel.pinnedActive": "{n} running pinned",
|
|
1179
|
+
"panel.showRecent": "Show latest {n}",
|
|
1180
|
+
"panel.showAll": "Show all {n}",
|
|
1181
|
+
"panel.clearRunFilterTip": "Clear the run status filter",
|
|
1182
|
+
"panel.filtered": "Filtered {sel} · showing {shown}/{total} × clear",
|
|
1183
|
+
"panel.emptyRunFilter": "No runs match this filter.",
|
|
1184
|
+
"panel.runHint": "Click a row for the detail overlay; \"Open in session right bar\" jumps to the run's originating session and opens the right bar there (side by side with task-folder artifacts)",
|
|
1185
|
+
"panel.boardHint": "Click a status badge to filter (multi-select); terminal cards are collapsed by default and appear automatically while filtering",
|
|
1186
|
+
"panel.runDetail": "Run detail",
|
|
1187
|
+
"panel.goOwnerSession": "Go to originating session",
|
|
1188
|
+
"panel.goOwnerSessionTip": "Jump to the run's originating session and open its right bar there (side by side with task-folder artifacts)",
|
|
1189
|
+
"panel.remoteProductsUnavailable": "remote.products unavailable (plugin not mounted or outdated)",
|
|
1190
|
+
"panel.hintNoAddress": "{label}: the host produced no openable address (session context may be missing)",
|
|
1191
|
+
"panel.hintRightbarFailed": "Failed to open the right sidebar (the host mounts it only in the chat view): {label} — shown inline in this panel instead",
|
|
1192
|
+
"panel.hintSessionGone": "Originating session {sid}… is not in the session list (it may have been cleaned up) — showing the detail in this panel",
|
|
1193
|
+
"panel.hintSwitchedNoRightbar": "Switched to the originating session but the right bar did not open ({label}) — retry with \"⇥ Open in right bar\" in that session",
|
|
1194
|
+
"panel.hintInlineSuffix": " — shown inline in this panel instead",
|
|
1195
|
+
"runList.empty": "No runs recorded for this product line yet.",
|
|
1196
|
+
"runList.running": "Running",
|
|
1197
|
+
"runList.noRequirement": "(no requirement text)",
|
|
1198
|
+
"runList.stageProgress": "Stages {done}/{total}",
|
|
1199
|
+
"runList.openRightBar": "Open in session right bar",
|
|
1200
|
+
"runList.openRightBarTip": "Jump to the run's originating session and open the detail in that session's right bar (the right bar is session-scoped: attaching it to an unrelated session is meaningless)",
|
|
1201
|
+
"runList.callsSuffix": " · {n} calls",
|
|
1202
|
+
"panelBoard.empty": "Backlog is empty (this product line has no cards yet).",
|
|
1203
|
+
"panelBoard.groupCount": "{kind} · {n}",
|
|
1204
|
+
"panelBoard.activeCount": "Active {n}",
|
|
1205
|
+
"panelBoard.clearFilterTip": "Clear this group's filter",
|
|
1206
|
+
"panelBoard.collapseDone": "Hide {n} done",
|
|
1207
|
+
"panelBoard.expandDone": "{n} done ▸",
|
|
1208
|
+
"panelBoard.collapseDoneTip": "Hide done/closed cards",
|
|
1209
|
+
"panelBoard.expandDoneTip": "Show done/closed cards",
|
|
1210
|
+
"panelBoard.emptyFiltered": "No cards match this filter.",
|
|
1211
|
+
"panelBoard.filterChipOn": "Click to clear this status filter",
|
|
1212
|
+
"panelBoard.filterChipOff": "Click to filter by this status only (multi-select)",
|
|
1213
|
+
"panelItem.row.req": "Requirement",
|
|
1214
|
+
"panelItem.row.owner": "Owner",
|
|
1215
|
+
"panelItem.row.dev": "Dev",
|
|
1216
|
+
"panelItem.row.qa": "QA",
|
|
1217
|
+
"panelItem.row.accept": "Acceptance",
|
|
1218
|
+
"panelItem.row.retries": "Retries",
|
|
1219
|
+
"panelItem.row.runDocs": "Task folder",
|
|
1220
|
+
"panelItem.defectSection": "Defect detail (imported from the QA report)",
|
|
1221
|
+
"panelItem.row.defectId": "Defect id",
|
|
1222
|
+
"panelItem.row.severity": "Severity",
|
|
1223
|
+
"panelItem.row.module": "Module",
|
|
1224
|
+
"panelItem.row.reproduce": "Steps to reproduce",
|
|
1225
|
+
"panelItem.row.expected": "Expected",
|
|
1226
|
+
"panelItem.row.actual": "Actual",
|
|
1227
|
+
"panelItem.row.defectAc": "Related AC",
|
|
1228
|
+
"panelItem.row.defectCheck": "Check command",
|
|
1229
|
+
"panelItem.row.defectCriterion": "Pass criterion",
|
|
1230
|
+
"panelItem.noDefectDetail": "(No defect description on this card: the QA report only carried the three key fields when it was registered, or the card predates this version; see QA-REPORT.md in the linked run)",
|
|
1231
|
+
"panelItem.spec": "Spec",
|
|
1232
|
+
"panelItem.summary": "Summary",
|
|
1233
|
+
"panelItem.artifacts": "Task-folder artifacts · {n}",
|
|
1234
|
+
"panelItem.artifactTip": "{address}\n(opens in the right sidebar of the session that owns this artifact)",
|
|
1235
|
+
"panelItem.noArtifacts": "This item has no previewable task-folder artifacts (or no session context to build a file address).",
|
|
1236
|
+
"panelItem.subtasks": "Subtasks · {n}",
|
|
1237
|
+
"panelItem.bugs": "Linked defects · {n}",
|
|
1238
|
+
"panelItem.events": "Transition timeline · last {n}",
|
|
1239
|
+
"detail.runMissing": "Run not found (it may have been cleaned up, or the address is stale).",
|
|
1240
|
+
"detail.subagents": "Subagents {n}",
|
|
1241
|
+
"detail.stages": "Stages · {n}",
|
|
1242
|
+
"detail.stageFailed": "Failed to load stage detail: {err}",
|
|
1243
|
+
"detail.stageTitle": "Stage #{seq} detail",
|
|
1244
|
+
"detail.output": "Stage output",
|
|
1245
|
+
"detail.attempts": "Attempts for this task · {n}",
|
|
1246
|
+
"detail.logs": "Logs · last {n}",
|
|
1247
|
+
"detail.endedRunning": "running",
|
|
1248
|
+
"detail.subagentChip": "Subagent {id}",
|
|
1249
|
+
"tab.resolveFailed": "Cannot parse the run address: {address}",
|
|
1250
|
+
"tab.readingAddress": "Reading the tab address…",
|
|
1251
|
+
"tab.noHook": "Host tab-info hook unavailable (useTabInfo missing)",
|
|
1252
|
+
"tab.remoteUnavailable": "remote unavailable (plugin not mounted or outdated)",
|
|
1253
|
+
"tab.readingRun": "Loading run detail…"
|
|
1254
|
+
};
|
|
1255
|
+
//#endregion
|
|
604
1256
|
//#region client/panel.tsx
|
|
605
1257
|
/**
|
|
606
1258
|
* dsh-plugin-teamflow — 全局面板 + 右栏 run 详情 tab(v0.1.8 ①)。
|
|
@@ -649,14 +1301,17 @@ window.__ModuleLoader__.load({
|
|
|
649
1301
|
* 此时不能静默返回 undefined(调用方会把它再包成「未知错误」,丢掉定位信息),故显式报出原始信封。 */
|
|
650
1302
|
function unwrap$1(res, what) {
|
|
651
1303
|
if (!res || !res.ok) {
|
|
652
|
-
let detail = "
|
|
1304
|
+
let detail = t("common.unknownError");
|
|
653
1305
|
try {
|
|
654
|
-
detail = res && res.error && (res.error.message || res.error.code) || JSON.stringify(res) || "
|
|
1306
|
+
detail = res && res.error && (res.error.message || res.error.code) || JSON.stringify(res) || t("common.unknownError");
|
|
655
1307
|
} catch (e) {}
|
|
656
1308
|
try {
|
|
657
1309
|
console.warn("[teamflow] remote 调用失败", what, res);
|
|
658
1310
|
} catch (e) {}
|
|
659
|
-
throw new Error(
|
|
1311
|
+
throw new Error(t("common.remoteCallFailed", {
|
|
1312
|
+
what: what || "remote",
|
|
1313
|
+
detail
|
|
1314
|
+
}));
|
|
660
1315
|
}
|
|
661
1316
|
if (res.value === void 0) {
|
|
662
1317
|
try {
|
|
@@ -668,7 +1323,10 @@ window.__ModuleLoader__.load({
|
|
|
668
1323
|
} catch (e) {
|
|
669
1324
|
raw = String(res);
|
|
670
1325
|
}
|
|
671
|
-
throw new Error(
|
|
1326
|
+
throw new Error(t("common.remoteEmptyResult", {
|
|
1327
|
+
what: what || "remote",
|
|
1328
|
+
raw
|
|
1329
|
+
}));
|
|
672
1330
|
}
|
|
673
1331
|
return res.value;
|
|
674
1332
|
}
|
|
@@ -744,9 +1402,9 @@ window.__ModuleLoader__.load({
|
|
|
744
1402
|
...style
|
|
745
1403
|
} }, text);
|
|
746
1404
|
const runUsageText = (u) => {
|
|
747
|
-
if (!u || !(u.input || u.cacheRead || u.cacheWrite || u.output)) return "
|
|
1405
|
+
if (!u || !(u.input || u.cacheRead || u.cacheWrite || u.output)) return t("common.noUsage");
|
|
748
1406
|
const hit = hitRate(u);
|
|
749
|
-
return `⇅${fmtTokens(u.input)} ⇅${fmtTokens(u.cacheRead)} ⬆${fmtTokens(u.output)}${hit !== null ? ` ·${hit}%` : ""} · ${u.calls}
|
|
1407
|
+
return `⇅${fmtTokens(u.input)} ⇅${fmtTokens(u.cacheRead)} ⬆${fmtTokens(u.output)}${hit !== null ? ` ·${hit}%` : ""} · ${t("common.calls", { n: u.calls })}`;
|
|
750
1408
|
};
|
|
751
1409
|
const RUN_PREVIEW = 8;
|
|
752
1410
|
const TERMINAL_STATUSES = [
|
|
@@ -772,17 +1430,17 @@ window.__ModuleLoader__.load({
|
|
|
772
1430
|
fontSize: 11.5,
|
|
773
1431
|
fontWeight: 700,
|
|
774
1432
|
color: T.text2
|
|
775
|
-
} },
|
|
1433
|
+
} }, t("panel.railTitle", { n: products.length })), h("button", {
|
|
776
1434
|
style: panelBtn,
|
|
777
1435
|
onClick: onRefresh,
|
|
778
1436
|
disabled: busy,
|
|
779
|
-
title: "
|
|
780
|
-
}, busy ? "
|
|
1437
|
+
title: t("panel.rescanTip")
|
|
1438
|
+
}, busy ? t("panel.refreshing") : t("panel.refresh"))), h("div", { style: {
|
|
781
1439
|
flex: 1,
|
|
782
1440
|
minHeight: 0,
|
|
783
1441
|
overflowY: "auto",
|
|
784
1442
|
padding: "0 8px 12px"
|
|
785
|
-
} }, products.length === 0 ? muted("
|
|
1443
|
+
} }, products.length === 0 ? muted(t("panel.noProducts"), { padding: "10px 4px" }) : products.map((p) => {
|
|
786
1444
|
const on = p.key === current;
|
|
787
1445
|
return h("div", {
|
|
788
1446
|
key: p.key,
|
|
@@ -806,7 +1464,7 @@ window.__ModuleLoader__.load({
|
|
|
806
1464
|
overflow: "hidden",
|
|
807
1465
|
textOverflow: "ellipsis",
|
|
808
1466
|
whiteSpace: "nowrap"
|
|
809
|
-
} }, p.title || p.key), p.activeRuns > 0 ? chip(
|
|
1467
|
+
} }, p.title || p.key), p.activeRuns > 0 ? chip(t("panel.activeRuns", { n: p.activeRuns }), T.brand, { dot: true }) : null), h("div", { style: {
|
|
810
1468
|
fontSize: 10,
|
|
811
1469
|
color: T.text2,
|
|
812
1470
|
fontFamily: MONO,
|
|
@@ -820,10 +1478,10 @@ window.__ModuleLoader__.load({
|
|
|
820
1478
|
marginTop: 3,
|
|
821
1479
|
fontSize: 10,
|
|
822
1480
|
color: T.text2
|
|
823
|
-
} }, h("span", null, `run ${p.totalRuns}`), p.updatedAt ? h("span", null,
|
|
1481
|
+
} }, h("span", null, `run ${p.totalRuns}`), p.updatedAt ? h("span", null, t("panel.updated", { time: fmtTime(p.updatedAt) })) : null, p.key === loadingKey ? h("span", { style: {
|
|
824
1482
|
color: T.brand,
|
|
825
1483
|
fontWeight: 600
|
|
826
|
-
} }, "
|
|
1484
|
+
} }, t("panel.reading")) : null), p.lastRequirement ? h("div", { style: {
|
|
827
1485
|
fontSize: 10.5,
|
|
828
1486
|
color: T.text2,
|
|
829
1487
|
marginTop: 3,
|
|
@@ -832,11 +1490,11 @@ window.__ModuleLoader__.load({
|
|
|
832
1490
|
WebkitLineClamp: 2,
|
|
833
1491
|
WebkitBoxOrient: "vertical",
|
|
834
1492
|
overflow: "hidden"
|
|
835
|
-
} }, p.lastRequirement) : null, p.lastVerdict ? h("div", { style: { marginTop: 4 } }, chip(
|
|
1493
|
+
} }, p.lastRequirement) : null, p.lastVerdict ? h("div", { style: { marginTop: 4 } }, chip(t("panel.verdict", { v: p.lastVerdict }), stColor(p.lastVerdict === "accepted" ? "accepted" : p.lastVerdict))) : null);
|
|
836
1494
|
})));
|
|
837
1495
|
}
|
|
838
|
-
function RunList({ runs, activeRunId, onOpenRun, onInlineRun }) {
|
|
839
|
-
if (!runs.length) return muted("
|
|
1496
|
+
function RunList({ runs, activeRunId, onOpenRun, onInlineRun, onCancel }) {
|
|
1497
|
+
if (!runs.length) return muted(t("runList.empty"), { padding: "2px 2px 8px" });
|
|
840
1498
|
return h("div", { style: {
|
|
841
1499
|
display: "flex",
|
|
842
1500
|
flexDirection: "column",
|
|
@@ -864,35 +1522,46 @@ window.__ModuleLoader__.load({
|
|
|
864
1522
|
} }, h("div", { style: {
|
|
865
1523
|
...flexRow,
|
|
866
1524
|
gap: 6
|
|
867
|
-
} }, chip(
|
|
1525
|
+
} }, chip(runStatusText(r.status), stColor(r.status), { dot: true }), r.mode ? chip(String(r.mode), T.text2) : null, h("span", { style: {
|
|
868
1526
|
fontFamily: MONO,
|
|
869
1527
|
fontSize: 10,
|
|
870
1528
|
color: T.text2
|
|
871
1529
|
} }, r.id), active ? h("span", { style: {
|
|
872
1530
|
fontSize: 10,
|
|
873
1531
|
color: T.brand
|
|
874
|
-
} }, "
|
|
1532
|
+
} }, t("runList.running")) : null), h("div", { style: {
|
|
875
1533
|
fontSize: 11.5,
|
|
876
1534
|
color: T.text,
|
|
877
1535
|
marginTop: 3,
|
|
878
1536
|
overflow: "hidden",
|
|
879
1537
|
textOverflow: "ellipsis",
|
|
880
1538
|
whiteSpace: "nowrap"
|
|
881
|
-
} }, r.requirement ||
|
|
1539
|
+
} }, r.requirement || t("runList.noRequirement")), h("div", { style: {
|
|
882
1540
|
...flexRow,
|
|
883
1541
|
gap: 10,
|
|
884
1542
|
marginTop: 3,
|
|
885
1543
|
fontSize: 10,
|
|
886
1544
|
color: T.text2,
|
|
887
1545
|
fontFamily: MONO
|
|
888
|
-
} }, h("span", null,
|
|
1546
|
+
} }, h("span", null, t("runList.stageProgress", {
|
|
1547
|
+
done: r.doneStages,
|
|
1548
|
+
total: r.stageCount
|
|
1549
|
+
})), h("span", null, runUsageText(r.usage)), h("span", null, `${fmtTime(r.startedAt)}${r.endedAt ? ` → ${fmtTime(r.endedAt)}` : ""} ${fmtDur(r.startedAt, r.endedAt)}`))), r.status === "running" && onCancel ? h(CancelButton, {
|
|
1550
|
+
runId: r.id,
|
|
1551
|
+
title: t("cancel.tip", { id: r.id }),
|
|
1552
|
+
onConfirm: onCancel,
|
|
1553
|
+
style: {
|
|
1554
|
+
fontSize: 11,
|
|
1555
|
+
padding: "2px 9px"
|
|
1556
|
+
}
|
|
1557
|
+
}) : null, h("button", {
|
|
889
1558
|
style: brandBtn,
|
|
890
|
-
title: "
|
|
1559
|
+
title: t("runList.openRightBarTip"),
|
|
891
1560
|
onClick: (e) => {
|
|
892
1561
|
e.stopPropagation();
|
|
893
1562
|
onOpenRun(r);
|
|
894
1563
|
}
|
|
895
|
-
}, "
|
|
1564
|
+
}, t("runList.openRightBar")));
|
|
896
1565
|
}));
|
|
897
1566
|
}
|
|
898
1567
|
function BacklogCard({ kind, item, onOpen }) {
|
|
@@ -950,12 +1619,12 @@ window.__ModuleLoader__.load({
|
|
|
950
1619
|
marginTop: 3,
|
|
951
1620
|
fontSize: 10,
|
|
952
1621
|
color: T.text2
|
|
953
|
-
} }, item.devAssign ? h("span", null, `dev ${item.devAssign}`) : null, item.qaAssign ? h("span", null, `qa ${item.qaAssign}`) : null, item.acceptBy ? h("span", null,
|
|
1622
|
+
} }, item.devAssign ? h("span", null, `dev ${item.devAssign}`) : null, item.qaAssign ? h("span", null, `qa ${item.qaAssign}`) : null, item.acceptBy ? h("span", null, t("panel.verdict", { v: item.acceptBy })) : null, item.retries ? h("span", { style: { color: T.warn } }, t("board.retries", { n: item.retries })) : null, item.humanIntervention ? h("span", { style: { color: T.error } }, t("status.needs-human")) : null));
|
|
954
1623
|
}
|
|
955
1624
|
/** 可点筛选徽章(多选):单击切换该状态,选中态用状态色实心;再点取消。 */
|
|
956
1625
|
const filterChip = (text, color, on, onToggle) => h("button", {
|
|
957
1626
|
onClick: onToggle,
|
|
958
|
-
title: on ? "
|
|
1627
|
+
title: on ? t("panelBoard.filterChipOn") : t("panelBoard.filterChipOff"),
|
|
959
1628
|
style: {
|
|
960
1629
|
font: "inherit",
|
|
961
1630
|
fontSize: 11,
|
|
@@ -993,7 +1662,7 @@ window.__ModuleLoader__.load({
|
|
|
993
1662
|
["task", (backlog.tasks || []).filter((t) => t.type !== "subtask")],
|
|
994
1663
|
["bug", backlog.bugs || []]
|
|
995
1664
|
];
|
|
996
|
-
if (!groups.reduce((a, [, arr]) => a + arr.length, 0)) return muted("
|
|
1665
|
+
if (!groups.reduce((a, [, arr]) => a + arr.length, 0)) return muted(t("panelBoard.empty"), { padding: "2px 2px 8px" });
|
|
997
1666
|
return h("div", null, groups.map(([kind, list]) => {
|
|
998
1667
|
if (!list.length) return null;
|
|
999
1668
|
const done = list.filter((it) => TERMINAL_STATUSES.indexOf(it.status) !== -1 && !it.humanIntervention);
|
|
@@ -1028,33 +1697,40 @@ window.__ModuleLoader__.load({
|
|
|
1028
1697
|
fontWeight: 700,
|
|
1029
1698
|
color: T.text,
|
|
1030
1699
|
flex: "0 0 auto"
|
|
1031
|
-
} },
|
|
1700
|
+
} }, t("panelBoard.groupCount", {
|
|
1701
|
+
kind: kindTitle(kind),
|
|
1702
|
+
n: list.length
|
|
1703
|
+
})), h("span", { style: {
|
|
1032
1704
|
fontSize: 10,
|
|
1033
1705
|
color: T.text2,
|
|
1034
1706
|
flex: "0 0 auto"
|
|
1035
|
-
} },
|
|
1707
|
+
} }, t("panelBoard.activeCount", { n: active.length })), ...Object.keys(byStatus).map((s) => filterChip(`${stText(s)} ${byStatus[s]}`, stColor(s), selSet.has(s), () => toggle(s))), filtering ? h("button", {
|
|
1036
1708
|
style: {
|
|
1037
1709
|
...panelBtn,
|
|
1038
1710
|
marginLeft: "auto",
|
|
1039
1711
|
flex: "0 0 auto"
|
|
1040
1712
|
},
|
|
1041
|
-
title: "
|
|
1713
|
+
title: t("panelBoard.clearFilterTip"),
|
|
1042
1714
|
onClick: () => setFilters((m) => ({
|
|
1043
1715
|
...m,
|
|
1044
1716
|
[kind]: []
|
|
1045
1717
|
}))
|
|
1046
|
-
},
|
|
1718
|
+
}, t("panel.filtered", {
|
|
1719
|
+
sel: sel.length,
|
|
1720
|
+
shown: matched.length,
|
|
1721
|
+
total: list.length
|
|
1722
|
+
})) : done.length ? h("button", {
|
|
1047
1723
|
style: {
|
|
1048
1724
|
...panelBtn,
|
|
1049
1725
|
marginLeft: "auto",
|
|
1050
1726
|
flex: "0 0 auto"
|
|
1051
1727
|
},
|
|
1052
|
-
title: open ? "
|
|
1728
|
+
title: open ? t("panelBoard.collapseDoneTip") : t("panelBoard.expandDoneTip"),
|
|
1053
1729
|
onClick: () => setShowDone((m) => ({
|
|
1054
1730
|
...m,
|
|
1055
1731
|
[kind]: !open
|
|
1056
1732
|
}))
|
|
1057
|
-
}, open ?
|
|
1733
|
+
}, open ? t("panelBoard.collapseDone", { n: done.length }) : t("panelBoard.expandDone", { n: done.length })) : null), visible.length ? h("div", { style: {
|
|
1058
1734
|
display: "grid",
|
|
1059
1735
|
gridTemplateColumns: "repeat(auto-fill, minmax(228px, 1fr))",
|
|
1060
1736
|
gap: 6
|
|
@@ -1063,7 +1739,7 @@ window.__ModuleLoader__.load({
|
|
|
1063
1739
|
kind,
|
|
1064
1740
|
item: it,
|
|
1065
1741
|
onOpen
|
|
1066
|
-
}))) : muted("
|
|
1742
|
+
}))) : muted(t("panelBoard.emptyFiltered"), { fontSize: 10.5 }));
|
|
1067
1743
|
}));
|
|
1068
1744
|
}
|
|
1069
1745
|
function ItemDetailPane({ det, openArtifact, onClose }) {
|
|
@@ -1099,43 +1775,62 @@ window.__ModuleLoader__.load({
|
|
|
1099
1775
|
} }, det.id), chip(stText(det.status), stColor(det.status), { dot: true }), h("span", { style: {
|
|
1100
1776
|
fontSize: 10.5,
|
|
1101
1777
|
color: T.text2
|
|
1102
|
-
} },
|
|
1778
|
+
} }, kindTitle(det.kind))), h("button", {
|
|
1103
1779
|
style: panelBtn,
|
|
1104
1780
|
onClick: onClose
|
|
1105
|
-
}, "
|
|
1781
|
+
}, t("common.close"))), h("div", { style: {
|
|
1106
1782
|
fontSize: 13,
|
|
1107
1783
|
fontWeight: 600,
|
|
1108
1784
|
color: T.text,
|
|
1109
1785
|
lineHeight: 1.45
|
|
1110
|
-
} }, det.title), h("div", { style: {
|
|
1786
|
+
} }, det.title), det.kind === "bug" ? h("div", { style: {
|
|
1787
|
+
display: "flex",
|
|
1788
|
+
flexDirection: "column",
|
|
1789
|
+
gap: 4,
|
|
1790
|
+
padding: "8px 10px",
|
|
1791
|
+
borderRadius: 8,
|
|
1792
|
+
background: T.layer2,
|
|
1793
|
+
border: `1px solid ${T.border}`
|
|
1794
|
+
} }, h("div", { style: {
|
|
1795
|
+
...flexRow,
|
|
1796
|
+
gap: 6
|
|
1797
|
+
} }, h("span", { style: {
|
|
1798
|
+
fontSize: 11,
|
|
1799
|
+
fontWeight: 700,
|
|
1800
|
+
color: T.text
|
|
1801
|
+
} }, t("panelItem.defectSection")), det.severity ? chip(String(det.severity), String(det.severity) === "P0" ? T.error : String(det.severity) === "P1" ? T.warn : T.text2) : null), row(t("panelItem.row.defectId"), det.defectId), row(t("panelItem.row.module"), det.module), row(t("panelItem.row.reproduce"), det.reproduce), row(t("panelItem.row.expected"), det.expected), row(t("panelItem.row.actual"), det.actual), row(t("panelItem.row.defectCheck"), det.defectCheck), row(t("panelItem.row.defectCriterion"), det.defectCriterion), row(t("panelItem.row.defectAc"), det.defectAc), det.reproduce || det.expected || det.actual ? null : h("div", { style: {
|
|
1802
|
+
fontSize: 10.5,
|
|
1803
|
+
color: T.warn,
|
|
1804
|
+
lineHeight: 1.5
|
|
1805
|
+
} }, t("panelItem.noDefectDetail"))) : null, h("div", { style: {
|
|
1111
1806
|
display: "flex",
|
|
1112
1807
|
flexDirection: "column",
|
|
1113
1808
|
gap: 3
|
|
1114
|
-
} }, row("
|
|
1809
|
+
} }, row(t("panelItem.row.req"), det.reqId), row(t("panelItem.row.owner"), det.owner), row(t("panelItem.row.dev"), det.devAssign), row(t("panelItem.row.qa"), det.qaAssign), row(t("panelItem.row.accept"), det.assignBy), row(t("panelItem.row.retries"), det.retries), row(t("panelItem.row.runDocs"), det.runDocs)), det.spec ? h("div", null, h("div", { style: {
|
|
1115
1810
|
fontSize: 11,
|
|
1116
1811
|
color: T.text2,
|
|
1117
1812
|
marginBottom: 3
|
|
1118
|
-
} }, "
|
|
1813
|
+
} }, t("panelItem.spec")), h(FoldableText, { text: det.spec })) : null, det.summary ? h("div", null, h("div", { style: {
|
|
1119
1814
|
fontSize: 11,
|
|
1120
1815
|
color: T.text2,
|
|
1121
1816
|
marginBottom: 3
|
|
1122
|
-
} }, "
|
|
1817
|
+
} }, t("panelItem.summary")), h(FoldableText, { text: det.summary })) : null, det.artifacts && det.artifacts.length ? h("div", null, h("div", { style: {
|
|
1123
1818
|
fontSize: 11,
|
|
1124
1819
|
color: T.text2,
|
|
1125
1820
|
marginBottom: 4
|
|
1126
|
-
} },
|
|
1821
|
+
} }, t("panelItem.artifacts", { n: det.artifacts.length })), h("div", { style: {
|
|
1127
1822
|
...flexRow,
|
|
1128
1823
|
gap: 5
|
|
1129
1824
|
} }, det.artifacts.map((a) => h("button", {
|
|
1130
1825
|
key: a.name,
|
|
1131
1826
|
style: brandBtn,
|
|
1132
|
-
title:
|
|
1827
|
+
title: t("panelItem.artifactTip", { address: a.address }),
|
|
1133
1828
|
onClick: () => openArtifact && openArtifact(a.address, a.name, det.runInfo && det.runInfo.ownerSession || null)
|
|
1134
|
-
}, a.name)))) : muted("
|
|
1829
|
+
}, a.name)))) : muted(t("panelItem.noArtifacts")), det.subtasks && det.subtasks.length ? h("div", null, h("div", { style: {
|
|
1135
1830
|
fontSize: 11,
|
|
1136
1831
|
color: T.text2,
|
|
1137
1832
|
marginBottom: 4
|
|
1138
|
-
} },
|
|
1833
|
+
} }, t("panelItem.subtasks", { n: det.subtasks.length })), h("div", { style: {
|
|
1139
1834
|
display: "flex",
|
|
1140
1835
|
flexDirection: "column",
|
|
1141
1836
|
gap: 4
|
|
@@ -1160,11 +1855,11 @@ window.__ModuleLoader__.load({
|
|
|
1160
1855
|
overflow: "hidden",
|
|
1161
1856
|
textOverflow: "ellipsis",
|
|
1162
1857
|
whiteSpace: "nowrap"
|
|
1163
|
-
} }, s.title), s.failed ? chip("
|
|
1858
|
+
} }, s.title), s.failed ? chip(t("common.failed"), T.error) : null)))) : null, det.bugs && det.bugs.length ? h("div", null, h("div", { style: {
|
|
1164
1859
|
fontSize: 11,
|
|
1165
1860
|
color: T.text2,
|
|
1166
1861
|
marginBottom: 4
|
|
1167
|
-
} },
|
|
1862
|
+
} }, t("panelItem.bugs", { n: det.bugs.length })), h("div", { style: {
|
|
1168
1863
|
display: "flex",
|
|
1169
1864
|
flexDirection: "column",
|
|
1170
1865
|
gap: 4
|
|
@@ -1193,7 +1888,7 @@ window.__ModuleLoader__.load({
|
|
|
1193
1888
|
fontSize: 11,
|
|
1194
1889
|
color: T.text2,
|
|
1195
1890
|
marginBottom: 4
|
|
1196
|
-
} },
|
|
1891
|
+
} }, t("panelItem.events", { n: Math.min(det.events.length, 30) })), h("div", { style: {
|
|
1197
1892
|
display: "flex",
|
|
1198
1893
|
flexDirection: "column",
|
|
1199
1894
|
gap: 3,
|
|
@@ -1202,14 +1897,14 @@ window.__ModuleLoader__.load({
|
|
|
1202
1897
|
color: T.text2
|
|
1203
1898
|
} }, det.events.slice(-30).map((e, i) => h("div", { key: i }, `${fmtTime(e.at)} ${e.from || "—"} → ${e.to || "—"}${e.by ? ` · ${e.by}` : ""}${e.reason ? ` · ${e.reason}` : ""}`)))) : null);
|
|
1204
1899
|
}
|
|
1205
|
-
function RunDetailPane({ snap, product, api }) {
|
|
1900
|
+
function RunDetailPane({ snap, product, api, onCancel }) {
|
|
1206
1901
|
const [sel, setSel] = react.default.useState(null);
|
|
1207
1902
|
const [err, setErr] = react.default.useState(null);
|
|
1208
1903
|
react.default.useEffect(() => {
|
|
1209
1904
|
setSel(null);
|
|
1210
1905
|
setErr(null);
|
|
1211
1906
|
}, [snap && snap.id]);
|
|
1212
|
-
if (!snap) return muted("
|
|
1907
|
+
if (!snap) return muted(t("detail.runMissing"), { padding: 12 });
|
|
1213
1908
|
const stages = snap.stages || [];
|
|
1214
1909
|
const totals = stages.reduce((a, s) => {
|
|
1215
1910
|
const u = s.usage;
|
|
@@ -1248,7 +1943,7 @@ window.__ModuleLoader__.load({
|
|
|
1248
1943
|
} }, h("div", { style: {
|
|
1249
1944
|
...flexRow,
|
|
1250
1945
|
gap: 6
|
|
1251
|
-
} }, chip(
|
|
1946
|
+
} }, chip(runStatusText(snap.status), stColor(snap.status), { dot: true }), snap.options && snap.options.mode ? chip(String(snap.options.mode), T.text2) : null, h("span", { style: {
|
|
1252
1947
|
fontFamily: MONO,
|
|
1253
1948
|
fontSize: 10.5,
|
|
1254
1949
|
color: T.text2
|
|
@@ -1256,17 +1951,29 @@ window.__ModuleLoader__.load({
|
|
|
1256
1951
|
fontSize: 10,
|
|
1257
1952
|
color: T.text2,
|
|
1258
1953
|
fontFamily: MONO
|
|
1259
|
-
} }, product) : null
|
|
1954
|
+
} }, product) : null, snap.status === "running" && onCancel ? h(CancelButton, {
|
|
1955
|
+
runId: snap.id,
|
|
1956
|
+
title: t("cancel.tip", { id: snap.id }),
|
|
1957
|
+
onConfirm: onCancel,
|
|
1958
|
+
style: {
|
|
1959
|
+
marginLeft: "auto",
|
|
1960
|
+
fontSize: 10.5,
|
|
1961
|
+
padding: "2px 9px"
|
|
1962
|
+
}
|
|
1963
|
+
}) : null), h("div", { style: {
|
|
1260
1964
|
fontSize: 12,
|
|
1261
1965
|
color: T.text,
|
|
1262
1966
|
lineHeight: 1.5
|
|
1263
|
-
} }, snap.requirement ||
|
|
1967
|
+
} }, snap.requirement || t("runList.noRequirement")), h("div", { style: {
|
|
1264
1968
|
...flexRow,
|
|
1265
1969
|
gap: 12,
|
|
1266
1970
|
fontSize: 10.5,
|
|
1267
1971
|
color: T.text2,
|
|
1268
1972
|
fontFamily: MONO
|
|
1269
|
-
} }, h("span", null, `${fmtTime(snap.startedAt)}${snap.endedAt ? ` → ${fmtTime(snap.endedAt)}` : " →
|
|
1973
|
+
} }, h("span", null, `${fmtTime(snap.startedAt)}${snap.endedAt ? ` → ${fmtTime(snap.endedAt)}` : " → " + t("detail.endedRunning")} · ${fmtDur(snap.startedAt, snap.endedAt)}`), h("span", null, t("runList.stageProgress", {
|
|
1974
|
+
done: stages.filter((s) => s.status === "done").length,
|
|
1975
|
+
total: stages.length
|
|
1976
|
+
})), h("span", null, t("detail.subagents", { n: snap.agentsStarted || 0 }))), h("div", { style: {
|
|
1270
1977
|
...flexRow,
|
|
1271
1978
|
gap: 10,
|
|
1272
1979
|
fontSize: 10.5,
|
|
@@ -1276,7 +1983,7 @@ window.__ModuleLoader__.load({
|
|
|
1276
1983
|
borderRadius: 8,
|
|
1277
1984
|
background: `color-mix(in srgb, ${T.layer2} 60%, transparent)`,
|
|
1278
1985
|
border: `1px solid ${T.border}`
|
|
1279
|
-
} }, h("span", null,
|
|
1986
|
+
} }, h("span", null, t("token.inputMiss", { v: fmtTokens(totals.input) || "0" })), h("span", null, t("token.inputHit", { v: fmtTokens(totals.cacheRead) || "0" })), h("span", null, t("token.cacheWrite", { v: fmtTokens(totals.cacheWrite) || "0" })), h("span", null, t("token.output", { v: fmtTokens(totals.output) || "0" })), h("span", null, t("common.callsCount", { n: totals.calls })), h("span", null, t("token.total", { v: fmtTokens(totalTokens(totals)) || "0" }))), sectionTitle(t("detail.stages", { n: stages.length })), h("div", { style: {
|
|
1280
1987
|
display: "flex",
|
|
1281
1988
|
flexDirection: "column",
|
|
1282
1989
|
gap: 4
|
|
@@ -1305,7 +2012,7 @@ window.__ModuleLoader__.load({
|
|
|
1305
2012
|
fontSize: 11.5,
|
|
1306
2013
|
color: T.text,
|
|
1307
2014
|
fontWeight: 500
|
|
1308
|
-
} },
|
|
2015
|
+
} }, stageLabelOf(s)), chip(stageStatusText(s.status), color, { dot: true }), s.outcome && s.outcome !== "completed" && s.outcome !== "cancelled" ? chip(String(s.outcome), stColor(s.outcome)) : null, h("span", { style: {
|
|
1309
2016
|
marginLeft: "auto",
|
|
1310
2017
|
fontFamily: MONO,
|
|
1311
2018
|
fontSize: 10,
|
|
@@ -1317,13 +2024,13 @@ window.__ModuleLoader__.load({
|
|
|
1317
2024
|
fontFamily: MONO,
|
|
1318
2025
|
fontSize: 10,
|
|
1319
2026
|
color: T.text2
|
|
1320
|
-
} }, h("span", null, stageUsageLine(s) || "
|
|
2027
|
+
} }, h("span", null, stageUsageLine(s) || t("common.noUsage")), s.childId ? h("span", { title: s.childId }, t("detail.subagentChip", { id: String(s.childId).slice(0, 14) })) : null), s.summary ? h("div", { style: {
|
|
1321
2028
|
fontSize: 10.5,
|
|
1322
2029
|
color: T.text2,
|
|
1323
2030
|
marginTop: 3,
|
|
1324
2031
|
lineHeight: 1.45
|
|
1325
2032
|
} }, String(s.summary).slice(0, 200)) : null);
|
|
1326
|
-
})), err ? muted(
|
|
2033
|
+
})), err ? muted(t("detail.stageFailed", { err }), { color: T.error }) : null, sel ? h("div", { style: {
|
|
1327
2034
|
padding: "9px 10px",
|
|
1328
2035
|
borderRadius: 9,
|
|
1329
2036
|
border: `1px solid ${T.border}`,
|
|
@@ -1341,18 +2048,18 @@ window.__ModuleLoader__.load({
|
|
|
1341
2048
|
fontSize: 11.5,
|
|
1342
2049
|
fontWeight: 700,
|
|
1343
2050
|
color: T.text
|
|
1344
|
-
} },
|
|
2051
|
+
} }, t("detail.stageTitle", { seq: sel.seq })), chip(stageStatusText(sel.status), stColor(sel.status))), h("button", {
|
|
1345
2052
|
style: panelBtn,
|
|
1346
2053
|
onClick: () => setSel(null)
|
|
1347
|
-
}, "
|
|
2054
|
+
}, t("common.collapse"))), h("div", { style: {
|
|
1348
2055
|
fontSize: 10.5,
|
|
1349
2056
|
color: T.text2,
|
|
1350
2057
|
fontFamily: MONO
|
|
1351
|
-
} },
|
|
2058
|
+
} }, t("token.stageLine", { v: stageUsageLine(sel) || t("common.none") })), sel.verifyEvidence ? h("div", null, h("div", { style: {
|
|
1352
2059
|
fontSize: 11,
|
|
1353
2060
|
color: T.success,
|
|
1354
2061
|
marginBottom: 3
|
|
1355
|
-
} }, "
|
|
2062
|
+
} }, t("stage.evidenceTitle")), h("div", { style: {
|
|
1356
2063
|
whiteSpace: "pre-wrap",
|
|
1357
2064
|
wordBreak: "break-word",
|
|
1358
2065
|
fontSize: 11,
|
|
@@ -1365,11 +2072,11 @@ window.__ModuleLoader__.load({
|
|
|
1365
2072
|
maxHeight: 200,
|
|
1366
2073
|
overflowY: "auto",
|
|
1367
2074
|
fontFamily: MONO
|
|
1368
|
-
} }, sel.verifyEvidence)) : muted("
|
|
2075
|
+
} }, sel.verifyEvidence)) : muted(t("stage.evidenceMissingShort"), { color: T.warn }), sel.output ? h("div", null, h("div", { style: {
|
|
1369
2076
|
fontSize: 11,
|
|
1370
2077
|
color: T.text2,
|
|
1371
2078
|
marginBottom: 3
|
|
1372
|
-
} }, "
|
|
2079
|
+
} }, t("detail.output")), h(FoldableText, {
|
|
1373
2080
|
text: sel.output,
|
|
1374
2081
|
charLimit: 400,
|
|
1375
2082
|
lineLimit: 8,
|
|
@@ -1381,7 +2088,7 @@ window.__ModuleLoader__.load({
|
|
|
1381
2088
|
fontSize: 11,
|
|
1382
2089
|
color: T.text2,
|
|
1383
2090
|
marginBottom: 3
|
|
1384
|
-
} },
|
|
2091
|
+
} }, t("detail.attempts", { n: sel.attempts.length })), h("div", { style: {
|
|
1385
2092
|
display: "flex",
|
|
1386
2093
|
flexDirection: "column",
|
|
1387
2094
|
gap: 3
|
|
@@ -1394,7 +2101,7 @@ window.__ModuleLoader__.load({
|
|
|
1394
2101
|
fontFamily: MONO,
|
|
1395
2102
|
color: T.text2
|
|
1396
2103
|
}
|
|
1397
|
-
}, h("span", null, `#${a.seq}`), chip(
|
|
2104
|
+
}, h("span", null, `#${a.seq}`), chip(stageStatusText(a.status), stColor(a.status)), a.outcome && a.outcome !== "cancelled" ? h("span", null, a.outcome) : null, h("span", { style: { marginLeft: "auto" } }, `${fmtTime(a.startedAt)} · ${fmtDur(a.startedAt, a.endedAt)}`))))) : null) : null, logs.length ? h("div", null, sectionTitle(t("detail.logs", { n: logs.length })), h("div", { style: {
|
|
1398
2105
|
display: "flex",
|
|
1399
2106
|
flexDirection: "column",
|
|
1400
2107
|
gap: 2,
|
|
@@ -1447,12 +2154,12 @@ window.__ModuleLoader__.load({
|
|
|
1447
2154
|
let alive = true;
|
|
1448
2155
|
if (!runId) {
|
|
1449
2156
|
setSnap(null);
|
|
1450
|
-
setErr(address ?
|
|
2157
|
+
setErr(address ? t("tab.resolveFailed", { address }) : readTab ? t("tab.readingAddress") : t("tab.noHook"));
|
|
1451
2158
|
return;
|
|
1452
2159
|
}
|
|
1453
2160
|
if (!api) {
|
|
1454
2161
|
setSnap(null);
|
|
1455
|
-
setErr("
|
|
2162
|
+
setErr(t("tab.remoteUnavailable"));
|
|
1456
2163
|
return;
|
|
1457
2164
|
}
|
|
1458
2165
|
api.runDetail(runId).then((v) => {
|
|
@@ -1481,6 +2188,16 @@ window.__ModuleLoader__.load({
|
|
|
1481
2188
|
nonce,
|
|
1482
2189
|
readTab
|
|
1483
2190
|
]);
|
|
2191
|
+
/** 中断后重新拉快照(nonce 触发上面那个 effect 重跑;右栏 tab 是会话级地址,与产品线选择无关)。 */
|
|
2192
|
+
const onCancel = async (rid) => {
|
|
2193
|
+
if (!api) return;
|
|
2194
|
+
try {
|
|
2195
|
+
await api.cancel(rid);
|
|
2196
|
+
setNonce((n) => n + 1);
|
|
2197
|
+
} catch (e) {
|
|
2198
|
+
setErr(String(e && e.message || e));
|
|
2199
|
+
}
|
|
2200
|
+
};
|
|
1484
2201
|
if (err && !snap) {
|
|
1485
2202
|
const pending = !address && !!readTab;
|
|
1486
2203
|
return h("div", { style: {
|
|
@@ -1488,19 +2205,20 @@ window.__ModuleLoader__.load({
|
|
|
1488
2205
|
display: "flex",
|
|
1489
2206
|
flexDirection: "column",
|
|
1490
2207
|
gap: 8
|
|
1491
|
-
} }, muted(pending ? "
|
|
2208
|
+
} }, muted(pending ? t("tab.readingAddress") : err, { color: pending ? T.text2 : T.error }), pending ? null : h("button", {
|
|
1492
2209
|
style: panelBtn,
|
|
1493
2210
|
onClick: () => {
|
|
1494
2211
|
setErr(null);
|
|
1495
2212
|
setNonce((n) => n + 1);
|
|
1496
2213
|
}
|
|
1497
|
-
}, "
|
|
2214
|
+
}, t("common.retry")));
|
|
1498
2215
|
}
|
|
1499
|
-
if (!snap) return muted("
|
|
2216
|
+
if (!snap) return muted(t("tab.readingRun"), { padding: 12 });
|
|
1500
2217
|
return h(RunDetailPane, {
|
|
1501
2218
|
snap,
|
|
1502
2219
|
product,
|
|
1503
|
-
api
|
|
2220
|
+
api,
|
|
2221
|
+
onCancel
|
|
1504
2222
|
});
|
|
1505
2223
|
}
|
|
1506
2224
|
function productApi(remote, product) {
|
|
@@ -1508,7 +2226,13 @@ window.__ModuleLoader__.load({
|
|
|
1508
2226
|
view: async () => unwrap$1(await remote.productView(product), "productView"),
|
|
1509
2227
|
runDetail: async (runId) => unwrap$1(await remote.productRunDetail(product, runId), "productRunDetail"),
|
|
1510
2228
|
stageDetail: async (runId, seq) => unwrap$1(await remote.productStageDetail(product, runId, seq), "productStageDetail"),
|
|
1511
|
-
itemDetail: async (kind, id, sessionId) => unwrap$1(await remote.productItemDetail(product, kind, id, sessionId), "productItemDetail")
|
|
2229
|
+
itemDetail: async (kind, id, sessionId) => unwrap$1(await remote.productItemDetail(product, kind, id, sessionId), "productItemDetail"),
|
|
2230
|
+
/** 中断运行:runId 全局寻址(与产品线无关),放这里是为了让面板行/详情/右栏共用同一处解包与报错。 */
|
|
2231
|
+
cancel: async (runId) => {
|
|
2232
|
+
const r = unwrap$1(await remote.cancel(runId), "cancel");
|
|
2233
|
+
if (!r || r.ok !== true) throw new Error(t("cancel.failed"));
|
|
2234
|
+
return true;
|
|
2235
|
+
}
|
|
1512
2236
|
};
|
|
1513
2237
|
}
|
|
1514
2238
|
/** 全局面板 props:root scope 标准 props(useSessions 取当前会话)+ 插件注入(remote/打开回调)。 */
|
|
@@ -1545,7 +2269,7 @@ window.__ModuleLoader__.load({
|
|
|
1545
2269
|
const attempt = (quiet) => !!(props.openResource && address && props.openResource(address, label, quiet));
|
|
1546
2270
|
if (attempt(false)) return;
|
|
1547
2271
|
if (!address) {
|
|
1548
|
-
setHint(
|
|
2272
|
+
setHint(t("panel.hintNoAddress", { label }));
|
|
1549
2273
|
if (fallback) fallback();
|
|
1550
2274
|
return;
|
|
1551
2275
|
}
|
|
@@ -1560,7 +2284,7 @@ window.__ModuleLoader__.load({
|
|
|
1560
2284
|
setTimeout(tick, 120);
|
|
1561
2285
|
return;
|
|
1562
2286
|
}
|
|
1563
|
-
setHint(
|
|
2287
|
+
setHint(t("panel.hintRightbarFailed", { label }) + (fallback ? t("panel.hintInlineSuffix") : ""));
|
|
1564
2288
|
if (fallback) fallback();
|
|
1565
2289
|
};
|
|
1566
2290
|
setTimeout(tick, 140);
|
|
@@ -1568,8 +2292,10 @@ window.__ModuleLoader__.load({
|
|
|
1568
2292
|
/**
|
|
1569
2293
|
* **跳到资源所属的会话,再在那个会话的右栏打开**(全局面板的正确语义)。
|
|
1570
2294
|
* 右侧栏是会话级的:从全局面板看 tetris 的 run 却把 tab 挂到"用户当前所在会话"上没有意义
|
|
1571
|
-
* (用户 2026-09-11 提出)。所以先 `
|
|
1572
|
-
*
|
|
2295
|
+
* (用户 2026-09-11 提出)。所以先 `uiWorkspace.openSession(ownerSession)`,再小步重试等右栏 seat 就绪后 openResource。
|
|
2296
|
+
* **2026-09-23 迁移(宿主 0.1.7-alpha.1)**:`sessions.open` 与 `sessions.openSubagent` 同批被移除,
|
|
2297
|
+
* 跳会话唯一入口改为 `uiWorkspace.openSession(target)`;同时 `SessionListState` 已无 `current` 字段,
|
|
2298
|
+
* 故原先"等当前会话真的切过去"的判据删除(契约变更后它恒为 undefined,等于死代码),只留时间维度的重试。
|
|
1573
2299
|
* @param target.ownerSession - 资源所属会话(run 的发起会话 / 产物地址里的会话)
|
|
1574
2300
|
* @param target.address - host 生成的 dsh-resource 地址
|
|
1575
2301
|
* @param target.label - 提示用的名字
|
|
@@ -1577,15 +2303,15 @@ window.__ModuleLoader__.load({
|
|
|
1577
2303
|
*/
|
|
1578
2304
|
const goOwnerSessionAndOpen = (target) => {
|
|
1579
2305
|
const { ownerSession, address, label, fallback } = target || {};
|
|
1580
|
-
const
|
|
1581
|
-
if (!ownerSession || !
|
|
2306
|
+
const ws = props.uiWorkspace;
|
|
2307
|
+
if (!ownerSession || !ws || typeof ws.openSession !== "function") {
|
|
1582
2308
|
openInConversationRightbar(address, label, fallback);
|
|
1583
2309
|
return;
|
|
1584
2310
|
}
|
|
1585
2311
|
try {
|
|
1586
|
-
|
|
2312
|
+
ws.openSession(ownerSession);
|
|
1587
2313
|
} catch (e) {
|
|
1588
|
-
setHint(
|
|
2314
|
+
setHint(t("panel.hintSessionGone", { sid: String(ownerSession).slice(0, 8) }));
|
|
1589
2315
|
if (fallback) fallback();
|
|
1590
2316
|
return;
|
|
1591
2317
|
}
|
|
@@ -1595,18 +2321,12 @@ window.__ModuleLoader__.load({
|
|
|
1595
2321
|
let tries = 0;
|
|
1596
2322
|
const tick = () => {
|
|
1597
2323
|
tries += 1;
|
|
1598
|
-
|
|
1599
|
-
try {
|
|
1600
|
-
nowCurrent = sessions.list && sessions.list.getSnapshot ? sessions.list.getSnapshot().current : null;
|
|
1601
|
-
} catch (e) {
|
|
1602
|
-
nowCurrent = null;
|
|
1603
|
-
}
|
|
1604
|
-
if ((nowCurrent === ownerSession || tries >= 6) && props.openResource && address && props.openResource(address, label, tries < 6)) return;
|
|
2324
|
+
if (props.openResource && address && props.openResource(address, label, tries < 6)) return;
|
|
1605
2325
|
if (tries < 14) {
|
|
1606
2326
|
setTimeout(tick, 130);
|
|
1607
2327
|
return;
|
|
1608
2328
|
}
|
|
1609
|
-
setHint(
|
|
2329
|
+
setHint(t("panel.hintSwitchedNoRightbar", { label }));
|
|
1610
2330
|
};
|
|
1611
2331
|
setTimeout(tick, 140);
|
|
1612
2332
|
};
|
|
@@ -1614,7 +2334,7 @@ window.__ModuleLoader__.load({
|
|
|
1614
2334
|
if (!remote || typeof remote.products !== "function") {
|
|
1615
2335
|
setState((s) => ({
|
|
1616
2336
|
...s,
|
|
1617
|
-
err: "
|
|
2337
|
+
err: t("panel.remoteProductsUnavailable")
|
|
1618
2338
|
}));
|
|
1619
2339
|
return;
|
|
1620
2340
|
}
|
|
@@ -1731,6 +2451,25 @@ window.__ModuleLoader__.load({
|
|
|
1731
2451
|
}
|
|
1732
2452
|
};
|
|
1733
2453
|
const closeDetail = () => setDetail(null);
|
|
2454
|
+
/** 中断运行(面板内两个入口共用):成功后刷新产品线视图;详情浮层开着就顺手把快照也换新。 */
|
|
2455
|
+
const cancelRun = async (runId) => {
|
|
2456
|
+
if (!api) return;
|
|
2457
|
+
try {
|
|
2458
|
+
await api.cancel(runId);
|
|
2459
|
+
loadView(state.current, true);
|
|
2460
|
+
if (detail && detail.kind === "run" && detail.data && detail.data.id === runId) setDetail({
|
|
2461
|
+
kind: "run",
|
|
2462
|
+
data: await api.runDetail(runId),
|
|
2463
|
+
run: detail.run
|
|
2464
|
+
});
|
|
2465
|
+
setHint(t("cancel.sent"));
|
|
2466
|
+
} catch (e) {
|
|
2467
|
+
setState((s) => ({
|
|
2468
|
+
...s,
|
|
2469
|
+
err: String(e && e.message || e)
|
|
2470
|
+
}));
|
|
2471
|
+
}
|
|
2472
|
+
};
|
|
1734
2473
|
const openRun = (r) => {
|
|
1735
2474
|
goOwnerSessionAndOpen({
|
|
1736
2475
|
ownerSession: r.ownerSession,
|
|
@@ -1802,32 +2541,32 @@ window.__ModuleLoader__.load({
|
|
|
1802
2541
|
} }, h("span", { style: {
|
|
1803
2542
|
fontSize: 13,
|
|
1804
2543
|
fontWeight: 700
|
|
1805
|
-
} }, "
|
|
2544
|
+
} }, t("panel.title")), h("span", { style: {
|
|
1806
2545
|
fontSize: 11,
|
|
1807
2546
|
color: T.text2
|
|
1808
|
-
} }, "
|
|
2547
|
+
} }, t("panel.subtitle")), currentSessionId ? h("span", { style: {
|
|
1809
2548
|
fontSize: 10,
|
|
1810
2549
|
color: T.text2,
|
|
1811
2550
|
fontFamily: MONO
|
|
1812
|
-
} },
|
|
2551
|
+
} }, t("panel.currentSession", { sid: String(currentSessionId).slice(0, 8) })) : h("span", { style: {
|
|
1813
2552
|
fontSize: 10,
|
|
1814
2553
|
color: T.text2
|
|
1815
|
-
} }, "
|
|
2554
|
+
} }, t("panel.noSession"))), h("div", { style: {
|
|
1816
2555
|
...flexRow,
|
|
1817
2556
|
gap: 6
|
|
1818
2557
|
} }, product ? h("button", {
|
|
1819
2558
|
style: panelBtn,
|
|
1820
2559
|
onClick: () => loadView(state.current)
|
|
1821
|
-
}, "
|
|
2560
|
+
}, t("panel.reload")) : null, h("button", {
|
|
1822
2561
|
style: panelBtn,
|
|
1823
|
-
title: "
|
|
2562
|
+
title: t("panel.backToChatTip"),
|
|
1824
2563
|
onClick: () => {
|
|
1825
2564
|
try {
|
|
1826
2565
|
const layout = props.layout;
|
|
1827
2566
|
if (layout && layout.selectPanel) layout.selectPanel(null);
|
|
1828
2567
|
} catch (e) {}
|
|
1829
2568
|
}
|
|
1830
|
-
}, "
|
|
2569
|
+
}, t("panel.backToChat")))), state.err ? h("div", { style: {
|
|
1831
2570
|
padding: "6px 14px",
|
|
1832
2571
|
fontSize: 11,
|
|
1833
2572
|
color: T.error,
|
|
@@ -1844,7 +2583,7 @@ window.__ModuleLoader__.load({
|
|
|
1844
2583
|
} }, h("span", null, hint), h("button", {
|
|
1845
2584
|
style: panelBtn,
|
|
1846
2585
|
onClick: () => setHint(null)
|
|
1847
|
-
}, "
|
|
2586
|
+
}, t("common.gotIt"))) : null, h("div", { style: {
|
|
1848
2587
|
flex: 1,
|
|
1849
2588
|
minHeight: 0,
|
|
1850
2589
|
display: "flex",
|
|
@@ -1862,7 +2601,7 @@ window.__ModuleLoader__.load({
|
|
|
1862
2601
|
minHeight: 0,
|
|
1863
2602
|
display: "flex",
|
|
1864
2603
|
flexDirection: "column"
|
|
1865
|
-
} }, !state.current ? h("div", { style: { padding: "12px 14px" } }, muted("
|
|
2604
|
+
} }, !state.current ? h("div", { style: { padding: "12px 14px" } }, muted(t("panel.pickProduct"))) : !view ? h("div", { style: { padding: "12px 14px" } }, muted(t("panel.loadingView"))) : h(react.default.Fragment, null, h("div", { style: {
|
|
1866
2605
|
...flexRow,
|
|
1867
2606
|
justifyContent: "space-between",
|
|
1868
2607
|
gap: 10,
|
|
@@ -1883,21 +2622,21 @@ window.__ModuleLoader__.load({
|
|
|
1883
2622
|
...flexRow,
|
|
1884
2623
|
gap: 6,
|
|
1885
2624
|
flex: "0 0 auto"
|
|
1886
|
-
} }, chip(
|
|
2625
|
+
} }, chip(t("panel.runChip", { n: product.totalRuns }), T.text2), product.activeRuns > 0 ? chip(t("panel.activeChip", { n: product.activeRuns }), T.brand, { dot: true }) : null, product.lastVerdict ? chip(t("panel.verdictChip", { v: product.lastVerdict }), stColor("accepted")) : null)), h("div", { style: {
|
|
1887
2626
|
display: "flex",
|
|
1888
2627
|
alignItems: "flex-end",
|
|
1889
2628
|
gap: 2,
|
|
1890
2629
|
padding: "0 14px",
|
|
1891
2630
|
borderBottom: `1px solid ${T.border}`
|
|
1892
|
-
} }, panelTabBtn("run",
|
|
2631
|
+
} }, panelTabBtn("run", t("panel.tabRuns", { n: runs.length })), panelTabBtn("backlog", t("panel.tabBacklog", { n: backlogCount })), h("div", { style: {
|
|
1893
2632
|
marginLeft: "auto",
|
|
1894
2633
|
...flexRow,
|
|
1895
2634
|
gap: 6,
|
|
1896
2635
|
paddingBottom: 7
|
|
1897
|
-
} }, panelTab === "run" && !runsExpanded && pinnedActive.length > 0 ? chip(
|
|
2636
|
+
} }, panelTab === "run" && !runsExpanded && pinnedActive.length > 0 ? chip(t("panel.pinnedActive", { n: pinnedActive.length }), T.brand, { dot: true }) : null, panelTab === "run" && runsMatched.length > RUN_PREVIEW ? h("button", {
|
|
1898
2637
|
style: panelBtn,
|
|
1899
2638
|
onClick: () => setRunsExpanded((v) => !v)
|
|
1900
|
-
}, runsExpanded ?
|
|
2639
|
+
}, runsExpanded ? t("panel.showRecent", { n: RUN_PREVIEW }) : t("panel.showAll", { n: runsMatched.length })) : null)), h("div", { style: {
|
|
1901
2640
|
flex: 1,
|
|
1902
2641
|
minHeight: 0,
|
|
1903
2642
|
overflowY: "auto",
|
|
@@ -1907,19 +2646,24 @@ window.__ModuleLoader__.load({
|
|
|
1907
2646
|
...flexRow,
|
|
1908
2647
|
gap: 6,
|
|
1909
2648
|
marginBottom: 8
|
|
1910
|
-
} }, ...RUN_STATUS_ORDER.filter((st) => runStatusCounts[st]).map((st) => filterChip(`${
|
|
2649
|
+
} }, ...RUN_STATUS_ORDER.filter((st) => runStatusCounts[st]).map((st) => filterChip(`${runStatusText(st)} ${runStatusCounts[st]}`, stColor(st), runSelSet.has(st), () => toggleRunStatus(st))), runFiltering ? h("button", {
|
|
1911
2650
|
style: panelBtn,
|
|
1912
|
-
title: "
|
|
2651
|
+
title: t("panel.clearRunFilterTip"),
|
|
1913
2652
|
onClick: () => setRunFilter([])
|
|
1914
|
-
},
|
|
2653
|
+
}, t("panel.filtered", {
|
|
2654
|
+
sel: runSel.length,
|
|
2655
|
+
shown: runsMatched.length,
|
|
2656
|
+
total: runs.length
|
|
2657
|
+
})) : null), visibleRuns.length ? h(RunList, {
|
|
1915
2658
|
runs: visibleRuns,
|
|
1916
2659
|
activeRunId: detail && detail.kind === "run" && detail.run ? detail.run.id : null,
|
|
1917
2660
|
onOpenRun: openRun,
|
|
1918
|
-
onInlineRun: showInline
|
|
1919
|
-
|
|
2661
|
+
onInlineRun: showInline,
|
|
2662
|
+
onCancel: cancelRun
|
|
2663
|
+
}) : muted(t("panel.emptyRunFilter"), { fontSize: 10.5 }), muted(t("panel.runHint"), {
|
|
1920
2664
|
fontSize: 10,
|
|
1921
2665
|
marginTop: 8
|
|
1922
|
-
})) : h(react.default.Fragment, null, muted("
|
|
2666
|
+
})) : h(react.default.Fragment, null, muted(t("panel.boardHint"), {
|
|
1923
2667
|
fontSize: 10,
|
|
1924
2668
|
marginBottom: 8
|
|
1925
2669
|
}), h(BacklogGroups, {
|
|
@@ -1952,24 +2696,25 @@ window.__ModuleLoader__.load({
|
|
|
1952
2696
|
fontSize: 11.5,
|
|
1953
2697
|
fontWeight: 700,
|
|
1954
2698
|
color: T.text
|
|
1955
|
-
} }, "
|
|
2699
|
+
} }, t("panel.runDetail")), h("div", { style: {
|
|
1956
2700
|
...flexRow,
|
|
1957
2701
|
gap: 6
|
|
1958
2702
|
} }, detail && detail.data && detail.data.address ? h("button", {
|
|
1959
2703
|
style: brandBtn,
|
|
1960
|
-
title: "
|
|
2704
|
+
title: t("panel.goOwnerSessionTip"),
|
|
1961
2705
|
onClick: () => goOwnerSessionAndOpen({
|
|
1962
2706
|
ownerSession: detail.data.ownerSession,
|
|
1963
2707
|
address: detail.data.address,
|
|
1964
2708
|
label: detail.data.id
|
|
1965
2709
|
})
|
|
1966
|
-
}, "
|
|
2710
|
+
}, t("panel.goOwnerSession")) : null, h("button", {
|
|
1967
2711
|
style: panelBtn,
|
|
1968
2712
|
onClick: closeDetail
|
|
1969
|
-
}, "
|
|
2713
|
+
}, t("common.close")))), h(RunDetailPane, {
|
|
1970
2714
|
snap: detail && detail.data,
|
|
1971
2715
|
product: state.current,
|
|
1972
|
-
api
|
|
2716
|
+
api,
|
|
2717
|
+
onCancel: cancelRun
|
|
1973
2718
|
}))) : null));
|
|
1974
2719
|
}
|
|
1975
2720
|
//#endregion
|
|
@@ -1991,7 +2736,7 @@ window.__ModuleLoader__.load({
|
|
|
1991
2736
|
const inject = [
|
|
1992
2737
|
"remote",
|
|
1993
2738
|
"slots",
|
|
1994
|
-
"
|
|
2739
|
+
"uiWorkspace",
|
|
1995
2740
|
"locale"
|
|
1996
2741
|
];
|
|
1997
2742
|
const NODE_W = 300;
|
|
@@ -2012,7 +2757,7 @@ window.__ModuleLoader__.load({
|
|
|
2012
2757
|
let maxH = 0;
|
|
2013
2758
|
groups.forEach((g, i) => {
|
|
2014
2759
|
const anyRun = g.stages.some((s) => s.status === "running");
|
|
2015
|
-
const anyFail = g.stages.some((s) => s.status === "failed" || s.status === "needs-human"
|
|
2760
|
+
const anyFail = g.stages.some((s) => s.status === "failed" || s.status === "needs-human");
|
|
2016
2761
|
const allDone = g.stages.length > 0 && g.stages.every((s) => s.status === "done");
|
|
2017
2762
|
const headColor = anyRun ? T.brand : anyFail ? T.error : allDone ? T.success : T.text2;
|
|
2018
2763
|
const h = 48 + g.stages.reduce((a, s) => a + cardH(s), 0) + Math.max(0, g.stages.length - 1) * 7;
|
|
@@ -2059,7 +2804,7 @@ window.__ModuleLoader__.load({
|
|
|
2059
2804
|
const usage = stageUsageLine(s);
|
|
2060
2805
|
return h("div", {
|
|
2061
2806
|
key,
|
|
2062
|
-
title:
|
|
2807
|
+
title: t("stage.cardTip", { label: stageLabelOf(s) }),
|
|
2063
2808
|
onMouseDown: (e) => e.stopPropagation(),
|
|
2064
2809
|
onClick: () => onOpen && onOpen(s),
|
|
2065
2810
|
style: {
|
|
@@ -2108,8 +2853,11 @@ window.__ModuleLoader__.load({
|
|
|
2108
2853
|
textOverflow: "ellipsis",
|
|
2109
2854
|
whiteSpace: "nowrap"
|
|
2110
2855
|
}
|
|
2111
|
-
}, s
|
|
2112
|
-
title:
|
|
2856
|
+
}, stageLabelOf(s)), s.attempts && s.attempts.length > 1 ? h("span", {
|
|
2857
|
+
title: t("stage.retryTip", {
|
|
2858
|
+
n: s.attempts.length - 1,
|
|
2859
|
+
m: s.attempts.length
|
|
2860
|
+
}),
|
|
2113
2861
|
style: {
|
|
2114
2862
|
fontFamily: MONO,
|
|
2115
2863
|
fontSize: 10,
|
|
@@ -2121,7 +2869,7 @@ window.__ModuleLoader__.load({
|
|
|
2121
2869
|
lineHeight: "15px",
|
|
2122
2870
|
flex: "0 0 auto"
|
|
2123
2871
|
}
|
|
2124
|
-
}, `↻${s.attempts.length - 1}`) : null, chip(
|
|
2872
|
+
}, `↻${s.attempts.length - 1}`) : null, chip(stageStatusText(s.status), color, { dot: true }), h("span", { style: {
|
|
2125
2873
|
color: T.text2,
|
|
2126
2874
|
fontSize: 11,
|
|
2127
2875
|
opacity: .5
|
|
@@ -2234,7 +2982,7 @@ window.__ModuleLoader__.load({
|
|
|
2234
2982
|
/** 阶段详情抽屉(卡片点击打开;浮于画布右侧,不参与拖动/缩放)。
|
|
2235
2983
|
* 2026-09-06 状态机化:同任务多次尝试 → 顶部尝试时间线 + 选中展开(默认最新);
|
|
2236
2984
|
* 单次尝试保持现状(不渲染时间线)。 */
|
|
2237
|
-
function StageDetailDrawer({ det, onClose, sessionId,
|
|
2985
|
+
function StageDetailDrawer({ det, onClose, sessionId, uiWorkspace }) {
|
|
2238
2986
|
const [sel, setSel] = react.default.useState(null);
|
|
2239
2987
|
const st = det.stage;
|
|
2240
2988
|
const d = det.data;
|
|
@@ -2245,18 +2993,18 @@ window.__ModuleLoader__.load({
|
|
|
2245
2993
|
const ownerSession = d && d.ownerSession ? String(d.ownerSession) : null;
|
|
2246
2994
|
const mySession = sessionId ? String(sessionId) : null;
|
|
2247
2995
|
const crossSession = !!ownerSession && !!mySession && ownerSession !== mySession;
|
|
2248
|
-
const hasChild = !!(!crossSession && cur && cur.childId &&
|
|
2996
|
+
const hasChild = !!(!crossSession && cur && cur.childId && uiWorkspace && typeof uiWorkspace.openSession === "function");
|
|
2249
2997
|
const openChild = () => {
|
|
2250
2998
|
if (crossSession || !hasChild) return;
|
|
2251
2999
|
try {
|
|
2252
|
-
|
|
3000
|
+
uiWorkspace.openSession({
|
|
2253
3001
|
parentSessionId: ownerSession || sessionId,
|
|
2254
3002
|
childSessionId: cur.childId,
|
|
2255
3003
|
mode: "one-shot"
|
|
2256
3004
|
});
|
|
2257
3005
|
} catch (e) {}
|
|
2258
3006
|
};
|
|
2259
|
-
const outText = cur && cur.output ? cur.output : cur && cur.summary ?
|
|
3007
|
+
const outText = cur && cur.output ? cur.output : cur && cur.summary ? t("stage.summaryFallback", { summary: cur.summary }) : det.err ? t("stage.loadFailed", { err: det.err }) : det.loading ? t("common.loading") : t("stage.noOutput");
|
|
2260
3008
|
const closeBtn = {
|
|
2261
3009
|
font: "inherit",
|
|
2262
3010
|
width: 26,
|
|
@@ -2318,16 +3066,16 @@ window.__ModuleLoader__.load({
|
|
|
2318
3066
|
textOverflow: "ellipsis",
|
|
2319
3067
|
whiteSpace: "nowrap"
|
|
2320
3068
|
}
|
|
2321
|
-
}, st ? st
|
|
3069
|
+
}, st ? stageLabelOf(st) : t("stage.detailTitle")), h("div", { style: {
|
|
2322
3070
|
fontSize: 10.5,
|
|
2323
3071
|
color: T.text2,
|
|
2324
3072
|
marginTop: 1,
|
|
2325
3073
|
fontFamily: MONO,
|
|
2326
3074
|
fontVariantNumeric: "tabular-nums"
|
|
2327
|
-
} }, `${st ? `#${st.seq} · ${st.phase}` : ""}${st && (st.startedAt || st.endedAt) ? ` · ${fmtDur(st.startedAt, st.endedAt)}` : ""}`)), st ? chip(
|
|
3075
|
+
} }, `${st ? `#${st.seq} · ${st.phase}` : ""}${st && (st.startedAt || st.endedAt) ? ` · ${fmtDur(st.startedAt, st.endedAt)}` : ""}`)), st ? chip(stageStatusText(st.status), color, { dot: true }) : null, h("button", {
|
|
2328
3076
|
onClick: onClose,
|
|
2329
3077
|
style: closeBtn,
|
|
2330
|
-
title: "
|
|
3078
|
+
title: t("common.close")
|
|
2331
3079
|
}, "✕")), h("div", { style: {
|
|
2332
3080
|
flex: 1,
|
|
2333
3081
|
overflowY: "auto",
|
|
@@ -2344,7 +3092,7 @@ window.__ModuleLoader__.load({
|
|
|
2344
3092
|
fontWeight: 700,
|
|
2345
3093
|
color: T.text2,
|
|
2346
3094
|
letterSpacing: .3
|
|
2347
|
-
} }, "
|
|
3095
|
+
} }, t("token.officialTitle")), h("span", { style: {
|
|
2348
3096
|
fontSize: 11.5,
|
|
2349
3097
|
fontFamily: MONO,
|
|
2350
3098
|
color: T.text,
|
|
@@ -2358,7 +3106,7 @@ window.__ModuleLoader__.load({
|
|
|
2358
3106
|
fontWeight: 700,
|
|
2359
3107
|
color: T.text2,
|
|
2360
3108
|
letterSpacing: .3
|
|
2361
|
-
} },
|
|
3109
|
+
} }, t("stage.attemptHistory", { n: attempts.length })), h("div", { style: {
|
|
2362
3110
|
display: "flex",
|
|
2363
3111
|
flexDirection: "column",
|
|
2364
3112
|
gap: 4
|
|
@@ -2389,7 +3137,7 @@ window.__ModuleLoader__.load({
|
|
|
2389
3137
|
color: aColor,
|
|
2390
3138
|
flex: "0 0 64px",
|
|
2391
3139
|
fontWeight: 700
|
|
2392
|
-
} }, a.status === "done" ? "
|
|
3140
|
+
} }, a.status === "done" ? t("stage.attemptDone") : a.status === "failed" ? t("stage.attemptFailed", { outcome: a.outcome || t("common.failed") }) : a.status === "cancelled" ? t("stageStatus.cancelled") : t("stage.attemptRunning")), h("span", { style: {
|
|
2393
3141
|
flex: 1,
|
|
2394
3142
|
minWidth: 0,
|
|
2395
3143
|
fontSize: 10.5,
|
|
@@ -2397,7 +3145,7 @@ window.__ModuleLoader__.load({
|
|
|
2397
3145
|
overflow: "hidden",
|
|
2398
3146
|
textOverflow: "ellipsis",
|
|
2399
3147
|
whiteSpace: "nowrap"
|
|
2400
|
-
} }, (a.summary || a.outcome || "
|
|
3148
|
+
} }, (a.summary || a.outcome || t("common.noSummary")).slice(0, 80)), h("span", { style: {
|
|
2401
3149
|
fontFamily: MONO,
|
|
2402
3150
|
fontSize: 10,
|
|
2403
3151
|
color: T.text2,
|
|
@@ -2415,7 +3163,7 @@ window.__ModuleLoader__.load({
|
|
|
2415
3163
|
} }, h("button", {
|
|
2416
3164
|
onClick: openChild,
|
|
2417
3165
|
disabled: !hasChild,
|
|
2418
|
-
title: crossSession ?
|
|
3166
|
+
title: crossSession ? t("stage.childCrossSessionTip", { sid: ownerSession.slice(-6) }) : hasChild ? t("stage.childJumpTip") : t("stage.childNoneTip"),
|
|
2419
3167
|
style: {
|
|
2420
3168
|
font: "inherit",
|
|
2421
3169
|
fontSize: 12,
|
|
@@ -2432,17 +3180,17 @@ window.__ModuleLoader__.load({
|
|
|
2432
3180
|
color: T.brand,
|
|
2433
3181
|
opacity: hasChild ? 1 : .45
|
|
2434
3182
|
}
|
|
2435
|
-
}, "
|
|
3183
|
+
}, t("stage.childJumpBtn")), crossSession ? h("div", { style: {
|
|
2436
3184
|
fontSize: 10.5,
|
|
2437
3185
|
color: T.text2,
|
|
2438
3186
|
textAlign: "center",
|
|
2439
3187
|
lineHeight: 1.55
|
|
2440
|
-
} },
|
|
3188
|
+
} }, t("stage.childCrossSessionNote", { sid: ownerSession ? ownerSession.slice(-6) : "" })) : hasChild ? h("div", { style: {
|
|
2441
3189
|
fontSize: 10.5,
|
|
2442
3190
|
color: T.text2,
|
|
2443
3191
|
textAlign: "center",
|
|
2444
3192
|
lineHeight: 1.55
|
|
2445
|
-
} }, "
|
|
3193
|
+
} }, t("stage.childJumpNote")) : null), st && phaseKeyOf(st.phase) === "dev" ? h("div", { style: {
|
|
2446
3194
|
display: "flex",
|
|
2447
3195
|
flexDirection: "column",
|
|
2448
3196
|
gap: 5
|
|
@@ -2451,7 +3199,7 @@ window.__ModuleLoader__.load({
|
|
|
2451
3199
|
fontWeight: 700,
|
|
2452
3200
|
color: T.text2,
|
|
2453
3201
|
letterSpacing: .3
|
|
2454
|
-
} }, "
|
|
3202
|
+
} }, t("stage.evidenceTitle")), cur && cur.verifyEvidence ? h("div", { style: {
|
|
2455
3203
|
whiteSpace: "pre-wrap",
|
|
2456
3204
|
wordBreak: "break-word",
|
|
2457
3205
|
fontSize: 11.5,
|
|
@@ -2472,7 +3220,7 @@ window.__ModuleLoader__.load({
|
|
|
2472
3220
|
borderRadius: 10,
|
|
2473
3221
|
padding: "8px 12px",
|
|
2474
3222
|
lineHeight: 1.55
|
|
2475
|
-
} }, "
|
|
3223
|
+
} }, t("stage.evidenceMissing"))) : null, h("div", { style: {
|
|
2476
3224
|
display: "flex",
|
|
2477
3225
|
flexDirection: "column",
|
|
2478
3226
|
gap: 5
|
|
@@ -2481,7 +3229,7 @@ window.__ModuleLoader__.load({
|
|
|
2481
3229
|
fontWeight: 700,
|
|
2482
3230
|
color: T.text2,
|
|
2483
3231
|
letterSpacing: .3
|
|
2484
|
-
} }, "
|
|
3232
|
+
} }, t("stage.artifactsTitle")), h("div", { style: {
|
|
2485
3233
|
whiteSpace: "pre-wrap",
|
|
2486
3234
|
wordBreak: "break-word",
|
|
2487
3235
|
fontSize: 12,
|
|
@@ -2495,7 +3243,7 @@ window.__ModuleLoader__.load({
|
|
|
2495
3243
|
overflowY: "auto"
|
|
2496
3244
|
} }, outText))));
|
|
2497
3245
|
}
|
|
2498
|
-
function PipelinePanel({ active, api, runId, sessionId,
|
|
3246
|
+
function PipelinePanel({ active, api, runId, sessionId, uiWorkspace }) {
|
|
2499
3247
|
if (!active) return h("div", { style: {
|
|
2500
3248
|
color: T.text2,
|
|
2501
3249
|
fontSize: 13,
|
|
@@ -2504,7 +3252,7 @@ window.__ModuleLoader__.load({
|
|
|
2504
3252
|
} }, h("div", { style: {
|
|
2505
3253
|
fontSize: 28,
|
|
2506
3254
|
marginBottom: 8
|
|
2507
|
-
} }, "🏭"), "
|
|
3255
|
+
} }, "🏭"), t("pipeline.empty"));
|
|
2508
3256
|
const groups = [];
|
|
2509
3257
|
const taskKeyOf = (s) => String(s.taskKey || String(s.label || "").replace(/^开发 · /, "").replace(/((?:第 \d+ 次重试|补跑))$/, "").trim());
|
|
2510
3258
|
for (const st of active.stages || []) {
|
|
@@ -2779,7 +3527,7 @@ window.__ModuleLoader__.load({
|
|
|
2779
3527
|
justifyContent: "center",
|
|
2780
3528
|
color: T.text2,
|
|
2781
3529
|
fontSize: 13
|
|
2782
|
-
} }, "
|
|
3530
|
+
} }, t("pipeline.noNodes")) : null, h("div", {
|
|
2783
3531
|
onMouseDown: (e) => e.stopPropagation(),
|
|
2784
3532
|
style: {
|
|
2785
3533
|
position: "absolute",
|
|
@@ -2797,7 +3545,7 @@ window.__ModuleLoader__.load({
|
|
|
2797
3545
|
boxShadow: "0 6px 20px rgba(0,0,0,.16)"
|
|
2798
3546
|
}
|
|
2799
3547
|
}, h("button", {
|
|
2800
|
-
title: "
|
|
3548
|
+
title: t("pipeline.zoomOut"),
|
|
2801
3549
|
onClick: () => zoomBy(.86),
|
|
2802
3550
|
style: zoomStyle
|
|
2803
3551
|
}, "−"), h("span", { style: {
|
|
@@ -2807,7 +3555,7 @@ window.__ModuleLoader__.load({
|
|
|
2807
3555
|
minWidth: 34,
|
|
2808
3556
|
textAlign: "center"
|
|
2809
3557
|
} }, `${Math.round(view.s * 100)}%`), h("button", {
|
|
2810
|
-
title: "
|
|
3558
|
+
title: t("pipeline.zoomIn"),
|
|
2811
3559
|
onClick: () => zoomBy(1.16),
|
|
2812
3560
|
style: zoomStyle
|
|
2813
3561
|
}, "+"), h("span", { style: {
|
|
@@ -2815,7 +3563,7 @@ window.__ModuleLoader__.load({
|
|
|
2815
3563
|
height: 14,
|
|
2816
3564
|
background: T.border
|
|
2817
3565
|
} }), h("button", {
|
|
2818
|
-
title: "
|
|
3566
|
+
title: t("pipeline.fitCanvas"),
|
|
2819
3567
|
onClick: fitNow,
|
|
2820
3568
|
style: {
|
|
2821
3569
|
...zoomStyle,
|
|
@@ -2830,11 +3578,11 @@ window.__ModuleLoader__.load({
|
|
|
2830
3578
|
color: T.text2,
|
|
2831
3579
|
paddingRight: 4,
|
|
2832
3580
|
opacity: .85
|
|
2833
|
-
} }, "
|
|
3581
|
+
} }, t("pipeline.canvasHint"))), det ? h(StageDetailDrawer, {
|
|
2834
3582
|
det,
|
|
2835
3583
|
onClose: closeDet,
|
|
2836
3584
|
sessionId,
|
|
2837
|
-
|
|
3585
|
+
uiWorkspace
|
|
2838
3586
|
}) : null);
|
|
2839
3587
|
}
|
|
2840
3588
|
function BoardPanel({ backlog, api, onRefresh, sessionId, onShowRun, openArtifact }) {
|
|
@@ -2876,12 +3624,12 @@ window.__ModuleLoader__.load({
|
|
|
2876
3624
|
} }, h("div", { style: {
|
|
2877
3625
|
fontSize: 28,
|
|
2878
3626
|
marginBottom: 8
|
|
2879
|
-
} }, "📋"), "
|
|
3627
|
+
} }, "📋"), t("board.empty"));
|
|
2880
3628
|
const subtaskMap = {};
|
|
2881
3629
|
for (const t of backlog.tasks || []) if (t.type === "subtask" && t.id) subtaskMap[t.id] = t;
|
|
2882
3630
|
const move = async (kind, id, to) => {
|
|
2883
3631
|
try {
|
|
2884
|
-
await api.backlogUpdate(kind, id, to, sessionId, "
|
|
3632
|
+
await api.backlogUpdate(kind, id, to, sessionId, t("board.dragReason"));
|
|
2885
3633
|
} catch (e) {}
|
|
2886
3634
|
onRefresh();
|
|
2887
3635
|
};
|
|
@@ -2911,7 +3659,11 @@ window.__ModuleLoader__.load({
|
|
|
2911
3659
|
transition: "opacity .1s ease, transform .12s ease",
|
|
2912
3660
|
boxShadow: "0 1px 2px rgba(0,0,0,.05)"
|
|
2913
3661
|
},
|
|
2914
|
-
title:
|
|
3662
|
+
title: t("board.cardTip", {
|
|
3663
|
+
id: item.id,
|
|
3664
|
+
status: item.status,
|
|
3665
|
+
summary: item.summary ? "\n" + item.summary : ""
|
|
3666
|
+
})
|
|
2915
3667
|
}, h("div", { style: {
|
|
2916
3668
|
display: "flex",
|
|
2917
3669
|
alignItems: "center",
|
|
@@ -2961,7 +3713,7 @@ window.__ModuleLoader__.load({
|
|
|
2961
3713
|
fontFamily: MONO,
|
|
2962
3714
|
minWidth: 0
|
|
2963
3715
|
} }, item.devAssign ? h("span", {
|
|
2964
|
-
title:
|
|
3716
|
+
title: t("board.assignDevTip", { who: item.devAssign }),
|
|
2965
3717
|
style: {
|
|
2966
3718
|
display: "inline-flex",
|
|
2967
3719
|
alignItems: "center",
|
|
@@ -2973,7 +3725,7 @@ window.__ModuleLoader__.load({
|
|
|
2973
3725
|
whiteSpace: "nowrap"
|
|
2974
3726
|
}
|
|
2975
3727
|
}, `👨💻${item.devAssign}`) : null, item.qaAssign ? h("span", {
|
|
2976
|
-
title:
|
|
3728
|
+
title: t("board.assignQaTip", { who: item.qaAssign }),
|
|
2977
3729
|
style: {
|
|
2978
3730
|
display: "inline-flex",
|
|
2979
3731
|
alignItems: "center",
|
|
@@ -2985,7 +3737,7 @@ window.__ModuleLoader__.load({
|
|
|
2985
3737
|
whiteSpace: "nowrap"
|
|
2986
3738
|
}
|
|
2987
3739
|
}, `🧪${item.qaAssign}`) : null, item.acceptBy ? h("span", {
|
|
2988
|
-
title:
|
|
3740
|
+
title: t("board.acceptTip", { who: item.acceptBy }),
|
|
2989
3741
|
style: {
|
|
2990
3742
|
display: "inline-flex",
|
|
2991
3743
|
alignItems: "center",
|
|
@@ -3027,7 +3779,7 @@ window.__ModuleLoader__.load({
|
|
|
3027
3779
|
color: T.text2,
|
|
3028
3780
|
fontFamily: MONO,
|
|
3029
3781
|
marginBottom: 3
|
|
3030
|
-
} }, h("span", null,
|
|
3782
|
+
} }, h("span", null, t("board.subtaskCount", { n: subs.length })), done > 0 ? h("span", { style: { color: T.success } }, `${done}✓`) : null, running > 0 ? h("span", { style: { color: T.brand } }, `${running}⟳`) : null, failed > 0 ? h("span", { style: { color: T.error } }, `${failed}✗`) : null), subs.map((sub) => h("div", {
|
|
3031
3783
|
key: sub.id,
|
|
3032
3784
|
style: {
|
|
3033
3785
|
display: "flex",
|
|
@@ -3057,7 +3809,7 @@ window.__ModuleLoader__.load({
|
|
|
3057
3809
|
whiteSpace: "nowrap"
|
|
3058
3810
|
}
|
|
3059
3811
|
}, (sub.title || "").replace(/^开发 · /, "")), sub.devAssign ? h("span", {
|
|
3060
|
-
title:
|
|
3812
|
+
title: t("board.assignDevTip", { who: sub.devAssign }),
|
|
3061
3813
|
style: {
|
|
3062
3814
|
flex: "0 1 auto",
|
|
3063
3815
|
color: T.text2,
|
|
@@ -3112,7 +3864,7 @@ window.__ModuleLoader__.load({
|
|
|
3112
3864
|
fontWeight: 700,
|
|
3113
3865
|
fontSize: 13,
|
|
3114
3866
|
padding: "6px 0 8px"
|
|
3115
|
-
} }, h("span", { style: { fontSize: 14 } }, kind === "req" ? "📌" : kind === "task" ? "🔧" : "🐞"),
|
|
3867
|
+
} }, h("span", { style: { fontSize: 14 } }, kind === "req" ? "📌" : kind === "task" ? "🔧" : "🐞"), kindTitle(kind), h("span", { style: {
|
|
3116
3868
|
fontSize: 11,
|
|
3117
3869
|
fontWeight: 600,
|
|
3118
3870
|
color: T.text2,
|
|
@@ -3188,7 +3940,7 @@ window.__ModuleLoader__.load({
|
|
|
3188
3940
|
}) : null);
|
|
3189
3941
|
}
|
|
3190
3942
|
function fmtAt(ts) {
|
|
3191
|
-
return ts ? new Date(ts).toLocaleTimeString(
|
|
3943
|
+
return ts ? new Date(ts).toLocaleTimeString(localeTag(), {
|
|
3192
3944
|
hour: "2-digit",
|
|
3193
3945
|
minute: "2-digit"
|
|
3194
3946
|
}) : "—";
|
|
@@ -3266,7 +4018,7 @@ window.__ModuleLoader__.load({
|
|
|
3266
4018
|
color: T.warn,
|
|
3267
4019
|
flex: "0 0 auto"
|
|
3268
4020
|
} }, `⛽${fmtTokens((extra.usage.input || 0) + (extra.usage.cacheRead || 0) + (extra.usage.cacheWrite || 0) + (extra.usage.output || 0))}`) : null, extra && extra.assignee ? h("span", {
|
|
3269
|
-
title:
|
|
4021
|
+
title: t("board.assignDevTip", { who: extra.assignee }),
|
|
3270
4022
|
style: {
|
|
3271
4023
|
display: "inline-flex",
|
|
3272
4024
|
alignItems: "center",
|
|
@@ -3345,7 +4097,7 @@ window.__ModuleLoader__.load({
|
|
|
3345
4097
|
} }, `${d.id} · ${d.kind}${d.severity ? " · " + d.severity : ""}`) : null), d ? chip(stText(d.status), color, { dot: true }) : null, h("button", {
|
|
3346
4098
|
onClick: onClose,
|
|
3347
4099
|
style: closeBtn,
|
|
3348
|
-
title: "
|
|
4100
|
+
title: t("common.close")
|
|
3349
4101
|
}, "✕")), h("div", { style: {
|
|
3350
4102
|
flex: 1,
|
|
3351
4103
|
overflowY: "auto",
|
|
@@ -3357,7 +4109,7 @@ window.__ModuleLoader__.load({
|
|
|
3357
4109
|
color: T.text2,
|
|
3358
4110
|
fontSize: 12,
|
|
3359
4111
|
padding: 12
|
|
3360
|
-
} }, "
|
|
4112
|
+
} }, t("common.loading")) : err ? h("div", { style: {
|
|
3361
4113
|
color: T.error,
|
|
3362
4114
|
fontSize: 12,
|
|
3363
4115
|
padding: 12
|
|
@@ -3366,7 +4118,7 @@ window.__ModuleLoader__.load({
|
|
|
3366
4118
|
display: "flex",
|
|
3367
4119
|
flexDirection: "column",
|
|
3368
4120
|
gap: 4
|
|
3369
|
-
} }, secTitle("
|
|
4121
|
+
} }, secTitle(t("item.overview")), d.spec ? h(FoldableText, {
|
|
3370
4122
|
text: d.spec,
|
|
3371
4123
|
charLimit: 300,
|
|
3372
4124
|
lineLimit: 4,
|
|
@@ -3384,7 +4136,28 @@ window.__ModuleLoader__.load({
|
|
|
3384
4136
|
flexDirection: "column",
|
|
3385
4137
|
gap: 2,
|
|
3386
4138
|
marginTop: 2
|
|
3387
|
-
} }, d.devAssign ? kv("👨💻 dev", d.devAssign.slice(0, 26), true) : null, d.qaAssign ? kv("🧪 qa", d.qaAssign.slice(0, 26), true) : null, d.assignBy ? kv("
|
|
4139
|
+
} }, d.devAssign ? kv("👨💻 dev", d.devAssign.slice(0, 26), true) : null, d.qaAssign ? kv("🧪 qa", d.qaAssign.slice(0, 26), true) : null, d.assignBy ? kv(t("item.acceptRow"), d.assignBy.slice(0, 26), true) : null, d.owner ? kv("👤 owner", d.owner.slice(0, 26), true) : null, typeof d.retries === "number" && d.retries > 0 ? kv(t("item.retryRow"), String(d.retries)) : null, d.humanIntervention ? kv(t("item.humanRow"), t("common.needsHuman")) : null, kv(t("item.updatedAt"), fmtAt(d.updatedAt) || "—"))),
|
|
4140
|
+
kind === "bug" ? h("div", { style: {
|
|
4141
|
+
display: "flex",
|
|
4142
|
+
flexDirection: "column",
|
|
4143
|
+
gap: 4,
|
|
4144
|
+
padding: "8px 10px",
|
|
4145
|
+
borderRadius: 8,
|
|
4146
|
+
background: T.layer2,
|
|
4147
|
+
border: `1px solid ${T.border}`
|
|
4148
|
+
} }, secTitle(t("panelItem.defectSection")), d.spec ? h(FoldableText, {
|
|
4149
|
+
text: d.spec,
|
|
4150
|
+
charLimit: 300,
|
|
4151
|
+
lineLimit: 4
|
|
4152
|
+
}) : null, h("div", { style: {
|
|
4153
|
+
display: "flex",
|
|
4154
|
+
flexDirection: "column",
|
|
4155
|
+
gap: 2
|
|
4156
|
+
} }, d.defectId ? kv(t("panelItem.row.defectId"), String(d.defectId)) : null, d.severity ? kv(t("panelItem.row.severity"), String(d.severity)) : null, d.module ? kv(t("panelItem.row.module"), String(d.module)) : null, d.reproduce ? kv(t("panelItem.row.reproduce"), String(d.reproduce)) : null, d.expected ? kv(t("panelItem.row.expected"), String(d.expected)) : null, d.actual ? kv(t("panelItem.row.actual"), String(d.actual)) : null, d.defectCheck ? kv(t("panelItem.row.defectCheck"), String(d.defectCheck)) : null, d.defectCriterion ? kv(t("panelItem.row.defectCriterion"), String(d.defectCriterion)) : null, d.defectAc ? kv(t("panelItem.row.defectAc"), String(d.defectAc)) : null), d.reproduce || d.expected || d.actual ? null : h("div", { style: {
|
|
4157
|
+
fontSize: 10.5,
|
|
4158
|
+
color: T.warn,
|
|
4159
|
+
lineHeight: 1.5
|
|
4160
|
+
} }, t("panelItem.noDefectDetail"))) : null,
|
|
3388
4161
|
d.runInfo ? (() => {
|
|
3389
4162
|
const ri = d.runInfo;
|
|
3390
4163
|
return h("div", { style: {
|
|
@@ -3399,14 +4172,14 @@ window.__ModuleLoader__.load({
|
|
|
3399
4172
|
display: "flex",
|
|
3400
4173
|
alignItems: "center",
|
|
3401
4174
|
gap: 8
|
|
3402
|
-
} }, secTitle("
|
|
4175
|
+
} }, secTitle(t("item.runSection")), h("span", { style: {
|
|
3403
4176
|
fontFamily: MONO,
|
|
3404
4177
|
fontSize: 10.5,
|
|
3405
4178
|
color: T.text2,
|
|
3406
4179
|
whiteSpace: "nowrap"
|
|
3407
4180
|
} }, ri.runId), chip(stText(ri.status), stColor(ri.status)), onShowRun ? h("button", {
|
|
3408
4181
|
onClick: () => onShowRun(ri.runId),
|
|
3409
|
-
title:
|
|
4182
|
+
title: t("item.jumpRunTip", { id: String(ri.runId).slice(-6) }),
|
|
3410
4183
|
style: {
|
|
3411
4184
|
marginLeft: "auto",
|
|
3412
4185
|
flex: "0 0 auto",
|
|
@@ -3421,7 +4194,7 @@ window.__ModuleLoader__.load({
|
|
|
3421
4194
|
color: T.brand,
|
|
3422
4195
|
lineHeight: "16px"
|
|
3423
4196
|
}
|
|
3424
|
-
}, "
|
|
4197
|
+
}, t("item.runBtn")) : null), ri.startedAt || ri.endedAt ? h("span", { style: {
|
|
3425
4198
|
fontSize: 10.5,
|
|
3426
4199
|
color: T.text2,
|
|
3427
4200
|
fontFamily: MONO
|
|
@@ -3429,8 +4202,8 @@ window.__ModuleLoader__.load({
|
|
|
3429
4202
|
display: "flex",
|
|
3430
4203
|
flexDirection: "column",
|
|
3431
4204
|
gap: 4
|
|
3432
|
-
} }, secTitle("
|
|
3433
|
-
text: ri.requirement || "
|
|
4205
|
+
} }, secTitle(t("item.requirement")), h(FoldableText, {
|
|
4206
|
+
text: ri.requirement || t("item.noRequirement"),
|
|
3434
4207
|
charLimit: 300,
|
|
3435
4208
|
lineLimit: 4,
|
|
3436
4209
|
style: {
|
|
@@ -3444,7 +4217,7 @@ window.__ModuleLoader__.load({
|
|
|
3444
4217
|
display: "flex",
|
|
3445
4218
|
flexDirection: "column",
|
|
3446
4219
|
gap: 4
|
|
3447
|
-
} }, secTitle("
|
|
4220
|
+
} }, secTitle(t("item.runDocs")), h("div", { style: {
|
|
3448
4221
|
fontSize: 11.5,
|
|
3449
4222
|
fontFamily: MONO,
|
|
3450
4223
|
color: T.brand,
|
|
@@ -3462,7 +4235,7 @@ window.__ModuleLoader__.load({
|
|
|
3462
4235
|
onClick: () => {
|
|
3463
4236
|
if (openArtifact) openArtifact(a.address, a.name);
|
|
3464
4237
|
},
|
|
3465
|
-
title:
|
|
4238
|
+
title: t("item.previewTip", { path: `${d.runDocs}/${a.name}` }),
|
|
3466
4239
|
style: {
|
|
3467
4240
|
fontSize: 10.5,
|
|
3468
4241
|
fontWeight: 600,
|
|
@@ -3480,7 +4253,7 @@ window.__ModuleLoader__.load({
|
|
|
3480
4253
|
display: "flex",
|
|
3481
4254
|
flexDirection: "column",
|
|
3482
4255
|
gap: 4
|
|
3483
|
-
} }, secTitle("
|
|
4256
|
+
} }, secTitle(t("token.officialTitle")), h("span", { style: {
|
|
3484
4257
|
fontSize: 11.5,
|
|
3485
4258
|
fontFamily: MONO,
|
|
3486
4259
|
color: T.text,
|
|
@@ -3492,7 +4265,7 @@ window.__ModuleLoader__.load({
|
|
|
3492
4265
|
marginTop: 2
|
|
3493
4266
|
} }, Object.entries(d.byRole).sort((a, b) => totalTokens(b[1]) - totalTokens(a[1])).map(([role, uRaw]) => {
|
|
3494
4267
|
const u = uRaw;
|
|
3495
|
-
const label = role
|
|
4268
|
+
const label = roleChip(role);
|
|
3496
4269
|
return h("div", {
|
|
3497
4270
|
key: role,
|
|
3498
4271
|
style: {
|
|
@@ -3503,13 +4276,13 @@ window.__ModuleLoader__.load({
|
|
|
3503
4276
|
fontFamily: MONO,
|
|
3504
4277
|
color: T.text2
|
|
3505
4278
|
}
|
|
3506
|
-
}, h("span", { style: { flex: "0 0 64px" } }, label), h("span", { style: { color: T.text } }, `⛽${fmtTokens(totalTokens(u))}`), h("span", null, `${fmtTokens(u.input || 0)}i / ${fmtTokens(u.cacheRead || 0)}c / ${fmtTokens(u.output || 0)}o · ${u.calls || 0}
|
|
4279
|
+
}, h("span", { style: { flex: "0 0 64px" } }, label), h("span", { style: { color: T.text } }, `⛽${fmtTokens(totalTokens(u))}`), h("span", null, `${fmtTokens(u.input || 0)}i / ${fmtTokens(u.cacheRead || 0)}c / ${fmtTokens(u.output || 0)}o · ${t("common.calls", { n: u.calls || 0 })}`));
|
|
3507
4280
|
})) : null) : null,
|
|
3508
4281
|
d.subtasks && d.subtasks.length > 0 ? h("div", { style: {
|
|
3509
4282
|
display: "flex",
|
|
3510
4283
|
flexDirection: "column",
|
|
3511
4284
|
gap: 4
|
|
3512
|
-
} }, secTitle(
|
|
4285
|
+
} }, secTitle(t("item.subtasks", { n: d.subtasks.length })), h("div", { style: {
|
|
3513
4286
|
display: "flex",
|
|
3514
4287
|
flexDirection: "column",
|
|
3515
4288
|
gap: 5
|
|
@@ -3521,7 +4294,7 @@ window.__ModuleLoader__.load({
|
|
|
3521
4294
|
display: "flex",
|
|
3522
4295
|
flexDirection: "column",
|
|
3523
4296
|
gap: 4
|
|
3524
|
-
} }, secTitle(
|
|
4297
|
+
} }, secTitle(t("item.bugs", { n: d.bugs.length })), h("div", { style: {
|
|
3525
4298
|
display: "flex",
|
|
3526
4299
|
flexDirection: "column",
|
|
3527
4300
|
gap: 5
|
|
@@ -3530,7 +4303,7 @@ window.__ModuleLoader__.load({
|
|
|
3530
4303
|
display: "flex",
|
|
3531
4304
|
flexDirection: "column",
|
|
3532
4305
|
gap: 4
|
|
3533
|
-
} }, secTitle(
|
|
4306
|
+
} }, secTitle(t("item.timeline", { n: d.events.length })), h("div", { style: {
|
|
3534
4307
|
display: "flex",
|
|
3535
4308
|
flexDirection: "column",
|
|
3536
4309
|
gap: 0
|
|
@@ -3566,7 +4339,7 @@ window.__ModuleLoader__.load({
|
|
|
3566
4339
|
}, ev.reason || ""))))) : null
|
|
3567
4340
|
]));
|
|
3568
4341
|
}
|
|
3569
|
-
function TeamSelector({ sessionId, remote }) {
|
|
4342
|
+
function TeamSelector({ sessionId, remote, locale }) {
|
|
3570
4343
|
const [teams, setTeams] = react.default.useState([]);
|
|
3571
4344
|
const [active, setActive] = react.default.useState(null);
|
|
3572
4345
|
const [open, setOpen] = react.default.useState(false);
|
|
@@ -3579,7 +4352,11 @@ window.__ModuleLoader__.load({
|
|
|
3579
4352
|
const at = unwrap(await remote.getActiveTeam(sessionId), "getActiveTeam");
|
|
3580
4353
|
setActive(at && at.team ? at.team : null);
|
|
3581
4354
|
} catch (e) {}
|
|
3582
|
-
}, [
|
|
4355
|
+
}, [
|
|
4356
|
+
remote,
|
|
4357
|
+
sessionId,
|
|
4358
|
+
locale
|
|
4359
|
+
]);
|
|
3583
4360
|
react.default.useEffect(() => {
|
|
3584
4361
|
load();
|
|
3585
4362
|
}, [load]);
|
|
@@ -3610,7 +4387,7 @@ window.__ModuleLoader__.load({
|
|
|
3610
4387
|
style: { position: "relative" }
|
|
3611
4388
|
}, h("button", {
|
|
3612
4389
|
onClick: () => setOpen(!open),
|
|
3613
|
-
title: active ?
|
|
4390
|
+
title: active ? t("team.currentTip", { name: active.name }) : t("team.pick"),
|
|
3614
4391
|
style: {
|
|
3615
4392
|
display: "inline-flex",
|
|
3616
4393
|
alignItems: "center",
|
|
@@ -3627,14 +4404,14 @@ window.__ModuleLoader__.load({
|
|
|
3627
4404
|
transition: "all .12s ease"
|
|
3628
4405
|
}
|
|
3629
4406
|
}, h("span", { style: { fontSize: 12 } }, active ? active.icon : "🏭"), h("span", {
|
|
3630
|
-
title: active && active.name ? String(active.name) : "
|
|
4407
|
+
title: active && active.name ? String(active.name) : t("team.label"),
|
|
3631
4408
|
style: {
|
|
3632
4409
|
maxWidth: 80,
|
|
3633
4410
|
overflow: "hidden",
|
|
3634
4411
|
textOverflow: "ellipsis",
|
|
3635
4412
|
whiteSpace: "nowrap"
|
|
3636
4413
|
}
|
|
3637
|
-
}, active ? active.name : "
|
|
4414
|
+
}, active ? active.name : t("team.label")), h("span", { style: {
|
|
3638
4415
|
fontSize: 8,
|
|
3639
4416
|
opacity: .6
|
|
3640
4417
|
} }, open ? "▲" : "▼")), open ? h("div", { style: {
|
|
@@ -3654,7 +4431,7 @@ window.__ModuleLoader__.load({
|
|
|
3654
4431
|
fontSize: 10,
|
|
3655
4432
|
color: T.text2,
|
|
3656
4433
|
borderBottom: `1px solid ${T.border}`
|
|
3657
|
-
} }, "
|
|
4434
|
+
} }, t("team.pick")), h("button", {
|
|
3658
4435
|
onClick: () => select(null),
|
|
3659
4436
|
style: {
|
|
3660
4437
|
display: "flex",
|
|
@@ -3683,14 +4460,14 @@ window.__ModuleLoader__.load({
|
|
|
3683
4460
|
} }, h("div", { style: {
|
|
3684
4461
|
fontWeight: 600,
|
|
3685
4462
|
lineHeight: 1.35
|
|
3686
|
-
} }, "
|
|
4463
|
+
} }, t("team.none")), h("div", { style: {
|
|
3687
4464
|
fontSize: 10.5,
|
|
3688
4465
|
color: T.text2,
|
|
3689
4466
|
marginTop: 3,
|
|
3690
4467
|
lineHeight: 1.45,
|
|
3691
4468
|
whiteSpace: "normal",
|
|
3692
4469
|
wordBreak: "break-word"
|
|
3693
|
-
} }, "
|
|
4470
|
+
} }, t("team.noneNote"))), !active ? h("span", { style: {
|
|
3694
4471
|
marginLeft: "auto",
|
|
3695
4472
|
color: T.text2,
|
|
3696
4473
|
fontSize: 12,
|
|
@@ -3739,7 +4516,10 @@ window.__ModuleLoader__.load({
|
|
|
3739
4516
|
}
|
|
3740
4517
|
/** 解包 remote 信封:失败抛错;成功返回 value。 */
|
|
3741
4518
|
function unwrap(res, what) {
|
|
3742
|
-
if (!res || !res.ok) throw new Error(
|
|
4519
|
+
if (!res || !res.ok) throw new Error(t("common.remoteCallFailed", {
|
|
4520
|
+
what: what || "remote",
|
|
4521
|
+
detail: res && res.error && (res.error.message || res.error.code) || t("common.unknownError")
|
|
4522
|
+
}));
|
|
3743
4523
|
return res.value;
|
|
3744
4524
|
}
|
|
3745
4525
|
function TeamFlowView(props) {
|
|
@@ -3758,7 +4538,7 @@ window.__ModuleLoader__.load({
|
|
|
3758
4538
|
if (!api) {
|
|
3759
4539
|
setState((s) => ({
|
|
3760
4540
|
...s,
|
|
3761
|
-
err: "
|
|
4541
|
+
err: t("common.remoteNotReady")
|
|
3762
4542
|
}));
|
|
3763
4543
|
return;
|
|
3764
4544
|
}
|
|
@@ -3820,6 +4600,26 @@ window.__ModuleLoader__.load({
|
|
|
3820
4600
|
setBusy(false);
|
|
3821
4601
|
};
|
|
3822
4602
|
const anyRunning = runs.some((r) => r.status === "running" || r.status === "pending");
|
|
4603
|
+
const runningRun = !!(activeRun && activeRun.status === "running");
|
|
4604
|
+
/** 中断请求已发出:按钮先隐藏,等 2s 轮询把状态刷成 cancelled(避免重复点出「未生效」提示)。 */
|
|
4605
|
+
const [cancelSentFor, setCancelSentFor] = react.default.useState(null);
|
|
4606
|
+
react.default.useEffect(() => {
|
|
4607
|
+
setCancelSentFor(null);
|
|
4608
|
+
}, [activeRun && activeRun.id, activeRun && activeRun.status]);
|
|
4609
|
+
const onCancel = async (id) => {
|
|
4610
|
+
if (!api) return;
|
|
4611
|
+
try {
|
|
4612
|
+
const r = unwrap(await api.cancel(id), "cancel");
|
|
4613
|
+
if (!r || r.ok !== true) throw new Error(t("cancel.failed"));
|
|
4614
|
+
setCancelSentFor(id);
|
|
4615
|
+
refresh();
|
|
4616
|
+
} catch (e) {
|
|
4617
|
+
setState((s) => ({
|
|
4618
|
+
...s,
|
|
4619
|
+
err: String(e && e.message || e)
|
|
4620
|
+
}));
|
|
4621
|
+
}
|
|
4622
|
+
};
|
|
3823
4623
|
const btn = {
|
|
3824
4624
|
font: "inherit",
|
|
3825
4625
|
fontSize: 12,
|
|
@@ -3887,7 +4687,7 @@ window.__ModuleLoader__.load({
|
|
|
3887
4687
|
fontWeight: 700,
|
|
3888
4688
|
fontSize: 14,
|
|
3889
4689
|
lineHeight: "18px"
|
|
3890
|
-
} }, "
|
|
4690
|
+
} }, t("workbench.title")), h("span", { style: {
|
|
3891
4691
|
fontSize: 11,
|
|
3892
4692
|
color: T.text2,
|
|
3893
4693
|
display: "flex",
|
|
@@ -3900,7 +4700,7 @@ window.__ModuleLoader__.load({
|
|
|
3900
4700
|
display: "inline-block",
|
|
3901
4701
|
background: anyRunning ? T.success : T.text2,
|
|
3902
4702
|
animation: anyRunning ? "tf-pulse 1.6s ease-in-out infinite" : "none"
|
|
3903
|
-
} }), anyRunning ? "
|
|
4703
|
+
} }), anyRunning ? t("workbench.running") : t("workbench.idle"))), h("button", {
|
|
3904
4704
|
onClick: refresh,
|
|
3905
4705
|
style: {
|
|
3906
4706
|
...btn,
|
|
@@ -3909,10 +4709,18 @@ window.__ModuleLoader__.load({
|
|
|
3909
4709
|
alignItems: "center",
|
|
3910
4710
|
gap: 5
|
|
3911
4711
|
}
|
|
3912
|
-
}, "
|
|
4712
|
+
}, t("workbench.refresh")), runningRun && cancelSentFor !== activeRun.id ? h(CancelButton, {
|
|
4713
|
+
runId: activeRun.id,
|
|
4714
|
+
label: t("cancel.btnWithId", { id: String(activeRun.id).slice(-6) }),
|
|
4715
|
+
title: t("cancel.tip", { id: activeRun.id }),
|
|
4716
|
+
onConfirm: onCancel
|
|
4717
|
+
}) : null, canResume ? h("button", {
|
|
3913
4718
|
onClick: onResume,
|
|
3914
4719
|
disabled: busy,
|
|
3915
|
-
title:
|
|
4720
|
+
title: t("workbench.resumeTip", {
|
|
4721
|
+
id: activeRun.id,
|
|
4722
|
+
status: runStatusText(activeRun.status)
|
|
4723
|
+
}),
|
|
3916
4724
|
style: {
|
|
3917
4725
|
...btn,
|
|
3918
4726
|
background: T.error,
|
|
@@ -3920,14 +4728,14 @@ window.__ModuleLoader__.load({
|
|
|
3920
4728
|
border: "none",
|
|
3921
4729
|
fontWeight: 600
|
|
3922
4730
|
}
|
|
3923
|
-
}, busy ? "
|
|
4731
|
+
}, busy ? t("workbench.resuming") : t("workbench.resumeBtn", { id: String(activeRun.id).slice(-6) })) : null), err ? h("div", { style: {
|
|
3924
4732
|
color: T.error,
|
|
3925
4733
|
fontSize: 12,
|
|
3926
4734
|
background: `color-mix(in srgb, ${T.error} 8%, transparent)`,
|
|
3927
4735
|
border: `1px solid color-mix(in srgb, ${T.error} 30%, transparent)`,
|
|
3928
4736
|
borderRadius: 8,
|
|
3929
4737
|
padding: "7px 11px"
|
|
3930
|
-
} },
|
|
4738
|
+
} }, t("workbench.loadFailed", { err })) : null, needHuman.length > 0 ? h("div", { style: {
|
|
3931
4739
|
display: "flex",
|
|
3932
4740
|
alignItems: "center",
|
|
3933
4741
|
gap: 10,
|
|
@@ -3940,13 +4748,13 @@ window.__ModuleLoader__.load({
|
|
|
3940
4748
|
fontWeight: 700,
|
|
3941
4749
|
color: T.warn,
|
|
3942
4750
|
fontSize: 12.5
|
|
3943
|
-
} },
|
|
4751
|
+
} }, t("workbench.needsHumanBanner", { n: needHuman.length })), needHuman.slice(0, 5).map((item) => {
|
|
3944
4752
|
const kind = (backlog.requirements || []).some((r) => r.id === item.id) ? "req" : (backlog.tasks || []).some((t) => t.id === item.id) ? "task" : "bug";
|
|
3945
4753
|
const fin = kind === "bug" ? "verified" : "accepted";
|
|
3946
4754
|
return h("button", {
|
|
3947
4755
|
key: item.id,
|
|
3948
4756
|
onClick: async () => {
|
|
3949
|
-
await api.backlogUpdate(kind, item.id, fin, props.sessionId, "
|
|
4757
|
+
await api.backlogUpdate(kind, item.id, fin, props.sessionId, t("workbench.manualReason"));
|
|
3950
4758
|
refresh();
|
|
3951
4759
|
},
|
|
3952
4760
|
style: {
|
|
@@ -3956,7 +4764,7 @@ window.__ModuleLoader__.load({
|
|
|
3956
4764
|
border: "none",
|
|
3957
4765
|
fontWeight: 600
|
|
3958
4766
|
}
|
|
3959
|
-
},
|
|
4767
|
+
}, t("workbench.handle", { id: item.id }));
|
|
3960
4768
|
})) : null, h("div", { style: {
|
|
3961
4769
|
display: "flex",
|
|
3962
4770
|
alignItems: "center",
|
|
@@ -3965,10 +4773,10 @@ window.__ModuleLoader__.load({
|
|
|
3965
4773
|
} }, h("button", {
|
|
3966
4774
|
onClick: () => setTab("pipeline"),
|
|
3967
4775
|
style: tabBtn(tab === "pipeline")
|
|
3968
|
-
}, "
|
|
4776
|
+
}, t("workbench.tabPipeline")), h("button", {
|
|
3969
4777
|
onClick: () => setTab("board"),
|
|
3970
4778
|
style: tabBtn(tab === "board")
|
|
3971
|
-
}, "
|
|
4779
|
+
}, t("workbench.tabBoard")), h("div", { style: {
|
|
3972
4780
|
marginLeft: "auto",
|
|
3973
4781
|
display: "flex",
|
|
3974
4782
|
alignItems: "center",
|
|
@@ -3984,14 +4792,14 @@ window.__ModuleLoader__.load({
|
|
|
3984
4792
|
color: activeRun.status === "interrupted" ? T.warn : T.text2,
|
|
3985
4793
|
border: `1px solid ${T.border}`
|
|
3986
4794
|
}
|
|
3987
|
-
}, `#${String(activeRun.id).slice(-8)} · ${
|
|
4795
|
+
}, `#${String(activeRun.id).slice(-8)} · ${runStatusText(activeRun.status)}`) : null, total && total.input + total.cacheRead + total.cacheWrite + total.output > 0 ? h("span", {
|
|
3988
4796
|
style: {
|
|
3989
4797
|
fontSize: 11.5,
|
|
3990
4798
|
fontFamily: MONO,
|
|
3991
4799
|
color: T.text2,
|
|
3992
4800
|
cursor: "help"
|
|
3993
4801
|
},
|
|
3994
|
-
title: "
|
|
4802
|
+
title: t("token.allStagesTip")
|
|
3995
4803
|
}, `∑ ⇅${fmtTokens(total.input)}/⇅${fmtTokens(total.cacheRead)}·⬆${fmtTokens(total.output)}`) : null)), h("div", { style: {
|
|
3996
4804
|
display: "flex",
|
|
3997
4805
|
alignItems: "center",
|
|
@@ -3999,7 +4807,7 @@ window.__ModuleLoader__.load({
|
|
|
3999
4807
|
fontSize: 12,
|
|
4000
4808
|
flexWrap: "wrap"
|
|
4001
4809
|
} }, h("span", {
|
|
4002
|
-
title:
|
|
4810
|
+
title: t("workbench.workspaceTip", { path: workspace && workspace.path || t("workbench.noWorkspace") }),
|
|
4003
4811
|
style: {
|
|
4004
4812
|
display: "inline-flex",
|
|
4005
4813
|
alignItems: "center",
|
|
@@ -4024,7 +4832,7 @@ window.__ModuleLoader__.load({
|
|
|
4024
4832
|
display: "flex",
|
|
4025
4833
|
alignItems: "center",
|
|
4026
4834
|
gap: 5
|
|
4027
|
-
} }, h("span", { style: { color: T.text2 } }, "
|
|
4835
|
+
} }, h("span", { style: { color: T.text2 } }, t("workbench.history")), runs.length ? runs.map((r) => {
|
|
4028
4836
|
const sel = r.id === (runId || runs[0] && runs[0].id);
|
|
4029
4837
|
return h("button", {
|
|
4030
4838
|
key: r.id,
|
|
@@ -4035,13 +4843,13 @@ window.__ModuleLoader__.load({
|
|
|
4035
4843
|
}) : h("span", { style: {
|
|
4036
4844
|
color: T.text2,
|
|
4037
4845
|
fontSize: 11.5
|
|
4038
|
-
} }, "
|
|
4846
|
+
} }, t("common.noneDash"))) : null, tab === "pipeline" && activeRun && activeRun.address ? h("button", {
|
|
4039
4847
|
style: chipBtn(false),
|
|
4040
|
-
title: "
|
|
4848
|
+
title: t("workbench.openRightBarTip"),
|
|
4041
4849
|
onClick: () => {
|
|
4042
4850
|
if (!(props.openResource && props.openResource(activeRun.address, activeRun.id))) console.warn("[teamflow] 右侧栏不可用,run 详情请在画布节点里查看");
|
|
4043
4851
|
}
|
|
4044
|
-
}, "
|
|
4852
|
+
}, t("workbench.openRightBarBtn")) : null), h("div", { style: {
|
|
4045
4853
|
flex: 1,
|
|
4046
4854
|
minHeight: 0,
|
|
4047
4855
|
display: "flex",
|
|
@@ -4051,7 +4859,7 @@ window.__ModuleLoader__.load({
|
|
|
4051
4859
|
api,
|
|
4052
4860
|
runId: activeRun ? activeRun.id : null,
|
|
4053
4861
|
sessionId: props.sessionId,
|
|
4054
|
-
|
|
4862
|
+
uiWorkspace: props.uiWorkspace
|
|
4055
4863
|
}) : h(BoardPanel, {
|
|
4056
4864
|
backlog,
|
|
4057
4865
|
api,
|
|
@@ -4082,6 +4890,24 @@ window.__ModuleLoader__.load({
|
|
|
4082
4890
|
async function apply(ctx) {
|
|
4083
4891
|
await ctx.remote.$mount(TEAMFLOW_REMOTE_CONTRIBUTION);
|
|
4084
4892
|
const teamflow = ctx.get("remote.teamflow");
|
|
4893
|
+
const t = ctx.locale.bind(NS);
|
|
4894
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
4895
|
+
zh,
|
|
4896
|
+
en
|
|
4897
|
+
}), "teamflow: dictionaries");
|
|
4898
|
+
ctx.effect(() => ctx.locale.subscribe(() => setTranslator(t, () => ctx.locale.getSnapshot().active)), "teamflow: translator sync");
|
|
4899
|
+
setTranslator(t, () => ctx.locale.getSnapshot().active);
|
|
4900
|
+
const pushLocaleToHost = (active) => {
|
|
4901
|
+
try {
|
|
4902
|
+
if (typeof active !== "string" || !active) return;
|
|
4903
|
+
const p = teamflow.setLocale(active);
|
|
4904
|
+
if (p && typeof p.catch === "function") p.catch(() => {});
|
|
4905
|
+
} catch (e) {}
|
|
4906
|
+
};
|
|
4907
|
+
ctx.effect(() => {
|
|
4908
|
+
pushLocaleToHost(ctx.locale.getSnapshot().active);
|
|
4909
|
+
return ctx.locale.subscribe(() => pushLocaleToHost(ctx.locale.getSnapshot().active));
|
|
4910
|
+
}, "teamflow: host locale push");
|
|
4085
4911
|
const openResourceSafe = (address, label, quiet) => {
|
|
4086
4912
|
try {
|
|
4087
4913
|
const sidebarRight = ctx.get("sidebarRight");
|
|
@@ -4103,14 +4929,16 @@ window.__ModuleLoader__.load({
|
|
|
4103
4929
|
name: "sidebar.panellist",
|
|
4104
4930
|
id: "teamflow",
|
|
4105
4931
|
order: 60,
|
|
4106
|
-
|
|
4932
|
+
locale: NS,
|
|
4933
|
+
label: () => t("workbench.title")
|
|
4107
4934
|
}, TeamflowPanelIcon));
|
|
4108
4935
|
ctx.slots.inject("main", () => ctx.slots.register({
|
|
4109
4936
|
name: "main",
|
|
4110
4937
|
key: "teamflow",
|
|
4938
|
+
locale: NS,
|
|
4111
4939
|
inject: () => ({
|
|
4112
4940
|
remote: teamflow,
|
|
4113
|
-
|
|
4941
|
+
uiWorkspace: ctx.get("uiWorkspace"),
|
|
4114
4942
|
layout: ctx.get("layout"),
|
|
4115
4943
|
openResource: openResourceSafe,
|
|
4116
4944
|
openArtifact
|
|
@@ -4126,6 +4954,7 @@ window.__ModuleLoader__.load({
|
|
|
4126
4954
|
ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
|
|
4127
4955
|
name: "sidebar.right.pane.tab",
|
|
4128
4956
|
key: RUN_TAB_ID,
|
|
4957
|
+
locale: NS,
|
|
4129
4958
|
inject: () => ({
|
|
4130
4959
|
remote: teamflow,
|
|
4131
4960
|
openArtifact
|
|
@@ -4135,11 +4964,12 @@ window.__ModuleLoader__.load({
|
|
|
4135
4964
|
name: "conversation.view",
|
|
4136
4965
|
id: "teamflow",
|
|
4137
4966
|
order: 20,
|
|
4138
|
-
|
|
4967
|
+
locale: NS,
|
|
4968
|
+
label: () => `🏭 ${t("workbench.title")}`,
|
|
4139
4969
|
inject: (sessionId) => ({
|
|
4140
4970
|
sessionId,
|
|
4141
4971
|
remote: teamflow,
|
|
4142
|
-
|
|
4972
|
+
uiWorkspace: ctx.get("uiWorkspace"),
|
|
4143
4973
|
openArtifact,
|
|
4144
4974
|
openResource: openResourceSafe
|
|
4145
4975
|
})
|
|
@@ -4148,9 +4978,11 @@ window.__ModuleLoader__.load({
|
|
|
4148
4978
|
name: "conversation.input.right",
|
|
4149
4979
|
id: "teamflow-team-select",
|
|
4150
4980
|
order: 5,
|
|
4981
|
+
locale: NS,
|
|
4151
4982
|
inject: (sessionId) => ({
|
|
4152
4983
|
sessionId,
|
|
4153
|
-
remote: teamflow
|
|
4984
|
+
remote: teamflow,
|
|
4985
|
+
locale: ctx.locale.getSnapshot().active
|
|
4154
4986
|
})
|
|
4155
4987
|
}, TeamSelector));
|
|
4156
4988
|
}
|