dsh-task-dispatch-table 0.1.12 → 0.1.13

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/dist/client.js CHANGED
@@ -57283,35 +57283,6 @@ window.__ModuleLoader__.load({
57283
57283
  * 客户端由 `connectFs({ path })` 建 HTTP face。路径与插件路由同前缀,避免与其他插件撞车。
57284
57284
  */
57285
57285
  const FS_ROUTE = "/api/task-dispatch-table/fs";
57286
- // src/client/config-panel.tsx
57287
- function ConfigPanel(props) {
57288
- const { t, view } = props;
57289
- if (view !== void 0 && view !== "page") return null;
57290
- return (0, react$1.createElement)("div", { style: {
57291
- padding: "4px 2px",
57292
- maxWidth: "640px"
57293
- } }, (0, react$1.createElement)("p", { style: {
57294
- fontSize: "var(--dshpc-font-md)",
57295
- color: "var(--dshpc-fg,#1f2328)",
57296
- lineHeight: 1.6,
57297
- margin: 0,
57298
- whiteSpace: "pre-wrap"
57299
- } }, t("settingsIntro")));
57300
- }
57301
- // src/client/error-text.ts
57302
- /**
57303
- * 把宿主返回的机器码错误翻成人话(保底用:客户端校验已拦掉绝大多数必填问题,这里只兜底漏网的)。
57304
- * 命中已知 zod 片段就翻译,否则原样返回(前缀「任务定义不合法(…)」尽量保留上下文)。
57305
- */
57306
- function humanizeTaskError(raw) {
57307
- if (raw.includes("target.workspace") && raw.toLowerCase().includes("too small")) return "工作区不能为空,请先选择工作区";
57308
- if (raw.includes("target.prompt") && raw.toLowerCase().includes("too small")) return "提示词不能为空,请先填写提示词";
57309
- if (raw.includes("title") && raw.toLowerCase().includes("too small")) return "任务名称不能为空";
57310
- if (raw.includes("ISO 8601") || raw.includes("schedule.window")) return "「允许延迟」的时长不合法——请从下拉里重选一个(如 4 小时)。";
57311
- if (raw.includes("schedule.cron")) return "执行排期不合法——请重新选一次执行频率。";
57312
- if (raw.includes("附件 ref 非法") || raw.includes("attachments") && raw.includes("ref")) return "附加文件的引用路径不合法——必须是工作区内的相对路径。请删掉那个附件、重新选择一次。";
57313
- return raw;
57314
- }
57315
57286
  // src/client/http.ts
57316
57287
  const DEFAULT_TIMEOUT_MS = 8e3;
57317
57288
  /** 带超时的 fetch:超时即 `controller.abort()`(调用方按「本次请求失败」处理,保持上一份数据)。 */
@@ -57442,1974 +57413,2350 @@ window.__ModuleLoader__.load({
57442
57413
  if (!Array.isArray(body.events)) throw new Error("事件读取失败:events 形状不符");
57443
57414
  return body.events;
57444
57415
  }
57445
- // src/client/event-subscribe.ts
57446
- /** 事件流地址:前缀取自唯一真源 `query.ts`(M9;不 import index 以免成环)。 */
57447
- const EVENTS_URL = `${API_PREFIX}/events`;
57448
- /** 看门狗巡检间隔。 */
57449
- const WATCHDOG_MS = 5e3;
57450
- /** 连续未连上的容忍上限:超过它主动重建连接。 */
57451
- const RECONNECT_AFTER_MS = 3e4;
57452
- /**
57453
- * 「连上了但收不到东西」的容忍上限(毫秒):服务端每 20s 必发一条心跳 `sys.ping`(**真实 data 帧**,
57454
- * 客户端 `onmessage` 见得到)⇒ 超过 3 倍心跳仍一无所获,判定为**半死连接**(反向代理静默丢流、
57455
- * 或没有 FIN 的黑洞),主动重建。这是 `readyState` 判据覆盖不到的那一半(2026-10-06 审计 🟡)。
57456
- */
57457
- const SILENT_AFTER_MS = 75e3;
57458
- const byType = /* @__PURE__ */ new Map();
57459
- const resyncHandlers = /* @__PURE__ */ new Set();
57460
- let source = null;
57461
- let watchdog = null;
57462
- /** 本轮「未连上」的起始时刻(0 = 当前处于 OPEN)。 */
57463
- let unhealthySince = 0;
57464
- /** 最后一次**收到任何帧**的时刻(含心跳)——半死连接的唯一可观测判据。 */
57465
- let lastSeenAt = 0;
57466
- /** 「本环境没有 EventSource」只告警一次(降级会让所有页面失去刷新通道,必须留痕又不能刷屏)。 */
57467
- let warnedNoEventSource = false;
57468
- /** 已重建过几次连接(含看门狗主动重建)——**排查手册的关键数字**(2026-10-07 可观测性审计)。 */
57469
- let reconnects = 0;
57470
- /** 广播「(重)连成功」——各页据此重读一次当前值。 */
57471
- function dispatchResync() {
57472
- for (const handler of [...resyncHandlers]) try {
57473
- handler();
57474
- } catch {}
57475
- }
57476
- function openSource() {
57477
- if (source !== null) reconnects += 1;
57478
- let es;
57479
- try {
57480
- es = new EventSource(EVENTS_URL);
57481
- } catch (error) {
57482
- console.warn("[tdt] EventSource 建立失败,稍后重试:", error);
57483
- return;
57484
- }
57485
- lastSeenAt = Date.now();
57486
- es.onmessage = (msg) => {
57487
- lastSeenAt = Date.now();
57488
- let event;
57489
- try {
57490
- event = JSON.parse(msg.data);
57491
- } catch {
57492
- return;
57493
- }
57494
- const handlers = byType.get(event.type);
57495
- if (handlers === void 0) return;
57496
- for (const handler of [...handlers]) try {
57497
- handler(event);
57498
- } catch (error) {
57499
- console.error(`[tdt] 事件订阅回调抛异常(该页可能停止更新),type=${event.type}:`, error);
57500
- }
57501
- };
57502
- es.onopen = () => {
57503
- unhealthySince = 0;
57504
- dispatchResync();
57505
- };
57506
- source = es;
57507
- }
57508
- /** 看门狗:连不上超过阈值 ⇒ 主动重建(浏览器一直重试却连不上时的唯一出路)。 */
57509
- function startWatchdog() {
57510
- if (watchdog !== null) return;
57511
- watchdog = window.setInterval(() => {
57512
- if (source !== null && source.readyState === EventSource.OPEN) {
57513
- if (Date.now() - lastSeenAt > SILENT_AFTER_MS) {
57514
- source.close();
57515
- openSource();
57516
- unhealthySince = 0;
57517
- return;
57518
- }
57519
- unhealthySince = 0;
57520
- return;
57521
- }
57522
- if (unhealthySince === 0) {
57523
- unhealthySince = Date.now();
57524
- return;
57525
- }
57526
- if (Date.now() - unhealthySince < RECONNECT_AFTER_MS) return;
57527
- source?.close();
57528
- openSource();
57529
- unhealthySince = 0;
57530
- }, WATCHDOG_MS);
57531
- }
57532
- /** 懒建单例连接(首个订阅者出现时才连;**建了就不主动关**——「只要页面在,就有重连机制」)。 */
57533
- function ensureSource() {
57534
- if (typeof EventSource === "undefined") {
57535
- if (!warnedNoEventSource) {
57536
- warnedNoEventSource = true;
57537
- console.warn("[tdt] 本环境没有 EventSource:事件推送不可用,页面只在打开时取一次数据。");
57538
- }
57539
- return;
57540
- }
57541
- startWatchdog();
57542
- if (source !== null) return;
57543
- unhealthySince = 0;
57544
- openSource();
57545
- }
57546
- /** 订阅一组事件类型;`handler` 每次渲染都换也不会反复重建订阅(内部走 ref)。 */
57547
- function useEvents(types, handler) {
57548
- const ref = (0, react$1.useRef)(handler);
57549
- ref.current = handler;
57550
- const key = types.join(",");
57551
- (0, react$1.useEffect)(() => {
57552
- ensureSource();
57553
- const stable = (event) => ref.current(event);
57554
- const sets = [];
57555
- for (const type of types) {
57556
- let set = byType.get(type);
57557
- if (set === void 0) {
57558
- set = /* @__PURE__ */ new Set();
57559
- byType.set(type, set);
57560
- }
57561
- set.add(stable);
57562
- sets.push(set);
57563
- }
57564
- return () => {
57565
- for (const set of sets) set.delete(stable);
57566
- };
57567
- }, [key]);
57568
- }
57569
- /** 订阅「(重)连成功」——各页据此补读一次当前值(断线期间可能漏过事件)。 */
57570
- function useResync(handler) {
57571
- const ref = (0, react$1.useRef)(handler);
57572
- ref.current = handler;
57573
- (0, react$1.useEffect)(() => {
57574
- ensureSource();
57575
- const stable = () => ref.current();
57576
- resyncHandlers.add(stable);
57577
- return () => {
57578
- resyncHandlers.delete(stable);
57579
- };
57580
- }, []);
57581
- }
57582
- // src/client/format.ts
57583
- /** 两位补零:`9` → `09`(数字 / 纯数字字符串都收)。 */
57584
- function pad2(value) {
57585
- return String(value).padStart(2, "0");
57586
- }
57587
- /**
57588
- * 路径取末段(产出物行 / 文件名显示用)。**单源**:`/` 与 `\` 都认(M5)。
57589
- * ⚠️ **必须先剔尾斜杠**(2026-10-06 审计 🔴):本仓「目录」用尾斜杠表达(产出示例 `["a.md","b/"]`,
57590
- * 见 `query.ts` / `task-file-context.tsx` 的 displayName);不剔的话 `baseNameOf('b/')` 返回**空串**
57591
- * ⇒ 目录产出物在卡片/记录页渲染成**空标签、空 Tooltip**。
57592
- */
57593
- function baseNameOf(path) {
57594
- const trimmed = path.replace(/[\\/]+$/, "");
57595
- if (trimmed === "") return path;
57596
- const cut = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
57597
- return cut < 0 ? trimmed : trimmed.slice(cut + 1);
57598
- }
57416
+ // src/client/ui/style.ts
57599
57417
  /**
57600
- * ISO → `YYYY-MM-DD HH:mm`(`seconds: true` 时补 `:ss`)。
57601
- * 解析失败 ⇒ `fallback`(缺省返回**原串**,不编造时间;要占位符就显式传,如 `fallback: '—'`)。
57418
+ * 样式注入器(薄壳):实现全部交给 `dsh-plugin-core` 的 style kit。
57419
+ *
57420
+ * 保留本文件只为两件事:
57421
+ * ① 固定本插件的 `<style>` id(多插件同页互不覆盖);
57422
+ * ② 给历史调用点(`ensureXxxStyle()` / `applyStyle`)一个过渡入口。
57423
+ * 注入机制、域排序、token 表都不再在本仓实现 —— 真源是
57424
+ * `dsh-plugin-core/design`(`createStyleKit` + `tokens.css`)。
57425
+ *
57426
+ * 规矩:全站仍然只有**一处**注入器(kit 的标签),业务文件不许自己
57427
+ * `document.createElement('style')`,也不许再定义 `--dshpc-*`。
57602
57428
  */
57603
- function formatDateTime(iso, opts) {
57604
- const ms = Date.parse(iso);
57605
- if (Number.isNaN(ms)) return opts?.fallback ?? iso;
57606
- const d = new Date(ms);
57607
- const base = `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
57608
- return opts?.seconds === true ? `${base}:${pad2(d.getSeconds())}` : base;
57429
+ /** 本插件的样式标签 id(改名 = 大范围回归,不要动)。 */
57430
+ const UI_STYLE_ID = "dsh-task-dispatch-table-ui";
57431
+ /** token 域固定名(kit 内 `tokens` 恒排最前)。 */
57432
+ const TOKENS_DOMAIN = "tokens";
57433
+ setStyleKit(createStyleKit({ id: UI_STYLE_ID }));
57434
+ const active = getStyleKit();
57435
+ /** 登记一个域的样式(同名重复登记 = 覆盖)。 */
57436
+ function registerStyle(domain, css) {
57437
+ active.registerStyle(domain, css);
57609
57438
  }
57610
- /**
57611
- * 四位年日期(用户 2026-10-03:卡片标题后的创建时间标签 `[2026-10-03 创建]`):`YYYY-MM-DD`。
57612
- * 与 `formatShortStamp` 的差别就是**带年份**——创建时间要能跨年看,缺年份会认错。
57613
- */
57614
- function formatYmd(iso) {
57615
- const ms = Date.parse(iso);
57616
- if (Number.isNaN(ms)) return "—";
57617
- const d = new Date(ms);
57618
- return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
57439
+ /** 确保标签已挂载且内容最新(幂等)。 */
57440
+ function ensureUiStyles() {
57441
+ active.ensureStyles();
57619
57442
  }
57620
- /** 计划执行列(用户 2026-10-02 改**四位年**:形如 `2026-09-30 15:10`):`YYYY-MM-DD HH:mm`。 */
57621
- function formatPlanStamp(iso) {
57622
- const ms = Date.parse(iso);
57623
- if (Number.isNaN(ms)) return "-";
57624
- const d = new Date(ms);
57625
- return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
57443
+ /** 登记 + 立刻确保挂载。 */
57444
+ function applyStyle(domain, css) {
57445
+ active.applyStyle(domain, css);
57626
57446
  }
57627
- /** 实际开始列:只到 `HH:mm:ss`;未派发(null)或解析失败 ⇒ `-`。 */
57628
- function formatClock(iso) {
57629
- if (iso === null) return "-";
57630
- const ms = Date.parse(iso);
57631
- if (Number.isNaN(ms)) return "-";
57632
- const d = new Date(ms);
57633
- return `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
57447
+ /** 登记核心 token 层并确保样式已注入(幂等)。 */
57448
+ function ensureUiBase() {
57449
+ registerStyle(TOKENS_DOMAIN, active.tokensCss);
57450
+ ensureUiStyles();
57634
57451
  }
57452
+ // src/client/ui/controls-css.ts
57635
57453
  /**
57636
- * 执行时长列(用户 2026-10-02 第四轮):有小时 ⇒ `H:MM:SS`(如 `1:15:30`);
57637
- * 不足 1 小时 ⇒ `MM:SS`(**分、秒一律两位补零**,如 `05:30` / `00:30`)。NaN / 负 ⇒ `-`。
57454
+ * L2 组件皮肤:公用控件规则(一类控件**唯一实现**)
57455
+ *
57456
+ * 规矩(docs/design/ui-style-guide.md §三):
57457
+ * - **只消费 `var(--dshpc-*)`**,一个颜色 / 尺寸 / 圆角 / 字号字面量都不许出现;
57458
+ * - 结构、尺寸、交互规则只此一份;「外观差异」只允许靠**覆盖一两个变量**表达。
57459
+ *
57460
+ * 2026-11-06:分段 / 按钮 / 输入 / 数字步进 / 多行文本 / 跑马灯 / 下拉 / 日期时间 / 小问号
57461
+ * 已全部改接核心 `dsh-plugin-core/ui`,对应本地皮肤整段删除。
57462
+ * 本文件只剩**核心没有**的业务件:产出物 chip、开关选色,「过程文件」块。
57638
57463
  */
57639
- function formatDurationHms(ms) {
57640
- if (!Number.isFinite(ms) || ms < 0) return "-";
57641
- const totalSec = Math.round(ms / 1e3);
57642
- const h = Math.floor(totalSec / 3600);
57643
- const m = Math.floor(totalSec % 3600 / 60);
57644
- const s = totalSec % 60;
57645
- return h > 0 ? `${h}:${pad2(m)}:${pad2(s)}` : `${pad2(m)}:${pad2(s)}`;
57464
+ /** 产出物图标 chip 的皮肤规则。 */
57465
+ const BUTTON_CSS = `
57466
+ /* ── 产出物「图标 chip」:28×28 方角小底板 + 官方文件类型图标 ──────────────
57467
+ 用途:产出物列表里**只给图标**的小按钮(卡片「执行记录」面板、执行记录总查询页、展开区都挂它)。
57468
+ ⚠️ 底板**必须走 class**——inline background 会盖掉 :hover(用户 2026-10-03 踩过)。
57469
+ hover 用「--dshpc-chip-bg-hover」:浅色更深、暗色更亮(两端都是「更明显」);不许拿
57470
+ 「--dshpc-plate-hover」顶——暗色下反而更淡,鼠标移上去底板就消失了。 */
57471
+ .dsh-tdt-chip{appearance:none;-webkit-appearance:none;box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;
57472
+ width:var(--dshpc-control-h-md);height:var(--dshpc-control-h-md);padding:0;border:none;border-radius:var(--dshpc-radius-sm);
57473
+ background:transparent;color:var(--dshpc-fg-2);font-family:inherit;line-height:0;cursor:pointer;
57474
+ transition:background-color var(--dshpc-dur) var(--dshpc-ease),color var(--dshpc-dur) var(--dshpc-ease);}
57475
+ .dsh-tdt-chip:hover:not(:disabled){background:var(--dshpc-chip-bg-hover);}
57476
+ .dsh-tdt-chip:focus-visible{outline:2px solid var(--dshpc-focus);outline-offset:1px;}
57477
+ .dsh-tdt-chip:disabled{cursor:default;background:transparent;color:var(--dshpc-fg-3);}
57478
+ .dsh-tdt-chip--label{width:auto;padding:0 6px;font-size:var(--dshpc-font-md);line-height:var(--dshpc-line-md);}
57479
+
57480
+ /* 文件行(图标 + 文件名)统一走核心 **Button ghost**(2026-11-06 用户拍板:
57481
+ 「一个图标后面跟着文字」——产出物 / 其他产出 / 过程文件 / 执行记录展开区 / 附件全站同一套皮肤,
57482
+ 圆角、hover、内边距全归核心按钮;本仓不再保留第二份文件行皮肤。 */
57483
+
57484
+ @media (prefers-reduced-motion: reduce){.dsh-tdt-chip{transition:none;}}
57485
+ `;
57486
+ /** 开关 / 拖拽分隔条的皮肤规则(核心没有的宿主件)。 */
57487
+ const FIELD_CSS = `
57488
+ /* 官方 Switch 包装统一:选中 = success 绿。 */
57489
+ .dsh-tdt-switch button[role='switch'][aria-checked='true']{background:var(--dshpc-success);}
57490
+
57491
+ /* 拖拽分隔条已随两条分栏一起归核心 PanelDock(2026-11-06),本仓不再自绘命中条。 */
57492
+ `;
57493
+ /** 下拉的皮肤规则(核心 `Picker` 暂缺 `error` 档,这里只补一条描红)。 */
57494
+ const SELECT_CSS = `
57495
+ /* 核心 Picker 的校验描红(className 挂在 .dshpc-picker 根上)。 */
57496
+ .dsh-tdt-picker--error .dshpc-picker__trigger{border-color:var(--dshpc-danger);box-shadow:0 0 0 1px var(--dshpc-danger);}
57497
+ /* 整行下拉:核心 Picker 根是 inline-block,要显式撑满父宽。 */
57498
+ .dsh-tdt-picker--block{display:block;width:100%;}
57499
+
57500
+ /* 省略号三件套(ui-style-guide §三「待抽象 #1」的收敛点):**新代码一律挂这个类**,不再各写一遍。 */
57501
+ .dsh-tdt-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
57502
+ `;
57503
+ /** 「过程文件」块的皮肤规则(2026-10-10:回执过程文件桶的次级展示件)。 */
57504
+ const PROCESS_FILES_CSS = `
57505
+ /* ── 「过程文件」块:会话弹窗交付卡下方 / 执行记录展开区 / 查看档产出清单三处共用 ──────────
57506
+ (结构在 client/process-files.tsx)。观感纪律 = **次级**:小字浅灰标题 + 数量、默认收起,
57507
+ 不许抢主文件(交付卡 / 产出物清单)的视线;展开后的文件行 = 核心 Button ghost(图标 + 文字,全站同一套)。 */
57508
+ .dsh-tdt-proc{display:flex;flex-direction:column;gap:4px;margin-top:10px;min-width:0;}
57509
+ .dsh-tdt-proc-list{display:flex;flex-wrap:wrap;gap:2px 4px;min-width:0;}
57510
+
57511
+ `;
57512
+ /** 细滚动条只认核心的 `.dshpc-scroll-thin`(3px,design/tokens 域全局注入、挂类即生效)。
57513
+ * 2026-11-08 用户点名删掉本仓自绘的 1px 版:本插件**不再定义任何 ::-webkit-scrollbar 规则**。
57514
+ * 该类只作用于挂它的那个容器(外加其 .cm-scroller 后代),所以每个 `overflow:auto` 的容器
57515
+ * 都要自己挂 `dshpc-scroll-thin`,见各组件。 */
57516
+ /** 控件皮肤域的固定名(注入顺序在 tokens 之后)。 */
57517
+ const CONTROLS_DOMAIN = "controls";
57518
+ /**
57519
+ * 确保控件皮肤已登记并注入(幂等;组件渲染时调用一次即可)。
57520
+ */
57521
+ function ensureControlsStyle() {
57522
+ applyStyle(CONTROLS_DOMAIN, BUTTON_CSS + FIELD_CSS + SELECT_CSS + PROCESS_FILES_CSS);
57646
57523
  }
57647
- /** token / 计数的大众格式(用户 2026-10-02:别写上千的数字):≥1K 用 K、≥1M 用 M(1234→1.2K、12345→12.3K、123456→123K)。 */
57648
- function formatTokenCount(n) {
57649
- if (!Number.isFinite(n)) return "—";
57650
- const trim = (v) => Math.abs(v) >= 100 ? String(Math.round(v)) : v.toFixed(1).replace(/\.0$/, "");
57651
- if (Math.abs(n) >= 1e6) return `${trim(n / 1e6)}M`;
57652
- if (Math.abs(n) >= 1e3) return `${trim(n / 1e3)}K`;
57653
- return String(n);
57524
+ // src/client/ui/Field.tsx
57525
+ /**
57526
+ * 下拉选择 —— 核心 `Picker` 的薄包装(2026-11-06)。
57527
+ *
57528
+ * 触发器 / 弹层 / 分组 / 搜索 / 空态 / 跑马灯 / 键盘与明暗全部归核心;
57529
+ * 本仓只补核心 `Picker` 尚未提供的一点:
57530
+ * - `error` 校验描红:外层 `className` + 一条皮肤规则(`.dsh-tdt-picker--error`)。
57531
+ * 悬停提示**不在这里**(2026-11-06:全站原生 title 清退;需要的调用方在真 DOM 外套官方 `Tooltip`)。
57532
+ * 空列表 ⇒ 禁用并显示业务空态文案(不塞假值)。
57533
+ */
57534
+ /**
57535
+ * 下拉选择:核心 `Picker`(portal 到 body,选中项自动带对勾,内容超长走跑马灯)。
57536
+ * 空列表 ⇒ 禁用并显示空态文案(接不到真数据时不塞假值)。
57537
+ */
57538
+ function SelectField(props) {
57539
+ ensureControlsStyle();
57540
+ const size = props.size ?? "lg";
57541
+ const groups = props.groups;
57542
+ const empty = groups === void 0 ? (props.options ?? []).length === 0 : groups.every((group) => group.options.length === 0);
57543
+ const usable = !empty && props.disabled !== true;
57544
+ const width = props.width ?? (props.block === true ? "100%" : void 0);
57545
+ const cls = [props.block === true ? "dsh-tdt-picker--block" : "", props.error === true ? "dsh-tdt-picker--error" : ""].filter(Boolean).join(" ");
57546
+ return (0, react$1.createElement)(Picker, {
57547
+ ...groups === void 0 ? { options: props.options ?? [] } : { groups },
57548
+ value: props.value,
57549
+ onChange: (next) => {
57550
+ if (typeof next === "string") props.onChange(next);
57551
+ },
57552
+ placeholder: empty ? props.emptyLabel : props.placeholder,
57553
+ disabled: !usable,
57554
+ size,
57555
+ icon: props.icon,
57556
+ className: cls === "" ? void 0 : cls,
57557
+ width,
57558
+ ...props.maxWidth === void 0 ? {} : { maxWidth: props.maxWidth },
57559
+ ariaLabel: props.ariaLabel,
57560
+ ...props.searchable === true ? { searchable: true } : {}
57561
+ });
57654
57562
  }
57563
+ // src/client/ui/TaskPicker.tsx
57655
57564
  /**
57656
- * token 明细:`输入 / 输出 / 缓存`(缺失档给 `—`)—— 挂在总量标签的 `title` 上,悬停看明细。
57565
+ * 任务选择器 —— **核心 `Picker` 的薄包装**(2026-11-06 起)。
57657
57566
  *
57658
- * ⚠️ 2026-10-04 从 `task-list.tsx` 上提到这里(改名 `formatTokenDetail`):卡片「执行记录」面板与
57659
- * **执行记录总查询页**共用一份,避免同一串格式两处各写。
57567
+ * 本文件只做四件业务事,其余(浮层、搜索、分页、空态、键盘、明暗)全交给 `dsh-plugin-core/ui`:
57568
+ * ① 按 `scope`(工作区)与 `excludeIds` 过滤候选;
57569
+ * ② 「全部任务」作为**第一个普通选项**(`value: ''`)传进去(用户 2026-11-05 拍板,核心零改动);
57570
+ * ③ 停用任务照常可选,只在 `label` 尾部拼 `disabledTag`(不加字段);
57571
+ * ④ 已选项掉出作用域 ⇒ 仍留在列表里 + 触发器前置 `!` 告警(官方 `Tooltip` 给说明),不静默清空。
57572
+ *
57573
+ * 搜索:核心按 `label` + `keywords` 子串匹配;这里把任务 UUID 放进 `keywords`(只搜不显示)。
57660
57574
  */
57661
- function formatTokenDetail(row) {
57662
- const part = (v) => v === null ? "—" : formatTokenCount(v);
57663
- return `${part(row.token_in)} / ${part(row.token_out)} / ${part(row.token_in_cache)}`;
57575
+ /** 带搜索的任务选择器。 */
57576
+ function TaskPicker(props) {
57577
+ const scope = props.scope ?? "";
57578
+ const excludes = props.excludeIds ?? [];
57579
+ const current = props.options.find((o) => o.id === props.value);
57580
+ const candidates = (0, react$1.useMemo)(() => props.options.filter((o) => (scope === "" || o.workspace === scope) && !excludes.includes(o.id)), [
57581
+ props.options,
57582
+ scope,
57583
+ excludes
57584
+ ]);
57585
+ const labelOf = (o) => o.enabled ? o.label : `${o.label}${props.disabledTag ?? ""}`;
57586
+ const list = [];
57587
+ if (props.allOption !== void 0) list.push({
57588
+ value: props.allOption.value,
57589
+ label: props.allOption.label
57590
+ });
57591
+ for (const o of candidates) list.push({
57592
+ value: o.id,
57593
+ label: labelOf(o),
57594
+ keywords: o.id
57595
+ });
57596
+ const outOfScope = current !== void 0 && props.value !== "" && scope !== "" && current.workspace !== scope;
57597
+ if (outOfScope && current !== void 0) list.splice(props.allOption === void 0 ? 0 : 1, 0, {
57598
+ value: current.id,
57599
+ label: labelOf(current),
57600
+ keywords: current.id
57601
+ });
57602
+ return (0, react$1.createElement)(Picker, {
57603
+ options: list,
57604
+ value: props.value,
57605
+ onChange: (next) => {
57606
+ const id = Array.isArray(next) ? next[0] ?? "" : next;
57607
+ props.onChange(id);
57608
+ },
57609
+ searchable: true,
57610
+ limit: props.recentLimit ?? 10,
57611
+ placeholder: props.placeholder,
57612
+ size: props.size,
57613
+ width: props.width,
57614
+ disabled: props.disabled,
57615
+ ariaLabel: props.ariaLabel,
57616
+ icon: outOfScope ? props.outOfScopeHint === void 0 ? (0, react$1.createElement)("span", { style: {
57617
+ color: "var(--dshpc-warning)",
57618
+ fontWeight: 600
57619
+ } }, "!") : (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
57620
+ label: props.outOfScopeHint,
57621
+ side: "bottom"
57622
+ }, (0, react$1.createElement)("span", { style: {
57623
+ color: "var(--dshpc-warning)",
57624
+ fontWeight: 600
57625
+ } }, "!")) : void 0,
57626
+ translate: (key, vars) => {
57627
+ if (key === "pickerSearch") return props.searchPlaceholder;
57628
+ if (key === "pickerEmpty" || key === "pickerNoMatch") return props.emptyLabel;
57629
+ if (key === "pickerMore" && props.moreLabel !== void 0) return props.moreLabel;
57630
+ return t$1(key, vars);
57631
+ }
57632
+ });
57664
57633
  }
57665
- // src/client/locales.ts
57666
- /** 把宿主给的无参 t 包成带占位符替换的 t(官方模板一律 `{name}`)。 */
57667
- function interpolateTranslate(base) {
57668
- return (key, params) => {
57669
- const raw = base(key);
57670
- if (params === void 0) return raw;
57671
- return raw.replace(/\{(\w+)\}/g, (match, name) => name in params ? String(params[name]) : match);
57634
+ // src/client/ui/time-range.ts
57635
+ /** 全部预设档(缺省显示顺序)。 */
57636
+ const ALL_TIME_PRESETS = [
57637
+ "today",
57638
+ "yesterday",
57639
+ "thisWeek",
57640
+ "lastWeek",
57641
+ "thisMonth",
57642
+ "lastMonth"
57643
+ ];
57644
+ /** 值串 → 年月日时分;不合法返回 null。 */
57645
+ function parseValue(value) {
57646
+ const match = /^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}))?$/.exec(value);
57647
+ if (match === null) return null;
57648
+ return {
57649
+ y: Number(match[1]),
57650
+ m: Number(match[2]),
57651
+ d: Number(match[3]),
57652
+ hh: match[4] === void 0 ? 0 : Number(match[4]),
57653
+ mm: match[5] === void 0 ? 0 : Number(match[5])
57672
57654
  };
57673
57655
  }
57674
- /** 中文文案。 */
57675
- const zh = {
57676
- title: "任务调度表(dsh-task-dispatch-table)",
57677
- description: "用任务表驱动定时派发:配置任务、查看每次执行的记录。点击打开面板。",
57678
- unavailable: "设置命名空间当前不可用(插件未运行或宿主未提供),暂时无法配置。",
57679
- tasksInlineLabel: "任务表(tasksInline,JSON 数组)",
57680
- tasksInlineHint: "每项一个任务定义;非空时优先于任务目录 tasksDir。清空并保存 = 回到默认(空,改用 tasksDir)。",
57681
- invalidJson: "任务表不是合法 JSON,已阻止保存;请修正后重试。",
57682
- save: "保存",
57683
- saving: "保存中…",
57684
- saveFailed: "保存未生效:草稿已保留,请修改后重试(宿主可能拒绝了部分值或已有并发修改)。",
57685
- discard: "放弃更改",
57686
- paramsTitle: "运行参数(只读)",
57687
- paramDefault: "(默认)",
57688
- paramStatePath: "状态库路径 statePath",
57689
- paramTickMs: "调度周期 tickMs(毫秒)",
57690
- paramDispatchGraceMs: "派发宽限 dispatchGraceMs(毫秒)",
57691
- paramLeaseMs: "运行租约 leaseMs(毫秒)",
57692
- paramUnknownGraceMs: "观察宽限 unknownGraceMs(毫秒)",
57693
- paramTasksDir: "任务目录 tasksDir",
57694
- paramDefaultProvider: "默认模型 provider defaultProvider",
57695
- paramDefaultModel: "默认模型 model defaultModel",
57696
- debugClose: "关闭",
57697
- debugRefreshedAt: "手动刷新于",
57698
- debugEmpty: "暂无快照:宿主完成一次调度(或派发 / 会话事件)后自动写入。若持续为空,说明宿主侧运行的还是旧版插件,请重装后重试。",
57699
- debugRaw: "快照解析失败,原文如下:",
57700
- debugWarns: "最近告警 / 错误(≤20 条)",
57701
- debugNoWarns: "(无)",
57702
- panelTitle: "定时任务调度器",
57703
- backToConversation: "返回会话",
57704
- tabConfig: "任务配置",
57705
- tabRecords: "执行记录",
57706
- tabCalendar: "任务日程",
57707
- tabDebug: "调试",
57708
- calLoadFail: "日程读取失败",
57709
- calRetry: "重试",
57710
- calEmpty: "这个月没有执行记录,也没有计划任务",
57711
- calDayEmpty: "这一天没有执行记录,也没有计划任务",
57712
- calTruncated: "该月记录超过上限(3000 条),只显示了其中一部分;请用上方过滤缩小范围。",
57713
- calPlanTag: "计划:",
57714
- calNotExecuted: "未执行",
57715
- calPlanHint: "此任务尚未执行,时间为预计执行时间",
57716
- calToday: "今天",
57717
- calCellMore: "还有 {n} 条,点开看全部",
57718
- debugDbHint: "状态库(state.db)三张表的原始记录,只读展示:task_instances = 每次执行一行、task_events = 每个事件一行、meta = 插件元数据(含内嵌任务表)。每表最多显示最新 500 行,点右上角刷新重取。",
57719
- debugDbLoading: "状态库读取中…",
57720
- debugDbFail: "状态库读取失败(未就绪或请求被拒),稍后点刷新重试。",
57721
- debugDbEmpty: "(空表:还没有任何记录)",
57722
- debugDbTruncated: "行数超出上限,仅显示最新一部分",
57723
- tasksParsedTitle: "已解析的任务(id 由系统生成,改名字不影响历史)",
57724
- tasksParsedEmpty: "(无任务:内嵌任务表为空且任务目录无合法定义)",
57725
- colTitle: "名称",
57726
- colCode: "编号",
57727
- colId: "任务ID",
57728
- colSchedule: "周期",
57729
- colNext: "下次执行",
57730
- colStatus: "状态",
57731
- colSession: "会话",
57732
- filterAll: "全部",
57733
- listExpandHint: "展开任务详情",
57734
- listDeferred: "延期",
57735
- listDeferredTitle: "已过计划时刻但还没开始执行。常见原因:前置任务未完成 / 附加文件找不到 / 上一轮还在运行。",
57736
- viewSession: "查看会话",
57737
- sessionViewerTitle: "会话记录(只读)",
57738
- sessionUnknownKind: "未支持的节点类型:",
57739
- sessionLoading: "正在加载会话记录…",
57740
- sessionEmpty: "该会话暂无可显示的记录(可能刚建窗或已被清理)。",
57741
- sessionLoadFailed: "会话记录加载失败(会话可能已不可读)。",
57742
- sessionLoadOlder: "加载更早记录",
57743
- sessionProcess: "过程",
57744
- triggerRequest: "收到执行请求",
57745
- triggerGoal: "继续执行目标",
57746
- triggerAgent: "收到任务消息",
57747
- triggerTeam: "收到团队消息",
57748
- triggerSubagent: "子任务状态更新",
57749
- triggerGithub: "收到 GitHub 事件",
57750
- triggerWebhook: "收到外部事件",
57751
- triggerSchedule: "定时任务",
57752
- triggerJob: "后台任务状态更新",
57753
- triggerPlugin: "插件状态更新",
57754
- triggerExplanation: "这条通知触发了本轮回复。",
57755
- turnProcessTook: "用时 {duration}",
57756
- turnProcessDeepDiving: "深度求索中,用时{duration}",
57757
- turnProcessWorked: "已完成工作",
57758
- turnProcessFailed: "处理失败",
57759
- turnStopped: "已停止",
57760
- chatDeepDiving: "深度求索中",
57761
- thinkLabel: "思考",
57762
- durationSeconds: "{seconds}秒",
57763
- durationMinutes: "{minutes}分{seconds}秒",
57764
- durationHours: "{hours}小时{minutes}分{seconds}秒",
57765
- retryActive: "正在重试模型请求",
57766
- retryCancelled: "模型请求重试已取消",
57767
- retryStarted: "已重试模型请求",
57768
- retryScheduled: "等待重试模型请求",
57769
- retryStatus: "{label}({retry}/{maximum}) · {seconds}s",
57770
- retryDelay: "重试延迟:",
57771
- retryFailure: "失败原因:",
57772
- durationMilliseconds: "{milliseconds}毫秒",
57773
- turnErrorTitle: "本轮运行失败",
57774
- accountStopped: "任务已停止",
57775
- maxTokensTitle: "已达到输出 token 上限",
57776
- maxTokensHint: "回答被截断,已有输出保留在对话中。发送“继续”可让模型接着输出。",
57777
- failureAuth: "API 密钥无效",
57778
- failureQuota: "当前请求的额度已用尽",
57779
- failureAccountSignedOut: "任务已因退出 DeepSeek 登录而停止。",
57780
- failureAccountSignInRequired: "请先登录 DeepSeek,并确认请求地址支持账号认证。",
57781
- clockDate: "{m}月{d}日",
57782
- clockDateYear: "{y}年{m}月{d}日",
57783
- copyLabel: "复制",
57784
- copiedLabel: "已复制",
57785
- branchLabel: "在新对话中分支",
57786
- branchUnavailableLabel: "只读会话记录不可分支",
57787
- "presented.unavailable": "此主机没有可用的桌面,无法使用外部程序打开文件或文件夹;文件仍可在侧边栏预览",
57788
- turnUsageTitle: "本轮用量",
57789
- turnUsageModel: "提供方 / 模型",
57790
- turnUsageCacheHit: "缓存命中",
57791
- turnUsageInput: "未缓存输入",
57792
- turnUsageCacheRead: "缓存读取",
57793
- turnUsageCacheWrite: "缓存写入",
57794
- turnUsageOutput: "输出",
57795
- turnUsageReasoning: "(其中推理 {tokens})",
57796
- turnUsageConsumed: "用量 {total}",
57797
- turnUsageCount: "{count} tok",
57798
- numberThousand: "{value}K",
57799
- numberMillion: "{value}M",
57800
- numberGroupSeparator: ",",
57801
- stepProcessDoneThinking: "已完成分析",
57802
- stepProcessDoneRead: "已读取文件",
57803
- stepProcessDoneReadImage: "已读取图片",
57804
- stepProcessDoneWrite: "已写入文件",
57805
- stepProcessDoneSearch: "已搜索代码",
57806
- stepProcessDoneEdit: "修改了文件",
57807
- stepProcessDoneCommands: "执行了命令",
57808
- stepProcessDoneCode: "运行了代码",
57809
- stepProcessDoneWebSearch: "已搜索网页",
57810
- stepProcessDoneWebFetch: "已访问网页",
57811
- stepProcessDoneSubagents: "已协调子智能体",
57812
- stepProcessDonePlan: "更新了计划",
57813
- stepProcessDoneQuestions: "向用户提出了问题",
57814
- stepProcessDoneTools: "已调用工具",
57815
- stepProcessJoinTwo: "{first}并{second}",
57816
- stepProcessComma: ",",
57817
- stepProcessSharedPrefix: "已",
57818
- stepProcessMore: "{title}等",
57819
- toolTitleRead: "读取",
57820
- toolTitleReadImage: "读取图片",
57821
- toolTitleGrep: "搜索文件内容",
57822
- toolTitleGlob: "查找文件",
57823
- toolTitleBash: "运行命令",
57824
- toolTitleWrite: "写入",
57825
- toolTitleEdit: "编辑",
57826
- toolTitleCode: "代码",
57827
- toolTitleWebSearch: "网页搜索",
57828
- toolTitleWebFetch: "网页获取",
57829
- toolTitleGeneric: "工具调用",
57830
- toolTitleSearch: "搜索",
57831
- toolInputLabel: "输入",
57832
- toolOutputLabel: "输出",
57833
- rowPreparing: "正在准备调用",
57834
- rowRunning: "运行中",
57835
- rowFailed: "失败",
57836
- rowStopped: "已停止",
57837
- collapseLabel: "收起",
57838
- readWindow: "显示 {shown} / {total} 行",
57839
- readCollapseAria: "收起内容",
57840
- readExpandAria: "展开其余 {count} 行",
57841
- readExpandRest: "… 其余 {count} 行",
57842
- terminalSignal: "信号 {signal}",
57843
- terminalExitCode: "退出码 {code}",
57844
- terminalNoExitCode: "未正常退出",
57845
- terminalRunning: "运行中",
57846
- terminalFailed: "失败",
57847
- terminalDone: "已完成",
57848
- terminalNoOutput: "无输出",
57849
- terminalCollapseAria: "收起输出",
57850
- terminalExpandAria: "展开其余 {n} 行输出",
57851
- terminalExpandRest: "… 其余 {n} 行",
57852
- terminalSendInput: "(发送输入)",
57853
- terminalSession: "终端 {sessionId}",
57854
- codeBlockLabel: "代码块",
57855
- diffWrapLabel: "自动换行",
57856
- diffUnwrapLabel: "取消换行",
57857
- diffCollapseAria: "收起差异",
57858
- diffExpandAria: "展开其余 {count} 行差异",
57859
- diffExpandRest: "… 其余 {count} 行",
57860
- continueBranch: "继续对话",
57861
- forkConfirmTitle: "开分支继续对话",
57862
- forkConfirmText: "是否需要基于此会话开一个新分支继续对话?原会话保持只读留档,新分支复制本会话内容并可继续对话。",
57863
- forkConfirmAccept: "开分支并跳转",
57864
- forkCancel: "取消",
57865
- forkWorking: "正在开分支…",
57866
- forkFailed: "开分支失败:{error}",
57867
- previewClose: "关闭预览",
57868
- previewLoadMore: "加载更多",
57869
- previewFileLabel: "文件",
57870
- previewCopyPath: "复制路径",
57871
- previewRefresh: "刷新",
57872
- previewNotFound: "文件不存在(可能已被移动或删除)。",
57873
- previewTooLarge: "文件过大,超出预览上限({limit})。",
57874
- previewDirectory: "这是一个目录,暂不支持目录浏览。",
57875
- previewNotRegular: "该路径不是常规文件(符号链接等),暂不支持预览。",
57876
- previewOutsideWorkspace: "该路径在会话工作区之外(常见于指向外部的符号链接),官方接口不允许浏览。",
57877
- previewError: "读取失败:{code}",
57878
- previewUnknownBinary: "二进制文件,暂不支持预览。可复制路径后在工作区中打开。",
57879
- previewOfficeUnavailable: "读取失败:Office 预览不可用。请在运行 DeepSeek Harness 的主机上启用文档预览服务。",
57880
- previewOfficeFailed: "Office 文件转换失败,无法预览。可复制路径后在工作区中打开。",
57881
- previewBadPayload: "读取结果不符合官方契约(已记控制台日志),未渲染内容。",
57882
- previewRenderFailed: "预览渲染失败(错误已记录,面板其余部分不受影响)。",
57883
- previewResize: "拖动调整预览栏宽度",
57884
- previewSource: "源码",
57885
- previewRender: "预览",
57886
- previewMdSwitchAria: "Markdown 视图切换",
57887
- previewHtmlSwitchAria: "HTML 视图切换",
57888
- previewHtmlFrame: "HTML 文档预览",
57889
- previewHtmlFailed: "无法预览这份 HTML 文档",
57890
- previewTruncated: "文件过大,仅显示前 {size}",
57891
- previewTruncatedSize: "256KB",
57892
- explorerEmpty: "空目录",
57893
- explorerTruncated: "目录内容过多,仅显示部分条目。",
57894
- explorerCrumbsAria: "目录路径导航",
57895
- explorerLevels: "选择目录层级",
57896
- explorerRootName: "(工作区根目录)",
57897
- explorerExpand: "展开目录",
57898
- explorerCollapse: "收起目录",
57899
- explorerEnter: "进入",
57900
- colOutputs: "产出",
57901
- procFilesTitle: "其他产出 · {count} 项",
57902
- deliverRowTitle: "交付文件",
57903
- deliverRowPreparing: "准备交付",
57904
- deliverRowRunning: "正在交付",
57905
- deliverRowOk: "已交付",
57906
- deliverRowError: "交付失败",
57907
- deliverRowStopped: "已中断",
57908
- deliverFileLabel: "文件",
57909
- deliverPreviewHint: "预览",
57910
- deliverPreviewCard: "预览 {name}",
57911
- deliverAll: "全部 {count} 个文件",
57912
- deliverCollapse: "收起",
57913
- deliverExpandAria: "展开全部 {count} 个交付文件",
57914
- deliverCollapseAria: "收起交付文件列表",
57915
- tfcReceived: "接收 · {files} 个文件 · 来自 {tasks} 个前置任务",
57916
- tfcAttached: "任务附件 · {count} 个文件",
57917
- tfcNoOutputs: "未声明产出",
57918
- tfcRelOnly: "相对路径",
57919
- tfcCrossWorkspace: "跨工作区",
57920
- tfcNoPath: "路径未解析,不可打开",
57921
- tfcMore: "全部 {count} 个文件",
57922
- tfcMoreTasks: "全部 {count} 个前置任务",
57923
- tfcCollapse: "收起",
57924
- tfcFromUpload: "上传",
57925
- tfcFromWorkspace: "链接",
57926
- tfcRestFiles: "还有 {count} 个",
57927
- editorNew: "新建任务",
57928
- editorEdit: "编辑任务",
57929
- editorClose: "关闭",
57930
- editorCancel: "取消",
57931
- editorSave: "保存",
57932
- editorSavePending: "保存逻辑待接(P2):本轮只做界面与交互",
57933
- editorEnabled: "启用",
57934
- editorEnabledOn: "任务已启用,到点会派发",
57935
- editorEnabledOff: "任务已停用,定义保留但不派发",
57936
- editorTitle: "任务名称",
57937
- editorTitlePh: "给任务起个名字",
57938
- editorCode: "任务编号",
57939
- editorCodePh: "可选,便于查询",
57940
- editorPrompt: "提示词",
57941
- editorPromptPh: "写给 agent 的指令……",
57942
- editorVersions: "版本历史",
57943
- editorWorkspace: "工作区",
57944
- editorWorkspacePh: "选择工作区",
57945
- editorModel: "模型",
57946
- editorModelPh: "默认模型",
57947
- editorFollowHost: "默认模型",
57948
- editorPermission: "权限",
57949
- editorPermissionHint: "本次执行授予 agent 的操作范围。默认 = 会话默认(沿用宿主新建会话时的权限设置);宿主暂未提供按任务下发权限的接口,所选档位以派发消息中的约束指令执行。",
57950
- editorPermDefault: "权限:默认",
57951
- editorPermReadOnly: "仅可查看",
57952
- editorPermWorkspace: "工作区内修改",
57953
- editorPermFull: "完全权限",
57954
- editorNoOptions: "暂无可选",
57955
- editorSchedule: "执行频率",
57956
- editorScheduleHint: "设置任务的执行时机与重复方式:可设为单次(到点执行一次,不再重复)、周期(每天 / 每周 / 每月等定期重复)或间隔(每隔固定时间执行)。",
57957
- editorSchedulePeriodic: "周期",
57958
- editorScheduleInterval: "间隔",
57959
- editorFreq: "频率",
57960
- editorFreqOnce: "单次",
57961
- editorFreqDaily: "每天",
57962
- editorFreqWeekly: "每周",
57963
- editorFreqMonthly: "每月",
57964
- editorFreqQuarterly: "每季度",
57965
- editorFreqYearly: "每年",
57966
- editorOnceHint: "到点执行一次,之后不再重复",
57967
- editorMonthEvery: "全部月份",
57968
- editorMonthOdd: "单数月",
57969
- editorMonthEven: "双数月",
57970
- editorQuarterMonthOption: "第 {m} 个月",
57971
- editorDate: "日期",
57972
- editorDatePh: "选择日期",
57973
- editorTime: "时间",
57974
- editorTimePh: "选择时间",
57975
- editorMonth: "月",
57976
- editorMonthOption: "{m} 月",
57977
- editorDayOfMonth: "第几日",
57978
- editorDayOption: "{d} 日",
57979
- editorIntervalEvery: "每隔",
57980
- editorIntervalStep: "间隔步长",
57981
- editorIntervalUnit: "间隔单位",
57982
- editorIntervalSuffix: "执行",
57983
- editorEveryNWeeks: "每 {n} 周",
57984
- editorAttachments: "附加文件",
57985
- editorAttachmentLink: "链接",
57986
- editorAttachmentUpload: "上传",
57987
- editorAttachmentRemove: "移除",
57988
- editorAttachmentView: "查看",
57989
- editorAttachmentAdd: "添加文件",
57990
- editorPickWorkspaceFile: "选择工作区文件",
57991
- editorPickWorkspaceFileShort: "工作区文件",
57992
- editorDropZoneHint: "点击或拖拽文件到此处上传,支持多选或单个文件",
57993
- editorDropZoneFormats: "支持常见文本 / 代码、图片、文档格式,单个文件不超过 20MB",
57994
- editorUploading: "上传中…",
57995
- editorUploadErrType: "格式不支持:仅支持常见文本 / 代码、图片、文档文件",
57996
- editorUploadErrSize: "文件超过大小限制(单个最大 20MB)",
57997
- editorUploadErrEmpty: "文件内容为空",
57998
- editorUploadErrGeneric: "上传失败,请重试",
57999
- editorPickerNoSession: "该工作区下还没有会话,无法读取文件。",
58000
- editorAttachmentsHint: "附加文件会随任务一起派发给执行的 Agent:任务执行过程中,Agent 可读取或操作这些文件的内容。也可从工作区选择整个目录(连带其下所有文件):Agent 拿到的是这个目录的路径,自己在里面找文件,提示词里写「附件里的 目录名/子文件路径」即可指定其中某一个。",
58001
- editorPickerPick: "选择此文件",
58002
- editorPickerPickDir: "选择此目录(连带其下所有文件)",
58003
- editorPickerSelect: "选择",
58004
- editorOpenEditor: "全屏编辑",
58005
- editorPromptEditorTitle: "提示词编辑器(.md)",
58006
- editorNoVersions: "暂无版本,保存后可在此回滚",
58007
- editorHistoryVersions: "历史版本",
58008
- editorNewTaskNoVersions: "新建任务暂未保存,无历史版本可查询",
58009
- editorDeleteVersion: "删除",
58010
- editorUseVersion: "使用此版本",
58011
- editorConfirmDeleteTitle: "删除版本",
58012
- editorConfirmDeleteDesc: "你确定要删除此版本的记录吗?删除后不可撤销,请谨慎操作。",
58013
- editorDeleteTask: "删除任务",
58014
- editorDeleteTaskTitle: "删除任务",
58015
- editorDeleteTaskDesc: "确定所有的移除都是找不回来的,不可逆的。",
58016
- editorReset: "重置",
58017
- editorResetDone: "已恢复为打开时的内容",
58018
- editorResetTitle: "重置当前编辑?",
58019
- editorResetDesc: "重置会放弃本任务所有未保存的修改,恢复到打开编辑时的内容。这一操作不可撤销。",
58020
- editorVersionToggle: "版本",
58021
- editorUseShort: "使用",
58022
- editorEnabledStateOn: "已启用",
58023
- editorEnabledStateOff: "已关闭",
58024
- editorToggleOn: "任务已启用",
58025
- editorToggleOff: "任务已关闭",
58026
- editorFullPermTitle: "这条任务将以完全权限运行",
58027
- editorFullPermDesc: "这条任务会以「完全权限」无人值守执行,意味着 AI 可以直接:",
58028
- editorFullPermB1: "写入 / 修改 / 删除工作区内的文件",
58029
- editorFullPermB2: "执行命令、发起网络请求等敏感操作",
58030
- editorFullPermCheck: "我已了解风险,并愿意为该任务的执行结果负责。",
58031
- editorTaskSaved: "任务已保存",
58032
- editorTaskDeleted: "任务已删除",
58033
- editorSchedHourlyOnce: "每小时执行一次",
58034
- editorSchedNoDaySuffix: ",但还没选生效日",
58035
- editorSchedWeeklyDayPrefix: "每周",
58036
- editorRestorePromptTitle: "只找回提示词",
58037
- editorRestorePromptDesc: "确定找回会用历史版本覆盖现有修改的所有数据(本次只覆盖提示词,其余设置保持现状)。",
58038
- editorTasksTitle: "任务列表",
58039
- editorTasksEmpty: "还没有任务:点右上角「+ 新建任务」创建第一条。",
58040
- editorDisabledTag: "已停用",
58041
- editorModeEdit: "编辑",
58042
- editorModePreview: "预览",
58043
- editorTaskStart: "开始时间",
58044
- editorTaskStartHint: "任务自该日起开始执行。可提前创建任务,至该日自动开跑;「每 N 周」亦自该日起算首周。",
58045
- editorWeekdayLabel: "星期",
58046
- editorWeekday1: "周一",
58047
- editorWeekday2: "周二",
58048
- editorWeekday3: "周三",
58049
- editorWeekday4: "周四",
58050
- editorWeekday5: "周五",
58051
- editorWeekday6: "周六",
58052
- editorWeekday7: "周日",
58053
- editorWeekdayShorts: "一|二|三|四|五|六|日",
58054
- editorWeekdayEmpty: "不选 = 每天",
58055
- calWeekdays: "星期一|星期二|星期三|星期四|星期五|星期六|星期日",
58056
- editorToday: "今天",
58057
- editorPrevMonth: "上个月",
58058
- editorNextMonth: "下个月",
58059
- editorPrevYear: "上一年",
58060
- editorNextYear: "下一年",
58061
- editorMonthTitle: "{y}年{m}月",
58062
- editorHour: "小时",
58063
- editorMinute: "分钟",
58064
- editorNow: "现在",
58065
- editorConfirm: "确定",
58066
- editorWindow: "允许延迟",
58067
- editorWindowHint: "任务到达计划开始执行时间后,若前置任务尚未完成或因其它原因需延后,最长允许在此时长内继续执行;超过该时长则跳过本次执行。",
58068
- editorSchedForecast: "预计执行",
58069
- editorSchedRun: "执行",
58070
- editorSchedEveryday: "每天",
58071
- editorSchedDaily: "每天",
58072
- editorSchedWeekly: "每周",
58073
- editorSchedEveryNWeek: "每 {n} 周",
58074
- editorSchedEveryNDay: "每 {n} 天",
58075
- editorSchedQuarterly: "每季度第 {n} 个月",
58076
- editorSchedIntervalMin: "每 {n} 分钟执行一次",
58077
- editorSchedIntervalHour: "每 {n} 小时执行一次",
58078
- editorSchedOnce: "执行一次",
58079
- editorSchedInvalidStep: "执行间隔没填,无法生成执行计划",
58080
- editorMonthMode_every: "每月",
58081
- editorMonthMode_odd: "单数月",
58082
- editorMonthMode_even: "双数月",
58083
- editorDiscardTitle: "放弃未保存的更改?",
58084
- editorDiscardDesc: "当前内容已修改且尚未保存,关闭后这些更改将丢失。",
58085
- editorDiscardStay: "继续编辑",
58086
- editorDiscardLeave: "放弃更改",
58087
- unitMinutes: "分钟",
58088
- unitHours: "小时",
58089
- unitDays: "天",
58090
- unitWeeks: "周",
58091
- editorDeps: "添加前置任务",
58092
- editorDepsHint: "此任务必须等待『所有』前置任务完成之后再开始执行。判定方式:此任务的所有前置任务,上一次执行必须是成功,本任务才可以执行(中间被跳过、只要没失败,都算前置任务成功)。放行执行时,系统会自动在前置任务完成后,将其产出的相关文件移交给本次任务。",
58093
- editorDepAdd: "添加",
58094
- editorDepTask: "任务",
58095
- editorDepTaskPh: "选择任务",
58096
- editorDepRemove: "移除",
58097
- editorDepEmpty: "尚未配置前置任务",
58098
- editorDepEmptyHint: "在下方选择任务后点「添加」",
58099
- editorDepDisabledTag: "(已停用)",
58100
- editorDepTaskEmpty: "暂无可选的前置任务",
58101
- localeTag: "zh-CN",
58102
- recordsEmpty: "该时间范围内没有执行记录",
58103
- recordsEmptyFiltered: "当前过滤条件下没有执行记录",
58104
- recordsLoadMore: "加载更多",
58105
- recordsNoMore: "没有更多了",
58106
- recordsLimitHint: "已加载 2000 条(已达上限)",
58107
- recordsLoadFail: "执行记录加载失败",
58108
- recordsRetry: "重试",
58109
- recordsDayCount: "{n} 条",
58110
- recordsTaskPh: "任务",
58111
- recordsTaskSearch: "搜索任务名或 ID",
58112
- recordsMore: "更多",
58113
- recordsOutOfScope: "所选任务不在当前工作区",
58114
- recPlan: "计划",
58115
- recActual: "实际",
58116
- recPrevDay: "前一天",
58117
- recNextDay: "次日",
58118
- recEventsTitle: "执行日志",
58119
- recTokenHint: "token 消耗",
58120
- recordsDepTip: "前置任务 {n}:{task}",
58121
- recordsDepFrom: "执行于 {time}",
58122
- recTokenDetail: "输入 {input} / 输出 {output} / 缓存 {cache}",
58123
- editorAdvanced: "高级设置",
58124
- editorAdvancedHelp: "此区域为高级配置区域,修改前请仔细阅读各项说明。常规任务建议使用默认值。",
58125
- editorRetry: "重试次数",
58126
- editorRetryOnce: "一次",
58127
- editorRetryTwice: "两次",
58128
- editorRetryThrice: "三次",
58129
- editorRetryFive: "五次",
58130
- editorRetryHint: "任务执行失败后,自动重试的次数。",
58131
- editorGoal: "以 dsh 内置的 /goal 开始执行任务",
58132
- editorGoalHint: "默认开启:执行时把任务目标作为持久目标交给 dsh 内置 /goal,会话会自动续跑多轮,直到 agent 标记目标完成,看板在该目标真正结束后才结算本次执行。关闭则只执行一轮普通对话。",
58133
- editorAgentTeam: "多 Agent 协作",
58134
- editorAgentTeamHint: "默认关闭。开启后 agent 以官方 Agent Teams(实验特性)方式执行:主会话作为队长,按需创建命名队友分工协作、共享任务板。需要宿主启用 Agent Teams 组件;未启用时自动按单 Agent 执行,并在执行记录日志留痕。",
58135
- editorPreview: "配置预览",
58136
- editorPreviewHint: "查看本任务的配置原文件。",
58137
- listFilterAll: "全部",
58138
- listFilterEnabled: "已开启",
58139
- listFilterDisabled: "已关闭",
58140
- listFilterAbnormal: "异常",
58141
- listFilterWorkspaceAll: "全部工作区",
58142
- listFilterTaskAll: "全部任务",
58143
- listSearchPlaceholder: "搜索任务名称或编号",
58144
- listRunning: "运行中",
58145
- listNextNone: "无后续执行",
58146
- listNever: "尚未执行",
58147
- listCreatedTag: "{date} 创建",
58148
- listCreatedUnknown: "创建时间未知",
58149
- relNow: "即将执行",
58150
- relMinutes: "{n} 分钟后",
58151
- relHours: "{n} 小时后",
58152
- relDays: "{n} 天后",
58153
- relPast: "已过期",
58154
- relJustNow: "刚刚",
58155
- relMinutesAgo: "{n} 分钟前",
58156
- relHoursAgo: "{n} 小时前",
58157
- relDaysAgo: "{n} 天前",
58158
- relWeeksAgo: "{n} 周前",
58159
- relMonthsAgo: "{n} 个月前",
58160
- relYearsAgo: "{n} 年前",
58161
- relToday: "今天 {time}",
58162
- relTomorrow: "明天 {time}",
58163
- relWeeks: "{n} 周后",
58164
- relMonths: "{n} 个月后",
58165
- relYears: "{n} 年后",
58166
- listLastFullTitle: "上次执行:{when}",
58167
- listNextFullTitle: "下次执行:{when}",
58168
- listEmpty: "还没有任务。点右上角「+ 新建任务」创建第一个。",
58169
- listEmptyFiltered: "没有符合当前筛选的任务。",
58170
- listEmptyTitle: "创建你的第一个定时任务",
58171
- listEmptyHint: "写好提示词,到点自动执行;支持每天 / 每周 / 间隔循环。",
58172
- listEmptyAction: "+ 新建定时任务",
58173
- listSectionAttachments: "附加文件",
58174
- listSectionDepends: "前置任务",
58175
- listFieldWorkspace: "工作区",
58176
- listFieldModel: "模型",
58177
- listFieldModelDefault: "默认模型",
58178
- listFieldRetry: "失败重试",
58179
- listFieldSchedule: "排期",
58180
- listFieldWindow: "允许延迟",
58181
- listNone: "(无)",
58182
- listDisabledTag: "(已停用)",
58183
- infoSectionConfig: "任务配置",
58184
- infoNextExec: "预计执行",
58185
- infoLastRun: "上次执行",
58186
- infoNoRun: "还没有执行记录",
58187
- infoFinishedAt: "结束时间",
58188
- infoSession: "任务会话",
58189
- infoDuration: "执行时长",
58190
- editorTabView: "查看",
58191
- editorTabEdit: "编辑",
58192
- infoStateEnabled: "正常",
58193
- infoStateDisabled: "已停用",
58194
- infoViewTask: "查看该任务",
58195
- editorViewDraftTag: "编辑的草稿(未保存)",
58196
- editorViewNewTag: "新建,尚未保存",
58197
- editorViewUntitled: "未命名任务",
58198
- editorViewPrompt: "提示词",
58199
- editorViewPromptEmpty: "还没填提示词",
58200
- editorViewNoRunDraft: "任务尚未保存,没有执行记录",
58201
- editorViewNotFilled: "未填",
58202
- editorViewSourceCode: "源码",
58203
- editorViewExpand: "展开",
58204
- editorViewCollapse: "收起",
58205
- editorViewSwitchTitle: "放弃未保存的修改?",
58206
- editorViewSwitchDesc: "当前任务有改过但还没保存的内容。继续会放弃这些修改,并打开你要查看的任务。",
58207
- editorEditSwitchTitle: "编辑内容尚未保存",
58208
- editorEditSwitchDesc: "你正在编辑的任务有未保存的修改。直接覆盖会丢弃这些修改并打开新任务的编辑器;继续编辑可保留当前内容。",
58209
- editorEditSwitchKeep: "继续编辑",
58210
- editorEditSwitchOverwrite: "直接覆盖",
58211
- cardTabInfo: "基础信息",
58212
- cardTabRecords: "执行记录",
58213
- cardTabLogs: "日志",
58214
- cardDelete: "删除",
58215
- cardDeleteTitle: "删除任务",
58216
- cardDeleteDesc: "确定要删除这个任务吗?任务定义、附加文件与历史版本都会被移除,不可恢复(执行记录保留备查)。",
58217
- cardCancel: "取消",
58218
- cardRunNow: "立即执行",
58219
- cardRunNowTitle: "立即执行任务",
58220
- cardRunNowDesc: "你确定要立即执行此任务吗?",
58221
- cardRunNowOk: "已提交执行,等待调度循环发动",
58222
- cardRunNowAlready: "该任务正在执行中,暂时不能再次执行",
58223
- cardRunNowBlocked: "前置任务未达标,本次未执行",
58224
- cardRunNowMissingDep: "前置任务已不存在,本次未执行",
58225
- cardRunNowWorkspace: "工作区未找到:{name}",
58226
- cardRunNowAttachment: "附加文件不存在:{name}",
58227
- cardRunNowNotFound: "找不到该任务,请刷新后重试",
58228
- cardRunNowNotReady: "插件尚未就绪,请稍后再试",
58229
- cardRunNowFailed: "本次未能执行({reason})",
58230
- cardFrom: "起始时间",
58231
- cardTo: "截止时间",
58232
- cardKeyword: "关键字",
58233
- cardLogLimit: "条数",
58234
- limitPrefix: "显示",
58235
- limitSuffix: "条",
58236
- cardRecordsEmpty: "(该任务还没有执行记录)",
58237
- cardRecordsEmptyFiltered: "(没有符合筛选条件的数据)",
58238
- cardLogsEmpty: "(该任务还没有日志)",
58239
- cardLoadFailed: "读取失败",
58240
- cardEventsEmpty: "(该次执行暂无事件)",
58241
- colTokens: "Token",
58242
- statusPending: "排队",
58243
- statusDispatched: "派发",
58244
- statusRunning: "运行",
58245
- statusSucceeded: "成功",
58246
- statusFailed: "失败",
58247
- statusSkipped: "跳过",
58248
- statusUnknown: "未知",
58249
- colDuration: "时长",
58250
- colPlanned: "计划执行",
58251
- colActualStart: "实际开始",
58252
- colView: "查看",
58253
- colNote: "备注",
58254
- filterRunning: "运行中",
58255
- trAll: "全部",
58256
- trToday: "今天",
58257
- trYesterday: "昨天",
58258
- trThisWeek: "本周",
58259
- trLastWeek: "上周",
58260
- trThisMonth: "本月",
58261
- trLastMonth: "上月",
58262
- schedCustom: "{cron}",
58263
- settingsBasic: "基础信息",
58264
- settingsTitleFormat: "标题写法",
58265
- settingsDesc: "简介",
58266
- settingsLang: "界面语言",
58267
- settingsLangValue: "跟随宿主(中文 / English)",
58268
- settingsParams: "运行参数(实时生效)",
58269
- settingsLoopSec: "循环间隔(秒)",
58270
- settingsLoopHint: "调度器多久巡检一次任务表。",
58271
- settingsLoopHelp: "调度循环每隔这么久巡检一遍,看有没有任务到点、该不该派发。改小 = 响应更及时、但检查更频繁也更占资源;改大则相反。一般 60 秒足够。",
58272
- settingsWaitSec: "派发宽限(秒)",
58273
- settingsWaitHint: "派发后等待会话启动的最长时间。",
58274
- settingsWaitHelp: "任务派发之后,等它的会话创建并开始运行,最多等这么久。超过还没跑起来就按「等待超时」处理、记一次失败,让下一次调度接着来。",
58275
- settingsLeaseSec: "运行租约(秒)",
58276
- settingsLeaseHint: "运行中多久没有动静才回收占用。",
58277
- settingsLeaseHelp: "任务在运行中,每被巡检到一次就续一次期;只有超过这么久**完全没有任何动静**(比如进程假死)才判定为卡死、回收它占的并发位。一直正常在跑的任务会被不断续期,不会被掐断。",
58278
- settingsUnknownSec: "观察宽限(秒)",
58279
- settingsUnknownHint: "会话状态未知时再观察多久。",
58280
- settingsUnknownHelp: "暂时拿不到会话状态时,先按这个宽限继续观察(可能是宿主接口抖动);超过仍没有回音才判定为异常并回收。",
58281
- settingsSaveSuccess: "设置已保存",
58282
- settingsLoadFailed: "读取当前设置失败,请稍后重试",
58283
- settingsUnitSec: "秒",
58284
- loading: "加载中",
58285
- invalidNumber: "请输入有效的整数秒",
58286
- statusRailOff: "已关闭",
58287
- statusRailLastFailed: "最近一次执行失败",
58288
- statusRailLastSkipped: "最近一次未执行(配置或前置不满足,详见执行记录)",
58289
- statusRailRunning: "计划运行中",
58290
- debugRowsSuffix: "行",
58291
- tabSettings: "设置",
58292
- settingsConfigTitle: "插件设置",
58293
- settingsBlockLogTitle: "插件日志",
58294
- settingsBlockDataTitle: "数据库表查询",
58295
- settingsCurrentConfig: "配置预览(只读)",
58296
- settingsDefaultValue: "默认",
58297
- settingsProvider: "默认模型供应商",
58298
- settingsModel: "默认模型",
58299
- settingsLogRetention: "运行日志保留(天)",
58300
- settingsLogRetentionHint: "诊断日志保留多久,到期清除。",
58301
- settingsLogRetentionHelp: "每次执行过程中产生的诊断日志(任务日志 + 插件日志)保留这么多天,到期自动清除。它只清日志明细,执行记录本身一律保留、不受影响。",
58302
- settingsAttachmentRetention: "临时附件保留(天)",
58303
- settingsAttachmentRetentionHint: "临时目录里的附件副本保留多久。",
58304
- settingsAttachmentRetentionHelp: "上传或引用附件时,插件会在自己的临时目录里先留一份副本,这么多天后清理掉。任务定义里正式引用的那份附件不受影响。",
58305
- settingsUnsaved: "有未保存的修改",
58306
- settingsDefaultOption: "默认",
58307
- settingsModelHint: "没选就用宿主的默认模型。",
58308
- settingsModelHelp: "先选供应商、再选具体模型,两个都要选。留空(默认模型)= 跟随宿主的默认模型。新建任务时会自动带出这里的选择,单个任务仍可单独改。",
58309
- settingsCustom: "自定义",
58310
- settingsResetDefault: "恢复默认",
58311
- settingsErrRange: "请填 {min} 到 {max} 之间的整数。",
58312
- settingsErrPair: "供应商和模型要一起选:选了其中一个,另一个也得选。",
58313
- settingsErrUpdateFailed: "配置没能写进去(宿主的配置面还没就绪),请稍后重试。",
58314
- settingsErrInvalidField: "「{name}」这个值不合法,请检查后重试。",
58315
- settingsErrRangeField: "「{name}」超出允许范围({min}–{max})。",
58316
- settingsErrEmpty: "没有要保存的改动。",
58317
- settingsAutoRefresh: "自动刷新(5 秒)",
58318
- settingsRefresh: "刷新",
58319
- settingsLogEmpty: "(暂无日志)",
58320
- settingsLogFail: "日志读取失败",
58321
- settingsTable: "表",
58322
- settingsTopN: "取前 N 条",
58323
- settingsPageSize: "每页显示条数",
58324
- settingsAddFilter: "添加筛选",
58325
- settingsNoFilters: "(无筛选:显示全部行)",
58326
- settingsQuery: "查询",
58327
- settingsTableTruncated: "行数超出上限,仅显示最新一部分",
58328
- settingsTableEmpty: "(空表:还没有任何记录)",
58329
- settingsTableFail: "读取失败",
58330
- settingsFilterColumn: "列",
58331
- settingsFilterOp: "运算符",
58332
- settingsFilterValue: "值",
58333
- settingsFilterRemove: "移除筛选",
58334
- settingsIntro: "本插件无需在此页面配置:任务与全部设置都在插件主界面(侧栏「定时任务调度器」面板)内完成。",
58335
- vTitleRequired: "还没填任务名称——任务列表里靠它认任务,请给任务起个名字。",
58336
- vWorkspaceRequired: "还没选工作区——任务必须挂在某个工作区下才能执行,请在上方下拉里选一个。",
58337
- vWorkspaceUnknown: "选中的工作区已经不存在了(可能被重命名或删除)——请在上方下拉里重新选择当前的工作区。",
58338
- vPromptRequired: "还没写提示词——这是告诉 Agent 要做什么的指令,不能为空,请填写具体内容。",
58339
- vIntervalInvalid: "执行间隔没填或填错——「每隔 N 分钟/小时」里的 N 必须是大于 0 的整数(比如 1 或 2)。",
58340
- vWeekdayRequired: "每周执行但没勾选任何星期——请至少勾选一天,否则任务永远不会跑。",
58341
- vAttachmentInvalid: "附加文件「{name}」的引用路径不合法——必须是工作区内的相对路径。请删掉它、重新选择一次。"
58342
- };
58343
- /** English copy. */
58344
- const en = {
58345
- title: "Task dispatch table (dsh-task-dispatch-table)",
58346
- description: "Schedule agent tasks from a task table: configure tasks and review every run. Click to open the panel.",
58347
- unavailable: "The settings namespace is currently unavailable (plugin not running or not served by the host); configuration is disabled.",
58348
- tasksInlineLabel: "Task table (tasksInline, JSON array)",
58349
- tasksInlineHint: "One task definition per entry; when non-empty it takes precedence over tasksDir. Clear and save to fall back to the default (empty, use tasksDir).",
58350
- invalidJson: "The task table is not valid JSON; the save was blocked. Fix it and try again.",
58351
- save: "Save",
58352
- saving: "Saving…",
58353
- saveFailed: "The save did not land; your draft was kept for correction (the host may have rejected values or applied concurrent changes).",
58354
- discard: "Discard changes",
58355
- paramsTitle: "Runtime parameters (read-only)",
58356
- paramDefault: "(default)",
58357
- paramStatePath: "State database path statePath",
58358
- paramTickMs: "Tick interval tickMs (ms)",
58359
- paramDispatchGraceMs: "Dispatch grace dispatchGraceMs (ms)",
58360
- paramLeaseMs: "Run lease leaseMs (ms)",
58361
- paramUnknownGraceMs: "Observation grace unknownGraceMs (ms)",
58362
- paramTasksDir: "Task directory tasksDir",
58363
- paramDefaultProvider: "Default model provider defaultProvider",
58364
- paramDefaultModel: "Default model defaultModel",
58365
- debugClose: "Close",
58366
- debugRefreshedAt: "Manual refresh at",
58367
- debugEmpty: "No snapshot yet: the host writes one after each scheduling pass (or dispatch / session event). If it stays empty, the host is still running an old plugin build — reinstall and retry.",
58368
- debugRaw: "Failed to parse the snapshot; raw text below:",
58369
- debugWarns: "Recent warnings / errors (≤20 entries)",
58370
- debugNoWarns: "(none)",
58371
- panelTitle: "Scheduled task dispatcher",
58372
- backToConversation: "Back to conversation",
58373
- tabConfig: "Configuration",
58374
- tabRecords: "Run records",
58375
- tabCalendar: "Schedule",
58376
- tabDebug: "Debug",
58377
- calLoadFail: "Failed to load the schedule",
58378
- calRetry: "Retry",
58379
- calEmpty: "No run records and no planned runs in this month",
58380
- calDayEmpty: "No run records and no planned runs on this day",
58381
- calTruncated: "This month exceeds the 3000-row limit and is truncated; narrow the filters above.",
58382
- calPlanTag: "Plan:",
58383
- calNotExecuted: "Not executed",
58384
- calPlanHint: "This task has not run yet; times shown are estimated",
58385
- calToday: "Today",
58386
- calCellMore: "{n} more — click to open",
58387
- debugDbHint: "Raw rows of all three state.db tables, read-only: task_instances = one row per run, task_events = one row per event, meta = plugin metadata (incl. the inline task table). Newest 500 rows per table; use the refresh button to re-fetch.",
58388
- debugDbLoading: "Loading state.db…",
58389
- debugDbFail: "Failed to read state.db (not ready or request rejected); retry with the refresh button.",
58390
- debugDbEmpty: "(empty table: no rows yet)",
58391
- debugDbTruncated: "row count exceeds the cap, showing only the newest rows",
58392
- tasksParsedTitle: "Parsed tasks (ids are generated; renaming never breaks history)",
58393
- tasksParsedEmpty: "(no tasks: inline table empty and task dir has no valid definition)",
58394
- colTitle: "Title",
58395
- colCode: "Code",
58396
- colId: "Task ID",
58397
- colSchedule: "Schedule",
58398
- colNext: "Next run",
58399
- colStatus: "Status",
58400
- colSession: "Session",
58401
- filterAll: "All",
58402
- listExpandHint: "Expand task details",
58403
- listDeferred: "Delayed",
58404
- listDeferredTitle: "Past its planned time but has not started. Usual causes: preceding task not finished / attachment missing / previous run still running.",
58405
- viewSession: "View session",
58406
- sessionViewerTitle: "Session transcript (read-only)",
58407
- sessionUnknownKind: "Unsupported node kind: ",
58408
- sessionLoading: "Loading session transcript…",
58409
- sessionEmpty: "Nothing to show for this session yet (window just opened, or the log was cleaned up).",
58410
- sessionLoadFailed: "Failed to load the session transcript (the session may no longer be readable).",
58411
- sessionLoadOlder: "Load earlier messages",
58412
- sessionProcess: "Process",
58413
- triggerRequest: "Execution request received",
58414
- triggerGoal: "Continuing the goal",
58415
- triggerAgent: "Task message received",
58416
- triggerTeam: "Team message received",
58417
- triggerSubagent: "Subagent status update",
58418
- triggerGithub: "GitHub event received",
58419
- triggerWebhook: "External event received",
58420
- triggerSchedule: "Scheduled task",
58421
- triggerJob: "Background task status update",
58422
- triggerPlugin: "Plugin status update",
58423
- triggerExplanation: "This notification triggered the reply below.",
58424
- turnProcessTook: "Took {duration}",
58425
- turnProcessDeepDiving: "Thinking, {duration}",
58426
- turnProcessWorked: "Work completed",
58427
- turnProcessFailed: "Failed",
58428
- turnStopped: "Stopped",
58429
- chatDeepDiving: "Thinking",
58430
- thinkLabel: "Think",
58431
- durationSeconds: "{seconds}s",
58432
- durationMinutes: "{minutes}m {seconds}s",
58433
- durationHours: "{hours}h {minutes}m {seconds}s",
58434
- retryActive: "Retrying model request",
58435
- retryCancelled: "Model request retry cancelled",
58436
- retryStarted: "Retried model request",
58437
- retryScheduled: "Waiting to retry model request",
58438
- retryStatus: "{label} ({retry}/{maximum}) · {seconds}s",
58439
- retryDelay: "Retry delay: ",
58440
- retryFailure: "Failure reason: ",
58441
- durationMilliseconds: "{milliseconds}ms",
58442
- turnErrorTitle: "This turn failed",
58443
- accountStopped: "Task stopped",
58444
- maxTokensTitle: "Output token limit reached",
58445
- maxTokensHint: "The reply was cut off; earlier output is preserved in the conversation. Send \"continue\" to let the model resume.",
58446
- failureAuth: "API key is invalid",
58447
- failureQuota: "Request quota exhausted.",
58448
- failureAccountSignedOut: "Stopped because you signed out of DeepSeek.",
58449
- failureAccountSignInRequired: "Sign in to DeepSeek and ensure the request destination supports account authentication.",
58450
- clockDate: "{m}/{d}",
58451
- clockDateYear: "{y}/{m}/{d}",
58452
- copyLabel: "Copy",
58453
- copiedLabel: "Copied",
58454
- branchLabel: "Branch into a new conversation",
58455
- branchUnavailableLabel: "A read-only transcript cannot be branched",
58456
- "presented.unavailable": "This host has no available desktop; external programs cannot open files or folders. Files can still be previewed in the sidebar.",
58457
- turnUsageTitle: "Turn usage",
58458
- turnUsageModel: "Provider / model",
58459
- turnUsageCacheHit: "Cache hit",
58460
- turnUsageInput: "Uncached input",
58461
- turnUsageCacheRead: "Cached input",
58462
- turnUsageCacheWrite: "Cache write",
58463
- turnUsageOutput: "Output",
58464
- turnUsageReasoning: " ({tokens} reasoning)",
58465
- turnUsageConsumed: "Usage {total}",
58466
- turnUsageCount: "{count} tok",
58467
- numberThousand: "{value}K",
58468
- numberMillion: "{value}M",
58469
- numberGroupSeparator: ",",
58470
- stepProcessDoneThinking: "Analysis completed",
58471
- stepProcessDoneRead: "Read files",
58472
- stepProcessDoneReadImage: "Read images",
58473
- stepProcessDoneWrite: "Wrote files",
58474
- stepProcessDoneSearch: "Searched code",
58475
- stepProcessDoneEdit: "Edited files",
58476
- stepProcessDoneCommands: "Ran commands",
58477
- stepProcessDoneCode: "Ran code",
58478
- stepProcessDoneWebSearch: "Searched the web",
58479
- stepProcessDoneWebFetch: "Fetched web pages",
58480
- stepProcessDoneSubagents: "Coordinated subagents",
58481
- stepProcessDonePlan: "Updated the plan",
58482
- stepProcessDoneQuestions: "Asked you questions",
58483
- stepProcessDoneTools: "Called tools",
58484
- stepProcessJoinTwo: "{first} and {second}",
58485
- stepProcessComma: ", ",
58486
- stepProcessSharedPrefix: "",
58487
- stepProcessMore: "{title}, etc.",
58488
- toolTitleRead: "Read",
58489
- toolTitleReadImage: "Read image",
58490
- toolTitleGrep: "Grep",
58491
- toolTitleGlob: "Glob",
58492
- toolTitleBash: "Bash",
58493
- toolTitleWrite: "Write",
58494
- toolTitleEdit: "Edit",
58495
- toolTitleCode: "Code",
58496
- toolTitleWebSearch: "Search",
58497
- toolTitleWebFetch: "Fetch",
58498
- toolTitleGeneric: "Tool call",
58499
- toolTitleSearch: "Search",
58500
- toolInputLabel: "IN",
58501
- toolOutputLabel: "OUT",
58502
- rowPreparing: "Preparing tool call",
58503
- rowRunning: "Running",
58504
- rowFailed: "Failed",
58505
- rowStopped: "Stopped",
58506
- collapseLabel: "Collapse",
58507
- readWindow: "Showing {shown} of {total} lines",
58508
- readCollapseAria: "Collapse content",
58509
- readExpandAria: "Expand {count} more lines",
58510
- readExpandRest: "… {count} more lines",
58511
- terminalSignal: "signal {signal}",
58512
- terminalExitCode: "exit code {code}",
58513
- terminalNoExitCode: "no exit code",
58514
- terminalRunning: "Running",
58515
- terminalFailed: "Failed",
58516
- terminalDone: "Done",
58517
- terminalNoOutput: "No output",
58518
- terminalCollapseAria: "Collapse output",
58519
- terminalExpandAria: "Expand the remaining {n} output lines",
58520
- terminalExpandRest: "… {n} more lines",
58521
- terminalSendInput: "(send input)",
58522
- terminalSession: "Terminal {sessionId}",
58523
- codeBlockLabel: "Code",
58524
- diffWrapLabel: "Wrap lines",
58525
- diffUnwrapLabel: "Unwrap lines",
58526
- diffCollapseAria: "Collapse diff",
58527
- diffExpandAria: "Expand {count} more diff lines",
58528
- diffExpandRest: "… {count} more lines",
58529
- continueBranch: "Continue conversation",
58530
- forkConfirmTitle: "Fork to continue",
58531
- forkConfirmText: "Start a new branch from this session to continue the conversation? The original stays read-only; the branch copies this conversation and can continue.",
58532
- forkConfirmAccept: "Fork & open",
58533
- forkCancel: "Cancel",
58534
- forkWorking: "Forking…",
58535
- forkFailed: "Fork failed: {error}",
58536
- previewClose: "Close preview",
58537
- previewLoadMore: "Load more",
58538
- previewFileLabel: "File",
58539
- previewCopyPath: "Copy path",
58540
- previewRefresh: "Refresh",
58541
- previewNotFound: "File not found (it may have been moved or deleted).",
58542
- previewTooLarge: "The file is too large to preview (limit: {limit}).",
58543
- previewDirectory: "This is a directory; browsing directories is not supported yet.",
58544
- previewNotRegular: "Not a regular file (symlink or similar); preview is not supported.",
58545
- previewOutsideWorkspace: "This path resolves outside the session workspace (often a symlink pointing outward); the official API refuses to browse it.",
58546
- previewError: "Failed to read: {code}",
58547
- previewUnknownBinary: "Binary file; preview is not supported. Copy the path to open it in the workspace.",
58548
- previewOfficeUnavailable: "Failed to read: Office preview is unavailable. Enable the document preview service on the host running DeepSeek Harness.",
58549
- previewOfficeFailed: "Office conversion failed; cannot preview. Copy the path to open it in the workspace.",
58550
- previewBadPayload: "Read result does not match the official contract (logged to the console); nothing rendered.",
58551
- previewRenderFailed: "Preview rendering failed (logged); the rest of the panel is unaffected.",
58552
- previewResize: "Drag to resize the preview pane",
58553
- previewSource: "Source",
58554
- previewRender: "Preview",
58555
- previewMdSwitchAria: "Markdown view switch",
58556
- previewHtmlSwitchAria: "HTML view switch",
58557
- previewHtmlFrame: "HTML document preview",
58558
- previewHtmlFailed: "This HTML document could not be previewed.",
58559
- previewTruncated: "File too large; showing only the first {size}.",
58560
- previewTruncatedSize: "256KB",
58561
- explorerEmpty: "Empty directory",
58562
- explorerTruncated: "The directory is too large; only some entries are shown.",
58563
- explorerCrumbsAria: "Directory path navigation",
58564
- explorerLevels: "Choose a directory level",
58565
- explorerRootName: "(workspace root)",
58566
- explorerExpand: "Expand directory",
58567
- explorerCollapse: "Collapse directory",
58568
- explorerEnter: "Open",
58569
- colOutputs: "Outputs",
58570
- procFilesTitle: "Other outputs · {count}",
58571
- deliverRowTitle: "Deliver files",
58572
- deliverRowPreparing: "Preparing delivery",
58573
- deliverRowRunning: "Delivering",
58574
- deliverRowOk: "Delivered",
58575
- deliverRowError: "Delivery failed",
58576
- deliverRowStopped: "Interrupted",
58577
- deliverFileLabel: "File",
58578
- deliverPreviewHint: "Preview",
58579
- deliverPreviewCard: "Preview {name}",
58580
- deliverAll: "All {count} files",
58581
- deliverCollapse: "Collapse",
58582
- deliverExpandAria: "Expand all {count} delivered files",
58583
- deliverCollapseAria: "Collapse the delivered-files list",
58584
- tfcReceived: "Received · {files} file(s) from {tasks} preceding task(s)",
58585
- tfcAttached: "Attached · {count} file(s)",
58586
- tfcNoOutputs: "No outputs declared",
58587
- tfcRelOnly: "relative",
58588
- tfcCrossWorkspace: "other workspace",
58589
- tfcNoPath: "path unresolved — cannot open",
58590
- tfcMore: "All {count} files",
58591
- tfcMoreTasks: "All {count} preceding tasks",
58592
- tfcCollapse: "Collapse",
58593
- tfcFromUpload: "uploaded",
58594
- tfcFromWorkspace: "linked",
58595
- tfcRestFiles: "{count} more",
58596
- editorNew: "New task",
58597
- editorEdit: "Edit task",
58598
- editorClose: "Close",
58599
- editorCancel: "Cancel",
58600
- editorSave: "Save",
58601
- editorSavePending: "Save logic not wired yet (P2): this round is UI only",
58602
- editorEnabled: "Enabled",
58603
- editorEnabledOn: "Enabled — dispatches when due",
58604
- editorEnabledOff: "Disabled — kept but never dispatches",
58605
- editorTitle: "Task name",
58606
- editorTitlePh: "Name this task",
58607
- editorCode: "Task code",
58608
- editorCodePh: "Optional, for lookup",
58609
- editorPrompt: "Prompt",
58610
- editorPromptPh: "Instructions for the agent…",
58611
- editorVersions: "Version history",
58612
- editorWorkspace: "Workspace",
58613
- editorWorkspacePh: "Choose workspace",
58614
- editorModel: "Model",
58615
- editorModelPh: "Default model",
58616
- editorFollowHost: "Default model",
58617
- editorPermission: "Permissions",
58618
- editorPermissionHint: "Scope of actions granted to the agent for this run. Default = session default (inherited from the host’s new-session permission setting). The host has no per-task permission API yet, so the chosen level is enforced through the dispatch instruction.",
58619
- editorPermDefault: "Permissions: default",
58620
- editorPermReadOnly: "View only",
58621
- editorPermWorkspace: "Edit in workspace",
58622
- editorPermFull: "Full access",
58623
- editorNoOptions: "Nothing to choose yet",
58624
- editorSchedule: "Schedule",
58625
- editorScheduleHint: "Set when and how often the task runs: once (runs a single time when due), recurring (daily / weekly / monthly and so on), or interval (every fixed span of time).",
58626
- editorSchedulePeriodic: "Recurring",
58627
- editorScheduleInterval: "Interval",
58628
- editorFreq: "Frequency",
58629
- editorFreqOnce: "Once",
58630
- editorFreqDaily: "Daily",
58631
- editorFreqWeekly: "Weekly",
58632
- editorFreqMonthly: "Monthly",
58633
- editorFreqQuarterly: "Quarterly",
58634
- editorFreqYearly: "Yearly",
58635
- editorOnceHint: "Runs once when due, never repeats",
58636
- editorMonthEvery: "All months",
58637
- editorMonthOdd: "Odd months",
58638
- editorMonthEven: "Even months",
58639
- editorQuarterMonthOption: "Month {m}",
58640
- editorDate: "Date",
58641
- editorDatePh: "Pick a date",
58642
- editorTime: "Time",
58643
- editorTimePh: "Pick a time",
58644
- editorMonth: "Month",
58645
- editorMonthOption: "Month {m}",
58646
- editorDayOfMonth: "Day",
58647
- editorDayOption: "{d}",
58648
- editorIntervalEvery: "Every",
58649
- editorIntervalStep: "Interval step",
58650
- editorIntervalUnit: "Interval unit",
58651
- editorIntervalSuffix: "",
58652
- editorEveryNWeeks: "Every {n} weeks",
58653
- editorAttachments: "Attachments",
58654
- editorAttachmentLink: "Link",
58655
- editorAttachmentUpload: "Upload",
58656
- editorAttachmentRemove: "Remove",
58657
- editorAttachmentView: "View",
58658
- editorAttachmentAdd: "Add file",
58659
- editorPickWorkspaceFile: "Pick workspace file",
58660
- editorPickWorkspaceFileShort: "Files",
58661
- editorDropZoneHint: "Click or drop files to upload — single or multiple",
58662
- editorDropZoneFormats: "Text/code, image and document formats, up to 20MB each",
58663
- editorUploading: "Uploading…",
58664
- editorUploadErrType: "Unsupported file type: only common text/code, image and document files are allowed",
58665
- editorUploadErrSize: "File exceeds the size limit (20MB max each)",
58666
- editorUploadErrEmpty: "File is empty",
58667
- editorUploadErrGeneric: "Upload failed, please retry",
58668
- editorPickerNoSession: "No sessions in this workspace yet — its files cannot be read.",
58669
- editorAttachmentsHint: "Attachments are dispatched along with the task: while it runs, the agent can read and operate on their contents. You can also pick a whole folder: the agent gets the folder path and explores it itself, so refer to a file inside as \"<folder name>/<relative path>\" in the prompt.",
58670
- editorPickerPick: "Pick this file",
58671
- editorPickerPickDir: "Pick this folder (with everything under it)",
58672
- editorPickerSelect: "Select",
58673
- editorOpenEditor: "Full-screen edit",
58674
- editorPromptEditorTitle: "Prompt editor (.md)",
58675
- editorNoVersions: "No versions yet — save one to roll back here",
58676
- editorHistoryVersions: "History versions",
58677
- editorNewTaskNoVersions: "New task not saved yet — no history versions to query",
58678
- editorDeleteVersion: "Delete",
58679
- editorUseVersion: "Use this version",
58680
- editorConfirmDeleteTitle: "Delete version",
58681
- editorConfirmDeleteDesc: "Delete this version record? This cannot be undone — please proceed with care.",
58682
- editorDeleteTask: "Delete task",
58683
- editorDeleteTaskTitle: "Delete task",
58684
- editorDeleteTaskDesc: "Everything removed is unrecoverable and irreversible.",
58685
- editorReset: "Reset",
58686
- editorResetDone: "Restored to the values from when you opened it",
58687
- editorResetTitle: "Reset current edits?",
58688
- editorResetDesc: "Reset discards all unsaved changes to this task and restores the values from when you opened it. This cannot be undone.",
58689
- editorVersionToggle: "Versions",
58690
- editorUseShort: "Use",
58691
- editorEnabledStateOn: "On",
58692
- editorEnabledStateOff: "Off",
58693
- editorToggleOn: "Task enabled",
58694
- editorToggleOff: "Task disabled",
58695
- editorFullPermTitle: "This task will run with full access",
58696
- editorFullPermDesc: "This task runs unattended with FULL access — the AI can directly:",
58697
- editorFullPermB1: "create / modify / delete files in the workspace",
58698
- editorFullPermB2: "run commands, make network requests and other sensitive operations",
58699
- editorFullPermCheck: "I understand the risk and accept responsibility for this task's results.",
58700
- editorTaskSaved: "Task saved",
58701
- editorTaskDeleted: "Task deleted",
58702
- editorSchedHourlyOnce: "runs hourly",
58703
- editorSchedNoDaySuffix: ", but no active day is selected",
58704
- editorSchedWeeklyDayPrefix: "every ",
58705
- editorRestorePromptTitle: "Restore prompt only",
58706
- editorRestorePromptDesc: "Restoring will overwrite all your current edits with the history version (prompt only; other settings stay as they are).",
58707
- editorTasksTitle: "Tasks",
58708
- editorTasksEmpty: "No tasks yet — use \"+ New task\" in the top-right corner to create the first one.",
58709
- editorDisabledTag: "Disabled",
58710
- editorModeEdit: "Edit",
58711
- editorModePreview: "Preview",
58712
- editorTaskStart: "Task start time",
58713
- editorTaskStartHint: "The task starts running on this day. You can create it in advance and it begins automatically once this day arrives; \"every N weeks\" also counts its first week from this day.",
58714
- editorWeekdayLabel: "Weekdays",
58715
- editorWeekday1: "Mon",
58716
- editorWeekday2: "Tue",
58717
- editorWeekday3: "Wed",
58718
- editorWeekday4: "Thu",
58719
- editorWeekday5: "Fri",
58720
- editorWeekday6: "Sat",
58721
- editorWeekday7: "Sun",
58722
- editorWeekdayShorts: "Mo|Tu|We|Th|Fr|Sa|Su",
58723
- editorWeekdayEmpty: "None = every day",
58724
- calWeekdays: "Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday",
58725
- editorToday: "Today",
58726
- editorPrevMonth: "Previous month",
58727
- editorNextMonth: "Next month",
58728
- editorPrevYear: "Previous year",
58729
- editorNextYear: "Next year",
58730
- editorMonthTitle: "{m}/{y}",
58731
- editorHour: "Hour",
58732
- editorMinute: "Minute",
58733
- editorNow: "Now",
58734
- editorConfirm: "OK",
58735
- editorWindow: "Allow delay",
58736
- editorWindowHint: "When the task reaches its planned start time but a dependency is unfinished or something else delays it, it may wait up to this long and still run; past this, this run is skipped.",
58737
- editorSchedForecast: "Forecast",
58738
- editorSchedRun: "runs",
58739
- editorSchedEveryday: "every day",
58740
- editorSchedDaily: "Every day",
58741
- editorSchedWeekly: "Weekly",
58742
- editorSchedEveryNWeek: "Every {n} weeks",
58743
- editorSchedEveryNDay: "Every {n} days",
58744
- editorSchedQuarterly: "Month {n} of each quarter",
58745
- editorSchedIntervalMin: "Runs every {n} minutes",
58746
- editorSchedIntervalHour: "Runs every {n} hours",
58747
- editorSchedOnce: "runs once",
58748
- editorSchedInvalidStep: "Interval not set — cannot build a schedule",
58749
- editorMonthMode_every: "Every month",
58750
- editorMonthMode_odd: "Odd months",
58751
- editorMonthMode_even: "Even months",
58752
- editorDiscardTitle: "Discard unsaved changes?",
58753
- editorDiscardDesc: "The content has been modified but not saved. These changes will be lost if you close now.",
58754
- editorDiscardStay: "Keep editing",
58755
- editorDiscardLeave: "Discard changes",
58756
- unitMinutes: "minutes",
58757
- unitHours: "hours",
58758
- unitDays: "days",
58759
- unitWeeks: "weeks",
58760
- editorDeps: "Add prerequisite tasks",
58761
- editorDepsHint: "This task waits for ALL of its prerequisites to finish before it can start. Rule: the latest run of every prerequisite must have succeeded before this task is allowed to run (skipped runs do not count as failure). At dispatch time the system automatically hands over the files produced by the prerequisites to this task.",
58762
- editorDepAdd: "Add",
58763
- editorDepTask: "Task",
58764
- editorDepTaskPh: "Select a task",
58765
- editorDepRemove: "Remove",
58766
- editorDepEmpty: "No prerequisites configured yet",
58767
- editorDepEmptyHint: "Pick a task below, then add",
58768
- editorDepDisabledTag: " (disabled)",
58769
- editorDepTaskEmpty: "No selectable prerequisites",
58770
- localeTag: "en-US",
58771
- recordsEmpty: "No executions in this time range",
58772
- recordsEmptyFiltered: "No executions match the current filters",
58773
- recordsLoadMore: "Load more",
58774
- recordsNoMore: "No more",
58775
- recordsLimitHint: "Loaded 2000 (hard limit reached)",
58776
- recordsLoadFail: "Failed to load executions",
58777
- recordsRetry: "Retry",
58778
- recordsDayCount: "{n} total",
58779
- recordsTaskPh: "Task",
58780
- recordsTaskSearch: "Search by name or ID",
58781
- recordsMore: "More",
58782
- recordsOutOfScope: "The selected task is not in this workspace",
58783
- recPlan: "Plan",
58784
- recActual: "Actual",
58785
- recPrevDay: "prev day",
58786
- recNextDay: "next day",
58787
- recEventsTitle: "Run log",
58788
- recTokenHint: "Token usage",
58789
- recordsDepTip: "Prerequisite {n}: {task}",
58790
- recordsDepFrom: "ran at {time}",
58791
- recTokenDetail: "input {input} / output {output} / cache {cache}",
58792
- editorAdvanced: "Advanced settings",
58793
- editorAdvancedHelp: "This is the advanced configuration area. Read each item’s description before changing it; default values are recommended for routine tasks.",
58794
- editorRetry: "Retry attempts",
58795
- editorRetryOnce: "Once",
58796
- editorRetryTwice: "Twice",
58797
- editorRetryThrice: "3 times",
58798
- editorRetryFive: "5 times",
58799
- editorRetryHint: "How many automatic retries happen after a failed run.",
58800
- editorGoal: "Start the task with the dsh built-in /goal",
58801
- editorGoalHint: "On by default: the task objective is handed to the dsh built-in /goal as a persistent goal; the session keeps running more turns until the agent marks the goal complete, and the board settles the run only after the goal truly finishes. Off means a single ordinary round.",
58802
- editorAgentTeam: "Multi-agent collaboration",
58803
- editorAgentTeamHint: "Off by default. When on, the agent runs via the official Agent Teams (experimental): the lead session spawns named teammates that share a task board. Requires the host to enable Agent Teams; otherwise it falls back to a single agent and leaves a log entry.",
58804
- editorPreview: "Config preview",
58805
- editorPreviewHint: "View the raw config file of this task.",
58806
- listFilterAll: "All",
58807
- listFilterEnabled: "Enabled",
58808
- listFilterDisabled: "Disabled",
58809
- listFilterAbnormal: "Abnormal",
58810
- listFilterWorkspaceAll: "All workspaces",
58811
- listFilterTaskAll: "All tasks",
58812
- listSearchPlaceholder: "Search by name or code",
58813
- listRunning: "Running",
58814
- listNextNone: "No further runs",
58815
- listNever: "Never run",
58816
- listCreatedTag: "{date} created",
58817
- listCreatedUnknown: "creation time unknown",
58818
- relNow: "due now",
58819
- relMinutes: "in {n} min",
58820
- relHours: "in {n} h",
58821
- relDays: "in {n} days",
58822
- relPast: "overdue",
58823
- relJustNow: "just now",
58824
- relMinutesAgo: "{n} min ago",
58825
- relHoursAgo: "{n} h ago",
58826
- relDaysAgo: "{n} days ago",
58827
- relWeeksAgo: "{n} weeks ago",
58828
- relMonthsAgo: "{n} months ago",
58829
- relYearsAgo: "{n} years ago",
58830
- relToday: "Today {time}",
58831
- relTomorrow: "Tomorrow {time}",
58832
- relWeeks: "in {n} weeks",
58833
- relMonths: "in {n} months",
58834
- relYears: "in {n} years",
58835
- listLastFullTitle: "Last run: {when}",
58836
- listNextFullTitle: "Next run: {when}",
58837
- listEmpty: "No tasks yet. Use “+ New task” to create the first one.",
58838
- listEmptyFiltered: "No task matches the current filter.",
58839
- listEmptyTitle: "Create your first scheduled task",
58840
- listEmptyHint: "Write a prompt and it runs automatically on time — every day, every week, or on an interval.",
58841
- listEmptyAction: "+ New scheduled task",
58842
- listSectionAttachments: "Attachments",
58843
- listSectionDepends: "Preceding tasks",
58844
- listFieldWorkspace: "Workspace",
58845
- listFieldModel: "Model",
58846
- listFieldModelDefault: "Default model",
58847
- listFieldRetry: "Retries",
58848
- listFieldSchedule: "Schedule",
58849
- listFieldWindow: "Late window",
58850
- listNone: "(none)",
58851
- listDisabledTag: "(disabled)",
58852
- infoSectionConfig: "Task settings",
58853
- infoNextExec: "Next run",
58854
- infoLastRun: "Last run",
58855
- infoNoRun: "No runs yet",
58856
- infoFinishedAt: "Ended at",
58857
- infoSession: "Session",
58858
- infoDuration: "Duration",
58859
- editorTabView: "View",
58860
- editorTabEdit: "Edit",
58861
- infoStateEnabled: "Active",
58862
- infoStateDisabled: "Disabled",
58863
- infoViewTask: "View this task",
58864
- editorViewDraftTag: "Draft edits (unsaved)",
58865
- editorViewNewTag: "New, not saved",
58866
- editorViewUntitled: "Untitled task",
58867
- editorViewPrompt: "Prompt",
58868
- editorViewPromptEmpty: "No prompt yet",
58869
- editorViewNoRunDraft: "Not saved yet — no run history",
58870
- editorViewNotFilled: "Not set",
58871
- editorViewSourceCode: "Source",
58872
- editorViewExpand: "Expand",
58873
- editorViewCollapse: "Collapse",
58874
- editorViewSwitchTitle: "Discard unsaved changes?",
58875
- editorViewSwitchDesc: "This task has unsaved edits. Continuing will discard them and open the task you want to view.",
58876
- editorEditSwitchTitle: "Unsaved edits",
58877
- editorEditSwitchDesc: "The task you are editing has unsaved changes. Overwriting discards them and opens the new task’s editor; keep editing to preserve your changes.",
58878
- editorEditSwitchKeep: "Keep editing",
58879
- editorEditSwitchOverwrite: "Overwrite",
58880
- cardTabInfo: "Basic info",
58881
- cardTabRecords: "Run records",
58882
- cardTabLogs: "Logs",
58883
- cardDelete: "Delete",
58884
- cardDeleteTitle: "Delete task",
58885
- cardDeleteDesc: "Delete this task? Its definition, attachments and version history will be removed permanently (run records are kept for audit).",
58886
- cardCancel: "Cancel",
58887
- cardRunNow: "Run now",
58888
- cardRunNowTitle: "Run this task now",
58889
- cardRunNowDesc: "Run this task immediately?",
58890
- cardRunNowOk: "Submitted — waiting for the dispatch loop",
58891
- cardRunNowAlready: "This task is already running; please wait",
58892
- cardRunNowBlocked: "Upstream task not satisfied — not executed",
58893
- cardRunNowMissingDep: "Upstream task no longer exists — not executed",
58894
- cardRunNowWorkspace: "Workspace not found: {name}",
58895
- cardRunNowAttachment: "Attachment missing: {name}",
58896
- cardRunNowNotFound: "Task not found; please refresh",
58897
- cardRunNowNotReady: "Plugin not ready yet; please retry",
58898
- cardRunNowFailed: "Could not run ({reason})",
58899
- cardFrom: "Start",
58900
- cardTo: "End",
58901
- cardKeyword: "Keyword",
58902
- cardLogLimit: "Rows",
58903
- limitPrefix: "Show",
58904
- limitSuffix: "rows",
58905
- cardRecordsEmpty: "(no run records for this task yet)",
58906
- cardRecordsEmptyFiltered: "(no data matches the current filter)",
58907
- cardLogsEmpty: "(no logs for this task yet)",
58908
- cardLoadFailed: "Failed to load",
58909
- cardEventsEmpty: "(no events for this run)",
58910
- colTokens: "Token",
58911
- statusPending: "Pending",
58912
- statusDispatched: "Dispatched",
58913
- statusRunning: "Running",
58914
- statusSucceeded: "Succeeded",
58915
- statusFailed: "Failed",
58916
- statusSkipped: "Skipped",
58917
- statusUnknown: "Unknown",
58918
- colDuration: "Duration",
58919
- colPlanned: "Scheduled",
58920
- colActualStart: "Started",
58921
- colView: "View",
58922
- colNote: "Note",
58923
- filterRunning: "Running",
58924
- trAll: "All",
58925
- trToday: "Today",
58926
- trYesterday: "Yesterday",
58927
- trThisWeek: "This week",
58928
- trLastWeek: "Last week",
58929
- trThisMonth: "This month",
58930
- trLastMonth: "Last month",
58931
- schedCustom: "{cron}",
58932
- settingsBasic: "Basic info",
58933
- settingsTitleFormat: "Title format",
58934
- settingsDesc: "Description",
58935
- settingsLang: "Interface language",
58936
- settingsLangValue: "Follow host (Chinese / English)",
58937
- settingsParams: "Runtime parameters (live)",
58938
- settingsLoopSec: "Loop interval (sec)",
58939
- settingsLoopHint: "How often the scheduler scans the task table.",
58940
- settingsLoopHelp: "Every this many seconds the dispatch loop scans for tasks that are due. Smaller = reacts sooner but scans more often and costs more; larger = the opposite. 60 seconds is usually enough.",
58941
- settingsWaitSec: "Dispatch grace (sec)",
58942
- settingsWaitHint: "How long to wait for the session to start after dispatch.",
58943
- settingsWaitHelp: "After a task is dispatched, this is how long we wait for its session to be created and start running. If it never starts, the run is marked \"wait timed out\" and the next schedule takes over.",
58944
- settingsLeaseSec: "Run lease (sec)",
58945
- settingsLeaseHint: "Reclaim the slot after this long with no sign of life.",
58946
- settingsLeaseHelp: "A running task renews its lease every time it is scanned. Only when there has been no sign of life for this long (e.g. the process hung) is it judged stuck and its concurrency slot reclaimed. A task that is genuinely running keeps renewing and is never cut off.",
58947
- settingsUnknownSec: "Unknown grace (sec)",
58948
- settingsUnknownHint: "How long to keep watching when the session state is unknown.",
58949
- settingsUnknownHelp: "When the session state cannot be read, keep watching for this long (it may be a transient host hiccup). Only if there is still no answer after that is the run judged abnormal and reclaimed.",
58950
- settingsSaveSuccess: "Settings saved",
58951
- settingsLoadFailed: "Failed to load current settings, please retry later",
58952
- settingsUnitSec: "sec",
58953
- loading: "Loading",
58954
- invalidNumber: "Please enter a valid integer number of seconds",
58955
- statusRailOff: "Disabled",
58956
- statusRailLastFailed: "Last run failed",
58957
- statusRailLastSkipped: "Last run skipped (config or prerequisites unsatisfied; see records)",
58958
- statusRailRunning: "Scheduled / running",
58959
- debugRowsSuffix: "rows",
58960
- tabSettings: "Settings",
58961
- settingsConfigTitle: "Plugin Settings",
58962
- settingsBlockLogTitle: "Plugin Log",
58963
- settingsBlockDataTitle: "Database Tables",
58964
- settingsCurrentConfig: "Config Preview (read-only)",
58965
- settingsDefaultValue: "default",
58966
- settingsProvider: "Default Model Provider",
58967
- settingsModel: "Default Model",
58968
- settingsLogRetention: "Run Log Retention (days)",
58969
- settingsLogRetentionHint: "How long diagnostic logs are kept before purging.",
58970
- settingsLogRetentionHelp: "Diagnostic logs produced during each run (task log + plugin log) are kept for this many days, then purged. Only log details are cleared — run records are always kept and never affected.",
58971
- settingsAttachmentRetention: "Temp Attachment Retention (days)",
58972
- settingsAttachmentRetentionHint: "How long attachment copies in the temp directory are kept.",
58973
- settingsAttachmentRetentionHelp: "When an attachment is uploaded or referenced, the plugin keeps a copy in its own temp directory, cleaned up after this many days. The attachment formally referenced by the task definition is not affected.",
58974
- settingsUnsaved: "You have unsaved changes",
58975
- settingsDefaultOption: "Default",
58976
- settingsModelHint: "With nothing selected, the host default model is used.",
58977
- settingsModelHelp: "Pick the provider first, then the concrete model — both are required. Leaving it empty (default model) means following the host default. New tasks inherit this choice, and each task can still override it.",
58978
- settingsCustom: "Custom",
58979
- settingsResetDefault: "Restore default",
58980
- settingsErrRange: "Enter a whole number between {min} and {max}.",
58981
- settingsErrPair: "Provider and model go together: if you pick one, you must pick the other.",
58982
- settingsErrUpdateFailed: "The settings could not be written (the host config surface is not ready yet). Please retry later.",
58983
- settingsErrInvalidField: "The value for \"{name}\" is not valid — please check and retry.",
58984
- settingsErrRangeField: "\"{name}\" is out of the allowed range ({min}–{max}).",
58985
- settingsErrEmpty: "There is nothing to save.",
58986
- settingsAutoRefresh: "Auto Refresh (5s)",
58987
- settingsRefresh: "Refresh",
58988
- settingsLogEmpty: "(no logs yet)",
58989
- settingsLogFail: "Failed to load logs",
58990
- settingsTable: "Table",
58991
- settingsTopN: "Top N Rows",
58992
- settingsPageSize: "Rows per page",
58993
- settingsAddFilter: "Add Filter",
58994
- settingsNoFilters: "(no filter: show all rows)",
58995
- settingsQuery: "Query",
58996
- settingsTableTruncated: "Row count exceeded limit; showing newest subset",
58997
- settingsTableEmpty: "(empty table: no records yet)",
58998
- settingsTableFail: "Failed to load",
58999
- settingsFilterColumn: "Column",
59000
- settingsFilterOp: "Operator",
59001
- settingsFilterValue: "Value",
59002
- settingsFilterRemove: "Remove filter",
59003
- settingsIntro: "Nothing to configure here — tasks and all settings live inside the plugin panel (sidebar \"Scheduled task dispatcher\").",
59004
- vTitleRequired: "Task name is missing — the list identifies tasks by name. Please give it one.",
59005
- vWorkspaceRequired: "No workspace selected — a task must belong to a workspace to run. Pick one above.",
59006
- vWorkspaceUnknown: "The selected workspace no longer exists (it may have been renamed or deleted). Pick a current workspace above.",
59007
- vPromptRequired: "The prompt is empty — it tells the agent what to do and cannot be blank.",
59008
- vIntervalInvalid: "Invalid interval — N in \"every N minutes/hours\" must be an integer greater than 0.",
59009
- vWeekdayRequired: "Weekly schedule with no weekday selected — pick at least one, or the task never runs.",
59010
- vAttachmentInvalid: "Attachment \"{name}\" has an invalid ref — it must be a workspace-relative path. Remove and re-pick it."
57656
+ /**
57657
+ * 受控值 → 查询用**半开区间** `[from, to)`:
57658
+ * - day:from = 当日 00:00,to = 结束日的**次日 00:00**;
57659
+ * - minute:from = 当时分 :00,to = 结束分的**下一分钟 :00**。
57660
+ * 空串 / 不合法 ⇒ 该端 undefined(不过滤)。
57661
+ */
57662
+ function rangeToQuery(value, precision) {
57663
+ const fromParsed = value.from === "" ? null : parseValue(value.from);
57664
+ const toParsed = value.to === "" ? null : parseValue(value.to);
57665
+ let fromTs;
57666
+ if (fromParsed !== null) {
57667
+ const d = precision === "minute" ? new Date(fromParsed.y, fromParsed.m - 1, fromParsed.d, fromParsed.hh, fromParsed.mm, 0, 0) : new Date(fromParsed.y, fromParsed.m - 1, fromParsed.d, 0, 0, 0, 0);
57668
+ fromTs = Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
57669
+ }
57670
+ let toTs;
57671
+ if (toParsed !== null) {
57672
+ const d = precision === "minute" ? new Date(toParsed.y, toParsed.m - 1, toParsed.d, toParsed.hh, toParsed.mm + 1, 0, 0) : new Date(toParsed.y, toParsed.m - 1, toParsed.d + 1, 0, 0, 0, 0);
57673
+ toTs = Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
57674
+ }
57675
+ return {
57676
+ fromTs,
57677
+ toTs
57678
+ };
57679
+ }
57680
+ // src/client/ui/TimeRange.tsx
57681
+ /**
57682
+ * 时间范围筛选 —— **核心 `DateTimePicker` 的薄包装**(2026-11-06 起)。
57683
+ *
57684
+ * 分工:
57685
+ * - 核心负责:触发器、两块日历、预设档竖列、「确定 / 取消 / 清除」、键盘与明暗;
57686
+ * - 本仓负责:值串 ⇄ 核心 ISO 的换算 + 预设档文案(`presets` 的 expr 由核心展开)。
57687
+ *
57688
+ * 值形态不变(`TimeRangeValue`:`YYYY-MM-DD` / `YYYY-MM-DD HH:mm`,空串 = 不过滤),
57689
+ * 查询边界仍由 `rangeToQuery`(`time-range.ts`)统一算,控件不碰边界。
57690
+ */
57691
+ /** 预设档 → 核心的 date-math 算式(整档取齐:`/d` `/w` `/M`;周一为周首,与核心一致)。 */
57692
+ const PRESET_EXPR = {
57693
+ today: "now/d",
57694
+ yesterday: "now-1d/d",
57695
+ thisWeek: "now/w",
57696
+ lastWeek: "now-1w/w",
57697
+ thisMonth: "now/M",
57698
+ lastMonth: "now-1M/M"
57699
+ };
57700
+ /** 值串 → 核心 ISO(本端 floor 到 00:00:00.000,对端 ceil 到 23:59:59.999)。 */
57701
+ function toIso(value, end, precision) {
57702
+ const minute = precision === "minute";
57703
+ const date = value.slice(0, 10);
57704
+ const time = minute ? value.length >= 16 ? value.slice(11, 16) : end ? "23:59" : "00:00" : "00:00";
57705
+ return minute ? `${date}T${time}:${end ? "59.999" : "00.000"}` : `${date}T${end ? "23:59:59.999" : "00:00:00.000"}`;
57706
+ }
57707
+ /** 本仓值 → 核心受控值;任一端为空 ⇒ 交给核心显示「未选」(核心的 range 需要两端齐全才提交)。 */
57708
+ function toCoreValue(value, precision) {
57709
+ if (value.from === "" || value.to === "") return null;
57710
+ return [toIso(value.from, false, precision), toIso(value.to, true, precision)];
57711
+ }
57712
+ /** 核心受控值 → 本仓值:day 取日期,minute 取到分钟(与 `TimeRangeValue` 的口径一致)。 */
57713
+ function fromCoreValue(out, precision) {
57714
+ if (out === null || Array.isArray(out) === false) return {
57715
+ from: "",
57716
+ to: ""
57717
+ };
57718
+ const fmt = (iso) => precision === "minute" ? `${iso.slice(0, 10)} ${iso.slice(11, 16)}` : iso.slice(0, 10);
57719
+ return {
57720
+ from: fmt(out[0]),
57721
+ to: fmt(out[1])
57722
+ };
57723
+ }
57724
+ function TimeRange(props) {
57725
+ const precision = props.precision ?? "day";
57726
+ const presetIds = props.presets ?? ALL_TIME_PRESETS;
57727
+ const presets = [{
57728
+ label: props.labels.all,
57729
+ expr: null
57730
+ }, ...presetIds.map((id) => ({
57731
+ label: props.labels.presets[id],
57732
+ expr: PRESET_EXPR[id]
57733
+ }))];
57734
+ return (0, react$1.createElement)(DateTimePicker, {
57735
+ mode: "range",
57736
+ precision,
57737
+ value: toCoreValue(props.value, precision),
57738
+ onChange: (next) => {
57739
+ props.onChange(fromCoreValue(next, precision));
57740
+ },
57741
+ presets,
57742
+ size: props.size ?? "md",
57743
+ disabled: props.disabled
57744
+ });
57745
+ }
57746
+ // src/client/ui/ticker.ts
57747
+ const tickerListeners = /* @__PURE__ */ new Set();
57748
+ let tickerTimer = null;
57749
+ /**
57750
+ * `visibilitychange` 处理器**只注册一次**(模块级)——2026-09-30 专家团复核:此前每次订阅起停
57751
+ * 都 `addEventListener` 且从不移除 ⇒ 反复重挂面板会累积 N 个监听、切回标签页时同一批订阅被调 N 次。
57752
+ * 这里注册一次、常驻(订阅集合空时遍历即空转,无副作用)。
57753
+ */
57754
+ const onVisibilityChange = () => {
57755
+ for (const l of [...tickerListeners]) l();
57756
+ };
57757
+ let visibilityBound = false;
57758
+ /** 订阅全局秒级心跳;返回退订。**第一个订阅者才起 interval,最后一个退订即停**。 */
57759
+ function subscribeTicker(cb) {
57760
+ tickerListeners.add(cb);
57761
+ if (tickerTimer === null) {
57762
+ tickerTimer = window.setInterval(() => {
57763
+ for (const l of [...tickerListeners]) l();
57764
+ }, 1e3);
57765
+ if (!visibilityBound && typeof document !== "undefined") {
57766
+ document.addEventListener("visibilitychange", onVisibilityChange);
57767
+ visibilityBound = true;
57768
+ }
57769
+ }
57770
+ return () => {
57771
+ tickerListeners.delete(cb);
57772
+ if (tickerListeners.size === 0 && tickerTimer !== null) {
57773
+ window.clearInterval(tickerTimer);
57774
+ tickerTimer = null;
57775
+ }
57776
+ };
57777
+ }
57778
+ /**
57779
+ * 每秒拿到「现在」(需要**时间值本身**时用,如悬浮文案);与 `LiveText` 共用同一条心跳。
57780
+ * 不适合整列表用(每次 tick 会 setState 一次)——只在真正需要时间的小块里调。
57781
+ */
57782
+ function useNowMs() {
57783
+ const [nowMs, setNowMs] = (0, react$1.useState)(() => Date.now());
57784
+ (0, react$1.useEffect)(() => subscribeTicker(() => setNowMs(Date.now())), []);
57785
+ return nowMs;
57786
+ }
57787
+ // src/client/ui/running.ts
57788
+ /** 运行中配色:蓝(前后两端统一;成功绿 / 失败红留给终态)。 */
57789
+ const RUNNING_TONE = "var(--dshpc-business)";
57790
+ /** 脉动动画类:挂到任意元素上即获得「运行中」明暗脉冲(与执行记录页同款 keyframe,单一定义)。 */
57791
+ const RUN_PULSE_CLASS = "dsh-tdt-run-pulse";
57792
+ const RUNNING_CSS = `
57793
+ @keyframes dsh-tdt-run-pulse { 0%,100% { opacity: 1 } 50% { opacity: .35 } }
57794
+ .${RUN_PULSE_CLASS} { animation: dsh-tdt-run-pulse var(--dshpc-dur-run) var(--dshpc-ease) infinite; }
57795
+ @media (prefers-reduced-motion: reduce) { .${RUN_PULSE_CLASS} { animation: none; } }
57796
+ `;
57797
+ let ensured = false;
57798
+ /** 幂等注入「运行中」脉动样式(keyframe + 动画类,全局生效一次即可)。 */
57799
+ const ensureRunningStyle = () => {
57800
+ if (ensured) return;
57801
+ ensured = true;
57802
+ applyStyle("domain:running", RUNNING_CSS);
57803
+ };
57804
+ // src/client/ui/index.ts
57805
+ /**
57806
+ * 主面板**内容列**的统一宽度锚点(任务配置 / 执行记录 两个 tab 必须一模一样,切换时不横向跳动)。
57807
+ * 同时它是基础层 `Loading` 的锚点契约(`Loading` 默认 `anchorId = PANEL_CONTENT_ID`)——
57808
+ * 所以**新页面也必须用同一个 id**,否则浮动 loading 找不到锚点、贴不到内容右缘。
57809
+ */
57810
+ const PANEL_CONTENT_ID = "dsh-tdt-main";
57811
+ /** 内容列几何(居中限宽 760 / 1120)。 */
57812
+ const PANEL_CONTENT_STYLE = {
57813
+ width: "100%",
57814
+ maxWidth: "1120px",
57815
+ minWidth: "760px",
57816
+ boxSizing: "border-box"
59011
57817
  };
59012
- // src/client/ui/style.ts
57818
+ // src/client/sidebar-entry.ts
59013
57819
  /**
59014
- * 样式注入器(薄壳):实现全部交给 `dsh-plugin-core` 的 style kit。
59015
- *
59016
- * 保留本文件只为两件事:
59017
- * ① 固定本插件的 `<style>` id(多插件同页互不覆盖);
59018
- * ② 给历史调用点(`ensureXxxStyle()` / `applyStyle`)一个过渡入口。
59019
- * 注入机制、域排序、token 表都不再在本仓实现 —— 真源是
59020
- * `dsh-plugin-core/design`(`createStyleKit` + `tokens.css`)。
57820
+ * 侧栏入口的「显示名 + 位置」:按插件设置把入口挂到一级菜单(`sidebar.panellist`)或
57821
+ * 「更多」菜单(`sidebar.panellist.actions`),并在设置变化后重挂。
59021
57822
  *
59022
- * 规矩:全站仍然只有**一处**注入器(kit 的标签),业务文件不许自己
59023
- * `document.createElement('style')`,也不许再定义 `--dshpc-*`。
57823
+ * 做法照抄兄弟仓 `dsh-task-ledger` 的同款模块(2026-11-08 用户要求「跟它几乎一模一样」):
57824
+ * 两个 slot 的 owner 到达顺序、以及设置与 slots 两块注入谁先到都没有保证 ⇒ 每次都用
57825
+ * **模块级当前值**重挂一遍;重复调用无害。
59024
57826
  */
59025
- /** 本插件的样式标签 id(改名 = 大范围回归,不要动)。 */
59026
- const UI_STYLE_ID = "dsh-task-dispatch-table-ui";
59027
- /** token 域固定名(kit 内 `tokens` 恒排最前)。 */
59028
- const TOKENS_DOMAIN = "tokens";
59029
- setStyleKit(createStyleKit({ id: UI_STYLE_ID }));
59030
- const active = getStyleKit();
59031
- /** 登记一个域的样式(同名重复登记 = 覆盖)。 */
59032
- function registerStyle(domain, css) {
59033
- active.registerStyle(domain, css);
57827
+ /** 主面板 id:`main` 槽的 key 与 `sidebar.panellist` 条目的 id 必须一致,选中才对得上。 */
57828
+ const PANEL_ID = "dsh-task-dispatch-table";
57829
+ let entryMode = "first";
57830
+ /** 自定义显示名;空串 = 用词典默认名。 */
57831
+ let customName = "";
57832
+ let slotsRef = null;
57833
+ let iconFace = null;
57834
+ let localeNs = PANEL_ID;
57835
+ let defaultLabel = () => PANEL_ID;
57836
+ /** 「更多」那一行点击后切到本面板(布局服务由 index 注入)。 */
57837
+ let navigate = () => {};
57838
+ let injected = null;
57839
+ let disposeEntry = null;
57840
+ /** 侧栏行标题:自定义名优先,空则回落词典默认名。 */
57841
+ function entryLabel() {
57842
+ return customName === "" ? defaultLabel() : customName;
57843
+ }
57844
+ /** 「更多」菜单里的一行(官方 menu 行样式,点完先关菜单再切页面)。 */
57845
+ function MoreEntry(props) {
57846
+ const icon = iconFace;
57847
+ return (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.MenuItemButton, {
57848
+ ...icon === null ? {} : { icon: (0, react$1.createElement)(icon, { size: 16 }) },
57849
+ onSelect: () => {
57850
+ props.onDismiss?.();
57851
+ navigate(PANEL_ID);
57852
+ },
57853
+ children: entryLabel()
57854
+ });
59034
57855
  }
59035
- /** 确保标签已挂载且内容最新(幂等)。 */
59036
- function ensureUiStyles() {
59037
- active.ensureStyles();
57856
+ /** 按当前 `entryMode` 重挂入口:先摘旧的 factory 与旧条目,再按现值注册。 */
57857
+ function applyEntry() {
57858
+ const slots = slotsRef;
57859
+ if (slots === null || iconFace === null) return;
57860
+ injected?.();
57861
+ disposeEntry?.();
57862
+ injected = null;
57863
+ disposeEntry = null;
57864
+ if (entryMode === "more") {
57865
+ injected = slots.inject("sidebar.panellist.actions", () => {
57866
+ disposeEntry = slots.register({
57867
+ name: "sidebar.panellist.actions",
57868
+ id: `${PANEL_ID}-more`,
57869
+ order: 30,
57870
+ locale: localeNs
57871
+ }, MoreEntry);
57872
+ return disposeEntry;
57873
+ });
57874
+ return;
57875
+ }
57876
+ const icon = iconFace;
57877
+ injected = slots.inject("sidebar.panellist", () => {
57878
+ disposeEntry = slots.register({
57879
+ name: "sidebar.panellist",
57880
+ id: PANEL_ID,
57881
+ order: 30,
57882
+ label: entryLabel,
57883
+ locale: localeNs
57884
+ }, (props) => (0, react$1.createElement)(icon, { size: props.size ?? 18 }));
57885
+ return disposeEntry;
57886
+ });
59038
57887
  }
59039
- /** 登记 + 立刻确保挂载。 */
59040
- function applyStyle(domain, css) {
59041
- active.applyStyle(domain, css);
57888
+ /**
57889
+ * 接上 slots 并挂入口(每次 slots 注入都会重跑一遍,幂等)。
57890
+ * @param options - slots 面、行图标、词典默认名与命名空间。
57891
+ */
57892
+ function initSidebarEntry(options) {
57893
+ slotsRef = options.slots;
57894
+ iconFace = options.icon;
57895
+ localeNs = options.locale;
57896
+ defaultLabel = options.defaultLabel;
57897
+ applyEntry();
59042
57898
  }
59043
- /** 登记核心 token 层并确保样式已注入(幂等)。 */
59044
- function ensureUiBase() {
59045
- registerStyle(TOKENS_DOMAIN, active.tokensCss);
59046
- ensureUiStyles();
57899
+ /** 布局服务就位后注入:点「更多」那一行才能切到本面板。 */
57900
+ function setSidebarNavigator(fn) {
57901
+ navigate = fn;
57902
+ }
57903
+ /**
57904
+ * 从 `GET /config` 取「显示名 / 入口位置」并重挂入口。
57905
+ * 读不到就维持当前外观(侧栏不能因为配置读失败而消失)。
57906
+ */
57907
+ async function refreshAppearance() {
57908
+ try {
57909
+ const body = await (await fetchWithTimeout(`${API_PREFIX}/config`, { cache: "no-store" })).json();
57910
+ if (body.ok !== true) return;
57911
+ const name = body.config?.displayName;
57912
+ customName = typeof name === "string" ? name.trim() : "";
57913
+ entryMode = body.config?.entryMode === "first" ? "first" : "more";
57914
+ applyEntry();
57915
+ } catch {}
57916
+ }
57917
+ // src/client/config-panel.tsx
57918
+ /** 本仓库地址(设置区的第一个链接)。 */
57919
+ const PROJECT_URL = "https://github.com/cq-guojia/dsh-task-dispatch-table";
57920
+ /** 友情链接:组件库(UI 基础控件包)。 */
57921
+ const COMPONENTS_URL = "https://github.com/cq-guojia/dsh-plugin-core";
57922
+ /** 设置区三个字段的默认值(与 host 侧 `CONFIG_DEFAULTS` 同值;用来看「是否改过」)。 */
57923
+ const DEFAULTS = {
57924
+ displayName: "",
57925
+ entryMode: "first",
57926
+ hideOfficialSchedule: false
57927
+ };
57928
+ /**
57929
+ * 详情页与面板页的 URL 目录不同:先按相对路径打,失败再按根路径打一次。
57930
+ * 403/404 时 `res.ok` 为假 / 返回非 JSON,都会落到下一次尝试。
57931
+ */
57932
+ async function requestConfig(init) {
57933
+ let last = null;
57934
+ for (const base of [API_PREFIX, `/${API_PREFIX}`]) try {
57935
+ const res = await fetchWithTimeout(`${base}/config`, {
57936
+ cache: "no-store",
57937
+ ...init
57938
+ });
57939
+ const body = await res.json();
57940
+ if (body.ok !== true) throw new Error(`config ${res.status}`);
57941
+ const config = body.config ?? {};
57942
+ return {
57943
+ displayName: typeof config.displayName === "string" ? config.displayName : DEFAULTS.displayName,
57944
+ entryMode: config.entryMode === "first" ? "first" : "more",
57945
+ hideOfficialSchedule: config.hideOfficialSchedule === true
57946
+ };
57947
+ } catch (error) {
57948
+ last = error;
57949
+ }
57950
+ throw last instanceof Error ? last : /* @__PURE__ */ new Error("config unavailable");
57951
+ }
57952
+ const CONFIG_CSS = `
57953
+ .tdt-config__pair{display:grid;grid-template-columns:1fr 1fr;gap:16px;align-items:start;
57954
+ border-top:1px solid var(--dshpc-border);}
57955
+ .tdt-config__field{display:flex;flex-direction:column;gap:6px;padding:24px 0 12px;}
57956
+ .tdt-config__head{display:flex;align-items:center;gap:8px;}
57957
+ .tdt-config__label{flex:1;min-width:0;font-size:var(--dshpc-font-md);font-weight:500;
57958
+ line-height:var(--dshpc-line-md);color:var(--dshpc-fg);}
57959
+ .tdt-config__hint{margin:0;font-size:var(--dshpc-font-sm);line-height:var(--dshpc-line-sm);
57960
+ color:var(--dshpc-fg-3);}
57961
+ .tdt-config__row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
57962
+ margin-top:24px;padding:0 0 32px;border-bottom:1px solid var(--dshpc-border);}
57963
+ .tdt-config__failed{margin:0;font-size:var(--dshpc-font-sm);line-height:var(--dshpc-line-sm);
57964
+ color:var(--dshpc-danger);}
57965
+ .tdt-config__note{margin:0;font-size:var(--dshpc-font-sm);line-height:var(--dshpc-line-sm);
57966
+ color:var(--dshpc-fg-3);}
57967
+ `;
57968
+ /** 一个字段:标签行(标签 + 改过徽标 + 恢复默认)→ 控件 → 说明。 */
57969
+ function configField(props) {
57970
+ return (0, react$1.createElement)("div", { className: "tdt-config__field" }, (0, react$1.createElement)("div", { className: "tdt-config__head" }, (0, react$1.createElement)("span", {
57971
+ className: "tdt-config__label",
57972
+ id: props.id
57973
+ }, props.label), props.overridden ? (0, react$1.createElement)(Chip, {
57974
+ tone: "neutral",
57975
+ size: "xs"
57976
+ }, props.overriddenLabel) : null, props.overridden ? (0, react$1.createElement)(Button$1, {
57977
+ variant: "link",
57978
+ size: "sm",
57979
+ onClick: props.onReset
57980
+ }, props.resetLabel) : null), props.control, (0, react$1.createElement)("p", { className: "tdt-config__hint" }, props.hint));
57981
+ }
57982
+ /** 一个小外链按钮(图标 + 文案,核心 Button,尺寸与兄弟仓一致)。 */
57983
+ const linkButton = (label, url) => (0, react$1.createElement)(Button$1, {
57984
+ size: "lg",
57985
+ variant: "outline",
57986
+ icon: (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconLinkOutlineRegular, { size: 16 }),
57987
+ onClick: () => {
57988
+ window.open(url, "_blank", "noopener");
57989
+ }
57990
+ }, label);
57991
+ function ConfigPanel(props) {
57992
+ const { t, view } = props;
57993
+ const [loaded, setLoaded] = (0, react$1.useState)(null);
57994
+ const [draft, setDraft] = (0, react$1.useState)(null);
57995
+ const [saving, setSaving] = (0, react$1.useState)(false);
57996
+ const [failed, setFailed] = (0, react$1.useState)(false);
57997
+ (0, react$1.useEffect)(() => {
57998
+ applyStyle("domain:config-panel", CONFIG_CSS);
57999
+ }, []);
58000
+ const load = (0, react$1.useCallback)(async () => {
58001
+ try {
58002
+ const next = await requestConfig();
58003
+ setLoaded(next);
58004
+ setDraft(next);
58005
+ setFailed(false);
58006
+ } catch {
58007
+ setLoaded(DEFAULTS);
58008
+ setDraft(DEFAULTS);
58009
+ setFailed(true);
58010
+ }
58011
+ }, []);
58012
+ (0, react$1.useEffect)(() => {
58013
+ load();
58014
+ }, [load]);
58015
+ if (view !== void 0 && view !== "page") return null;
58016
+ if (draft === null || loaded === null) return (0, react$1.createElement)("p", {
58017
+ className: "tdt-config__note",
58018
+ role: "status"
58019
+ }, t("configUnavailable"));
58020
+ const dirty = JSON.stringify(draft) !== JSON.stringify(loaded);
58021
+ const mode = draft.entryMode === "more" ? "more" : "first";
58022
+ const save = () => {
58023
+ setSaving(true);
58024
+ setFailed(false);
58025
+ (async () => {
58026
+ try {
58027
+ await requestConfig({
58028
+ method: "POST",
58029
+ headers: { "content-type": "application/json" },
58030
+ body: JSON.stringify(draft)
58031
+ });
58032
+ setLoaded(draft);
58033
+ refreshAppearance();
58034
+ } catch {
58035
+ setFailed(true);
58036
+ } finally {
58037
+ setSaving(false);
58038
+ }
58039
+ })();
58040
+ };
58041
+ return (0, react$1.createElement)("div", null, (0, react$1.createElement)("div", { className: "tdt-config__pair" }, configField({
58042
+ id: "tdt-config-name",
58043
+ label: t("configNameLabel"),
58044
+ hint: t("configNameHint"),
58045
+ overridden: draft.displayName.trim() !== DEFAULTS.displayName,
58046
+ overriddenLabel: t("configOverridden"),
58047
+ resetLabel: t("configReset"),
58048
+ onReset: () => {
58049
+ setDraft({
58050
+ ...draft,
58051
+ displayName: DEFAULTS.displayName
58052
+ });
58053
+ },
58054
+ control: (0, react$1.createElement)(Input, {
58055
+ value: draft.displayName,
58056
+ onChange: (text) => {
58057
+ setDraft({
58058
+ ...draft,
58059
+ displayName: text
58060
+ });
58061
+ },
58062
+ placeholder: t("panelTitle"),
58063
+ size: "lg",
58064
+ "aria-label": t("configNameLabel")
58065
+ })
58066
+ }), configField({
58067
+ id: "tdt-config-entry",
58068
+ label: t("configEntryLabel"),
58069
+ hint: t("configEntryHint"),
58070
+ overridden: mode !== DEFAULTS.entryMode,
58071
+ overriddenLabel: t("configOverridden"),
58072
+ resetLabel: t("configReset"),
58073
+ onReset: () => {
58074
+ setDraft({
58075
+ ...draft,
58076
+ entryMode: DEFAULTS.entryMode
58077
+ });
58078
+ },
58079
+ control: (0, react$1.createElement)(Picker, {
58080
+ value: mode,
58081
+ options: [{
58082
+ value: "first",
58083
+ label: t("configEntryFirst")
58084
+ }, {
58085
+ value: "more",
58086
+ label: t("configEntryMore")
58087
+ }],
58088
+ mode: "single",
58089
+ size: "lg",
58090
+ width: "100%",
58091
+ ariaLabel: t("configEntryLabel"),
58092
+ onChange: (next) => {
58093
+ const picked = String(next);
58094
+ if (picked !== mode) setDraft({
58095
+ ...draft,
58096
+ entryMode: picked === "more" ? "more" : "first"
58097
+ });
58098
+ }
58099
+ })
58100
+ }), configField({
58101
+ id: "tdt-config-hide-official",
58102
+ label: t("configHideOfficialLabel"),
58103
+ hint: t("configHideOfficialHint"),
58104
+ overridden: draft.hideOfficialSchedule !== DEFAULTS.hideOfficialSchedule,
58105
+ overriddenLabel: t("configOverridden"),
58106
+ resetLabel: t("configReset"),
58107
+ onReset: () => {
58108
+ setDraft({
58109
+ ...draft,
58110
+ hideOfficialSchedule: DEFAULTS.hideOfficialSchedule
58111
+ });
58112
+ },
58113
+ control: (0, react$1.createElement)(Picker, {
58114
+ value: draft.hideOfficialSchedule ? "hide" : "show",
58115
+ options: [{
58116
+ value: "show",
58117
+ label: t("configHideOff")
58118
+ }, {
58119
+ value: "hide",
58120
+ label: t("configHideOn")
58121
+ }],
58122
+ mode: "single",
58123
+ size: "lg",
58124
+ width: "100%",
58125
+ ariaLabel: t("configHideOfficialLabel"),
58126
+ onChange: (next) => {
58127
+ const picked = String(next) === "hide";
58128
+ if (picked !== draft.hideOfficialSchedule) setDraft({
58129
+ ...draft,
58130
+ hideOfficialSchedule: picked
58131
+ });
58132
+ }
58133
+ })
58134
+ })), (0, react$1.createElement)("div", { className: "tdt-config__row" }, (0, react$1.createElement)(Button$1, {
58135
+ variant: "primary",
58136
+ size: "lg",
58137
+ disabled: !dirty || saving,
58138
+ onClick: save
58139
+ }, saving ? t("configSaving") : t("configSave")), linkButton(t("configGithub"), PROJECT_URL), linkButton(t("configComponents"), COMPONENTS_URL), failed ? (0, react$1.createElement)("p", {
58140
+ className: "tdt-config__failed",
58141
+ role: "status"
58142
+ }, t("configSaveFailed")) : null));
58143
+ }
58144
+ // src/client/error-text.ts
58145
+ /**
58146
+ * 把宿主返回的机器码错误翻成人话(保底用:客户端校验已拦掉绝大多数必填问题,这里只兜底漏网的)。
58147
+ * 命中已知 zod 片段就翻译,否则原样返回(前缀「任务定义不合法(…)」尽量保留上下文)。
58148
+ */
58149
+ function humanizeTaskError(raw) {
58150
+ if (raw.includes("target.workspace") && raw.toLowerCase().includes("too small")) return "工作区不能为空,请先选择工作区";
58151
+ if (raw.includes("target.prompt") && raw.toLowerCase().includes("too small")) return "提示词不能为空,请先填写提示词";
58152
+ if (raw.includes("title") && raw.toLowerCase().includes("too small")) return "任务名称不能为空";
58153
+ if (raw.includes("ISO 8601") || raw.includes("schedule.window")) return "「允许延迟」的时长不合法——请从下拉里重选一个(如 4 小时)。";
58154
+ if (raw.includes("schedule.cron")) return "执行排期不合法——请重新选一次执行频率。";
58155
+ if (raw.includes("附件 ref 非法") || raw.includes("attachments") && raw.includes("ref")) return "附加文件的引用路径不合法——必须是工作区内的相对路径。请删掉那个附件、重新选择一次。";
58156
+ return raw;
58157
+ }
58158
+ // src/client/event-subscribe.ts
58159
+ /** 事件流地址:前缀取自唯一真源 `query.ts`(M9;不 import index 以免成环)。 */
58160
+ const EVENTS_URL = `${API_PREFIX}/events`;
58161
+ /** 看门狗巡检间隔。 */
58162
+ const WATCHDOG_MS = 5e3;
58163
+ /** 连续未连上的容忍上限:超过它主动重建连接。 */
58164
+ const RECONNECT_AFTER_MS = 3e4;
58165
+ /**
58166
+ * 「连上了但收不到东西」的容忍上限(毫秒):服务端每 20s 必发一条心跳 `sys.ping`(**真实 data 帧**,
58167
+ * 客户端 `onmessage` 见得到)⇒ 超过 3 倍心跳仍一无所获,判定为**半死连接**(反向代理静默丢流、
58168
+ * 或没有 FIN 的黑洞),主动重建。这是 `readyState` 判据覆盖不到的那一半(2026-10-06 审计 🟡)。
58169
+ */
58170
+ const SILENT_AFTER_MS = 75e3;
58171
+ const byType = /* @__PURE__ */ new Map();
58172
+ const resyncHandlers = /* @__PURE__ */ new Set();
58173
+ let source = null;
58174
+ let watchdog = null;
58175
+ /** 本轮「未连上」的起始时刻(0 = 当前处于 OPEN)。 */
58176
+ let unhealthySince = 0;
58177
+ /** 最后一次**收到任何帧**的时刻(含心跳)——半死连接的唯一可观测判据。 */
58178
+ let lastSeenAt = 0;
58179
+ /** 「本环境没有 EventSource」只告警一次(降级会让所有页面失去刷新通道,必须留痕又不能刷屏)。 */
58180
+ let warnedNoEventSource = false;
58181
+ /** 已重建过几次连接(含看门狗主动重建)——**排查手册的关键数字**(2026-10-07 可观测性审计)。 */
58182
+ let reconnects = 0;
58183
+ /** 广播「(重)连成功」——各页据此重读一次当前值。 */
58184
+ function dispatchResync() {
58185
+ for (const handler of [...resyncHandlers]) try {
58186
+ handler();
58187
+ } catch {}
58188
+ }
58189
+ function openSource() {
58190
+ if (source !== null) reconnects += 1;
58191
+ let es;
58192
+ try {
58193
+ es = new EventSource(EVENTS_URL);
58194
+ } catch (error) {
58195
+ console.warn("[tdt] EventSource 建立失败,稍后重试:", error);
58196
+ return;
58197
+ }
58198
+ lastSeenAt = Date.now();
58199
+ es.onmessage = (msg) => {
58200
+ lastSeenAt = Date.now();
58201
+ let event;
58202
+ try {
58203
+ event = JSON.parse(msg.data);
58204
+ } catch {
58205
+ return;
58206
+ }
58207
+ const handlers = byType.get(event.type);
58208
+ if (handlers === void 0) return;
58209
+ for (const handler of [...handlers]) try {
58210
+ handler(event);
58211
+ } catch (error) {
58212
+ console.error(`[tdt] 事件订阅回调抛异常(该页可能停止更新),type=${event.type}:`, error);
58213
+ }
58214
+ };
58215
+ es.onopen = () => {
58216
+ unhealthySince = 0;
58217
+ dispatchResync();
58218
+ };
58219
+ source = es;
58220
+ }
58221
+ /** 看门狗:连不上超过阈值 ⇒ 主动重建(浏览器一直重试却连不上时的唯一出路)。 */
58222
+ function startWatchdog() {
58223
+ if (watchdog !== null) return;
58224
+ watchdog = window.setInterval(() => {
58225
+ if (source !== null && source.readyState === EventSource.OPEN) {
58226
+ if (Date.now() - lastSeenAt > SILENT_AFTER_MS) {
58227
+ source.close();
58228
+ openSource();
58229
+ unhealthySince = 0;
58230
+ return;
58231
+ }
58232
+ unhealthySince = 0;
58233
+ return;
58234
+ }
58235
+ if (unhealthySince === 0) {
58236
+ unhealthySince = Date.now();
58237
+ return;
58238
+ }
58239
+ if (Date.now() - unhealthySince < RECONNECT_AFTER_MS) return;
58240
+ source?.close();
58241
+ openSource();
58242
+ unhealthySince = 0;
58243
+ }, WATCHDOG_MS);
58244
+ }
58245
+ /** 懒建单例连接(首个订阅者出现时才连;**建了就不主动关**——「只要页面在,就有重连机制」)。 */
58246
+ function ensureSource() {
58247
+ if (typeof EventSource === "undefined") {
58248
+ if (!warnedNoEventSource) {
58249
+ warnedNoEventSource = true;
58250
+ console.warn("[tdt] 本环境没有 EventSource:事件推送不可用,页面只在打开时取一次数据。");
58251
+ }
58252
+ return;
58253
+ }
58254
+ startWatchdog();
58255
+ if (source !== null) return;
58256
+ unhealthySince = 0;
58257
+ openSource();
58258
+ }
58259
+ /** 订阅一组事件类型;`handler` 每次渲染都换也不会反复重建订阅(内部走 ref)。 */
58260
+ function useEvents(types, handler) {
58261
+ const ref = (0, react$1.useRef)(handler);
58262
+ ref.current = handler;
58263
+ const key = types.join(",");
58264
+ (0, react$1.useEffect)(() => {
58265
+ ensureSource();
58266
+ const stable = (event) => ref.current(event);
58267
+ const sets = [];
58268
+ for (const type of types) {
58269
+ let set = byType.get(type);
58270
+ if (set === void 0) {
58271
+ set = /* @__PURE__ */ new Set();
58272
+ byType.set(type, set);
58273
+ }
58274
+ set.add(stable);
58275
+ sets.push(set);
58276
+ }
58277
+ return () => {
58278
+ for (const set of sets) set.delete(stable);
58279
+ };
58280
+ }, [key]);
59047
58281
  }
59048
- // src/client/ui/controls-css.ts
59049
- /**
59050
- * L2 组件皮肤:公用控件规则(一类控件**唯一实现**)
59051
- *
59052
- * 规矩(docs/design/ui-style-guide.md §三):
59053
- * - **只消费 `var(--dshpc-*)`**,一个颜色 / 尺寸 / 圆角 / 字号字面量都不许出现;
59054
- * - 结构、尺寸、交互规则只此一份;「外观差异」只允许靠**覆盖一两个变量**表达。
59055
- *
59056
- * 2026-11-06:分段 / 按钮 / 输入 / 数字步进 / 多行文本 / 跑马灯 / 下拉 / 日期时间 / 小问号
59057
- * 已全部改接核心 `dsh-plugin-core/ui`,对应本地皮肤整段删除。
59058
- * 本文件只剩**核心没有**的业务件:产出物 chip、开关选色,「过程文件」块。
59059
- */
59060
- /** 产出物图标 chip 的皮肤规则。 */
59061
- const BUTTON_CSS = `
59062
- /* ── 产出物「图标 chip」:28×28 方角小底板 + 官方文件类型图标 ──────────────
59063
- 用途:产出物列表里**只给图标**的小按钮(卡片「执行记录」面板、执行记录总查询页、展开区都挂它)。
59064
- ⚠️ 底板**必须走 class**——inline background 会盖掉 :hover(用户 2026-10-03 踩过)。
59065
- hover 用「--dshpc-chip-bg-hover」:浅色更深、暗色更亮(两端都是「更明显」);不许拿
59066
- 「--dshpc-plate-hover」顶——暗色下反而更淡,鼠标移上去底板就消失了。 */
59067
- .dsh-tdt-chip{appearance:none;-webkit-appearance:none;box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;
59068
- width:var(--dshpc-control-h-md);height:var(--dshpc-control-h-md);padding:0;border:none;border-radius:var(--dshpc-radius-sm);
59069
- background:transparent;color:var(--dshpc-fg-2);font-family:inherit;line-height:0;cursor:pointer;
59070
- transition:background-color var(--dshpc-dur) var(--dshpc-ease),color var(--dshpc-dur) var(--dshpc-ease);}
59071
- .dsh-tdt-chip:hover:not(:disabled){background:var(--dshpc-chip-bg-hover);}
59072
- .dsh-tdt-chip:focus-visible{outline:2px solid var(--dshpc-focus);outline-offset:1px;}
59073
- .dsh-tdt-chip:disabled{cursor:default;background:transparent;color:var(--dshpc-fg-3);}
59074
- .dsh-tdt-chip--label{width:auto;padding:0 6px;font-size:var(--dshpc-font-md);line-height:var(--dshpc-line-md);}
59075
-
59076
- /* 文件行(图标 + 文件名)统一走核心 **Button ghost**(2026-11-06 用户拍板:
59077
- 「一个图标后面跟着文字」——产出物 / 其他产出 / 过程文件 / 执行记录展开区 / 附件全站同一套皮肤,
59078
- 圆角、hover、内边距全归核心按钮;本仓不再保留第二份文件行皮肤。 */
59079
-
59080
- @media (prefers-reduced-motion: reduce){.dsh-tdt-chip{transition:none;}}
59081
- `;
59082
- /** 开关 / 拖拽分隔条的皮肤规则(核心没有的宿主件)。 */
59083
- const FIELD_CSS = `
59084
- /* 官方 Switch 包装统一:选中 = success 绿。 */
59085
- .dsh-tdt-switch button[role='switch'][aria-checked='true']{background:var(--dshpc-success);}
59086
-
59087
- /* 拖拽分隔条已随两条分栏一起归核心 PanelDock(2026-11-06),本仓不再自绘命中条。 */
59088
- `;
59089
- /** 下拉的皮肤规则(核心 `Picker` 暂缺 `error` 档,这里只补一条描红)。 */
59090
- const SELECT_CSS = `
59091
- /* 核心 Picker 的校验描红(className 挂在 .dshpc-picker 根上)。 */
59092
- .dsh-tdt-picker--error .dshpc-picker__trigger{border-color:var(--dshpc-danger);box-shadow:0 0 0 1px var(--dshpc-danger);}
59093
- /* 整行下拉:核心 Picker 根是 inline-block,要显式撑满父宽。 */
59094
- .dsh-tdt-picker--block{display:block;width:100%;}
59095
-
59096
- /* 省略号三件套(ui-style-guide §三「待抽象 #1」的收敛点):**新代码一律挂这个类**,不再各写一遍。 */
59097
- .dsh-tdt-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
59098
- `;
59099
- /** 「过程文件」块的皮肤规则(2026-10-10:回执过程文件桶的次级展示件)。 */
59100
- const PROCESS_FILES_CSS = `
59101
- /* ── 「过程文件」块:会话弹窗交付卡下方 / 执行记录展开区 / 查看档产出清单三处共用 ──────────
59102
- (结构在 client/process-files.tsx)。观感纪律 = **次级**:小字浅灰标题 + 数量、默认收起,
59103
- 不许抢主文件(交付卡 / 产出物清单)的视线;展开后的文件行 = 核心 Button ghost(图标 + 文字,全站同一套)。 */
59104
- .dsh-tdt-proc{display:flex;flex-direction:column;gap:4px;margin-top:10px;min-width:0;}
59105
- .dsh-tdt-proc-list{display:flex;flex-wrap:wrap;gap:2px 4px;min-width:0;}
59106
-
59107
- `;
59108
- /** 细滚动条只认核心的 `.dshpc-scroll-thin`(3px,design/tokens 域全局注入、挂类即生效)。
59109
- * 2026-11-08 用户点名删掉本仓自绘的 1px 版:本插件**不再定义任何 ::-webkit-scrollbar 规则**。
59110
- * 该类只作用于挂它的那个容器(外加其 .cm-scroller 后代),所以每个 `overflow:auto` 的容器
59111
- * 都要自己挂 `dshpc-scroll-thin`,见各组件。 */
59112
- /** 控件皮肤域的固定名(注入顺序在 tokens 之后)。 */
59113
- const CONTROLS_DOMAIN = "controls";
59114
- /**
59115
- * 确保控件皮肤已登记并注入(幂等;组件渲染时调用一次即可)。
59116
- */
59117
- function ensureControlsStyle() {
59118
- applyStyle(CONTROLS_DOMAIN, BUTTON_CSS + FIELD_CSS + SELECT_CSS + PROCESS_FILES_CSS);
58282
+ /** 订阅「(重)连成功」——各页据此补读一次当前值(断线期间可能漏过事件)。 */
58283
+ function useResync(handler) {
58284
+ const ref = (0, react$1.useRef)(handler);
58285
+ ref.current = handler;
58286
+ (0, react$1.useEffect)(() => {
58287
+ ensureSource();
58288
+ const stable = () => ref.current();
58289
+ resyncHandlers.add(stable);
58290
+ return () => {
58291
+ resyncHandlers.delete(stable);
58292
+ };
58293
+ }, []);
59119
58294
  }
59120
- // src/client/ui/Field.tsx
59121
- /**
59122
- * 下拉选择 —— 核心 `Picker` 的薄包装(2026-11-06)。
59123
- *
59124
- * 触发器 / 弹层 / 分组 / 搜索 / 空态 / 跑马灯 / 键盘与明暗全部归核心;
59125
- * 本仓只补核心 `Picker` 尚未提供的一点:
59126
- * - `error` 校验描红:外层 `className` + 一条皮肤规则(`.dsh-tdt-picker--error`)。
59127
- * 悬停提示**不在这里**(2026-11-06:全站原生 title 清退;需要的调用方在真 DOM 外套官方 `Tooltip`)。
59128
- * 空列表 ⇒ 禁用并显示业务空态文案(不塞假值)。
59129
- */
59130
- /**
59131
- * 下拉选择:核心 `Picker`(portal 到 body,选中项自动带对勾,内容超长走跑马灯)。
59132
- * 空列表 ⇒ 禁用并显示空态文案(接不到真数据时不塞假值)。
59133
- */
59134
- function SelectField(props) {
59135
- ensureControlsStyle();
59136
- const size = props.size ?? "lg";
59137
- const groups = props.groups;
59138
- const empty = groups === void 0 ? (props.options ?? []).length === 0 : groups.every((group) => group.options.length === 0);
59139
- const usable = !empty && props.disabled !== true;
59140
- const width = props.width ?? (props.block === true ? "100%" : void 0);
59141
- const cls = [props.block === true ? "dsh-tdt-picker--block" : "", props.error === true ? "dsh-tdt-picker--error" : ""].filter(Boolean).join(" ");
59142
- return (0, react$1.createElement)(Picker, {
59143
- ...groups === void 0 ? { options: props.options ?? [] } : { groups },
59144
- value: props.value,
59145
- onChange: (next) => {
59146
- if (typeof next === "string") props.onChange(next);
59147
- },
59148
- placeholder: empty ? props.emptyLabel : props.placeholder,
59149
- disabled: !usable,
59150
- size,
59151
- icon: props.icon,
59152
- className: cls === "" ? void 0 : cls,
59153
- width,
59154
- ...props.maxWidth === void 0 ? {} : { maxWidth: props.maxWidth },
59155
- ariaLabel: props.ariaLabel,
59156
- ...props.searchable === true ? { searchable: true } : {}
59157
- });
58295
+ // src/client/format.ts
58296
+ /** 两位补零:`9` → `09`(数字 / 纯数字字符串都收)。 */
58297
+ function pad2(value) {
58298
+ return String(value).padStart(2, "0");
59158
58299
  }
59159
- // src/client/ui/TaskPicker.tsx
59160
58300
  /**
59161
- * 任务选择器 —— **核心 `Picker` 的薄包装**(2026-11-06 起)。
59162
- *
59163
- * 本文件只做四件业务事,其余(浮层、搜索、分页、空态、键盘、明暗)全交给 `dsh-plugin-core/ui`:
59164
- * ① 按 `scope`(工作区)与 `excludeIds` 过滤候选;
59165
- * ② 「全部任务」作为**第一个普通选项**(`value: ''`)传进去(用户 2026-11-05 拍板,核心零改动);
59166
- * ③ 停用任务照常可选,只在 `label` 尾部拼 `disabledTag`(不加字段);
59167
- * ④ 已选项掉出作用域 ⇒ 仍留在列表里 + 触发器前置 `!` 告警(官方 `Tooltip` 给说明),不静默清空。
59168
- *
59169
- * 搜索:核心按 `label` + `keywords` 子串匹配;这里把任务 UUID 放进 `keywords`(只搜不显示)。
58301
+ * 路径取末段(产出物行 / 文件名显示用)。**单源**:`/` 与 `\` 都认(M5)。
58302
+ * ⚠️ **必须先剔尾斜杠**(2026-10-06 审计 🔴):本仓「目录」用尾斜杠表达(产出示例 `["a.md","b/"]`,
58303
+ * 见 `query.ts` / `task-file-context.tsx` 的 displayName);不剔的话 `baseNameOf('b/')` 返回**空串**
58304
+ * ⇒ 目录产出物在卡片/记录页渲染成**空标签、空 Tooltip**。
59170
58305
  */
59171
- /** 带搜索的任务选择器。 */
59172
- function TaskPicker(props) {
59173
- const scope = props.scope ?? "";
59174
- const excludes = props.excludeIds ?? [];
59175
- const current = props.options.find((o) => o.id === props.value);
59176
- const candidates = (0, react$1.useMemo)(() => props.options.filter((o) => (scope === "" || o.workspace === scope) && !excludes.includes(o.id)), [
59177
- props.options,
59178
- scope,
59179
- excludes
59180
- ]);
59181
- const labelOf = (o) => o.enabled ? o.label : `${o.label}${props.disabledTag ?? ""}`;
59182
- const list = [];
59183
- if (props.allOption !== void 0) list.push({
59184
- value: props.allOption.value,
59185
- label: props.allOption.label
59186
- });
59187
- for (const o of candidates) list.push({
59188
- value: o.id,
59189
- label: labelOf(o),
59190
- keywords: o.id
59191
- });
59192
- const outOfScope = current !== void 0 && props.value !== "" && scope !== "" && current.workspace !== scope;
59193
- if (outOfScope && current !== void 0) list.splice(props.allOption === void 0 ? 0 : 1, 0, {
59194
- value: current.id,
59195
- label: labelOf(current),
59196
- keywords: current.id
59197
- });
59198
- return (0, react$1.createElement)(Picker, {
59199
- options: list,
59200
- value: props.value,
59201
- onChange: (next) => {
59202
- const id = Array.isArray(next) ? next[0] ?? "" : next;
59203
- props.onChange(id);
59204
- },
59205
- searchable: true,
59206
- limit: props.recentLimit ?? 10,
59207
- placeholder: props.placeholder,
59208
- size: props.size,
59209
- width: props.width,
59210
- disabled: props.disabled,
59211
- ariaLabel: props.ariaLabel,
59212
- icon: outOfScope ? props.outOfScopeHint === void 0 ? (0, react$1.createElement)("span", { style: {
59213
- color: "var(--dshpc-warning)",
59214
- fontWeight: 600
59215
- } }, "!") : (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
59216
- label: props.outOfScopeHint,
59217
- side: "bottom"
59218
- }, (0, react$1.createElement)("span", { style: {
59219
- color: "var(--dshpc-warning)",
59220
- fontWeight: 600
59221
- } }, "!")) : void 0,
59222
- translate: (key, vars) => {
59223
- if (key === "pickerSearch") return props.searchPlaceholder;
59224
- if (key === "pickerEmpty" || key === "pickerNoMatch") return props.emptyLabel;
59225
- if (key === "pickerMore" && props.moreLabel !== void 0) return props.moreLabel;
59226
- return t$1(key, vars);
59227
- }
59228
- });
59229
- }
59230
- // src/client/ui/time-range.ts
59231
- /** 全部预设档(缺省显示顺序)。 */
59232
- const ALL_TIME_PRESETS = [
59233
- "today",
59234
- "yesterday",
59235
- "thisWeek",
59236
- "lastWeek",
59237
- "thisMonth",
59238
- "lastMonth"
59239
- ];
59240
- /** 值串 → 年月日时分;不合法返回 null。 */
59241
- function parseValue(value) {
59242
- const match = /^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}))?$/.exec(value);
59243
- if (match === null) return null;
59244
- return {
59245
- y: Number(match[1]),
59246
- m: Number(match[2]),
59247
- d: Number(match[3]),
59248
- hh: match[4] === void 0 ? 0 : Number(match[4]),
59249
- mm: match[5] === void 0 ? 0 : Number(match[5])
59250
- };
58306
+ function baseNameOf(path) {
58307
+ const trimmed = path.replace(/[\\/]+$/, "");
58308
+ if (trimmed === "") return path;
58309
+ const cut = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
58310
+ return cut < 0 ? trimmed : trimmed.slice(cut + 1);
59251
58311
  }
59252
58312
  /**
59253
- * 受控值 → 查询用**半开区间** `[from, to)`:
59254
- * - day:from = 当日 00:00,to = 结束日的**次日 00:00**;
59255
- * - minute:from = 当时分 :00,to = 结束分的**下一分钟 :00**。
59256
- * 空串 / 不合法 ⇒ 该端 undefined(不过滤)。
58313
+ * ISO → `YYYY-MM-DD HH:mm`(`seconds: true` 时补 `:ss`)。
58314
+ * 解析失败 ⇒ `fallback`(缺省返回**原串**,不编造时间;要占位符就显式传,如 `fallback: '—'`)。
59257
58315
  */
59258
- function rangeToQuery(value, precision) {
59259
- const fromParsed = value.from === "" ? null : parseValue(value.from);
59260
- const toParsed = value.to === "" ? null : parseValue(value.to);
59261
- let fromTs;
59262
- if (fromParsed !== null) {
59263
- const d = precision === "minute" ? new Date(fromParsed.y, fromParsed.m - 1, fromParsed.d, fromParsed.hh, fromParsed.mm, 0, 0) : new Date(fromParsed.y, fromParsed.m - 1, fromParsed.d, 0, 0, 0, 0);
59264
- fromTs = Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
59265
- }
59266
- let toTs;
59267
- if (toParsed !== null) {
59268
- const d = precision === "minute" ? new Date(toParsed.y, toParsed.m - 1, toParsed.d, toParsed.hh, toParsed.mm + 1, 0, 0) : new Date(toParsed.y, toParsed.m - 1, toParsed.d + 1, 0, 0, 0, 0);
59269
- toTs = Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
59270
- }
59271
- return {
59272
- fromTs,
59273
- toTs
59274
- };
58316
+ function formatDateTime(iso, opts) {
58317
+ const ms = Date.parse(iso);
58318
+ if (Number.isNaN(ms)) return opts?.fallback ?? iso;
58319
+ const d = new Date(ms);
58320
+ const base = `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
58321
+ return opts?.seconds === true ? `${base}:${pad2(d.getSeconds())}` : base;
59275
58322
  }
59276
- // src/client/ui/TimeRange.tsx
59277
58323
  /**
59278
- * 时间范围筛选 —— **核心 `DateTimePicker` 的薄包装**(2026-11-06 起)。
59279
- *
59280
- * 分工:
59281
- * - 核心负责:触发器、两块日历、预设档竖列、「确定 / 取消 / 清除」、键盘与明暗;
59282
- * - 本仓负责:值串 ⇄ 核心 ISO 的换算 + 预设档文案(`presets` 的 expr 由核心展开)。
59283
- *
59284
- * 值形态不变(`TimeRangeValue`:`YYYY-MM-DD` / `YYYY-MM-DD HH:mm`,空串 = 不过滤),
59285
- * 查询边界仍由 `rangeToQuery`(`time-range.ts`)统一算,控件不碰边界。
58324
+ * 四位年日期(用户 2026-10-03:卡片标题后的创建时间标签 `[2026-10-03 创建]`):`YYYY-MM-DD`。
58325
+ * 与 `formatShortStamp` 的差别就是**带年份**——创建时间要能跨年看,缺年份会认错。
59286
58326
  */
59287
- /** 预设档 → 核心的 date-math 算式(整档取齐:`/d` `/w` `/M`;周一为周首,与核心一致)。 */
59288
- const PRESET_EXPR = {
59289
- today: "now/d",
59290
- yesterday: "now-1d/d",
59291
- thisWeek: "now/w",
59292
- lastWeek: "now-1w/w",
59293
- thisMonth: "now/M",
59294
- lastMonth: "now-1M/M"
59295
- };
59296
- /** 值串 → 核心 ISO(本端 floor 到 00:00:00.000,对端 ceil 到 23:59:59.999)。 */
59297
- function toIso(value, end, precision) {
59298
- const minute = precision === "minute";
59299
- const date = value.slice(0, 10);
59300
- const time = minute ? value.length >= 16 ? value.slice(11, 16) : end ? "23:59" : "00:00" : "00:00";
59301
- return minute ? `${date}T${time}:${end ? "59.999" : "00.000"}` : `${date}T${end ? "23:59:59.999" : "00:00:00.000"}`;
59302
- }
59303
- /** 本仓值 → 核心受控值;任一端为空 ⇒ 交给核心显示「未选」(核心的 range 需要两端齐全才提交)。 */
59304
- function toCoreValue(value, precision) {
59305
- if (value.from === "" || value.to === "") return null;
59306
- return [toIso(value.from, false, precision), toIso(value.to, true, precision)];
58327
+ function formatYmd(iso) {
58328
+ const ms = Date.parse(iso);
58329
+ if (Number.isNaN(ms)) return "—";
58330
+ const d = new Date(ms);
58331
+ return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
59307
58332
  }
59308
- /** 核心受控值 → 本仓值:day 取日期,minute 取到分钟(与 `TimeRangeValue` 的口径一致)。 */
59309
- function fromCoreValue(out, precision) {
59310
- if (out === null || Array.isArray(out) === false) return {
59311
- from: "",
59312
- to: ""
59313
- };
59314
- const fmt = (iso) => precision === "minute" ? `${iso.slice(0, 10)} ${iso.slice(11, 16)}` : iso.slice(0, 10);
59315
- return {
59316
- from: fmt(out[0]),
59317
- to: fmt(out[1])
59318
- };
58333
+ /** 计划执行列(用户 2026-10-02 改**四位年**:形如 `2026-09-30 15:10`):`YYYY-MM-DD HH:mm`。 */
58334
+ function formatPlanStamp(iso) {
58335
+ const ms = Date.parse(iso);
58336
+ if (Number.isNaN(ms)) return "-";
58337
+ const d = new Date(ms);
58338
+ return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
59319
58339
  }
59320
- function TimeRange(props) {
59321
- const precision = props.precision ?? "day";
59322
- const presetIds = props.presets ?? ALL_TIME_PRESETS;
59323
- const presets = [{
59324
- label: props.labels.all,
59325
- expr: null
59326
- }, ...presetIds.map((id) => ({
59327
- label: props.labels.presets[id],
59328
- expr: PRESET_EXPR[id]
59329
- }))];
59330
- return (0, react$1.createElement)(DateTimePicker, {
59331
- mode: "range",
59332
- precision,
59333
- value: toCoreValue(props.value, precision),
59334
- onChange: (next) => {
59335
- props.onChange(fromCoreValue(next, precision));
59336
- },
59337
- presets,
59338
- size: props.size ?? "md",
59339
- disabled: props.disabled
59340
- });
58340
+ /** 实际开始列:只到 `HH:mm:ss`;未派发(null)或解析失败 ⇒ `-`。 */
58341
+ function formatClock(iso) {
58342
+ if (iso === null) return "-";
58343
+ const ms = Date.parse(iso);
58344
+ if (Number.isNaN(ms)) return "-";
58345
+ const d = new Date(ms);
58346
+ return `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
59341
58347
  }
59342
- // src/client/ui/ticker.ts
59343
- const tickerListeners = /* @__PURE__ */ new Set();
59344
- let tickerTimer = null;
59345
58348
  /**
59346
- * `visibilitychange` 处理器**只注册一次**(模块级)——2026-09-30 专家团复核:此前每次订阅起停
59347
- * 都 `addEventListener` 且从不移除 ⇒ 反复重挂面板会累积 N 个监听、切回标签页时同一批订阅被调 N 次。
59348
- * 这里注册一次、常驻(订阅集合空时遍历即空转,无副作用)。
58349
+ * 执行时长列(用户 2026-10-02 第四轮):有小时 ⇒ `H:MM:SS`(如 `1:15:30`);
58350
+ * 不足 1 小时 ⇒ `MM:SS`(**分、秒一律两位补零**,如 `05:30` / `00:30`)。NaN / 负 ⇒ `-`。
59349
58351
  */
59350
- const onVisibilityChange = () => {
59351
- for (const l of [...tickerListeners]) l();
59352
- };
59353
- let visibilityBound = false;
59354
- /** 订阅全局秒级心跳;返回退订。**第一个订阅者才起 interval,最后一个退订即停**。 */
59355
- function subscribeTicker(cb) {
59356
- tickerListeners.add(cb);
59357
- if (tickerTimer === null) {
59358
- tickerTimer = window.setInterval(() => {
59359
- for (const l of [...tickerListeners]) l();
59360
- }, 1e3);
59361
- if (!visibilityBound && typeof document !== "undefined") {
59362
- document.addEventListener("visibilitychange", onVisibilityChange);
59363
- visibilityBound = true;
59364
- }
59365
- }
59366
- return () => {
59367
- tickerListeners.delete(cb);
59368
- if (tickerListeners.size === 0 && tickerTimer !== null) {
59369
- window.clearInterval(tickerTimer);
59370
- tickerTimer = null;
59371
- }
59372
- };
58352
+ function formatDurationHms(ms) {
58353
+ if (!Number.isFinite(ms) || ms < 0) return "-";
58354
+ const totalSec = Math.round(ms / 1e3);
58355
+ const h = Math.floor(totalSec / 3600);
58356
+ const m = Math.floor(totalSec % 3600 / 60);
58357
+ const s = totalSec % 60;
58358
+ return h > 0 ? `${h}:${pad2(m)}:${pad2(s)}` : `${pad2(m)}:${pad2(s)}`;
58359
+ }
58360
+ /** token / 计数的大众格式(用户 2026-10-02:别写上千的数字):≥1K 用 K、≥1M 用 M(1234→1.2K、12345→12.3K、123456→123K)。 */
58361
+ function formatTokenCount(n) {
58362
+ if (!Number.isFinite(n)) return "—";
58363
+ const trim = (v) => Math.abs(v) >= 100 ? String(Math.round(v)) : v.toFixed(1).replace(/\.0$/, "");
58364
+ if (Math.abs(n) >= 1e6) return `${trim(n / 1e6)}M`;
58365
+ if (Math.abs(n) >= 1e3) return `${trim(n / 1e3)}K`;
58366
+ return String(n);
59373
58367
  }
59374
58368
  /**
59375
- * 每秒拿到「现在」(需要**时间值本身**时用,如悬浮文案);与 `LiveText` 共用同一条心跳。
59376
- * 不适合整列表用(每次 tick 会 setState 一次)——只在真正需要时间的小块里调。
58369
+ * token 明细:`输入 / 输出 / 缓存`(缺失档给 `—`)—— 挂在总量标签的 `title` 上,悬停看明细。
58370
+ *
58371
+ * ⚠️ 2026-10-04 从 `task-list.tsx` 上提到这里(改名 `formatTokenDetail`):卡片「执行记录」面板与
58372
+ * **执行记录总查询页**共用一份,避免同一串格式两处各写。
59377
58373
  */
59378
- function useNowMs() {
59379
- const [nowMs, setNowMs] = (0, react$1.useState)(() => Date.now());
59380
- (0, react$1.useEffect)(() => subscribeTicker(() => setNowMs(Date.now())), []);
59381
- return nowMs;
58374
+ function formatTokenDetail(row) {
58375
+ const part = (v) => v === null ? "—" : formatTokenCount(v);
58376
+ return `${part(row.token_in)} / ${part(row.token_out)} / ${part(row.token_in_cache)}`;
59382
58377
  }
59383
- // src/client/ui/running.ts
59384
- /** 运行中配色:蓝(前后两端统一;成功绿 / 失败红留给终态)。 */
59385
- const RUNNING_TONE = "var(--dshpc-business)";
59386
- /** 脉动动画类:挂到任意元素上即获得「运行中」明暗脉冲(与执行记录页同款 keyframe,单一定义)。 */
59387
- const RUN_PULSE_CLASS = "dsh-tdt-run-pulse";
59388
- const RUNNING_CSS = `
59389
- @keyframes dsh-tdt-run-pulse { 0%,100% { opacity: 1 } 50% { opacity: .35 } }
59390
- .${RUN_PULSE_CLASS} { animation: dsh-tdt-run-pulse var(--dshpc-dur-run) var(--dshpc-ease) infinite; }
59391
- @media (prefers-reduced-motion: reduce) { .${RUN_PULSE_CLASS} { animation: none; } }
59392
- `;
59393
- let ensured = false;
59394
- /** 幂等注入「运行中」脉动样式(keyframe + 动画类,全局生效一次即可)。 */
59395
- const ensureRunningStyle = () => {
59396
- if (ensured) return;
59397
- ensured = true;
59398
- applyStyle("domain:running", RUNNING_CSS);
58378
+ // src/client/locales.ts
58379
+ /** 把宿主给的无参 t 包成带占位符替换的 t(官方模板一律 `{name}`)。 */
58380
+ function interpolateTranslate(base) {
58381
+ return (key, params) => {
58382
+ const raw = base(key);
58383
+ if (params === void 0) return raw;
58384
+ return raw.replace(/\{(\w+)\}/g, (match, name) => name in params ? String(params[name]) : match);
58385
+ };
58386
+ }
58387
+ /** 中文文案。 */
58388
+ const zh = {
58389
+ title: "任务调度表(dsh-task-dispatch-table)",
58390
+ description: "用任务表驱动定时派发:配置任务、查看每次执行的记录。点击打开面板。",
58391
+ unavailable: "设置命名空间当前不可用(插件未运行或宿主未提供),暂时无法配置。",
58392
+ tasksInlineLabel: "任务表(tasksInline,JSON 数组)",
58393
+ tasksInlineHint: "每项一个任务定义;非空时优先于任务目录 tasksDir。清空并保存 = 回到默认(空,改用 tasksDir)。",
58394
+ invalidJson: "任务表不是合法 JSON,已阻止保存;请修正后重试。",
58395
+ save: "保存",
58396
+ saving: "保存中…",
58397
+ saveFailed: "保存未生效:草稿已保留,请修改后重试(宿主可能拒绝了部分值或已有并发修改)。",
58398
+ discard: "放弃更改",
58399
+ paramsTitle: "运行参数(只读)",
58400
+ paramDefault: "(默认)",
58401
+ paramStatePath: "状态库路径 statePath",
58402
+ paramTickMs: "调度周期 tickMs(毫秒)",
58403
+ paramDispatchGraceMs: "派发宽限 dispatchGraceMs(毫秒)",
58404
+ paramLeaseMs: "运行租约 leaseMs(毫秒)",
58405
+ paramUnknownGraceMs: "观察宽限 unknownGraceMs(毫秒)",
58406
+ paramTasksDir: "任务目录 tasksDir",
58407
+ paramDefaultProvider: "默认模型 provider defaultProvider",
58408
+ paramDefaultModel: "默认模型 model defaultModel",
58409
+ debugClose: "关闭",
58410
+ debugRefreshedAt: "手动刷新于",
58411
+ debugEmpty: "暂无快照:宿主完成一次调度(或派发 / 会话事件)后自动写入。若持续为空,说明宿主侧运行的还是旧版插件,请重装后重试。",
58412
+ debugRaw: "快照解析失败,原文如下:",
58413
+ debugWarns: "最近告警 / 错误(≤20 条)",
58414
+ debugNoWarns: "(无)",
58415
+ panelTitle: "定时任务调度器",
58416
+ backToConversation: "返回会话",
58417
+ tabConfig: "任务配置",
58418
+ tabRecords: "执行记录",
58419
+ tabCalendar: "任务日程",
58420
+ tabDebug: "调试",
58421
+ calLoadFail: "日程读取失败",
58422
+ calRetry: "重试",
58423
+ calEmpty: "这个月没有执行记录,也没有计划任务",
58424
+ calDayEmpty: "这一天没有执行记录,也没有计划任务",
58425
+ calTruncated: "该月记录超过上限(3000 条),只显示了其中一部分;请用上方过滤缩小范围。",
58426
+ calPlanTag: "计划:",
58427
+ calNotExecuted: "未执行",
58428
+ calPlanHint: "此任务尚未执行,时间为预计执行时间",
58429
+ calToday: "今天",
58430
+ calCellMore: "还有 {n} 条,点开看全部",
58431
+ debugDbHint: "状态库(state.db)三张表的原始记录,只读展示:task_instances = 每次执行一行、task_events = 每个事件一行、meta = 插件元数据(含内嵌任务表)。每表最多显示最新 500 行,点右上角刷新重取。",
58432
+ debugDbLoading: "状态库读取中…",
58433
+ debugDbFail: "状态库读取失败(未就绪或请求被拒),稍后点刷新重试。",
58434
+ debugDbEmpty: "(空表:还没有任何记录)",
58435
+ debugDbTruncated: "行数超出上限,仅显示最新一部分",
58436
+ tasksParsedTitle: "已解析的任务(id 由系统生成,改名字不影响历史)",
58437
+ tasksParsedEmpty: "(无任务:内嵌任务表为空且任务目录无合法定义)",
58438
+ colTitle: "名称",
58439
+ colCode: "编号",
58440
+ colId: "任务ID",
58441
+ colSchedule: "周期",
58442
+ colNext: "下次执行",
58443
+ colStatus: "状态",
58444
+ colSession: "会话",
58445
+ filterAll: "全部",
58446
+ listExpandHint: "展开任务详情",
58447
+ listDeferred: "延期",
58448
+ listDeferredTitle: "已过计划时刻但还没开始执行。常见原因:前置任务未完成 / 附加文件找不到 / 上一轮还在运行。",
58449
+ viewSession: "查看会话",
58450
+ sessionViewerTitle: "会话记录(只读)",
58451
+ sessionUnknownKind: "未支持的节点类型:",
58452
+ sessionLoading: "正在加载会话记录…",
58453
+ sessionEmpty: "该会话暂无可显示的记录(可能刚建窗或已被清理)。",
58454
+ sessionLoadFailed: "会话记录加载失败(会话可能已不可读)。",
58455
+ sessionLoadOlder: "加载更早记录",
58456
+ sessionProcess: "过程",
58457
+ triggerRequest: "收到执行请求",
58458
+ triggerGoal: "继续执行目标",
58459
+ triggerAgent: "收到任务消息",
58460
+ triggerTeam: "收到团队消息",
58461
+ triggerSubagent: "子任务状态更新",
58462
+ triggerGithub: "收到 GitHub 事件",
58463
+ triggerWebhook: "收到外部事件",
58464
+ triggerSchedule: "定时任务",
58465
+ triggerJob: "后台任务状态更新",
58466
+ triggerPlugin: "插件状态更新",
58467
+ triggerExplanation: "这条通知触发了本轮回复。",
58468
+ turnProcessTook: "用时 {duration}",
58469
+ turnProcessDeepDiving: "深度求索中,用时{duration}",
58470
+ turnProcessWorked: "已完成工作",
58471
+ turnProcessFailed: "处理失败",
58472
+ turnStopped: "已停止",
58473
+ chatDeepDiving: "深度求索中",
58474
+ thinkLabel: "思考",
58475
+ durationSeconds: "{seconds}秒",
58476
+ durationMinutes: "{minutes}分{seconds}秒",
58477
+ durationHours: "{hours}小时{minutes}分{seconds}秒",
58478
+ retryActive: "正在重试模型请求",
58479
+ retryCancelled: "模型请求重试已取消",
58480
+ retryStarted: "已重试模型请求",
58481
+ retryScheduled: "等待重试模型请求",
58482
+ retryStatus: "{label}({retry}/{maximum}) · {seconds}s",
58483
+ retryDelay: "重试延迟:",
58484
+ retryFailure: "失败原因:",
58485
+ durationMilliseconds: "{milliseconds}毫秒",
58486
+ turnErrorTitle: "本轮运行失败",
58487
+ accountStopped: "任务已停止",
58488
+ maxTokensTitle: "已达到输出 token 上限",
58489
+ maxTokensHint: "回答被截断,已有输出保留在对话中。发送“继续”可让模型接着输出。",
58490
+ failureAuth: "API 密钥无效",
58491
+ failureQuota: "当前请求的额度已用尽",
58492
+ failureAccountSignedOut: "任务已因退出 DeepSeek 登录而停止。",
58493
+ failureAccountSignInRequired: "请先登录 DeepSeek,并确认请求地址支持账号认证。",
58494
+ clockDate: "{m}月{d}日",
58495
+ clockDateYear: "{y}年{m}月{d}日",
58496
+ copyLabel: "复制",
58497
+ copiedLabel: "已复制",
58498
+ branchLabel: "在新对话中分支",
58499
+ branchUnavailableLabel: "只读会话记录不可分支",
58500
+ "presented.unavailable": "此主机没有可用的桌面,无法使用外部程序打开文件或文件夹;文件仍可在侧边栏预览",
58501
+ turnUsageTitle: "本轮用量",
58502
+ turnUsageModel: "提供方 / 模型",
58503
+ turnUsageCacheHit: "缓存命中",
58504
+ turnUsageInput: "未缓存输入",
58505
+ turnUsageCacheRead: "缓存读取",
58506
+ turnUsageCacheWrite: "缓存写入",
58507
+ turnUsageOutput: "输出",
58508
+ turnUsageReasoning: "(其中推理 {tokens})",
58509
+ turnUsageConsumed: "用量 {total}",
58510
+ turnUsageCount: "{count} tok",
58511
+ numberThousand: "{value}K",
58512
+ numberMillion: "{value}M",
58513
+ numberGroupSeparator: ",",
58514
+ stepProcessDoneThinking: "已完成分析",
58515
+ stepProcessDoneRead: "已读取文件",
58516
+ stepProcessDoneReadImage: "已读取图片",
58517
+ stepProcessDoneWrite: "已写入文件",
58518
+ stepProcessDoneSearch: "已搜索代码",
58519
+ stepProcessDoneEdit: "修改了文件",
58520
+ stepProcessDoneCommands: "执行了命令",
58521
+ stepProcessDoneCode: "运行了代码",
58522
+ stepProcessDoneWebSearch: "已搜索网页",
58523
+ stepProcessDoneWebFetch: "已访问网页",
58524
+ stepProcessDoneSubagents: "已协调子智能体",
58525
+ stepProcessDonePlan: "更新了计划",
58526
+ stepProcessDoneQuestions: "向用户提出了问题",
58527
+ stepProcessDoneTools: "已调用工具",
58528
+ stepProcessJoinTwo: "{first}并{second}",
58529
+ stepProcessComma: ",",
58530
+ stepProcessSharedPrefix: "已",
58531
+ stepProcessMore: "{title}等",
58532
+ toolTitleRead: "读取",
58533
+ toolTitleReadImage: "读取图片",
58534
+ toolTitleGrep: "搜索文件内容",
58535
+ toolTitleGlob: "查找文件",
58536
+ toolTitleBash: "运行命令",
58537
+ toolTitleWrite: "写入",
58538
+ toolTitleEdit: "编辑",
58539
+ toolTitleCode: "代码",
58540
+ toolTitleWebSearch: "网页搜索",
58541
+ toolTitleWebFetch: "网页获取",
58542
+ toolTitleGeneric: "工具调用",
58543
+ toolTitleSearch: "搜索",
58544
+ toolInputLabel: "输入",
58545
+ toolOutputLabel: "输出",
58546
+ rowPreparing: "正在准备调用",
58547
+ rowRunning: "运行中",
58548
+ rowFailed: "失败",
58549
+ rowStopped: "已停止",
58550
+ collapseLabel: "收起",
58551
+ readWindow: "显示 {shown} / {total} 行",
58552
+ readCollapseAria: "收起内容",
58553
+ readExpandAria: "展开其余 {count} 行",
58554
+ readExpandRest: "… 其余 {count} 行",
58555
+ terminalSignal: "信号 {signal}",
58556
+ terminalExitCode: "退出码 {code}",
58557
+ terminalNoExitCode: "未正常退出",
58558
+ terminalRunning: "运行中",
58559
+ terminalFailed: "失败",
58560
+ terminalDone: "已完成",
58561
+ terminalNoOutput: "无输出",
58562
+ terminalCollapseAria: "收起输出",
58563
+ terminalExpandAria: "展开其余 {n} 行输出",
58564
+ terminalExpandRest: "… 其余 {n} 行",
58565
+ terminalSendInput: "(发送输入)",
58566
+ terminalSession: "终端 {sessionId}",
58567
+ codeBlockLabel: "代码块",
58568
+ diffWrapLabel: "自动换行",
58569
+ diffUnwrapLabel: "取消换行",
58570
+ diffCollapseAria: "收起差异",
58571
+ diffExpandAria: "展开其余 {count} 行差异",
58572
+ diffExpandRest: "… 其余 {count} 行",
58573
+ continueBranch: "继续对话",
58574
+ forkConfirmTitle: "开分支继续对话",
58575
+ forkConfirmText: "是否需要基于此会话开一个新分支继续对话?原会话保持只读留档,新分支复制本会话内容并可继续对话。",
58576
+ forkConfirmAccept: "开分支并跳转",
58577
+ forkCancel: "取消",
58578
+ forkWorking: "正在开分支…",
58579
+ forkFailed: "开分支失败:{error}",
58580
+ previewClose: "关闭预览",
58581
+ previewLoadMore: "加载更多",
58582
+ previewFileLabel: "文件",
58583
+ previewCopyPath: "复制路径",
58584
+ previewRefresh: "刷新",
58585
+ previewNotFound: "文件不存在(可能已被移动或删除)。",
58586
+ previewTooLarge: "文件过大,超出预览上限({limit})。",
58587
+ previewDirectory: "这是一个目录,暂不支持目录浏览。",
58588
+ previewNotRegular: "该路径不是常规文件(符号链接等),暂不支持预览。",
58589
+ previewOutsideWorkspace: "该路径在会话工作区之外(常见于指向外部的符号链接),官方接口不允许浏览。",
58590
+ previewError: "读取失败:{code}",
58591
+ previewUnknownBinary: "二进制文件,暂不支持预览。可复制路径后在工作区中打开。",
58592
+ previewOfficeUnavailable: "读取失败:Office 预览不可用。请在运行 DeepSeek Harness 的主机上启用文档预览服务。",
58593
+ previewOfficeFailed: "Office 文件转换失败,无法预览。可复制路径后在工作区中打开。",
58594
+ previewBadPayload: "读取结果不符合官方契约(已记控制台日志),未渲染内容。",
58595
+ previewRenderFailed: "预览渲染失败(错误已记录,面板其余部分不受影响)。",
58596
+ previewResize: "拖动调整预览栏宽度",
58597
+ previewSource: "源码",
58598
+ previewRender: "预览",
58599
+ previewMdSwitchAria: "Markdown 视图切换",
58600
+ previewHtmlSwitchAria: "HTML 视图切换",
58601
+ previewHtmlFrame: "HTML 文档预览",
58602
+ previewHtmlFailed: "无法预览这份 HTML 文档",
58603
+ previewTruncated: "文件过大,仅显示前 {size}",
58604
+ previewTruncatedSize: "256KB",
58605
+ explorerEmpty: "空目录",
58606
+ explorerTruncated: "目录内容过多,仅显示部分条目。",
58607
+ explorerCrumbsAria: "目录路径导航",
58608
+ explorerLevels: "选择目录层级",
58609
+ explorerRootName: "(工作区根目录)",
58610
+ explorerExpand: "展开目录",
58611
+ explorerCollapse: "收起目录",
58612
+ explorerEnter: "进入",
58613
+ colOutputs: "产出",
58614
+ procFilesTitle: "其他产出 · {count} 项",
58615
+ deliverRowTitle: "交付文件",
58616
+ deliverRowPreparing: "准备交付",
58617
+ deliverRowRunning: "正在交付",
58618
+ deliverRowOk: "已交付",
58619
+ deliverRowError: "交付失败",
58620
+ deliverRowStopped: "已中断",
58621
+ deliverFileLabel: "文件",
58622
+ deliverPreviewHint: "预览",
58623
+ deliverPreviewCard: "预览 {name}",
58624
+ deliverAll: "全部 {count} 个文件",
58625
+ deliverCollapse: "收起",
58626
+ deliverExpandAria: "展开全部 {count} 个交付文件",
58627
+ deliverCollapseAria: "收起交付文件列表",
58628
+ tfcReceived: "接收 · {files} 个文件 · 来自 {tasks} 个前置任务",
58629
+ tfcAttached: "任务附件 · {count} 个文件",
58630
+ tfcNoOutputs: "未声明产出",
58631
+ tfcRelOnly: "相对路径",
58632
+ tfcCrossWorkspace: "跨工作区",
58633
+ tfcNoPath: "路径未解析,不可打开",
58634
+ tfcMore: "全部 {count} 个文件",
58635
+ tfcMoreTasks: "全部 {count} 个前置任务",
58636
+ tfcCollapse: "收起",
58637
+ tfcFromUpload: "上传",
58638
+ tfcFromWorkspace: "链接",
58639
+ tfcRestFiles: "还有 {count} 个",
58640
+ editorNew: "新建任务",
58641
+ editorEdit: "编辑任务",
58642
+ editorClose: "关闭",
58643
+ editorCancel: "取消",
58644
+ editorSave: "保存",
58645
+ editorSavePending: "保存逻辑待接(P2):本轮只做界面与交互",
58646
+ editorEnabled: "启用",
58647
+ editorEnabledOn: "任务已启用,到点会派发",
58648
+ editorEnabledOff: "任务已停用,定义保留但不派发",
58649
+ editorTitle: "任务名称",
58650
+ editorTitlePh: "给任务起个名字",
58651
+ editorCode: "任务编号",
58652
+ editorCodePh: "可选,便于查询",
58653
+ editorPrompt: "提示词",
58654
+ editorPromptPh: "写给 agent 的指令……",
58655
+ editorVersions: "版本历史",
58656
+ editorWorkspace: "工作区",
58657
+ editorWorkspacePh: "选择工作区",
58658
+ editorModel: "模型",
58659
+ editorModelPh: "默认模型",
58660
+ editorFollowHost: "默认模型",
58661
+ editorPermission: "权限",
58662
+ editorPermissionHint: "本次执行授予 agent 的操作范围。默认 = 会话默认(沿用宿主新建会话时的权限设置);宿主暂未提供按任务下发权限的接口,所选档位以派发消息中的约束指令执行。",
58663
+ editorPermDefault: "权限:默认",
58664
+ editorPermReadOnly: "仅可查看",
58665
+ editorPermWorkspace: "工作区内修改",
58666
+ editorPermFull: "完全权限",
58667
+ editorNoOptions: "暂无可选",
58668
+ editorSchedule: "执行频率",
58669
+ editorScheduleHint: "设置任务的执行时机与重复方式:可设为单次(到点执行一次,不再重复)、周期(每天 / 每周 / 每月等定期重复)或间隔(每隔固定时间执行)。",
58670
+ editorSchedulePeriodic: "周期",
58671
+ editorScheduleInterval: "间隔",
58672
+ editorFreq: "频率",
58673
+ editorFreqOnce: "单次",
58674
+ editorFreqDaily: "每天",
58675
+ editorFreqWeekly: "每周",
58676
+ editorFreqMonthly: "每月",
58677
+ editorFreqQuarterly: "每季度",
58678
+ editorFreqYearly: "每年",
58679
+ editorOnceHint: "到点执行一次,之后不再重复",
58680
+ editorMonthEvery: "全部月份",
58681
+ editorMonthOdd: "单数月",
58682
+ editorMonthEven: "双数月",
58683
+ editorQuarterMonthOption: "第 {m} 个月",
58684
+ editorDate: "日期",
58685
+ editorDatePh: "选择日期",
58686
+ editorTime: "时间",
58687
+ editorTimePh: "选择时间",
58688
+ editorMonth: "月",
58689
+ editorMonthOption: "{m} 月",
58690
+ editorDayOfMonth: "第几日",
58691
+ editorDayOption: "{d} 日",
58692
+ editorIntervalEvery: "每隔",
58693
+ editorIntervalStep: "间隔步长",
58694
+ editorIntervalUnit: "间隔单位",
58695
+ editorIntervalSuffix: "执行",
58696
+ editorEveryNWeeks: "每 {n} 周",
58697
+ editorAttachments: "附加文件",
58698
+ editorAttachmentLink: "链接",
58699
+ editorAttachmentUpload: "上传",
58700
+ editorAttachmentRemove: "移除",
58701
+ editorAttachmentView: "查看",
58702
+ editorAttachmentAdd: "添加文件",
58703
+ editorPickWorkspaceFile: "选择工作区文件",
58704
+ editorPickWorkspaceFileShort: "工作区文件",
58705
+ editorDropZoneHint: "点击或拖拽文件到此处上传,支持多选或单个文件",
58706
+ editorDropZoneFormats: "支持常见文本 / 代码、图片、文档格式,单个文件不超过 20MB",
58707
+ editorUploading: "上传中…",
58708
+ editorUploadErrType: "格式不支持:仅支持常见文本 / 代码、图片、文档文件",
58709
+ editorUploadErrSize: "文件超过大小限制(单个最大 20MB)",
58710
+ editorUploadErrEmpty: "文件内容为空",
58711
+ editorUploadErrGeneric: "上传失败,请重试",
58712
+ editorPickerNoSession: "该工作区下还没有会话,无法读取文件。",
58713
+ editorAttachmentsHint: "附加文件会随任务一起派发给执行的 Agent:任务执行过程中,Agent 可读取或操作这些文件的内容。也可从工作区选择整个目录(连带其下所有文件):Agent 拿到的是这个目录的路径,自己在里面找文件,提示词里写「附件里的 目录名/子文件路径」即可指定其中某一个。",
58714
+ editorPickerPick: "选择此文件",
58715
+ editorPickerPickDir: "选择此目录(连带其下所有文件)",
58716
+ editorPickerSelect: "选择",
58717
+ editorOpenEditor: "全屏编辑",
58718
+ editorPromptEditorTitle: "提示词编辑器(.md)",
58719
+ editorNoVersions: "暂无版本,保存后可在此回滚",
58720
+ editorHistoryVersions: "历史版本",
58721
+ editorNewTaskNoVersions: "新建任务暂未保存,无历史版本可查询",
58722
+ editorDeleteVersion: "删除",
58723
+ editorUseVersion: "使用此版本",
58724
+ editorConfirmDeleteTitle: "删除版本",
58725
+ editorConfirmDeleteDesc: "你确定要删除此版本的记录吗?删除后不可撤销,请谨慎操作。",
58726
+ editorDeleteTask: "删除任务",
58727
+ editorDeleteTaskTitle: "删除任务",
58728
+ editorDeleteTaskDesc: "确定所有的移除都是找不回来的,不可逆的。",
58729
+ editorReset: "重置",
58730
+ editorResetDone: "已恢复为打开时的内容",
58731
+ editorResetTitle: "重置当前编辑?",
58732
+ editorResetDesc: "重置会放弃本任务所有未保存的修改,恢复到打开编辑时的内容。这一操作不可撤销。",
58733
+ editorVersionToggle: "版本",
58734
+ editorUseShort: "使用",
58735
+ editorEnabledStateOn: "已启用",
58736
+ editorEnabledStateOff: "已关闭",
58737
+ editorToggleOn: "任务已启用",
58738
+ editorToggleOff: "任务已关闭",
58739
+ editorFullPermTitle: "这条任务将以完全权限运行",
58740
+ editorFullPermDesc: "这条任务会以「完全权限」无人值守执行,意味着 AI 可以直接:",
58741
+ editorFullPermB1: "写入 / 修改 / 删除工作区内的文件",
58742
+ editorFullPermB2: "执行命令、发起网络请求等敏感操作",
58743
+ editorFullPermCheck: "我已了解风险,并愿意为该任务的执行结果负责。",
58744
+ editorTaskSaved: "任务已保存",
58745
+ editorTaskDeleted: "任务已删除",
58746
+ editorSchedHourlyOnce: "每小时执行一次",
58747
+ editorSchedNoDaySuffix: ",但还没选生效日",
58748
+ editorSchedWeeklyDayPrefix: "每周",
58749
+ editorRestorePromptTitle: "只找回提示词",
58750
+ editorRestorePromptDesc: "确定找回会用历史版本覆盖现有修改的所有数据(本次只覆盖提示词,其余设置保持现状)。",
58751
+ editorTasksTitle: "任务列表",
58752
+ editorTasksEmpty: "还没有任务:点右上角「+ 新建任务」创建第一条。",
58753
+ editorDisabledTag: "已停用",
58754
+ editorModeEdit: "编辑",
58755
+ editorModePreview: "预览",
58756
+ editorTaskStart: "开始时间",
58757
+ editorTaskStartHint: "任务自该日起开始执行。可提前创建任务,至该日自动开跑;「每 N 周」亦自该日起算首周。",
58758
+ editorWeekdayLabel: "星期",
58759
+ editorWeekday1: "周一",
58760
+ editorWeekday2: "周二",
58761
+ editorWeekday3: "周三",
58762
+ editorWeekday4: "周四",
58763
+ editorWeekday5: "周五",
58764
+ editorWeekday6: "周六",
58765
+ editorWeekday7: "周日",
58766
+ editorWeekdayShorts: "一|二|三|四|五|六|日",
58767
+ editorWeekdayEmpty: "不选 = 每天",
58768
+ calWeekdays: "星期一|星期二|星期三|星期四|星期五|星期六|星期日",
58769
+ editorToday: "今天",
58770
+ editorPrevMonth: "上个月",
58771
+ editorNextMonth: "下个月",
58772
+ editorPrevYear: "上一年",
58773
+ editorNextYear: "下一年",
58774
+ editorMonthTitle: "{y}年{m}月",
58775
+ editorHour: "小时",
58776
+ editorMinute: "分钟",
58777
+ editorNow: "现在",
58778
+ editorConfirm: "确定",
58779
+ editorWindow: "允许延迟",
58780
+ editorWindowHint: "任务到达计划开始执行时间后,若前置任务尚未完成或因其它原因需延后,最长允许在此时长内继续执行;超过该时长则跳过本次执行。",
58781
+ editorSchedForecast: "预计执行",
58782
+ editorSchedRun: "执行",
58783
+ editorSchedEveryday: "每天",
58784
+ editorSchedDaily: "每天",
58785
+ editorSchedWeekly: "每周",
58786
+ editorSchedEveryNWeek: "每 {n} 周",
58787
+ editorSchedEveryNDay: "每 {n} 天",
58788
+ editorSchedQuarterly: "每季度第 {n} 个月",
58789
+ editorSchedIntervalMin: "每 {n} 分钟执行一次",
58790
+ editorSchedIntervalHour: "每 {n} 小时执行一次",
58791
+ editorSchedOnce: "执行一次",
58792
+ editorSchedInvalidStep: "执行间隔没填,无法生成执行计划",
58793
+ editorMonthMode_every: "每月",
58794
+ editorMonthMode_odd: "单数月",
58795
+ editorMonthMode_even: "双数月",
58796
+ editorDiscardTitle: "放弃未保存的更改?",
58797
+ editorDiscardDesc: "当前内容已修改且尚未保存,关闭后这些更改将丢失。",
58798
+ editorDiscardStay: "继续编辑",
58799
+ editorDiscardLeave: "放弃更改",
58800
+ unitMinutes: "分钟",
58801
+ unitHours: "小时",
58802
+ unitDays: "天",
58803
+ unitWeeks: "周",
58804
+ editorDeps: "添加前置任务",
58805
+ editorDepsHint: "此任务必须等待『所有』前置任务完成之后再开始执行。判定方式:此任务的所有前置任务,上一次执行必须是成功,本任务才可以执行(中间被跳过、只要没失败,都算前置任务成功)。放行执行时,系统会自动在前置任务完成后,将其产出的相关文件移交给本次任务。",
58806
+ editorDepAdd: "添加",
58807
+ editorDepTask: "任务",
58808
+ editorDepTaskPh: "选择任务",
58809
+ editorDepRemove: "移除",
58810
+ editorDepEmpty: "尚未配置前置任务",
58811
+ editorDepEmptyHint: "在下方选择任务后点「添加」",
58812
+ editorDepDisabledTag: "(已停用)",
58813
+ editorDepTaskEmpty: "暂无可选的前置任务",
58814
+ localeTag: "zh-CN",
58815
+ recordsEmpty: "该时间范围内没有执行记录",
58816
+ recordsEmptyFiltered: "当前过滤条件下没有执行记录",
58817
+ recordsLoadMore: "加载更多",
58818
+ recordsNoMore: "没有更多了",
58819
+ recordsLimitHint: "已加载 2000 条(已达上限)",
58820
+ recordsLoadFail: "执行记录加载失败",
58821
+ recordsRetry: "重试",
58822
+ recordsDayCount: "{n} 条",
58823
+ recordsTaskPh: "任务",
58824
+ recordsTaskSearch: "搜索任务名或 ID",
58825
+ recordsMore: "更多",
58826
+ recordsOutOfScope: "所选任务不在当前工作区",
58827
+ recPlan: "计划",
58828
+ recActual: "实际",
58829
+ recPrevDay: "前一天",
58830
+ recNextDay: "次日",
58831
+ recEventsTitle: "执行日志",
58832
+ recTokenHint: "token 消耗",
58833
+ recordsDepTip: "前置任务 {n}:{task}",
58834
+ recordsDepFrom: "执行于 {time}",
58835
+ recTokenDetail: "输入 {input} / 输出 {output} / 缓存 {cache}",
58836
+ editorAdvanced: "高级设置",
58837
+ editorAdvancedHelp: "此区域为高级配置区域,修改前请仔细阅读各项说明。常规任务建议使用默认值。",
58838
+ editorRetry: "重试次数",
58839
+ editorRetryOnce: "一次",
58840
+ editorRetryTwice: "两次",
58841
+ editorRetryThrice: "三次",
58842
+ editorRetryFive: "五次",
58843
+ editorRetryHint: "任务执行失败后,自动重试的次数。",
58844
+ editorGoal: "以 dsh 内置的 /goal 开始执行任务",
58845
+ editorGoalHint: "默认开启:执行时把任务目标作为持久目标交给 dsh 内置 /goal,会话会自动续跑多轮,直到 agent 标记目标完成,看板在该目标真正结束后才结算本次执行。关闭则只执行一轮普通对话。",
58846
+ editorAgentTeam: "多 Agent 协作",
58847
+ editorAgentTeamHint: "默认关闭。开启后 agent 以官方 Agent Teams(实验特性)方式执行:主会话作为队长,按需创建命名队友分工协作、共享任务板。需要宿主启用 Agent Teams 组件;未启用时自动按单 Agent 执行,并在执行记录日志留痕。",
58848
+ editorPreview: "配置预览",
58849
+ editorPreviewHint: "查看本任务的配置原文件。",
58850
+ listFilterAll: "全部",
58851
+ listFilterEnabled: "已开启",
58852
+ listFilterDisabled: "已关闭",
58853
+ listFilterAbnormal: "异常",
58854
+ listFilterWorkspaceAll: "全部工作区",
58855
+ listFilterTaskAll: "全部任务",
58856
+ listSearchPlaceholder: "搜索任务名称或编号",
58857
+ listRunning: "运行中",
58858
+ listNextNone: "无后续执行",
58859
+ listNever: "尚未执行",
58860
+ listCreatedTag: "{date} 创建",
58861
+ listCreatedUnknown: "创建时间未知",
58862
+ relNow: "即将执行",
58863
+ relMinutes: "{n} 分钟后",
58864
+ relHours: "{n} 小时后",
58865
+ relDays: "{n} 天后",
58866
+ relPast: "已过期",
58867
+ relJustNow: "刚刚",
58868
+ relMinutesAgo: "{n} 分钟前",
58869
+ relHoursAgo: "{n} 小时前",
58870
+ relDaysAgo: "{n} 天前",
58871
+ relWeeksAgo: "{n} 周前",
58872
+ relMonthsAgo: "{n} 个月前",
58873
+ relYearsAgo: "{n} 年前",
58874
+ relToday: "今天 {time}",
58875
+ relTomorrow: "明天 {time}",
58876
+ relWeeks: "{n} 周后",
58877
+ relMonths: "{n} 个月后",
58878
+ relYears: "{n} 年后",
58879
+ listLastFullTitle: "上次执行:{when}",
58880
+ listNextFullTitle: "下次执行:{when}",
58881
+ listEmpty: "还没有任务。点右上角「+ 新建任务」创建第一个。",
58882
+ listEmptyFiltered: "没有符合当前筛选的任务。",
58883
+ listEmptyTitle: "创建你的第一个定时任务",
58884
+ listEmptyHint: "写好提示词,到点自动执行;支持每天 / 每周 / 间隔循环。",
58885
+ listEmptyAction: "+ 新建定时任务",
58886
+ listSectionAttachments: "附加文件",
58887
+ listSectionDepends: "前置任务",
58888
+ listFieldWorkspace: "工作区",
58889
+ listFieldModel: "模型",
58890
+ listFieldModelDefault: "默认模型",
58891
+ listFieldRetry: "失败重试",
58892
+ listFieldSchedule: "排期",
58893
+ listFieldWindow: "允许延迟",
58894
+ listNone: "(无)",
58895
+ listDisabledTag: "(已停用)",
58896
+ infoSectionConfig: "任务配置",
58897
+ infoNextExec: "预计执行",
58898
+ infoLastRun: "上次执行",
58899
+ infoNoRun: "还没有执行记录",
58900
+ infoFinishedAt: "结束时间",
58901
+ infoSession: "任务会话",
58902
+ infoDuration: "执行时长",
58903
+ editorTabView: "查看",
58904
+ editorTabEdit: "编辑",
58905
+ infoStateEnabled: "正常",
58906
+ infoStateDisabled: "已停用",
58907
+ infoViewTask: "查看该任务",
58908
+ editorViewDraftTag: "编辑的草稿(未保存)",
58909
+ editorViewNewTag: "新建,尚未保存",
58910
+ editorViewUntitled: "未命名任务",
58911
+ editorViewPrompt: "提示词",
58912
+ editorViewPromptEmpty: "还没填提示词",
58913
+ editorViewNoRunDraft: "任务尚未保存,没有执行记录",
58914
+ editorViewNotFilled: "未填",
58915
+ editorViewSourceCode: "源码",
58916
+ editorViewExpand: "展开",
58917
+ editorViewCollapse: "收起",
58918
+ editorViewSwitchTitle: "放弃未保存的修改?",
58919
+ editorViewSwitchDesc: "当前任务有改过但还没保存的内容。继续会放弃这些修改,并打开你要查看的任务。",
58920
+ editorEditSwitchTitle: "编辑内容尚未保存",
58921
+ editorEditSwitchDesc: "你正在编辑的任务有未保存的修改。直接覆盖会丢弃这些修改并打开新任务的编辑器;继续编辑可保留当前内容。",
58922
+ editorEditSwitchKeep: "继续编辑",
58923
+ editorEditSwitchOverwrite: "直接覆盖",
58924
+ cardTabInfo: "基础信息",
58925
+ cardTabRecords: "执行记录",
58926
+ cardTabLogs: "日志",
58927
+ cardDelete: "删除",
58928
+ cardDeleteTitle: "删除任务",
58929
+ cardDeleteDesc: "确定要删除这个任务吗?任务定义、附加文件与历史版本都会被移除,不可恢复(执行记录保留备查)。",
58930
+ cardCancel: "取消",
58931
+ cardRunNow: "立即执行",
58932
+ cardRunNowTitle: "立即执行任务",
58933
+ cardRunNowDesc: "你确定要立即执行此任务吗?",
58934
+ cardRunNowOk: "已提交执行,等待调度循环发动",
58935
+ cardRunNowAlready: "该任务正在执行中,暂时不能再次执行",
58936
+ cardRunNowBlocked: "前置任务未达标,本次未执行",
58937
+ cardRunNowMissingDep: "前置任务已不存在,本次未执行",
58938
+ cardRunNowWorkspace: "工作区未找到:{name}",
58939
+ cardRunNowAttachment: "附加文件不存在:{name}",
58940
+ cardRunNowNotFound: "找不到该任务,请刷新后重试",
58941
+ cardRunNowNotReady: "插件尚未就绪,请稍后再试",
58942
+ cardRunNowFailed: "本次未能执行({reason})",
58943
+ cardFrom: "起始时间",
58944
+ cardTo: "截止时间",
58945
+ cardKeyword: "关键字",
58946
+ cardLogLimit: "条数",
58947
+ limitPrefix: "显示",
58948
+ limitSuffix: "条",
58949
+ cardRecordsEmpty: "(该任务还没有执行记录)",
58950
+ cardRecordsEmptyFiltered: "(没有符合筛选条件的数据)",
58951
+ cardLogsEmpty: "(该任务还没有日志)",
58952
+ cardLoadFailed: "读取失败",
58953
+ cardEventsEmpty: "(该次执行暂无事件)",
58954
+ colTokens: "Token",
58955
+ statusPending: "排队",
58956
+ statusDispatched: "派发",
58957
+ statusRunning: "运行",
58958
+ statusSucceeded: "成功",
58959
+ statusFailed: "失败",
58960
+ statusSkipped: "跳过",
58961
+ statusUnknown: "未知",
58962
+ colDuration: "时长",
58963
+ colPlanned: "计划执行",
58964
+ colActualStart: "实际开始",
58965
+ colView: "查看",
58966
+ colNote: "备注",
58967
+ filterRunning: "运行中",
58968
+ trAll: "全部",
58969
+ trToday: "今天",
58970
+ trYesterday: "昨天",
58971
+ trThisWeek: "本周",
58972
+ trLastWeek: "上周",
58973
+ trThisMonth: "本月",
58974
+ trLastMonth: "上月",
58975
+ schedCustom: "{cron}",
58976
+ settingsBasic: "基础信息",
58977
+ settingsTitleFormat: "标题写法",
58978
+ settingsDesc: "简介",
58979
+ settingsLang: "界面语言",
58980
+ settingsLangValue: "跟随宿主(中文 / English)",
58981
+ settingsParams: "运行参数(实时生效)",
58982
+ settingsLoopSec: "循环间隔(秒)",
58983
+ settingsLoopHint: "调度器多久巡检一次任务表。",
58984
+ settingsLoopHelp: "调度循环每隔这么久巡检一遍,看有没有任务到点、该不该派发。改小 = 响应更及时、但检查更频繁也更占资源;改大则相反。一般 60 秒足够。",
58985
+ settingsWaitSec: "派发宽限(秒)",
58986
+ settingsWaitHint: "派发后等待会话启动的最长时间。",
58987
+ settingsWaitHelp: "任务派发之后,等它的会话创建并开始运行,最多等这么久。超过还没跑起来就按「等待超时」处理、记一次失败,让下一次调度接着来。",
58988
+ settingsLeaseSec: "运行租约(秒)",
58989
+ settingsLeaseHint: "运行中多久没有动静才回收占用。",
58990
+ settingsLeaseHelp: "任务在运行中,每被巡检到一次就续一次期;只有超过这么久**完全没有任何动静**(比如进程假死)才判定为卡死、回收它占的并发位。一直正常在跑的任务会被不断续期,不会被掐断。",
58991
+ settingsUnknownSec: "观察宽限(秒)",
58992
+ settingsUnknownHint: "会话状态未知时再观察多久。",
58993
+ settingsUnknownHelp: "暂时拿不到会话状态时,先按这个宽限继续观察(可能是宿主接口抖动);超过仍没有回音才判定为异常并回收。",
58994
+ settingsSaveSuccess: "设置已保存",
58995
+ settingsLoadFailed: "读取当前设置失败,请稍后重试",
58996
+ settingsUnitSec: "秒",
58997
+ loading: "加载中",
58998
+ invalidNumber: "请输入有效的整数秒",
58999
+ statusRailOff: "已关闭",
59000
+ statusRailLastFailed: "最近一次执行失败",
59001
+ statusRailLastSkipped: "最近一次未执行(配置或前置不满足,详见执行记录)",
59002
+ statusRailRunning: "计划运行中",
59003
+ debugRowsSuffix: "行",
59004
+ tabSettings: "设置",
59005
+ settingsConfigTitle: "插件设置",
59006
+ settingsBlockLogTitle: "插件日志",
59007
+ settingsBlockDataTitle: "数据库表查询",
59008
+ settingsCurrentConfig: "配置预览(只读)",
59009
+ settingsDefaultValue: "默认",
59010
+ settingsProvider: "默认模型供应商",
59011
+ settingsModel: "默认模型",
59012
+ settingsLogRetention: "运行日志保留(天)",
59013
+ settingsLogRetentionHint: "诊断日志保留多久,到期清除。",
59014
+ settingsLogRetentionHelp: "每次执行过程中产生的诊断日志(任务日志 + 插件日志)保留这么多天,到期自动清除。它只清日志明细,执行记录本身一律保留、不受影响。",
59015
+ settingsAttachmentRetention: "临时附件保留(天)",
59016
+ settingsAttachmentRetentionHint: "临时目录里的附件副本保留多久。",
59017
+ settingsAttachmentRetentionHelp: "上传或引用附件时,插件会在自己的临时目录里先留一份副本,这么多天后清理掉。任务定义里正式引用的那份附件不受影响。",
59018
+ settingsUnsaved: "有未保存的修改",
59019
+ settingsDefaultOption: "默认",
59020
+ settingsModelHint: "没选就用宿主的默认模型。",
59021
+ settingsModelHelp: "先选供应商、再选具体模型,两个都要选。留空(默认模型)= 跟随宿主的默认模型。新建任务时会自动带出这里的选择,单个任务仍可单独改。",
59022
+ settingsCustom: "自定义",
59023
+ settingsResetDefault: "恢复默认",
59024
+ settingsErrRange: "请填 {min} 到 {max} 之间的整数。",
59025
+ settingsErrPair: "供应商和模型要一起选:选了其中一个,另一个也得选。",
59026
+ settingsErrUpdateFailed: "配置没能写进去(宿主的配置面还没就绪),请稍后重试。",
59027
+ settingsErrInvalidField: "「{name}」这个值不合法,请检查后重试。",
59028
+ settingsErrRangeField: "「{name}」超出允许范围({min}–{max})。",
59029
+ settingsErrEmpty: "没有要保存的改动。",
59030
+ settingsAutoRefresh: "自动刷新(5 秒)",
59031
+ settingsRefresh: "刷新",
59032
+ settingsLogEmpty: "(暂无日志)",
59033
+ settingsLogFail: "日志读取失败",
59034
+ settingsTable: "表",
59035
+ settingsTopN: "取前 N 条",
59036
+ settingsPageSize: "每页显示条数",
59037
+ settingsAddFilter: "添加筛选",
59038
+ settingsNoFilters: "(无筛选:显示全部行)",
59039
+ settingsQuery: "查询",
59040
+ settingsTableTruncated: "行数超出上限,仅显示最新一部分",
59041
+ settingsTableEmpty: "(空表:还没有任何记录)",
59042
+ settingsTableFail: "读取失败",
59043
+ settingsFilterColumn: "列",
59044
+ settingsFilterOp: "运算符",
59045
+ settingsFilterValue: "值",
59046
+ settingsFilterRemove: "移除筛选",
59047
+ settingsIntro: "本插件无需在此页面配置:任务与全部设置都在插件主界面(侧栏「定时任务调度器」面板)内完成。",
59048
+ configNameLabel: "显示名称",
59049
+ configNameHint: "留空即用默认名「定时任务调度器」;填成与默认名相同则不保存这一项。",
59050
+ configEntryLabel: "入口位置",
59051
+ configEntryHint: "放在左侧栏一级,或收进侧栏底部的「更多」菜单。",
59052
+ configEntryFirst: "一级菜单",
59053
+ configEntryMore: "更多",
59054
+ configHideOfficialLabel: "屏蔽官方「自动化任务」",
59055
+ configHideOfficialHint: "关闭官方自动化任务入口。卸载本插件会自动恢复;切换后重启宿主或等热更新生效。",
59056
+ configHideOff: "不屏蔽",
59057
+ configHideOn: "屏蔽",
59058
+ configGithub: "访问 GitHub",
59059
+ configComponents: "蓝鲸组件库",
59060
+ configOverridden: "自定义",
59061
+ configReset: "恢复默认",
59062
+ configSave: "保存",
59063
+ configSaving: "保存中…",
59064
+ configSaveFailed: "保存失败,请重试",
59065
+ configUnavailable: "读不到当前设置(配置通道未就绪),稍后重新打开本页试试。",
59066
+ vTitleRequired: "还没填任务名称——任务列表里靠它认任务,请给任务起个名字。",
59067
+ vWorkspaceRequired: "还没选工作区——任务必须挂在某个工作区下才能执行,请在上方下拉里选一个。",
59068
+ vWorkspaceUnknown: "选中的工作区已经不存在了(可能被重命名或删除)——请在上方下拉里重新选择当前的工作区。",
59069
+ vPromptRequired: "还没写提示词——这是告诉 Agent 要做什么的指令,不能为空,请填写具体内容。",
59070
+ vIntervalInvalid: "执行间隔没填或填错——「每隔 N 分钟/小时」里的 N 必须是大于 0 的整数(比如 1 或 2)。",
59071
+ vWeekdayRequired: "每周执行但没勾选任何星期——请至少勾选一天,否则任务永远不会跑。",
59072
+ vAttachmentInvalid: "附加文件「{name}」的引用路径不合法——必须是工作区内的相对路径。请删掉它、重新选择一次。"
59399
59073
  };
59400
- // src/client/ui/index.ts
59401
- /**
59402
- * 主面板**内容列**的统一宽度锚点(任务配置 / 执行记录 两个 tab 必须一模一样,切换时不横向跳动)。
59403
- * 同时它是基础层 `Loading` 的锚点契约(`Loading` 默认 `anchorId = PANEL_CONTENT_ID`)——
59404
- * 所以**新页面也必须用同一个 id**,否则浮动 loading 找不到锚点、贴不到内容右缘。
59405
- */
59406
- const PANEL_CONTENT_ID = "dsh-tdt-main";
59407
- /** 内容列几何(居中限宽 760 / 1120)。 */
59408
- const PANEL_CONTENT_STYLE = {
59409
- width: "100%",
59410
- maxWidth: "1120px",
59411
- minWidth: "760px",
59412
- boxSizing: "border-box"
59074
+ /** English copy. */
59075
+ const en = {
59076
+ title: "Task dispatch table (dsh-task-dispatch-table)",
59077
+ description: "Schedule agent tasks from a task table: configure tasks and review every run. Click to open the panel.",
59078
+ unavailable: "The settings namespace is currently unavailable (plugin not running or not served by the host); configuration is disabled.",
59079
+ tasksInlineLabel: "Task table (tasksInline, JSON array)",
59080
+ tasksInlineHint: "One task definition per entry; when non-empty it takes precedence over tasksDir. Clear and save to fall back to the default (empty, use tasksDir).",
59081
+ invalidJson: "The task table is not valid JSON; the save was blocked. Fix it and try again.",
59082
+ save: "Save",
59083
+ saving: "Saving…",
59084
+ saveFailed: "The save did not land; your draft was kept for correction (the host may have rejected values or applied concurrent changes).",
59085
+ discard: "Discard changes",
59086
+ paramsTitle: "Runtime parameters (read-only)",
59087
+ paramDefault: "(default)",
59088
+ paramStatePath: "State database path statePath",
59089
+ paramTickMs: "Tick interval tickMs (ms)",
59090
+ paramDispatchGraceMs: "Dispatch grace dispatchGraceMs (ms)",
59091
+ paramLeaseMs: "Run lease leaseMs (ms)",
59092
+ paramUnknownGraceMs: "Observation grace unknownGraceMs (ms)",
59093
+ paramTasksDir: "Task directory tasksDir",
59094
+ paramDefaultProvider: "Default model provider defaultProvider",
59095
+ paramDefaultModel: "Default model defaultModel",
59096
+ debugClose: "Close",
59097
+ debugRefreshedAt: "Manual refresh at",
59098
+ debugEmpty: "No snapshot yet: the host writes one after each scheduling pass (or dispatch / session event). If it stays empty, the host is still running an old plugin build — reinstall and retry.",
59099
+ debugRaw: "Failed to parse the snapshot; raw text below:",
59100
+ debugWarns: "Recent warnings / errors (≤20 entries)",
59101
+ debugNoWarns: "(none)",
59102
+ panelTitle: "Scheduled task dispatcher",
59103
+ backToConversation: "Back to conversation",
59104
+ tabConfig: "Configuration",
59105
+ tabRecords: "Run records",
59106
+ tabCalendar: "Schedule",
59107
+ tabDebug: "Debug",
59108
+ calLoadFail: "Failed to load the schedule",
59109
+ calRetry: "Retry",
59110
+ calEmpty: "No run records and no planned runs in this month",
59111
+ calDayEmpty: "No run records and no planned runs on this day",
59112
+ calTruncated: "This month exceeds the 3000-row limit and is truncated; narrow the filters above.",
59113
+ calPlanTag: "Plan:",
59114
+ calNotExecuted: "Not executed",
59115
+ calPlanHint: "This task has not run yet; times shown are estimated",
59116
+ calToday: "Today",
59117
+ calCellMore: "{n} more — click to open",
59118
+ debugDbHint: "Raw rows of all three state.db tables, read-only: task_instances = one row per run, task_events = one row per event, meta = plugin metadata (incl. the inline task table). Newest 500 rows per table; use the refresh button to re-fetch.",
59119
+ debugDbLoading: "Loading state.db…",
59120
+ debugDbFail: "Failed to read state.db (not ready or request rejected); retry with the refresh button.",
59121
+ debugDbEmpty: "(empty table: no rows yet)",
59122
+ debugDbTruncated: "row count exceeds the cap, showing only the newest rows",
59123
+ tasksParsedTitle: "Parsed tasks (ids are generated; renaming never breaks history)",
59124
+ tasksParsedEmpty: "(no tasks: inline table empty and task dir has no valid definition)",
59125
+ colTitle: "Title",
59126
+ colCode: "Code",
59127
+ colId: "Task ID",
59128
+ colSchedule: "Schedule",
59129
+ colNext: "Next run",
59130
+ colStatus: "Status",
59131
+ colSession: "Session",
59132
+ filterAll: "All",
59133
+ listExpandHint: "Expand task details",
59134
+ listDeferred: "Delayed",
59135
+ listDeferredTitle: "Past its planned time but has not started. Usual causes: preceding task not finished / attachment missing / previous run still running.",
59136
+ viewSession: "View session",
59137
+ sessionViewerTitle: "Session transcript (read-only)",
59138
+ sessionUnknownKind: "Unsupported node kind: ",
59139
+ sessionLoading: "Loading session transcript…",
59140
+ sessionEmpty: "Nothing to show for this session yet (window just opened, or the log was cleaned up).",
59141
+ sessionLoadFailed: "Failed to load the session transcript (the session may no longer be readable).",
59142
+ sessionLoadOlder: "Load earlier messages",
59143
+ sessionProcess: "Process",
59144
+ triggerRequest: "Execution request received",
59145
+ triggerGoal: "Continuing the goal",
59146
+ triggerAgent: "Task message received",
59147
+ triggerTeam: "Team message received",
59148
+ triggerSubagent: "Subagent status update",
59149
+ triggerGithub: "GitHub event received",
59150
+ triggerWebhook: "External event received",
59151
+ triggerSchedule: "Scheduled task",
59152
+ triggerJob: "Background task status update",
59153
+ triggerPlugin: "Plugin status update",
59154
+ triggerExplanation: "This notification triggered the reply below.",
59155
+ turnProcessTook: "Took {duration}",
59156
+ turnProcessDeepDiving: "Thinking, {duration}",
59157
+ turnProcessWorked: "Work completed",
59158
+ turnProcessFailed: "Failed",
59159
+ turnStopped: "Stopped",
59160
+ chatDeepDiving: "Thinking",
59161
+ thinkLabel: "Think",
59162
+ durationSeconds: "{seconds}s",
59163
+ durationMinutes: "{minutes}m {seconds}s",
59164
+ durationHours: "{hours}h {minutes}m {seconds}s",
59165
+ retryActive: "Retrying model request",
59166
+ retryCancelled: "Model request retry cancelled",
59167
+ retryStarted: "Retried model request",
59168
+ retryScheduled: "Waiting to retry model request",
59169
+ retryStatus: "{label} ({retry}/{maximum}) · {seconds}s",
59170
+ retryDelay: "Retry delay: ",
59171
+ retryFailure: "Failure reason: ",
59172
+ durationMilliseconds: "{milliseconds}ms",
59173
+ turnErrorTitle: "This turn failed",
59174
+ accountStopped: "Task stopped",
59175
+ maxTokensTitle: "Output token limit reached",
59176
+ maxTokensHint: "The reply was cut off; earlier output is preserved in the conversation. Send \"continue\" to let the model resume.",
59177
+ failureAuth: "API key is invalid",
59178
+ failureQuota: "Request quota exhausted.",
59179
+ failureAccountSignedOut: "Stopped because you signed out of DeepSeek.",
59180
+ failureAccountSignInRequired: "Sign in to DeepSeek and ensure the request destination supports account authentication.",
59181
+ clockDate: "{m}/{d}",
59182
+ clockDateYear: "{y}/{m}/{d}",
59183
+ copyLabel: "Copy",
59184
+ copiedLabel: "Copied",
59185
+ branchLabel: "Branch into a new conversation",
59186
+ branchUnavailableLabel: "A read-only transcript cannot be branched",
59187
+ "presented.unavailable": "This host has no available desktop; external programs cannot open files or folders. Files can still be previewed in the sidebar.",
59188
+ turnUsageTitle: "Turn usage",
59189
+ turnUsageModel: "Provider / model",
59190
+ turnUsageCacheHit: "Cache hit",
59191
+ turnUsageInput: "Uncached input",
59192
+ turnUsageCacheRead: "Cached input",
59193
+ turnUsageCacheWrite: "Cache write",
59194
+ turnUsageOutput: "Output",
59195
+ turnUsageReasoning: " ({tokens} reasoning)",
59196
+ turnUsageConsumed: "Usage {total}",
59197
+ turnUsageCount: "{count} tok",
59198
+ numberThousand: "{value}K",
59199
+ numberMillion: "{value}M",
59200
+ numberGroupSeparator: ",",
59201
+ stepProcessDoneThinking: "Analysis completed",
59202
+ stepProcessDoneRead: "Read files",
59203
+ stepProcessDoneReadImage: "Read images",
59204
+ stepProcessDoneWrite: "Wrote files",
59205
+ stepProcessDoneSearch: "Searched code",
59206
+ stepProcessDoneEdit: "Edited files",
59207
+ stepProcessDoneCommands: "Ran commands",
59208
+ stepProcessDoneCode: "Ran code",
59209
+ stepProcessDoneWebSearch: "Searched the web",
59210
+ stepProcessDoneWebFetch: "Fetched web pages",
59211
+ stepProcessDoneSubagents: "Coordinated subagents",
59212
+ stepProcessDonePlan: "Updated the plan",
59213
+ stepProcessDoneQuestions: "Asked you questions",
59214
+ stepProcessDoneTools: "Called tools",
59215
+ stepProcessJoinTwo: "{first} and {second}",
59216
+ stepProcessComma: ", ",
59217
+ stepProcessSharedPrefix: "",
59218
+ stepProcessMore: "{title}, etc.",
59219
+ toolTitleRead: "Read",
59220
+ toolTitleReadImage: "Read image",
59221
+ toolTitleGrep: "Grep",
59222
+ toolTitleGlob: "Glob",
59223
+ toolTitleBash: "Bash",
59224
+ toolTitleWrite: "Write",
59225
+ toolTitleEdit: "Edit",
59226
+ toolTitleCode: "Code",
59227
+ toolTitleWebSearch: "Search",
59228
+ toolTitleWebFetch: "Fetch",
59229
+ toolTitleGeneric: "Tool call",
59230
+ toolTitleSearch: "Search",
59231
+ toolInputLabel: "IN",
59232
+ toolOutputLabel: "OUT",
59233
+ rowPreparing: "Preparing tool call",
59234
+ rowRunning: "Running",
59235
+ rowFailed: "Failed",
59236
+ rowStopped: "Stopped",
59237
+ collapseLabel: "Collapse",
59238
+ readWindow: "Showing {shown} of {total} lines",
59239
+ readCollapseAria: "Collapse content",
59240
+ readExpandAria: "Expand {count} more lines",
59241
+ readExpandRest: "… {count} more lines",
59242
+ terminalSignal: "signal {signal}",
59243
+ terminalExitCode: "exit code {code}",
59244
+ terminalNoExitCode: "no exit code",
59245
+ terminalRunning: "Running",
59246
+ terminalFailed: "Failed",
59247
+ terminalDone: "Done",
59248
+ terminalNoOutput: "No output",
59249
+ terminalCollapseAria: "Collapse output",
59250
+ terminalExpandAria: "Expand the remaining {n} output lines",
59251
+ terminalExpandRest: "… {n} more lines",
59252
+ terminalSendInput: "(send input)",
59253
+ terminalSession: "Terminal {sessionId}",
59254
+ codeBlockLabel: "Code",
59255
+ diffWrapLabel: "Wrap lines",
59256
+ diffUnwrapLabel: "Unwrap lines",
59257
+ diffCollapseAria: "Collapse diff",
59258
+ diffExpandAria: "Expand {count} more diff lines",
59259
+ diffExpandRest: "… {count} more lines",
59260
+ continueBranch: "Continue conversation",
59261
+ forkConfirmTitle: "Fork to continue",
59262
+ forkConfirmText: "Start a new branch from this session to continue the conversation? The original stays read-only; the branch copies this conversation and can continue.",
59263
+ forkConfirmAccept: "Fork & open",
59264
+ forkCancel: "Cancel",
59265
+ forkWorking: "Forking…",
59266
+ forkFailed: "Fork failed: {error}",
59267
+ previewClose: "Close preview",
59268
+ previewLoadMore: "Load more",
59269
+ previewFileLabel: "File",
59270
+ previewCopyPath: "Copy path",
59271
+ previewRefresh: "Refresh",
59272
+ previewNotFound: "File not found (it may have been moved or deleted).",
59273
+ previewTooLarge: "The file is too large to preview (limit: {limit}).",
59274
+ previewDirectory: "This is a directory; browsing directories is not supported yet.",
59275
+ previewNotRegular: "Not a regular file (symlink or similar); preview is not supported.",
59276
+ previewOutsideWorkspace: "This path resolves outside the session workspace (often a symlink pointing outward); the official API refuses to browse it.",
59277
+ previewError: "Failed to read: {code}",
59278
+ previewUnknownBinary: "Binary file; preview is not supported. Copy the path to open it in the workspace.",
59279
+ previewOfficeUnavailable: "Failed to read: Office preview is unavailable. Enable the document preview service on the host running DeepSeek Harness.",
59280
+ previewOfficeFailed: "Office conversion failed; cannot preview. Copy the path to open it in the workspace.",
59281
+ previewBadPayload: "Read result does not match the official contract (logged to the console); nothing rendered.",
59282
+ previewRenderFailed: "Preview rendering failed (logged); the rest of the panel is unaffected.",
59283
+ previewResize: "Drag to resize the preview pane",
59284
+ previewSource: "Source",
59285
+ previewRender: "Preview",
59286
+ previewMdSwitchAria: "Markdown view switch",
59287
+ previewHtmlSwitchAria: "HTML view switch",
59288
+ previewHtmlFrame: "HTML document preview",
59289
+ previewHtmlFailed: "This HTML document could not be previewed.",
59290
+ previewTruncated: "File too large; showing only the first {size}.",
59291
+ previewTruncatedSize: "256KB",
59292
+ explorerEmpty: "Empty directory",
59293
+ explorerTruncated: "The directory is too large; only some entries are shown.",
59294
+ explorerCrumbsAria: "Directory path navigation",
59295
+ explorerLevels: "Choose a directory level",
59296
+ explorerRootName: "(workspace root)",
59297
+ explorerExpand: "Expand directory",
59298
+ explorerCollapse: "Collapse directory",
59299
+ explorerEnter: "Open",
59300
+ colOutputs: "Outputs",
59301
+ procFilesTitle: "Other outputs · {count}",
59302
+ deliverRowTitle: "Deliver files",
59303
+ deliverRowPreparing: "Preparing delivery",
59304
+ deliverRowRunning: "Delivering",
59305
+ deliverRowOk: "Delivered",
59306
+ deliverRowError: "Delivery failed",
59307
+ deliverRowStopped: "Interrupted",
59308
+ deliverFileLabel: "File",
59309
+ deliverPreviewHint: "Preview",
59310
+ deliverPreviewCard: "Preview {name}",
59311
+ deliverAll: "All {count} files",
59312
+ deliverCollapse: "Collapse",
59313
+ deliverExpandAria: "Expand all {count} delivered files",
59314
+ deliverCollapseAria: "Collapse the delivered-files list",
59315
+ tfcReceived: "Received · {files} file(s) from {tasks} preceding task(s)",
59316
+ tfcAttached: "Attached · {count} file(s)",
59317
+ tfcNoOutputs: "No outputs declared",
59318
+ tfcRelOnly: "relative",
59319
+ tfcCrossWorkspace: "other workspace",
59320
+ tfcNoPath: "path unresolved — cannot open",
59321
+ tfcMore: "All {count} files",
59322
+ tfcMoreTasks: "All {count} preceding tasks",
59323
+ tfcCollapse: "Collapse",
59324
+ tfcFromUpload: "uploaded",
59325
+ tfcFromWorkspace: "linked",
59326
+ tfcRestFiles: "{count} more",
59327
+ editorNew: "New task",
59328
+ editorEdit: "Edit task",
59329
+ editorClose: "Close",
59330
+ editorCancel: "Cancel",
59331
+ editorSave: "Save",
59332
+ editorSavePending: "Save logic not wired yet (P2): this round is UI only",
59333
+ editorEnabled: "Enabled",
59334
+ editorEnabledOn: "Enabled — dispatches when due",
59335
+ editorEnabledOff: "Disabled — kept but never dispatches",
59336
+ editorTitle: "Task name",
59337
+ editorTitlePh: "Name this task",
59338
+ editorCode: "Task code",
59339
+ editorCodePh: "Optional, for lookup",
59340
+ editorPrompt: "Prompt",
59341
+ editorPromptPh: "Instructions for the agent…",
59342
+ editorVersions: "Version history",
59343
+ editorWorkspace: "Workspace",
59344
+ editorWorkspacePh: "Choose workspace",
59345
+ editorModel: "Model",
59346
+ editorModelPh: "Default model",
59347
+ editorFollowHost: "Default model",
59348
+ editorPermission: "Permissions",
59349
+ editorPermissionHint: "Scope of actions granted to the agent for this run. Default = session default (inherited from the host’s new-session permission setting). The host has no per-task permission API yet, so the chosen level is enforced through the dispatch instruction.",
59350
+ editorPermDefault: "Permissions: default",
59351
+ editorPermReadOnly: "View only",
59352
+ editorPermWorkspace: "Edit in workspace",
59353
+ editorPermFull: "Full access",
59354
+ editorNoOptions: "Nothing to choose yet",
59355
+ editorSchedule: "Schedule",
59356
+ editorScheduleHint: "Set when and how often the task runs: once (runs a single time when due), recurring (daily / weekly / monthly and so on), or interval (every fixed span of time).",
59357
+ editorSchedulePeriodic: "Recurring",
59358
+ editorScheduleInterval: "Interval",
59359
+ editorFreq: "Frequency",
59360
+ editorFreqOnce: "Once",
59361
+ editorFreqDaily: "Daily",
59362
+ editorFreqWeekly: "Weekly",
59363
+ editorFreqMonthly: "Monthly",
59364
+ editorFreqQuarterly: "Quarterly",
59365
+ editorFreqYearly: "Yearly",
59366
+ editorOnceHint: "Runs once when due, never repeats",
59367
+ editorMonthEvery: "All months",
59368
+ editorMonthOdd: "Odd months",
59369
+ editorMonthEven: "Even months",
59370
+ editorQuarterMonthOption: "Month {m}",
59371
+ editorDate: "Date",
59372
+ editorDatePh: "Pick a date",
59373
+ editorTime: "Time",
59374
+ editorTimePh: "Pick a time",
59375
+ editorMonth: "Month",
59376
+ editorMonthOption: "Month {m}",
59377
+ editorDayOfMonth: "Day",
59378
+ editorDayOption: "{d}",
59379
+ editorIntervalEvery: "Every",
59380
+ editorIntervalStep: "Interval step",
59381
+ editorIntervalUnit: "Interval unit",
59382
+ editorIntervalSuffix: "",
59383
+ editorEveryNWeeks: "Every {n} weeks",
59384
+ editorAttachments: "Attachments",
59385
+ editorAttachmentLink: "Link",
59386
+ editorAttachmentUpload: "Upload",
59387
+ editorAttachmentRemove: "Remove",
59388
+ editorAttachmentView: "View",
59389
+ editorAttachmentAdd: "Add file",
59390
+ editorPickWorkspaceFile: "Pick workspace file",
59391
+ editorPickWorkspaceFileShort: "Files",
59392
+ editorDropZoneHint: "Click or drop files to upload — single or multiple",
59393
+ editorDropZoneFormats: "Text/code, image and document formats, up to 20MB each",
59394
+ editorUploading: "Uploading…",
59395
+ editorUploadErrType: "Unsupported file type: only common text/code, image and document files are allowed",
59396
+ editorUploadErrSize: "File exceeds the size limit (20MB max each)",
59397
+ editorUploadErrEmpty: "File is empty",
59398
+ editorUploadErrGeneric: "Upload failed, please retry",
59399
+ editorPickerNoSession: "No sessions in this workspace yet — its files cannot be read.",
59400
+ editorAttachmentsHint: "Attachments are dispatched along with the task: while it runs, the agent can read and operate on their contents. You can also pick a whole folder: the agent gets the folder path and explores it itself, so refer to a file inside as \"<folder name>/<relative path>\" in the prompt.",
59401
+ editorPickerPick: "Pick this file",
59402
+ editorPickerPickDir: "Pick this folder (with everything under it)",
59403
+ editorPickerSelect: "Select",
59404
+ editorOpenEditor: "Full-screen edit",
59405
+ editorPromptEditorTitle: "Prompt editor (.md)",
59406
+ editorNoVersions: "No versions yet — save one to roll back here",
59407
+ editorHistoryVersions: "History versions",
59408
+ editorNewTaskNoVersions: "New task not saved yet — no history versions to query",
59409
+ editorDeleteVersion: "Delete",
59410
+ editorUseVersion: "Use this version",
59411
+ editorConfirmDeleteTitle: "Delete version",
59412
+ editorConfirmDeleteDesc: "Delete this version record? This cannot be undone — please proceed with care.",
59413
+ editorDeleteTask: "Delete task",
59414
+ editorDeleteTaskTitle: "Delete task",
59415
+ editorDeleteTaskDesc: "Everything removed is unrecoverable and irreversible.",
59416
+ editorReset: "Reset",
59417
+ editorResetDone: "Restored to the values from when you opened it",
59418
+ editorResetTitle: "Reset current edits?",
59419
+ editorResetDesc: "Reset discards all unsaved changes to this task and restores the values from when you opened it. This cannot be undone.",
59420
+ editorVersionToggle: "Versions",
59421
+ editorUseShort: "Use",
59422
+ editorEnabledStateOn: "On",
59423
+ editorEnabledStateOff: "Off",
59424
+ editorToggleOn: "Task enabled",
59425
+ editorToggleOff: "Task disabled",
59426
+ editorFullPermTitle: "This task will run with full access",
59427
+ editorFullPermDesc: "This task runs unattended with FULL access — the AI can directly:",
59428
+ editorFullPermB1: "create / modify / delete files in the workspace",
59429
+ editorFullPermB2: "run commands, make network requests and other sensitive operations",
59430
+ editorFullPermCheck: "I understand the risk and accept responsibility for this task's results.",
59431
+ editorTaskSaved: "Task saved",
59432
+ editorTaskDeleted: "Task deleted",
59433
+ editorSchedHourlyOnce: "runs hourly",
59434
+ editorSchedNoDaySuffix: ", but no active day is selected",
59435
+ editorSchedWeeklyDayPrefix: "every ",
59436
+ editorRestorePromptTitle: "Restore prompt only",
59437
+ editorRestorePromptDesc: "Restoring will overwrite all your current edits with the history version (prompt only; other settings stay as they are).",
59438
+ editorTasksTitle: "Tasks",
59439
+ editorTasksEmpty: "No tasks yet — use \"+ New task\" in the top-right corner to create the first one.",
59440
+ editorDisabledTag: "Disabled",
59441
+ editorModeEdit: "Edit",
59442
+ editorModePreview: "Preview",
59443
+ editorTaskStart: "Task start time",
59444
+ editorTaskStartHint: "The task starts running on this day. You can create it in advance and it begins automatically once this day arrives; \"every N weeks\" also counts its first week from this day.",
59445
+ editorWeekdayLabel: "Weekdays",
59446
+ editorWeekday1: "Mon",
59447
+ editorWeekday2: "Tue",
59448
+ editorWeekday3: "Wed",
59449
+ editorWeekday4: "Thu",
59450
+ editorWeekday5: "Fri",
59451
+ editorWeekday6: "Sat",
59452
+ editorWeekday7: "Sun",
59453
+ editorWeekdayShorts: "Mo|Tu|We|Th|Fr|Sa|Su",
59454
+ editorWeekdayEmpty: "None = every day",
59455
+ calWeekdays: "Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday",
59456
+ editorToday: "Today",
59457
+ editorPrevMonth: "Previous month",
59458
+ editorNextMonth: "Next month",
59459
+ editorPrevYear: "Previous year",
59460
+ editorNextYear: "Next year",
59461
+ editorMonthTitle: "{m}/{y}",
59462
+ editorHour: "Hour",
59463
+ editorMinute: "Minute",
59464
+ editorNow: "Now",
59465
+ editorConfirm: "OK",
59466
+ editorWindow: "Allow delay",
59467
+ editorWindowHint: "When the task reaches its planned start time but a dependency is unfinished or something else delays it, it may wait up to this long and still run; past this, this run is skipped.",
59468
+ editorSchedForecast: "Forecast",
59469
+ editorSchedRun: "runs",
59470
+ editorSchedEveryday: "every day",
59471
+ editorSchedDaily: "Every day",
59472
+ editorSchedWeekly: "Weekly",
59473
+ editorSchedEveryNWeek: "Every {n} weeks",
59474
+ editorSchedEveryNDay: "Every {n} days",
59475
+ editorSchedQuarterly: "Month {n} of each quarter",
59476
+ editorSchedIntervalMin: "Runs every {n} minutes",
59477
+ editorSchedIntervalHour: "Runs every {n} hours",
59478
+ editorSchedOnce: "runs once",
59479
+ editorSchedInvalidStep: "Interval not set — cannot build a schedule",
59480
+ editorMonthMode_every: "Every month",
59481
+ editorMonthMode_odd: "Odd months",
59482
+ editorMonthMode_even: "Even months",
59483
+ editorDiscardTitle: "Discard unsaved changes?",
59484
+ editorDiscardDesc: "The content has been modified but not saved. These changes will be lost if you close now.",
59485
+ editorDiscardStay: "Keep editing",
59486
+ editorDiscardLeave: "Discard changes",
59487
+ unitMinutes: "minutes",
59488
+ unitHours: "hours",
59489
+ unitDays: "days",
59490
+ unitWeeks: "weeks",
59491
+ editorDeps: "Add prerequisite tasks",
59492
+ editorDepsHint: "This task waits for ALL of its prerequisites to finish before it can start. Rule: the latest run of every prerequisite must have succeeded before this task is allowed to run (skipped runs do not count as failure). At dispatch time the system automatically hands over the files produced by the prerequisites to this task.",
59493
+ editorDepAdd: "Add",
59494
+ editorDepTask: "Task",
59495
+ editorDepTaskPh: "Select a task",
59496
+ editorDepRemove: "Remove",
59497
+ editorDepEmpty: "No prerequisites configured yet",
59498
+ editorDepEmptyHint: "Pick a task below, then add",
59499
+ editorDepDisabledTag: " (disabled)",
59500
+ editorDepTaskEmpty: "No selectable prerequisites",
59501
+ localeTag: "en-US",
59502
+ recordsEmpty: "No executions in this time range",
59503
+ recordsEmptyFiltered: "No executions match the current filters",
59504
+ recordsLoadMore: "Load more",
59505
+ recordsNoMore: "No more",
59506
+ recordsLimitHint: "Loaded 2000 (hard limit reached)",
59507
+ recordsLoadFail: "Failed to load executions",
59508
+ recordsRetry: "Retry",
59509
+ recordsDayCount: "{n} total",
59510
+ recordsTaskPh: "Task",
59511
+ recordsTaskSearch: "Search by name or ID",
59512
+ recordsMore: "More",
59513
+ recordsOutOfScope: "The selected task is not in this workspace",
59514
+ recPlan: "Plan",
59515
+ recActual: "Actual",
59516
+ recPrevDay: "prev day",
59517
+ recNextDay: "next day",
59518
+ recEventsTitle: "Run log",
59519
+ recTokenHint: "Token usage",
59520
+ recordsDepTip: "Prerequisite {n}: {task}",
59521
+ recordsDepFrom: "ran at {time}",
59522
+ recTokenDetail: "input {input} / output {output} / cache {cache}",
59523
+ editorAdvanced: "Advanced settings",
59524
+ editorAdvancedHelp: "This is the advanced configuration area. Read each item’s description before changing it; default values are recommended for routine tasks.",
59525
+ editorRetry: "Retry attempts",
59526
+ editorRetryOnce: "Once",
59527
+ editorRetryTwice: "Twice",
59528
+ editorRetryThrice: "3 times",
59529
+ editorRetryFive: "5 times",
59530
+ editorRetryHint: "How many automatic retries happen after a failed run.",
59531
+ editorGoal: "Start the task with the dsh built-in /goal",
59532
+ editorGoalHint: "On by default: the task objective is handed to the dsh built-in /goal as a persistent goal; the session keeps running more turns until the agent marks the goal complete, and the board settles the run only after the goal truly finishes. Off means a single ordinary round.",
59533
+ editorAgentTeam: "Multi-agent collaboration",
59534
+ editorAgentTeamHint: "Off by default. When on, the agent runs via the official Agent Teams (experimental): the lead session spawns named teammates that share a task board. Requires the host to enable Agent Teams; otherwise it falls back to a single agent and leaves a log entry.",
59535
+ editorPreview: "Config preview",
59536
+ editorPreviewHint: "View the raw config file of this task.",
59537
+ listFilterAll: "All",
59538
+ listFilterEnabled: "Enabled",
59539
+ listFilterDisabled: "Disabled",
59540
+ listFilterAbnormal: "Abnormal",
59541
+ listFilterWorkspaceAll: "All workspaces",
59542
+ listFilterTaskAll: "All tasks",
59543
+ listSearchPlaceholder: "Search by name or code",
59544
+ listRunning: "Running",
59545
+ listNextNone: "No further runs",
59546
+ listNever: "Never run",
59547
+ listCreatedTag: "{date} created",
59548
+ listCreatedUnknown: "creation time unknown",
59549
+ relNow: "due now",
59550
+ relMinutes: "in {n} min",
59551
+ relHours: "in {n} h",
59552
+ relDays: "in {n} days",
59553
+ relPast: "overdue",
59554
+ relJustNow: "just now",
59555
+ relMinutesAgo: "{n} min ago",
59556
+ relHoursAgo: "{n} h ago",
59557
+ relDaysAgo: "{n} days ago",
59558
+ relWeeksAgo: "{n} weeks ago",
59559
+ relMonthsAgo: "{n} months ago",
59560
+ relYearsAgo: "{n} years ago",
59561
+ relToday: "Today {time}",
59562
+ relTomorrow: "Tomorrow {time}",
59563
+ relWeeks: "in {n} weeks",
59564
+ relMonths: "in {n} months",
59565
+ relYears: "in {n} years",
59566
+ listLastFullTitle: "Last run: {when}",
59567
+ listNextFullTitle: "Next run: {when}",
59568
+ listEmpty: "No tasks yet. Use “+ New task” to create the first one.",
59569
+ listEmptyFiltered: "No task matches the current filter.",
59570
+ listEmptyTitle: "Create your first scheduled task",
59571
+ listEmptyHint: "Write a prompt and it runs automatically on time — every day, every week, or on an interval.",
59572
+ listEmptyAction: "+ New scheduled task",
59573
+ listSectionAttachments: "Attachments",
59574
+ listSectionDepends: "Preceding tasks",
59575
+ listFieldWorkspace: "Workspace",
59576
+ listFieldModel: "Model",
59577
+ listFieldModelDefault: "Default model",
59578
+ listFieldRetry: "Retries",
59579
+ listFieldSchedule: "Schedule",
59580
+ listFieldWindow: "Late window",
59581
+ listNone: "(none)",
59582
+ listDisabledTag: "(disabled)",
59583
+ infoSectionConfig: "Task settings",
59584
+ infoNextExec: "Next run",
59585
+ infoLastRun: "Last run",
59586
+ infoNoRun: "No runs yet",
59587
+ infoFinishedAt: "Ended at",
59588
+ infoSession: "Session",
59589
+ infoDuration: "Duration",
59590
+ editorTabView: "View",
59591
+ editorTabEdit: "Edit",
59592
+ infoStateEnabled: "Active",
59593
+ infoStateDisabled: "Disabled",
59594
+ infoViewTask: "View this task",
59595
+ editorViewDraftTag: "Draft edits (unsaved)",
59596
+ editorViewNewTag: "New, not saved",
59597
+ editorViewUntitled: "Untitled task",
59598
+ editorViewPrompt: "Prompt",
59599
+ editorViewPromptEmpty: "No prompt yet",
59600
+ editorViewNoRunDraft: "Not saved yet — no run history",
59601
+ editorViewNotFilled: "Not set",
59602
+ editorViewSourceCode: "Source",
59603
+ editorViewExpand: "Expand",
59604
+ editorViewCollapse: "Collapse",
59605
+ editorViewSwitchTitle: "Discard unsaved changes?",
59606
+ editorViewSwitchDesc: "This task has unsaved edits. Continuing will discard them and open the task you want to view.",
59607
+ editorEditSwitchTitle: "Unsaved edits",
59608
+ editorEditSwitchDesc: "The task you are editing has unsaved changes. Overwriting discards them and opens the new task’s editor; keep editing to preserve your changes.",
59609
+ editorEditSwitchKeep: "Keep editing",
59610
+ editorEditSwitchOverwrite: "Overwrite",
59611
+ cardTabInfo: "Basic info",
59612
+ cardTabRecords: "Run records",
59613
+ cardTabLogs: "Logs",
59614
+ cardDelete: "Delete",
59615
+ cardDeleteTitle: "Delete task",
59616
+ cardDeleteDesc: "Delete this task? Its definition, attachments and version history will be removed permanently (run records are kept for audit).",
59617
+ cardCancel: "Cancel",
59618
+ cardRunNow: "Run now",
59619
+ cardRunNowTitle: "Run this task now",
59620
+ cardRunNowDesc: "Run this task immediately?",
59621
+ cardRunNowOk: "Submitted — waiting for the dispatch loop",
59622
+ cardRunNowAlready: "This task is already running; please wait",
59623
+ cardRunNowBlocked: "Upstream task not satisfied — not executed",
59624
+ cardRunNowMissingDep: "Upstream task no longer exists — not executed",
59625
+ cardRunNowWorkspace: "Workspace not found: {name}",
59626
+ cardRunNowAttachment: "Attachment missing: {name}",
59627
+ cardRunNowNotFound: "Task not found; please refresh",
59628
+ cardRunNowNotReady: "Plugin not ready yet; please retry",
59629
+ cardRunNowFailed: "Could not run ({reason})",
59630
+ cardFrom: "Start",
59631
+ cardTo: "End",
59632
+ cardKeyword: "Keyword",
59633
+ cardLogLimit: "Rows",
59634
+ limitPrefix: "Show",
59635
+ limitSuffix: "rows",
59636
+ cardRecordsEmpty: "(no run records for this task yet)",
59637
+ cardRecordsEmptyFiltered: "(no data matches the current filter)",
59638
+ cardLogsEmpty: "(no logs for this task yet)",
59639
+ cardLoadFailed: "Failed to load",
59640
+ cardEventsEmpty: "(no events for this run)",
59641
+ colTokens: "Token",
59642
+ statusPending: "Pending",
59643
+ statusDispatched: "Dispatched",
59644
+ statusRunning: "Running",
59645
+ statusSucceeded: "Succeeded",
59646
+ statusFailed: "Failed",
59647
+ statusSkipped: "Skipped",
59648
+ statusUnknown: "Unknown",
59649
+ colDuration: "Duration",
59650
+ colPlanned: "Scheduled",
59651
+ colActualStart: "Started",
59652
+ colView: "View",
59653
+ colNote: "Note",
59654
+ filterRunning: "Running",
59655
+ trAll: "All",
59656
+ trToday: "Today",
59657
+ trYesterday: "Yesterday",
59658
+ trThisWeek: "This week",
59659
+ trLastWeek: "Last week",
59660
+ trThisMonth: "This month",
59661
+ trLastMonth: "Last month",
59662
+ schedCustom: "{cron}",
59663
+ settingsBasic: "Basic info",
59664
+ settingsTitleFormat: "Title format",
59665
+ settingsDesc: "Description",
59666
+ settingsLang: "Interface language",
59667
+ settingsLangValue: "Follow host (Chinese / English)",
59668
+ settingsParams: "Runtime parameters (live)",
59669
+ settingsLoopSec: "Loop interval (sec)",
59670
+ settingsLoopHint: "How often the scheduler scans the task table.",
59671
+ settingsLoopHelp: "Every this many seconds the dispatch loop scans for tasks that are due. Smaller = reacts sooner but scans more often and costs more; larger = the opposite. 60 seconds is usually enough.",
59672
+ settingsWaitSec: "Dispatch grace (sec)",
59673
+ settingsWaitHint: "How long to wait for the session to start after dispatch.",
59674
+ settingsWaitHelp: "After a task is dispatched, this is how long we wait for its session to be created and start running. If it never starts, the run is marked \"wait timed out\" and the next schedule takes over.",
59675
+ settingsLeaseSec: "Run lease (sec)",
59676
+ settingsLeaseHint: "Reclaim the slot after this long with no sign of life.",
59677
+ settingsLeaseHelp: "A running task renews its lease every time it is scanned. Only when there has been no sign of life for this long (e.g. the process hung) is it judged stuck and its concurrency slot reclaimed. A task that is genuinely running keeps renewing and is never cut off.",
59678
+ settingsUnknownSec: "Unknown grace (sec)",
59679
+ settingsUnknownHint: "How long to keep watching when the session state is unknown.",
59680
+ settingsUnknownHelp: "When the session state cannot be read, keep watching for this long (it may be a transient host hiccup). Only if there is still no answer after that is the run judged abnormal and reclaimed.",
59681
+ settingsSaveSuccess: "Settings saved",
59682
+ settingsLoadFailed: "Failed to load current settings, please retry later",
59683
+ settingsUnitSec: "sec",
59684
+ loading: "Loading",
59685
+ invalidNumber: "Please enter a valid integer number of seconds",
59686
+ statusRailOff: "Disabled",
59687
+ statusRailLastFailed: "Last run failed",
59688
+ statusRailLastSkipped: "Last run skipped (config or prerequisites unsatisfied; see records)",
59689
+ statusRailRunning: "Scheduled / running",
59690
+ debugRowsSuffix: "rows",
59691
+ tabSettings: "Settings",
59692
+ settingsConfigTitle: "Plugin Settings",
59693
+ settingsBlockLogTitle: "Plugin Log",
59694
+ settingsBlockDataTitle: "Database Tables",
59695
+ settingsCurrentConfig: "Config Preview (read-only)",
59696
+ settingsDefaultValue: "default",
59697
+ settingsProvider: "Default Model Provider",
59698
+ settingsModel: "Default Model",
59699
+ settingsLogRetention: "Run Log Retention (days)",
59700
+ settingsLogRetentionHint: "How long diagnostic logs are kept before purging.",
59701
+ settingsLogRetentionHelp: "Diagnostic logs produced during each run (task log + plugin log) are kept for this many days, then purged. Only log details are cleared — run records are always kept and never affected.",
59702
+ settingsAttachmentRetention: "Temp Attachment Retention (days)",
59703
+ settingsAttachmentRetentionHint: "How long attachment copies in the temp directory are kept.",
59704
+ settingsAttachmentRetentionHelp: "When an attachment is uploaded or referenced, the plugin keeps a copy in its own temp directory, cleaned up after this many days. The attachment formally referenced by the task definition is not affected.",
59705
+ settingsUnsaved: "You have unsaved changes",
59706
+ settingsDefaultOption: "Default",
59707
+ settingsModelHint: "With nothing selected, the host default model is used.",
59708
+ settingsModelHelp: "Pick the provider first, then the concrete model — both are required. Leaving it empty (default model) means following the host default. New tasks inherit this choice, and each task can still override it.",
59709
+ settingsCustom: "Custom",
59710
+ settingsResetDefault: "Restore default",
59711
+ settingsErrRange: "Enter a whole number between {min} and {max}.",
59712
+ settingsErrPair: "Provider and model go together: if you pick one, you must pick the other.",
59713
+ settingsErrUpdateFailed: "The settings could not be written (the host config surface is not ready yet). Please retry later.",
59714
+ settingsErrInvalidField: "The value for \"{name}\" is not valid — please check and retry.",
59715
+ settingsErrRangeField: "\"{name}\" is out of the allowed range ({min}–{max}).",
59716
+ settingsErrEmpty: "There is nothing to save.",
59717
+ settingsAutoRefresh: "Auto Refresh (5s)",
59718
+ settingsRefresh: "Refresh",
59719
+ settingsLogEmpty: "(no logs yet)",
59720
+ settingsLogFail: "Failed to load logs",
59721
+ settingsTable: "Table",
59722
+ settingsTopN: "Top N Rows",
59723
+ settingsPageSize: "Rows per page",
59724
+ settingsAddFilter: "Add Filter",
59725
+ settingsNoFilters: "(no filter: show all rows)",
59726
+ settingsQuery: "Query",
59727
+ settingsTableTruncated: "Row count exceeded limit; showing newest subset",
59728
+ settingsTableEmpty: "(empty table: no records yet)",
59729
+ settingsTableFail: "Failed to load",
59730
+ settingsFilterColumn: "Column",
59731
+ settingsFilterOp: "Operator",
59732
+ settingsFilterValue: "Value",
59733
+ settingsFilterRemove: "Remove filter",
59734
+ settingsIntro: "Nothing to configure here — tasks and all settings live inside the plugin panel (sidebar \"Scheduled task dispatcher\").",
59735
+ configNameLabel: "Display name",
59736
+ configNameHint: "Leave it empty to use the default name (Scheduled task dispatcher); saving the default itself stores nothing.",
59737
+ configEntryLabel: "Entry point",
59738
+ configEntryHint: "Show it as a sidebar item, or tuck it into the More menu at the sidebar foot.",
59739
+ configEntryFirst: "Sidebar",
59740
+ configEntryMore: "More menu",
59741
+ configHideOfficialLabel: "Hide the official Automation tasks",
59742
+ configHideOfficialHint: "Hides the official automation entry shipped with dsh. Uninstalling this plugin restores it; a restart or hot update applies the change.",
59743
+ configHideOff: "Show",
59744
+ configHideOn: "Hide",
59745
+ configGithub: "Visit GitHub",
59746
+ configComponents: "dsh-plugin-core",
59747
+ configOverridden: "Custom",
59748
+ configReset: "Restore default",
59749
+ configSave: "Save",
59750
+ configSaving: "Saving…",
59751
+ configSaveFailed: "Save failed, please retry",
59752
+ configUnavailable: "Cannot read the current settings (config channel not ready yet) — reopen this page later.",
59753
+ vTitleRequired: "Task name is missing — the list identifies tasks by name. Please give it one.",
59754
+ vWorkspaceRequired: "No workspace selected — a task must belong to a workspace to run. Pick one above.",
59755
+ vWorkspaceUnknown: "The selected workspace no longer exists (it may have been renamed or deleted). Pick a current workspace above.",
59756
+ vPromptRequired: "The prompt is empty — it tells the agent what to do and cannot be blank.",
59757
+ vIntervalInvalid: "Invalid interval — N in \"every N minutes/hours\" must be an integer greater than 0.",
59758
+ vWeekdayRequired: "Weekly schedule with no weekday selected — pick at least one, or the task never runs.",
59759
+ vAttachmentInvalid: "Attachment \"{name}\" has an invalid ref — it must be a workspace-relative path. Remove and re-pick it."
59413
59760
  };
59414
59761
  // src/client/process-files.tsx
59415
59762
  /** 目录以尾斜杠表达(与 `normalizeOutputs` / `baseNameOf` 同一约定)。 */
@@ -60291,6 +60638,21 @@ window.__ModuleLoader__.load({
60291
60638
  ], [t]);
60292
60639
  const titleById = (0, react$1.useMemo)(() => new Map(tasks.map((o) => [o.id, o.label])), [tasks]);
60293
60640
  /**
60641
+ * 记录标题**只认派发快照**(用户 2026-11-08 拍板):这次执行时任务叫什么,就显示什么。
60642
+ * 不查实时任务定义 —— 任务改名 / 删掉都不该回头改写历史;快照缺失(早期老行)才退原始 task_id。
60643
+ * 零成本:`SELECT *` 早把 snapshot 整包带回来了,这里只是解析。
60644
+ */
60645
+ const snapshotTitleOf = (row) => {
60646
+ const raw = row.snapshot;
60647
+ if (typeof raw !== "string" || raw === "") return "";
60648
+ try {
60649
+ const title = JSON.parse(raw).title;
60650
+ return typeof title === "string" ? title.trim() : "";
60651
+ } catch {
60652
+ return "";
60653
+ }
60654
+ };
60655
+ /**
60294
60656
  * 任务 id → 任务名(查不到退短 id 8 位,**绝不编造**)—— 前置圈码与前置清单用。
60295
60657
  * 与父级 `upstreamOf`(index.ts)同口径;`useCallback` 保证 memo 条目不被无谓重渲。
60296
60658
  */
@@ -60510,7 +60872,7 @@ window.__ModuleLoader__.load({
60510
60872
  }, (0, react$1.createElement)("div", { className: "dsh-tdt-rec-dayrow" }, (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconClockOutlineRegular, { size: 12 }), (0, react$1.createElement)("span", { className: "dsh-tdt-rec-daylabel" }, dayLabelOf(day.key, dayFormatter)), (0, react$1.createElement)("span", { className: "dsh-tdt-rec-sep" }, "·"), (0, react$1.createElement)("span", { className: "dsh-tdt-rec-daylabel" }, weekdayOf(day.key, weekdayFormatter)), (0, react$1.createElement)("span", { className: "dsh-tdt-rec-sep" }, "·"), (0, react$1.createElement)("span", { className: "dsh-tdt-rec-daycount" }, tt("recordsDayCount", { n: day.items.length }))), (0, react$1.createElement)("div", { className: "dsh-tdt-rec-items" }, day.items.map((row) => (0, react$1.createElement)(RecordItem, {
60511
60873
  key: row.id,
60512
60874
  row,
60513
- label: titleById.get(row.task_id) ?? row.task_id,
60875
+ label: snapshotTitleOf(row) || row.task_id,
60514
60876
  workspace: workspaceLabelOf(workspaces, workspaceOf(row)),
60515
60877
  t,
60516
60878
  tt,
@@ -79285,23 +79647,37 @@ window.__ModuleLoader__.load({
79285
79647
  * (宿主要的只是「某个有效会话」,与服务端 `attachmentsWithPaths` 的兜底同款)。
79286
79648
  * 算不出锚点 ⇒ 返回 null ⇒ 该行不可点:**没有会话的工作区宿主列不出文件,不给假入口**。
79287
79649
  */
79288
- const previewTargetOf = (att) => {
79650
+ /**
79651
+ * 取某个工作区的锚点会话。键有两套口径:草稿里存的是工作区 **id**(2026-11-06 改),
79652
+ * 而核心 `pickWorkspaceFile` 回传的 `workspace` 是工作区 **显示名**(`PickedPath.workspace`
79653
+ * = title)⇒ 名字也要认,否则刚选进来的附件算不出锚点、整行点不动(2026-11-08 用户报的)。
79654
+ */
79655
+ const anchorOfWorkspace = (key) => {
79289
79656
  const anchors = workspaceAnchors ?? {};
79657
+ if (key === "") return void 0;
79658
+ const byId = anchors[key];
79659
+ if (byId !== void 0 && byId !== "") return byId;
79660
+ const byTitle = workspaces.find((item) => item.label === key)?.value ?? "";
79661
+ const hit = byTitle === "" ? void 0 : anchors[byTitle];
79662
+ return hit === void 0 || hit === "" ? void 0 : hit;
79663
+ };
79664
+ const previewTargetOf = (att) => {
79290
79665
  const hit = resolvedAttachments?.find((r) => r.name === att.name && r.kind === att.kind && r.path !== void 0 && r.anchorSessionId !== void 0);
79291
79666
  if (hit !== void 0) return {
79292
79667
  path: hit.path,
79293
79668
  anchorSessionId: hit.anchorSessionId
79294
79669
  };
79295
79670
  if (att.kind === "link") {
79296
- const anchor = anchors[att.workspace !== void 0 && att.workspace !== "" ? att.workspace : draft.workspace];
79297
- return anchor === void 0 || anchor === "" ? null : {
79671
+ const source = att.workspace !== void 0 && att.workspace !== "" ? att.workspace : draft.workspace;
79672
+ const anchor = anchorOfWorkspace(source);
79673
+ return anchor === void 0 ? null : {
79298
79674
  path: att.ref,
79299
79675
  anchorSessionId: anchor
79300
79676
  };
79301
79677
  }
79302
79678
  const abs = uploadPaths[att.id];
79303
79679
  if (abs === void 0 || abs === "") return null;
79304
- const anyAnchor = Object.values(anchors).find((item) => typeof item === "string" && item !== "");
79680
+ const anyAnchor = Object.values(workspaceAnchors ?? {}).find((item) => typeof item === "string" && item !== "");
79305
79681
  return anyAnchor === void 0 ? null : {
79306
79682
  path: abs,
79307
79683
  anchorSessionId: anyAnchor
@@ -82595,8 +82971,6 @@ window.__ModuleLoader__.load({
82595
82971
  const SETTINGS_NS = "dsh-task-dispatch-table";
82596
82972
  /** 字典命名空间(locale 注册表独立于 settings 命名空间,取同名便于对应)。 */
82597
82973
  const LOCALE_NS = SETTINGS_NS;
82598
- /** 主面板 id:`main` 槽的 key 与 `sidebar.panellist` 条目的 id 必须一致,选中才对得上。 */
82599
- const PANEL_ID = SETTINGS_NS;
82600
82974
  /** 编辑分栏在核心 PanelDock 里的组名(预览 dock 是另一条、另一个控制器,同名也不冲突)。 */
82601
82975
  const EDITOR_PANEL_ID = "task-editor";
82602
82976
  /** 模块级 t 席位:`sidebar.panellist` 的 label 在渲染期由侧栏求值,拿不到组件 props 的 t。 */
@@ -83914,6 +84288,7 @@ window.__ModuleLoader__.load({
83914
84288
  function apply(ctx) {
83915
84289
  connectFs({ path: FS_ROUTE });
83916
84290
  connectHost(ctx);
84291
+ refreshAppearance();
83917
84292
  setTooltipRenderer((props) => (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
83918
84293
  label: props.label,
83919
84294
  side: props.side ?? "top",
@@ -83969,6 +84344,9 @@ window.__ModuleLoader__.load({
83969
84344
  if (layout !== void 0) selectPanel = (id) => {
83970
84345
  layout.selectPanel(id);
83971
84346
  };
84347
+ setSidebarNavigator((id) => {
84348
+ selectPanel(id);
84349
+ });
83972
84350
  });
83973
84351
  let cardRegistered = false;
83974
84352
  const registerCard = (sub) => {
@@ -84019,13 +84397,12 @@ window.__ModuleLoader__.load({
84019
84397
  }, ConfigPanel));
84020
84398
  });
84021
84399
  ctx.inject(["slots"], (sub) => {
84022
- sub.slots.inject("sidebar.panellist", () => sub.slots.register({
84023
- name: "sidebar.panellist",
84024
- id: PANEL_ID,
84025
- order: 30,
84026
- label: () => runtimeT("panelTitle"),
84027
- locale: LOCALE_NS
84028
- }, TaskPanelIcon));
84400
+ initSidebarEntry({
84401
+ slots: sub.slots,
84402
+ icon: TaskPanelIcon,
84403
+ locale: LOCALE_NS,
84404
+ defaultLabel: () => runtimeT("panelTitle")
84405
+ });
84029
84406
  sub.slots.inject("main", () => sub.slots.register({
84030
84407
  name: "main",
84031
84408
  key: PANEL_ID,