dsh-taskboard 0.5.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -160
- package/lib/client.js +2564 -678
- package/lib/host/execution.js +3 -0
- package/lib/host/execution.js.map +1 -1
- package/lib/host/routes.js +31 -1
- package/lib/host/routes.js.map +1 -1
- package/lib/host/session-sync.js +449 -0
- package/lib/host/session-sync.js.map +1 -0
- package/lib/host/store.js +9 -2
- package/lib/host/store.js.map +1 -1
- package/lib/index.js +109 -2
- package/lib/index.js.map +1 -1
- package/lib/shared/api.js.map +1 -1
- package/lib/shared/protocol.js +27 -1
- package/lib/shared/protocol.js.map +1 -1
- package/package.json +75 -75
- package/src/client/api.ts +8 -0
- package/src/client/board/AlertModal.tsx +3 -1
- package/src/client/board/ImportModal.tsx +26 -24
- package/src/client/board/SettingsModal.tsx +98 -22
- package/src/client/board/SlashPromptInput.tsx +272 -0
- package/src/client/board/TaskBoard.tsx +53 -49
- package/src/client/board/TaskCard.tsx +33 -21
- package/src/client/board/TaskDetail.tsx +169 -104
- package/src/client/board/TaskFormModal.tsx +254 -202
- package/src/client/board/TemplateManager.tsx +32 -29
- package/src/client/board/labels.ts +36 -27
- package/src/client/controller.ts +62 -2
- package/src/client/i18n/en.ts +455 -0
- package/src/client/i18n/runtime.ts +155 -0
- package/src/client/i18n/zh.ts +460 -0
- package/src/client/index.ts +182 -42
- package/src/client/sidebar-entry.ts +13 -3
- package/src/client/styles.ts +131 -0
- package/src/host/execution.ts +14 -1
- package/src/host/routes.ts +49 -1
- package/src/host/session-sync.ts +650 -0
- package/src/host/store.ts +15 -1
- package/src/index.ts +125 -1
- package/src/shared/api.ts +49 -0
- package/src/shared/protocol.ts +54 -0
- package/src/shared/version.ts +1 -1
package/lib/client.js
CHANGED
|
@@ -5,8 +5,8 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
8
|
-
let react_dom_client = require("react-dom/client");
|
|
9
8
|
let react = require("react");
|
|
9
|
+
let react_dom_client = require("react-dom/client");
|
|
10
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
11
11
|
|
|
12
12
|
//#region src/client/api.ts
|
|
@@ -68,6 +68,8 @@ window.__ModuleLoader__.load({
|
|
|
68
68
|
templateDelete: (id) => post("/dsh-taskboard/templates/delete", { id }),
|
|
69
69
|
settings: () => get("/dsh-taskboard/settings"),
|
|
70
70
|
updateSettings: (body) => post("/dsh-taskboard/settings/update", body),
|
|
71
|
+
promptCompletions: () => get("/dsh-taskboard/prompt-completions"),
|
|
72
|
+
modelCatalog: () => get("/dsh-taskboard/model-catalog"),
|
|
71
73
|
stream(onChange, onGap) {
|
|
72
74
|
const es = new EventSource("/dsh-taskboard/events");
|
|
73
75
|
let revision;
|
|
@@ -157,10 +159,29 @@ window.__ModuleLoader__.load({
|
|
|
157
159
|
* says otherwise. Before 0.5.0 the implicit default was 'worktree'.
|
|
158
160
|
*/
|
|
159
161
|
const DEFAULT_ISOLATION = "none";
|
|
162
|
+
/** Factory default permission preset (0.5.5). */
|
|
163
|
+
const DEFAULT_PERMISSION = "workspace-write";
|
|
164
|
+
/** Validate and normalize a permission string into a valid {@link PermissionMode}. */
|
|
165
|
+
function asPermission(raw) {
|
|
166
|
+
if (typeof raw !== "string") return DEFAULT_PERMISSION;
|
|
167
|
+
const normalized = raw.trim();
|
|
168
|
+
if (normalized === "workspace-write" || normalized === "workspaceWrite") return "workspace-write";
|
|
169
|
+
if (normalized === "read-only" || normalized === "readOnly") return "read-only";
|
|
170
|
+
if (normalized === "danger-full-access" || normalized === "fullAccess") return "danger-full-access";
|
|
171
|
+
throw new Error("permission must be 'workspace-write', 'read-only', or 'danger-full-access'");
|
|
172
|
+
}
|
|
160
173
|
/** The effective default isolation for NEW tasks (board setting → factory default). */
|
|
161
174
|
function defaultIsolationOf(settings) {
|
|
162
175
|
return settings?.defaultIsolation ?? "none";
|
|
163
176
|
}
|
|
177
|
+
/** The effective external session sync switch (board setting → factory default false). */
|
|
178
|
+
function defaultSyncExternalSessionsOf(settings) {
|
|
179
|
+
return settings?.syncExternalSessions ?? false;
|
|
180
|
+
}
|
|
181
|
+
/** The effective default permission preset for NEW tasks (board setting → factory default 'workspace-write'). */
|
|
182
|
+
function defaultPermissionOf(settings) {
|
|
183
|
+
return settings?.defaultPermission ?? "workspace-write";
|
|
184
|
+
}
|
|
164
185
|
/**
|
|
165
186
|
* Parse a five-field cron expression. Supported field syntax: star, star/step
|
|
166
187
|
* (`* / n` without spaces), a single number, an `a-b` range, and comma lists
|
|
@@ -262,6 +283,974 @@ window.__ModuleLoader__.load({
|
|
|
262
283
|
};
|
|
263
284
|
}
|
|
264
285
|
|
|
286
|
+
//#endregion
|
|
287
|
+
//#region src/client/i18n/zh.ts
|
|
288
|
+
/**
|
|
289
|
+
* zh-CN dictionary — the taskboard UI namespace's key canon.
|
|
290
|
+
*
|
|
291
|
+
* Every key the board renders goes here; en.ts is type-checked against
|
|
292
|
+
* this file's key set (Dict), so the two dictionaries can never drift
|
|
293
|
+
* apart at compile time. Keys are flat '<domain>.<object>[.<detail>'
|
|
294
|
+
* strings; values may carry {name} placeholders (translate() substitutes
|
|
295
|
+
* them; unknown names are left verbatim — {{lastExecution}} survives).
|
|
296
|
+
*
|
|
297
|
+
* @module dsh-taskboard/client/i18n/zh
|
|
298
|
+
*/
|
|
299
|
+
const zh = {
|
|
300
|
+
"shared.close": "关闭",
|
|
301
|
+
"shared.cancel": "取消",
|
|
302
|
+
"shared.loading": "读取中…",
|
|
303
|
+
"shared.blocked": "受阻",
|
|
304
|
+
"shared.permission": "权限",
|
|
305
|
+
"shared.confirmDelete": "确认删除",
|
|
306
|
+
"shared.current": "(当前:{name})",
|
|
307
|
+
"shared.entry.aria": "Agent 任务看板",
|
|
308
|
+
"shared.entry.label": "任务看板",
|
|
309
|
+
"shared.stats.title": "待办 {todo} | 进行中 {doing} | 待验收 {review}(待办|进行中|待验收)",
|
|
310
|
+
"shared.duplicate.suffix": "(副本)",
|
|
311
|
+
"status.column.backlog": "待规划",
|
|
312
|
+
"status.column.todo": "待办",
|
|
313
|
+
"status.column.in_progress": "进行中",
|
|
314
|
+
"status.column.in_review": "待验收",
|
|
315
|
+
"status.column.done": "已完成",
|
|
316
|
+
"status.column.canceled": "已取消",
|
|
317
|
+
"status.column.archived": "已归档",
|
|
318
|
+
"status.pill.backlog": "待规划",
|
|
319
|
+
"status.pill.todo": "待办",
|
|
320
|
+
"status.pill.in_progress": "进行中",
|
|
321
|
+
"status.pill.in_review": "待验收",
|
|
322
|
+
"status.pill.done": "完成",
|
|
323
|
+
"status.pill.canceled": "取消",
|
|
324
|
+
"status.pill.archived": "归档",
|
|
325
|
+
"status.move.backlog": "待规划",
|
|
326
|
+
"status.move.todo": "待办",
|
|
327
|
+
"status.move.in_progress": "进行中",
|
|
328
|
+
"status.move.in_review": "待验收",
|
|
329
|
+
"status.move.done": "完成",
|
|
330
|
+
"status.move.canceled": "取消",
|
|
331
|
+
"status.move.archived": "归档",
|
|
332
|
+
"urgency.urgent": "紧急",
|
|
333
|
+
"urgency.normal": "一般",
|
|
334
|
+
"urgency.relaxed": "不急",
|
|
335
|
+
"outcome.running": "执行中",
|
|
336
|
+
"outcome.succeeded": "成功",
|
|
337
|
+
"outcome.failed": "失败",
|
|
338
|
+
"outcome.cancelled": "已取消",
|
|
339
|
+
"board.title": "Agent 任务看板",
|
|
340
|
+
"board.count.tasks": "{n} 任务 · rev {rev}",
|
|
341
|
+
"board.action.newTask": "+ 新建任务 ▼",
|
|
342
|
+
"board.action.blankTask": "空白任务",
|
|
343
|
+
"board.action.manageTemplates": "⌗ 管理模板…",
|
|
344
|
+
"board.search.placeholder": "搜索标题 / ID…",
|
|
345
|
+
"board.filter.allProjects": "全部项目",
|
|
346
|
+
"board.sort.title": "列内排序",
|
|
347
|
+
"board.sort.default": "默认排序",
|
|
348
|
+
"board.sort.updated": "最近更新",
|
|
349
|
+
"board.sort.urgency": "按紧急度",
|
|
350
|
+
"board.sort.created": "创建时间",
|
|
351
|
+
"board.sort.byTitle": "按标题",
|
|
352
|
+
"board.action.backToBoard": "返回看板",
|
|
353
|
+
"board.action.otherTasks": "其它任务",
|
|
354
|
+
"board.action.settingsTitle": "看板设置:新建任务的默认执行隔离等",
|
|
355
|
+
"board.action.settings": "🛠 设置",
|
|
356
|
+
"board.action.diagTitle": "健康诊断:遗留 worktree、台账基本项",
|
|
357
|
+
"board.action.diag": "⚙ 诊断",
|
|
358
|
+
"board.action.importTitle": "从 JSON 备份文件导入台账(预览后合并或整册替换)",
|
|
359
|
+
"board.action.import": "⬆ 导入",
|
|
360
|
+
"board.action.exportTitle": "导出台账:完整 JSON 备份或任务清单 CSV",
|
|
361
|
+
"board.action.export": "⬇ 导出 ▼",
|
|
362
|
+
"board.export.jsonTitle": "完整台账备份(含执行历史与看板设置),可用于导入恢复",
|
|
363
|
+
"board.export.json": "完整台账(JSON)",
|
|
364
|
+
"board.export.csvTitle": "任务清单表格(Excel 可直接打开,中文已加 BOM)",
|
|
365
|
+
"board.export.csv": "任务清单(CSV)",
|
|
366
|
+
"board.drag.forbidden": "无法从「{from}」拖至「{to}」",
|
|
367
|
+
"board.empty": "无任务",
|
|
368
|
+
"board.secondary.empty": "无已取消 / 已归档 / 已删除任务",
|
|
369
|
+
"board.group.trashed": "已删除",
|
|
370
|
+
"diag.title": "健康诊断",
|
|
371
|
+
"diag.subtitle": "台账基本项与 worktree 遗留清理",
|
|
372
|
+
"diag.revision": "台账修订号",
|
|
373
|
+
"diag.tasks": "任务总数",
|
|
374
|
+
"diag.running": "执行中",
|
|
375
|
+
"diag.orphans": "遗留 worktree",
|
|
376
|
+
"diag.orphans.heading": "遗留 worktree(台账无主但目录存在)",
|
|
377
|
+
"diag.orphans.none": "无遗留 — 各项目 .dsh-worktrees 目录干净",
|
|
378
|
+
"diag.orphans.cleanup": "清理",
|
|
379
|
+
"diag.orphans.hint": "提示:有未提交修改的遗留目录会被拒绝清理,请先手动处理其内容。live 任务的 worktree 请在任务详情页删除。",
|
|
380
|
+
"diag.gitignore.heading": "gitignore 建议",
|
|
381
|
+
"diag.gitignore.none": "无待办 — 各 git 项目已忽略 .dsh-worktrees 目录",
|
|
382
|
+
"diag.gitignore.suggestA": "建议在 .gitignore 加入一行",
|
|
383
|
+
"diag.gitignore.suggestB": "(不会自动修改)",
|
|
384
|
+
"card.drag.running": "该任务正由会话执行中({title}),不能拖动",
|
|
385
|
+
"card.badge.stale": "⏱ 认领超时",
|
|
386
|
+
"card.badge.modelTitle": "固定模型: {model}",
|
|
387
|
+
"card.badge.modelEffort": " · 思考强度: {effort}",
|
|
388
|
+
"card.badge.checklistReview": "待验收:清单未全部勾选",
|
|
389
|
+
"card.badge.checklist": "验收清单进度",
|
|
390
|
+
"card.badge.pendingPurge": "待清除",
|
|
391
|
+
"card.session.jumpTitle": "点击一键跳转到会话:{id}",
|
|
392
|
+
"card.session.missing": "该会话已被删除({id}),无法打开",
|
|
393
|
+
"card.session.archived": "该会话已归档({id}),已从会话列表隐藏",
|
|
394
|
+
"card.session.unavailable": "会话导航不可用,会话 ID:{id}",
|
|
395
|
+
"card.reject.placeholder": "退回原因(可选,agent 开工前会读)…",
|
|
396
|
+
"card.reject.confirm": "退回待办",
|
|
397
|
+
"card.action.doneTitle": "验收完成:移至已完成",
|
|
398
|
+
"card.action.done": "✓ 完成",
|
|
399
|
+
"card.action.rejectTitle": "退回待办,可附退回原因",
|
|
400
|
+
"card.action.reject": "✗ 退回",
|
|
401
|
+
"alert.ok": "知道了",
|
|
402
|
+
"diff.commit": "提交 {hash}",
|
|
403
|
+
"diff.file": "文件 {path}",
|
|
404
|
+
"diff.truncated": "⚠ 内容过长已截断",
|
|
405
|
+
"diff.failed": "获取失败(原因见看板顶部错误条;对象可能已随 worktree 删除丢失)",
|
|
406
|
+
"checklist.title": "验收清单(DoD)",
|
|
407
|
+
"checklist.unchecked": " · {n} 项未完成",
|
|
408
|
+
"checklist.allDone": " · 全部完成",
|
|
409
|
+
"checklist.byUser": "👤 用户",
|
|
410
|
+
"checklist.uncheckedItem": "未完成",
|
|
411
|
+
"checklist.evidence": "证据:{note}",
|
|
412
|
+
"report.title": "执行报告",
|
|
413
|
+
"report.submitted": "由执行会话提交 · {time}",
|
|
414
|
+
"report.changedFiles": "改动文件",
|
|
415
|
+
"report.checks": "自验情况",
|
|
416
|
+
"report.artifacts": "产物",
|
|
417
|
+
"report.risk": "剩余风险",
|
|
418
|
+
"iso.title": "执行隔离",
|
|
419
|
+
"iso.none": "📁 原目录执行",
|
|
420
|
+
"iso.worktreeTitle": "执行隔离 · Worktree",
|
|
421
|
+
"iso.branch": "🌿 分支",
|
|
422
|
+
"iso.baseline": "基线 {base} → {head}",
|
|
423
|
+
"iso.changed": "改动 {n} 个文件",
|
|
424
|
+
"iso.commit.openTitle": "点击展开该提交的 diff",
|
|
425
|
+
"iso.commits.more": "… 共 {n} 个提交",
|
|
426
|
+
"iso.nocommit": "该次执行没有产生提交(改动可能未提交,见下方警告)",
|
|
427
|
+
"iso.dirty.toggle": "⚠ 有 {n} 处未提交修改(合并前请让 agent 提交,或手动处理)",
|
|
428
|
+
"iso.dirty.expand": " ▼ 查看文件",
|
|
429
|
+
"iso.dirty.collapse": " ▲",
|
|
430
|
+
"iso.dirty.openTitle": "点击查看该文件的未提交 diff",
|
|
431
|
+
"iso.dirty.more": "… 共 {n} 处(完整列表见任务台账)",
|
|
432
|
+
"iso.hint.running": "执行中 — 结束后可合并或清理",
|
|
433
|
+
"iso.merge.confirm": "将分支以 --no-ff 合并到主工作区?",
|
|
434
|
+
"iso.merge.go": "确认合并",
|
|
435
|
+
"iso.merge.title": "在主工作区 git merge --no-ff 该任务分支(要求主区干净;冲突会原样报告)",
|
|
436
|
+
"iso.merge.button": "⇥ 合并到主工作区",
|
|
437
|
+
"iso.merge.failed": "合并失败:{error}",
|
|
438
|
+
"iso.merge.noop": "该分支没有领先主工作区的新提交,无需合并(可退回续跑或直接清理)",
|
|
439
|
+
"iso.remove.wt": "🗑 删除 worktree",
|
|
440
|
+
"iso.remove.wtTitle": "git worktree remove(有未提交修改时拒绝)",
|
|
441
|
+
"iso.remove.wtb": "🗑 删 worktree + 分支",
|
|
442
|
+
"iso.remove.wtbTitle": "删除 worktree 并删除任务分支(有未提交修改时拒绝)",
|
|
443
|
+
"iso.remove.confirmWt": "删除 worktree 目录?",
|
|
444
|
+
"iso.remove.confirmWtb": "删除 worktree 并删除分支?",
|
|
445
|
+
"iso.remove.failed": "删除失败:{error}",
|
|
446
|
+
"iso.remove.branchFailed": "worktree 已删除,但分支删除失败:{error}",
|
|
447
|
+
"iso.hint.keep": "分支与 worktree 保留中 — 可退回继续修改",
|
|
448
|
+
"detail.session.jumpTitle": "一键跳转到对应会话:{id}",
|
|
449
|
+
"detail.session.jump": "🤖 跳转会话 ↗",
|
|
450
|
+
"detail.action.edit": "✎ 编辑",
|
|
451
|
+
"detail.action.duplicateTitle": "复制此任务的全部配置为一张新卡(待办列)",
|
|
452
|
+
"detail.action.duplicate": "⧉ 复制",
|
|
453
|
+
"detail.action.saveTplTitle": "把此任务的配置(含清单)保存为模板,新建任务时可用",
|
|
454
|
+
"detail.action.saveTplDone": "已存为模板(新建任务 ▼ 下拉可用,可在模板管理中改名)",
|
|
455
|
+
"detail.action.saveTpl": "⌗ 存为模板",
|
|
456
|
+
"detail.action.reuseTitle": "续跑:保留现有 worktree 与分支(上次的改动和提交都在原处),在其上继续执行;默认「立即执行」会重置为全新基线",
|
|
457
|
+
"detail.action.reuse": "↻ 续跑",
|
|
458
|
+
"detail.action.runTitleModel": "新会话执行({model})",
|
|
459
|
+
"detail.action.runTitleDefault": "新会话执行(默认模型)",
|
|
460
|
+
"detail.action.run": "▶ 立即执行",
|
|
461
|
+
"detail.action.stopConfirm": "停止该执行会话?",
|
|
462
|
+
"detail.action.stop": "停止",
|
|
463
|
+
"detail.action.stopTitle": "停止执行会话 {id}(任务回到待办)",
|
|
464
|
+
"detail.action.stopExec": "■ 停止执行",
|
|
465
|
+
"detail.chip.nextRun": "{cron} · 下次 {time}",
|
|
466
|
+
"detail.chip.checklist": "清单 {done}/{total}",
|
|
467
|
+
"detail.chip.isolated": "Worktree 隔离",
|
|
468
|
+
"detail.chip.holderTitle": "点击跳转至该会话:{id}",
|
|
469
|
+
"detail.chip.holderStale": "认领超时 · ",
|
|
470
|
+
"detail.chip.holderBy": "由 ",
|
|
471
|
+
"detail.chip.holderSuffix": " 持有 ↗",
|
|
472
|
+
"detail.chip.trashed": "已删除待清除",
|
|
473
|
+
"detail.sub.line": "更新 {time} · 最近操作 {who}",
|
|
474
|
+
"detail.updatedBy.system": "⚙️ 系统",
|
|
475
|
+
"detail.updatedBy.user": "👤 用户",
|
|
476
|
+
"detail.field.description": "描述",
|
|
477
|
+
"detail.field.prompt": "执行 Prompt",
|
|
478
|
+
"detail.move.to": "移至→{status}",
|
|
479
|
+
"detail.move.confirmDoneUnchecked": "仍有 {n} 项清单未勾选,确认完成?",
|
|
480
|
+
"detail.move.confirmDone": "确认完成?",
|
|
481
|
+
"detail.move.confirm": "确认",
|
|
482
|
+
"detail.blocked.unmark": "✓ 解除受阻",
|
|
483
|
+
"detail.blocked.mark": "⛔ 标记受阻",
|
|
484
|
+
"detail.release.title": "释放 {id} 的认领:任务回到待办(持有会话可能仍在工作,确认它已停止后再释放)",
|
|
485
|
+
"detail.release.button": "🔓 释放认领",
|
|
486
|
+
"detail.comments.title": "评论",
|
|
487
|
+
"detail.comments.empty": "暂无评论 — agent 交接时会在这里汇报改动与验证结果",
|
|
488
|
+
"detail.comments.user": "用户",
|
|
489
|
+
"detail.composer.placeholder": "以用户身份留言(agent 开工前会读)…",
|
|
490
|
+
"detail.composer.send": "发表",
|
|
491
|
+
"detail.exec.title": "执行记录",
|
|
492
|
+
"detail.exec.prunedTitle": "更早的 {n} 条执行记录已按保留上限清理",
|
|
493
|
+
"detail.exec.pruned": "+{n} 已清理",
|
|
494
|
+
"detail.exec.trigger.manual": "手动",
|
|
495
|
+
"detail.exec.trigger.scheduled": "定时",
|
|
496
|
+
"detail.exec.openTitle": "点击打开该执行会话:{id}",
|
|
497
|
+
"detail.danger.delete": "🗑 删除(标记待清除)",
|
|
498
|
+
"detail.danger.purgeConfirm": "物理清除不可恢复",
|
|
499
|
+
"detail.danger.purgeGo": "确认清除",
|
|
500
|
+
"detail.danger.purge": "🔥 物理清除(需确认)",
|
|
501
|
+
"form.title.create": "新建任务",
|
|
502
|
+
"form.title.edit": "编辑任务",
|
|
503
|
+
"form.subtitle.create": "推入看板,项目内会话可认领执行",
|
|
504
|
+
"form.subtitle.edit": "调整任务内容与执行配置",
|
|
505
|
+
"form.field.title": "标题",
|
|
506
|
+
"form.field.titlePlaceholder": "一句话说清要做什么",
|
|
507
|
+
"form.field.project": "项目",
|
|
508
|
+
"form.field.model": "模型(默认 = 会话默认模型)",
|
|
509
|
+
"form.field.modelDefault": "默认模型",
|
|
510
|
+
"form.model.option": "{name}({provider})",
|
|
511
|
+
"form.field.effort": "思考强度(Reasoning Effort)",
|
|
512
|
+
"form.field.effortTitle": "设置模型的思考强度(如 low/medium/high);默认 = 跟随模型/提供商默认",
|
|
513
|
+
"form.effort.follow": "跟随模型默认",
|
|
514
|
+
"form.effort.low": "低 (low)",
|
|
515
|
+
"form.effort.medium": "中 (medium)",
|
|
516
|
+
"form.effort.high": "高 (high)",
|
|
517
|
+
"form.effort.none": "关闭思考 (none)",
|
|
518
|
+
"form.field.preset": "执行模式(preset)",
|
|
519
|
+
"form.field.presetTitle": "执行会话按该 preset 组合(决定工具集与人设);默认 = 部署默认 preset",
|
|
520
|
+
"form.preset.follow": "跟随部署默认",
|
|
521
|
+
"form.preset.defaultTag": "(部署默认)",
|
|
522
|
+
"form.field.urgency": "紧急度",
|
|
523
|
+
"form.urgency.urgent": "紧急",
|
|
524
|
+
"form.urgency.urgentHint": "优先处理",
|
|
525
|
+
"form.urgency.normal": "一般",
|
|
526
|
+
"form.urgency.normalHint": "正常排期",
|
|
527
|
+
"form.urgency.relaxed": "不急",
|
|
528
|
+
"form.urgency.relaxedHint": "有空再做",
|
|
529
|
+
"form.field.description": "描述",
|
|
530
|
+
"form.field.descriptionOptional": "描述(可选)",
|
|
531
|
+
"form.desc.placeholder": "需求细节、背景说明、验收标准…",
|
|
532
|
+
"form.field.prompt": "执行 Prompt(实际 Prompt = 标题+任务描述+Prompt)",
|
|
533
|
+
"form.field.promptOptional": "执行 Prompt(可选;实际 Prompt = 标题+任务描述+Prompt)",
|
|
534
|
+
"form.prompt.placeholder": "追加在「标题+任务描述」之后发给执行会话的补充指令。支持模板变量:{{lastExecution}}(上次执行结果)、{{lastComments}}(最近 3 条评论)",
|
|
535
|
+
"form.field.mode": "执行方式",
|
|
536
|
+
"form.mode.claim": "🤝 认领制",
|
|
537
|
+
"form.mode.claimHint": "项目内会话认领",
|
|
538
|
+
"form.mode.scheduled": "⏰ 定时执行",
|
|
539
|
+
"form.mode.scheduledHint": "到点自动开跑",
|
|
540
|
+
"form.field.cron": "Cron 表达式",
|
|
541
|
+
"form.cron.placeholder": "分 时 日 月 周",
|
|
542
|
+
"form.cron.daily": "每天 09:00",
|
|
543
|
+
"form.cron.hourly": "每小时",
|
|
544
|
+
"form.cron.every10min": "每 10 分钟",
|
|
545
|
+
"form.cron.weekly": "每周一 09:00",
|
|
546
|
+
"form.cron.next": "下次 {time}",
|
|
547
|
+
"form.field.isolation": "执行隔离",
|
|
548
|
+
"form.iso.locked": "任务已有执行记录,隔离方式已锁定",
|
|
549
|
+
"form.iso.lockedShort": "已锁定(执行开始后不可更改)",
|
|
550
|
+
"form.iso.nonGit": "当前项目非 git 仓库",
|
|
551
|
+
"form.iso.worktree": "🌿 Worktree 隔离",
|
|
552
|
+
"form.iso.worktreeTitle": "每次执行在独立 worktree 分支上进行",
|
|
553
|
+
"form.iso.worktreeHint": "独立分支 task/标题+ID,互不污染",
|
|
554
|
+
"form.iso.none": "📁 原目录执行",
|
|
555
|
+
"form.iso.noneTitle": "直接在项目目录执行(不使用 git)",
|
|
556
|
+
"form.iso.noneHintNonGit": "当前项目非 git 仓库,将在原目录执行",
|
|
557
|
+
"form.iso.noneHint": "不使用 git,直接在项目目录工作",
|
|
558
|
+
"form.iso.nonGitNote": "当前项目非 git 仓库,将在原目录执行(任务仍按默认配置创建,运行时自动降级)",
|
|
559
|
+
"form.field.checklist": "验收清单(DoD)",
|
|
560
|
+
"form.field.checklistOptional": "验收清单(DoD,可选)",
|
|
561
|
+
"form.check.itemPlaceholder": "验收项 {n}(完成标准)",
|
|
562
|
+
"form.check.removeTitle": "删除该验收项",
|
|
563
|
+
"form.check.add": "+ 添加验收项",
|
|
564
|
+
"form.check.checkedTitle": "勾选状态随保存保留(当前勾选人:{who})",
|
|
565
|
+
"form.check.notCheckedYet": "未勾选",
|
|
566
|
+
"form.check.hintCreate": "共 {n} 项,执行会话按清单干活并逐项勾选,未完成项验收时高亮",
|
|
567
|
+
"form.check.hintEdit": "已勾选 {checked}/{total}(保存将整体覆盖清单,勾选状态保留)",
|
|
568
|
+
"form.hint.needTitle": "请填写标题",
|
|
569
|
+
"form.hint.needProject": "请选择项目",
|
|
570
|
+
"form.hint.cronBad": "Cron 表达式无效(分 时 日 月 周)",
|
|
571
|
+
"form.hint.nextRun": "下次运行 {time}",
|
|
572
|
+
"form.hint.saveVersion": "保存后版本 v{v} → v{next}",
|
|
573
|
+
"form.hint.createClaim": "创建后项目内会话可认领执行",
|
|
574
|
+
"form.action.runBlockedTitle": "任务正在执行中,不能重复发起",
|
|
575
|
+
"form.action.runBusyTitle": "正在提交…",
|
|
576
|
+
"form.action.runTitle": "保存后立即发起执行(新会话)",
|
|
577
|
+
"form.action.run": "⚡ 立即执行",
|
|
578
|
+
"form.action.save": "保存修改",
|
|
579
|
+
"form.action.create": "创建任务",
|
|
580
|
+
"tpl.aria": "管理模板",
|
|
581
|
+
"tpl.title": "任务模板",
|
|
582
|
+
"tpl.subtitle": "新建任务 ▼ 下拉的模板:改名 / 删除 / 直接使用;任务详情页「存为模板」可新增",
|
|
583
|
+
"tpl.empty": "暂无模板 — 在任务详情页点「存为模板」把常用配置沉淀下来",
|
|
584
|
+
"tpl.name.aria": "模板名 {name}",
|
|
585
|
+
"tpl.builtin": "内置",
|
|
586
|
+
"tpl.custom": "自建",
|
|
587
|
+
"tpl.meta.checklist": " · 清单 {n} 项",
|
|
588
|
+
"tpl.rename.title": "保存改名",
|
|
589
|
+
"tpl.rename.button": "改名",
|
|
590
|
+
"tpl.use.title": "用此模板打开新建表单",
|
|
591
|
+
"tpl.use.button": "用此新建",
|
|
592
|
+
"tpl.delete.title": "删除该模板",
|
|
593
|
+
"tpl.renamed": "模板已改名",
|
|
594
|
+
"tpl.foot.hint": "模板随台账一同保存在 DSH 主目录,升级不丢",
|
|
595
|
+
"imp.aria": "导入台账",
|
|
596
|
+
"imp.title": "导入台账",
|
|
597
|
+
"imp.subtitle": "选择导出的 JSON 备份文件:先预览、再合并或整册替换",
|
|
598
|
+
"imp.parseError": "文件不是合法 JSON",
|
|
599
|
+
"imp.note": "⬇ JSON 导出的文件即为同格式备份,可直接导入恢复;导入文件的 schemaVersion 必须与当前版本一致。",
|
|
600
|
+
"imp.previewing": "预览中…",
|
|
601
|
+
"imp.stat.create": "新增",
|
|
602
|
+
"imp.stat.overwrite": "覆盖(同 id)",
|
|
603
|
+
"imp.stat.invalid": "无效(跳过)",
|
|
604
|
+
"imp.sec.create": "新增任务",
|
|
605
|
+
"imp.sec.overwrite": "覆盖任务(整卡替换,含执行历史与评论)",
|
|
606
|
+
"imp.sec.invalid": "无效条目(不会导入)",
|
|
607
|
+
"imp.noId": "(无 id)",
|
|
608
|
+
"imp.mode.merge": "⊕ 合并",
|
|
609
|
+
"imp.mode.mergeHint": "新增 + 按 id 覆盖,其余不动",
|
|
610
|
+
"imp.mode.replace": "💣 整册替换",
|
|
611
|
+
"imp.mode.replaceHint": "清空当前台账,以导入文件为准(先自动备份)",
|
|
612
|
+
"imp.result.replace": "整册替换完成:导入 {n} 张(原 {total} 张已整册备份)",
|
|
613
|
+
"imp.result.merge": "合并完成:新增 {n} 张、覆盖 {m} 张",
|
|
614
|
+
"imp.foot.replaceConfirm": "⚠ 再次点击确认执行整册替换(不可撤销,已自动备份)",
|
|
615
|
+
"imp.foot.replaceNeedConfirm": "整册替换需要二次确认",
|
|
616
|
+
"imp.foot.mergeHint": "合并只写入预览中列出的任务",
|
|
617
|
+
"imp.action.run": "执行导入",
|
|
618
|
+
"imp.action.confirmReplace": "确认整册替换",
|
|
619
|
+
"set.aria": "看板设置",
|
|
620
|
+
"set.title": "看板设置",
|
|
621
|
+
"set.subtitle": "新建任务与会话同步的全局默认值",
|
|
622
|
+
"set.iso.heading": "默认执行隔离",
|
|
623
|
+
"set.iso.noneHint": "不使用 git,直接在项目目录工作(出厂默认)",
|
|
624
|
+
"set.iso.worktreeHint": "每次执行在独立 worktree 分支上进行(task/标题+ID),互不污染",
|
|
625
|
+
"set.iso.current": "当前保存的默认:{current}。仅影响之后新建的任务;已有任务保持创建时的选择,非 git 项目运行时仍自动降级原目录。",
|
|
626
|
+
"set.sync.heading": "自动同步工作区会话",
|
|
627
|
+
"set.sync.off.name": "🚫 关闭同步",
|
|
628
|
+
"set.sync.off.title": "仅管理在任务看板中创建和触发的任务",
|
|
629
|
+
"set.sync.off.hint": "仅管理看板任务(出厂默认)",
|
|
630
|
+
"set.sync.on.name": "🔄 自动纳入会话",
|
|
631
|
+
"set.sync.on.title": "各工作区直接新建的会话也会在看板展示,运行中进入「进行中」,完成后自动进入「待验收」",
|
|
632
|
+
"set.sync.on.hint": "跟踪运行并在完成后进入待验收",
|
|
633
|
+
"set.sync.stateOn": "已开启:工作区直接新建并执行的会话将自动在看板生成任务卡片,并在完成后流转至「待验收」列。",
|
|
634
|
+
"set.sync.stateOff": "已关闭:仅在看板内部创建与触发执行的任务会出现在看板上。",
|
|
635
|
+
"set.foot.dirty": "有未保存的修改",
|
|
636
|
+
"set.foot.clean": "与看板当前设置一致",
|
|
637
|
+
"set.action.save": "保存设置",
|
|
638
|
+
"form.field.permission": "执行权限",
|
|
639
|
+
"form.perm.write": "可写入工作区",
|
|
640
|
+
"form.perm.writeHint": "可读写工作区及临时目录(推荐默认)",
|
|
641
|
+
"form.perm.readOnly": "仅可查看",
|
|
642
|
+
"form.perm.readOnlyHint": "只读查看与检索,禁止修改文件或执行外部命令",
|
|
643
|
+
"form.perm.fullAccess": "完全权限",
|
|
644
|
+
"form.perm.fullAccessHint": "完全无限制权限,可访问全盘及执行外部命令",
|
|
645
|
+
"form.perm.defaultTag": "(默认)",
|
|
646
|
+
"set.perm.heading": "默认执行权限",
|
|
647
|
+
"set.perm.writeName": "📁 可写入工作区(出厂默认)",
|
|
648
|
+
"set.perm.writeHint": "可读写工作区及临时目录,写操作无需二次确认",
|
|
649
|
+
"set.perm.readOnlyName": "🔒 仅可查看",
|
|
650
|
+
"set.perm.readOnlyHint": "仅允许只读查看与检索,禁止修改文件或破坏性命令",
|
|
651
|
+
"set.perm.fullName": "⚡ 完全权限",
|
|
652
|
+
"set.perm.fullHint": "完全无限制权限,可访问全盘及执行系统外部命令",
|
|
653
|
+
"set.perm.current": "当前保存的默认:{current}。新建任务时预设的执行权限。",
|
|
654
|
+
"card.badge.permReadOnly": "🔒 仅查看",
|
|
655
|
+
"card.badge.permReadOnlyTitle": "权限:仅可查看",
|
|
656
|
+
"card.badge.permFull": "⚡ 全权限",
|
|
657
|
+
"card.badge.permFullTitle": "权限:完全权限",
|
|
658
|
+
"detail.chip.permReadOnly": "仅可查看",
|
|
659
|
+
"detail.chip.permFull": "完全权限",
|
|
660
|
+
"detail.chip.permWrite": "可写入工作区",
|
|
661
|
+
"tpl.meta.permReadOnly": "仅查看",
|
|
662
|
+
"tpl.meta.permFull": "全权限",
|
|
663
|
+
"md.imageAlt": "图片 {n}",
|
|
664
|
+
"md.imageTitle": "点击查看大图 ({alt})",
|
|
665
|
+
"md.lightboxAlt": "大图预览",
|
|
666
|
+
"md.closePreview": "关闭预览",
|
|
667
|
+
"slash.aria": "快捷命令与技能",
|
|
668
|
+
"slash.title": "快捷命令与技能补全",
|
|
669
|
+
"slash.hint": "↑↓ 选择 · Enter / Tab 确认 · Esc 关闭",
|
|
670
|
+
"slash.badge.command": "⚡ 命令",
|
|
671
|
+
"slash.badge.skill": "🧩 技能",
|
|
672
|
+
"slash.tipA": "💡 输入",
|
|
673
|
+
"slash.tipB": "可快速补全 Slash 命令与 Agent 技能",
|
|
674
|
+
"slash.cmd.goal.desc": "自主完成长期目标任务,持续深度推进",
|
|
675
|
+
"slash.cmd.goal.hint": "<目标描述>",
|
|
676
|
+
"slash.cmd.schedule.desc": "设置一次性定时或周期性 Cron 调度",
|
|
677
|
+
"slash.cmd.schedule.hint": "<时间/表达式>",
|
|
678
|
+
"slash.cmd.plan.desc": "在行动前制定分步实施计划并由用户确认",
|
|
679
|
+
"slash.cmd.browser.desc": "启动网页浏览器交互与实时页面检索",
|
|
680
|
+
"slash.cmd.grill-me.desc": "通过多轮单题访谈深入对齐需求与设计意图",
|
|
681
|
+
"slash.cmd.teamwork-preview.desc": "多智能体协作与团队工作流预览",
|
|
682
|
+
"slash.cmd.learn.desc": "沉淀解决经验与新规则到知识库",
|
|
683
|
+
"slash.cmd.review.desc": "多维度代码审查(正确性、架构与安全)",
|
|
684
|
+
"slash.cmd.security.desc": "安全加固与代码漏洞扫描",
|
|
685
|
+
"slash.cmd.permission.desc": "切换当前会话权限级别 (read-only / workspace-write / full-access)",
|
|
686
|
+
"slash.cmd.permission.hint": "<preset>",
|
|
687
|
+
"slash.skill.frontend-ui-engineering": "构建生产级、可访问的高品质前端界面与组件",
|
|
688
|
+
"slash.skill.api-and-interface-design": "设计稳定契约、清晰边界的 REST / RPC 接口",
|
|
689
|
+
"slash.skill.test-driven-development": "测试驱动开发(TDD),编写单元与集成测试",
|
|
690
|
+
"slash.skill.debugging-and-error-recovery": "系统化定位 Bug 根因并恢复错误",
|
|
691
|
+
"slash.skill.performance-optimization": "前后端性能调优、减少渲染开销与查询优化",
|
|
692
|
+
"slash.skill.ci-cd-and-automation": "自动化构建、CI/CD 流水线与质量门禁",
|
|
693
|
+
"slash.skill.code-review-and-quality": "多轴向代码审查与重构指导",
|
|
694
|
+
"slash.skill.code-simplification": "精简复杂逻辑,提升可读性与可维护性",
|
|
695
|
+
"slash.skill.context-engineering": "优化上下文结构与提示词工程",
|
|
696
|
+
"slash.skill.doubt-driven-development": "以怀疑驱动的对抗式审查,确保核心逻辑正确",
|
|
697
|
+
"slash.skill.git-workflow-and-versioning": "Git 工作流、分支管理、语义化版本与变更日志",
|
|
698
|
+
"slash.skill.idea-refine": "通过发散与收敛思维细化方案与假设检验",
|
|
699
|
+
"slash.skill.incremental-implementation": "小步快跑、增量交付多文件变更",
|
|
700
|
+
"slash.skill.interview-me": "深度访谈挖掘真实意图",
|
|
701
|
+
"slash.skill.memory-leak-debugging": "排查诊断 JavaScript/Node.js 内存泄漏",
|
|
702
|
+
"slash.skill.observability-and-instrumentation": "添加日志、指标打点与链路追踪",
|
|
703
|
+
"slash.skill.planning-and-task-breakdown": "将复杂需求拆解为有序可执行任务",
|
|
704
|
+
"slash.skill.security-and-hardening": "防御安全漏洞、输入过滤与鉴权加固",
|
|
705
|
+
"slash.skill.shipping-and-launch": "生产发布前检查清单与回滚策略",
|
|
706
|
+
"slash.skill.source-driven-development": "基于权威官方文档与源码进行设计实现",
|
|
707
|
+
"slash.skill.spec-driven-development": "在编码前制定清晰的技术规范",
|
|
708
|
+
"slash.skill.using-agent-skills": "发现并动态调用智能体各项专业技能"
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
//#endregion
|
|
712
|
+
//#region src/client/i18n/en.ts
|
|
713
|
+
const en = {
|
|
714
|
+
"shared.close": "Close",
|
|
715
|
+
"shared.cancel": "Cancel",
|
|
716
|
+
"shared.loading": "Loading…",
|
|
717
|
+
"shared.blocked": "Blocked",
|
|
718
|
+
"shared.permission": "Permission",
|
|
719
|
+
"shared.confirmDelete": "Delete",
|
|
720
|
+
"shared.current": " (current: {name})",
|
|
721
|
+
"shared.entry.aria": "Agent task board",
|
|
722
|
+
"shared.entry.label": "Task board",
|
|
723
|
+
"shared.stats.title": "To do {todo} | In progress {doing} | In review {review} (todo | in progress | in review)",
|
|
724
|
+
"shared.duplicate.suffix": " (copy)",
|
|
725
|
+
"status.column.backlog": "Backlog",
|
|
726
|
+
"status.column.todo": "To do",
|
|
727
|
+
"status.column.in_progress": "In progress",
|
|
728
|
+
"status.column.in_review": "In review",
|
|
729
|
+
"status.column.done": "Done",
|
|
730
|
+
"status.column.canceled": "Canceled",
|
|
731
|
+
"status.column.archived": "Archived",
|
|
732
|
+
"status.pill.backlog": "Planned",
|
|
733
|
+
"status.pill.todo": "To do",
|
|
734
|
+
"status.pill.in_progress": "In progress",
|
|
735
|
+
"status.pill.in_review": "In review",
|
|
736
|
+
"status.pill.done": "Done",
|
|
737
|
+
"status.pill.canceled": "Canceled",
|
|
738
|
+
"status.pill.archived": "Archived",
|
|
739
|
+
"status.move.backlog": "Backlog",
|
|
740
|
+
"status.move.todo": "To do",
|
|
741
|
+
"status.move.in_progress": "In progress",
|
|
742
|
+
"status.move.in_review": "In review",
|
|
743
|
+
"status.move.done": "Done",
|
|
744
|
+
"status.move.canceled": "Canceled",
|
|
745
|
+
"status.move.archived": "Archived",
|
|
746
|
+
"urgency.urgent": "Urgent",
|
|
747
|
+
"urgency.normal": "Normal",
|
|
748
|
+
"urgency.relaxed": "Relaxed",
|
|
749
|
+
"outcome.running": "Running",
|
|
750
|
+
"outcome.succeeded": "Succeeded",
|
|
751
|
+
"outcome.failed": "Failed",
|
|
752
|
+
"outcome.cancelled": "Canceled",
|
|
753
|
+
"board.title": "Agent Task Board",
|
|
754
|
+
"board.count.tasks": "{n} tasks · rev {rev}",
|
|
755
|
+
"board.action.newTask": "+ New Task ▼",
|
|
756
|
+
"board.action.blankTask": "Blank task",
|
|
757
|
+
"board.action.manageTemplates": "⌗ Manage templates…",
|
|
758
|
+
"board.search.placeholder": "Search title / ID…",
|
|
759
|
+
"board.filter.allProjects": "All projects",
|
|
760
|
+
"board.sort.title": "Sort within columns",
|
|
761
|
+
"board.sort.default": "Default order",
|
|
762
|
+
"board.sort.updated": "Recently updated",
|
|
763
|
+
"board.sort.urgency": "By urgency",
|
|
764
|
+
"board.sort.created": "Creation time",
|
|
765
|
+
"board.sort.byTitle": "By title",
|
|
766
|
+
"board.action.backToBoard": "Back to board",
|
|
767
|
+
"board.action.otherTasks": "Other tasks",
|
|
768
|
+
"board.action.settingsTitle": "Board settings: default execution isolation for new tasks, etc.",
|
|
769
|
+
"board.action.settings": "🛠 Settings",
|
|
770
|
+
"board.action.diagTitle": "Health diagnostics: orphan worktrees, ledger basics",
|
|
771
|
+
"board.action.diag": "⚙ Diagnostics",
|
|
772
|
+
"board.action.importTitle": "Import the ledger from a JSON backup (preview, then merge or replace)",
|
|
773
|
+
"board.action.import": "⬆ Import",
|
|
774
|
+
"board.action.exportTitle": "Export the ledger: full JSON backup or task-list CSV",
|
|
775
|
+
"board.action.export": "⬇ Export ▼",
|
|
776
|
+
"board.export.jsonTitle": "Full ledger backup (execution history and board settings included); import it to restore",
|
|
777
|
+
"board.export.json": "Full ledger (JSON)",
|
|
778
|
+
"board.export.csvTitle": "Task-list spreadsheet (opens directly in Excel; BOM added for CJK text)",
|
|
779
|
+
"board.export.csv": "Task list (CSV)",
|
|
780
|
+
"board.drag.forbidden": "Cannot drag from \"{from}\" to \"{to}\"",
|
|
781
|
+
"board.empty": "No tasks",
|
|
782
|
+
"board.secondary.empty": "No canceled / archived / deleted tasks",
|
|
783
|
+
"board.group.trashed": "Deleted",
|
|
784
|
+
"diag.title": "Health diagnostics",
|
|
785
|
+
"diag.subtitle": "Ledger basics and orphan-worktree cleanup",
|
|
786
|
+
"diag.revision": "Ledger revision",
|
|
787
|
+
"diag.tasks": "Total tasks",
|
|
788
|
+
"diag.running": "Running",
|
|
789
|
+
"diag.orphans": "Orphan worktrees",
|
|
790
|
+
"diag.orphans.heading": "Orphan worktrees (no owning task, directory still present)",
|
|
791
|
+
"diag.orphans.none": "None — every project’s .dsh-worktrees directory is clean",
|
|
792
|
+
"diag.orphans.cleanup": "Clean up",
|
|
793
|
+
"diag.orphans.hint": "Note: orphan directories with uncommitted changes are refused; handle their contents manually first. Remove a live task’s worktree from its task detail pane.",
|
|
794
|
+
"diag.gitignore.heading": "gitignore suggestions",
|
|
795
|
+
"diag.gitignore.none": "Nothing to do — every git project already ignores .dsh-worktrees",
|
|
796
|
+
"diag.gitignore.suggestA": "suggests adding one line to .gitignore:",
|
|
797
|
+
"diag.gitignore.suggestB": "(no automatic edits)",
|
|
798
|
+
"card.drag.running": "This task is being executed by a session ({title}) and cannot be dragged",
|
|
799
|
+
"card.badge.stale": "⏱ Claim stale",
|
|
800
|
+
"card.badge.modelTitle": "Pinned model: {model}",
|
|
801
|
+
"card.badge.modelEffort": " · reasoning effort: {effort}",
|
|
802
|
+
"card.badge.checklistReview": "In review: checklist has unchecked items",
|
|
803
|
+
"card.badge.checklist": "Acceptance checklist progress",
|
|
804
|
+
"card.badge.pendingPurge": "Pending purge",
|
|
805
|
+
"card.session.jumpTitle": "Click to jump to the session: {id}",
|
|
806
|
+
"card.session.missing": "The session has been deleted ({id}) and cannot be opened",
|
|
807
|
+
"card.session.archived": "The session is archived ({id}) and hidden from the session list",
|
|
808
|
+
"card.session.unavailable": "Session navigation unavailable; session id: {id}",
|
|
809
|
+
"card.reject.placeholder": "Reason for sending back (optional; the agent reads it before starting)…",
|
|
810
|
+
"card.reject.confirm": "Send back to todo",
|
|
811
|
+
"card.action.doneTitle": "Accept and complete: move to Done",
|
|
812
|
+
"card.action.done": "✓ Done",
|
|
813
|
+
"card.action.rejectTitle": "Send back to todo, optionally with a reason",
|
|
814
|
+
"card.action.reject": "✗ Send back",
|
|
815
|
+
"alert.ok": "Got it",
|
|
816
|
+
"diff.commit": "Commit {hash}",
|
|
817
|
+
"diff.file": "File {path}",
|
|
818
|
+
"diff.truncated": "⚠ Content truncated (too long)",
|
|
819
|
+
"diff.failed": "Failed to fetch (see the error bar at the top of the board; the object may be gone with a removed worktree)",
|
|
820
|
+
"checklist.title": "Acceptance checklist (DoD)",
|
|
821
|
+
"checklist.unchecked": " · {n} unchecked",
|
|
822
|
+
"checklist.allDone": " · all done",
|
|
823
|
+
"checklist.byUser": "👤 User",
|
|
824
|
+
"checklist.uncheckedItem": "Unchecked",
|
|
825
|
+
"checklist.evidence": "Evidence: {note}",
|
|
826
|
+
"report.title": "Execution report",
|
|
827
|
+
"report.submitted": "Submitted by the execution session · {time}",
|
|
828
|
+
"report.changedFiles": "Changed files",
|
|
829
|
+
"report.checks": "Self-verification",
|
|
830
|
+
"report.artifacts": "Artifacts",
|
|
831
|
+
"report.risk": "Remaining risks",
|
|
832
|
+
"iso.title": "Execution isolation",
|
|
833
|
+
"iso.none": "📁 Run in the original directory",
|
|
834
|
+
"iso.worktreeTitle": "Execution isolation · Worktree",
|
|
835
|
+
"iso.branch": "🌿 Branch",
|
|
836
|
+
"iso.baseline": "Baseline {base} → {head}",
|
|
837
|
+
"iso.changed": "{n} files changed",
|
|
838
|
+
"iso.commit.openTitle": "Click to expand this commit’s diff",
|
|
839
|
+
"iso.commits.more": "… {n} commits in total",
|
|
840
|
+
"iso.nocommit": "This execution produced no commits (changes may be uncommitted — see the warning below)",
|
|
841
|
+
"iso.dirty.toggle": "⚠ {n} uncommitted changes (have the agent commit before merging, or handle them manually)",
|
|
842
|
+
"iso.dirty.expand": " ▼ view files",
|
|
843
|
+
"iso.dirty.collapse": " ▲",
|
|
844
|
+
"iso.dirty.openTitle": "Click to view the uncommitted diff of this file",
|
|
845
|
+
"iso.dirty.more": "… {n} in total (full list in the task ledger)",
|
|
846
|
+
"iso.hint.running": "Running — merge or clean up after it ends",
|
|
847
|
+
"iso.merge.confirm": "Merge the branch into the main worktree with --no-ff?",
|
|
848
|
+
"iso.merge.go": "Merge",
|
|
849
|
+
"iso.merge.title": "git merge --no-ff the task branch into the main worktree (requires a clean main worktree; conflicts are reported as-is)",
|
|
850
|
+
"iso.merge.button": "⇥ Merge into main worktree",
|
|
851
|
+
"iso.merge.failed": "Merge failed: {error}",
|
|
852
|
+
"iso.merge.noop": "The branch has no commits ahead of the main worktree — nothing to merge (send it back to continue running, or clean it up)",
|
|
853
|
+
"iso.remove.wt": "🗑 Remove worktree",
|
|
854
|
+
"iso.remove.wtTitle": "git worktree remove (refused when uncommitted changes exist)",
|
|
855
|
+
"iso.remove.wtb": "🗑 Remove worktree + branch",
|
|
856
|
+
"iso.remove.wtbTitle": "Remove the worktree and delete the task branch (refused when uncommitted changes exist)",
|
|
857
|
+
"iso.remove.confirmWt": "Remove the worktree directory?",
|
|
858
|
+
"iso.remove.confirmWtb": "Remove the worktree and delete the branch?",
|
|
859
|
+
"iso.remove.failed": "Removal failed: {error}",
|
|
860
|
+
"iso.remove.branchFailed": "Worktree removed, but branch deletion failed: {error}",
|
|
861
|
+
"iso.hint.keep": "Branch and worktree kept — send back to continue editing",
|
|
862
|
+
"detail.session.jumpTitle": "Jump to the corresponding session: {id}",
|
|
863
|
+
"detail.session.jump": "🤖 Jump to session ↗",
|
|
864
|
+
"detail.action.edit": "✎ Edit",
|
|
865
|
+
"detail.action.duplicateTitle": "Duplicate this task’s full configuration as a new card (todo column)",
|
|
866
|
+
"detail.action.duplicate": "⧉ Duplicate",
|
|
867
|
+
"detail.action.saveTplTitle": "Save this task’s configuration (checklist included) as a template for new tasks",
|
|
868
|
+
"detail.action.saveTplDone": "Saved as a template (available in the New Task ▼ menu; rename it in template management)",
|
|
869
|
+
"detail.action.saveTpl": "⌗ Save as template",
|
|
870
|
+
"detail.action.reuseTitle": "Reuse-run: keep the existing worktree and branch (last run’s changes and commits stay in place) and continue on top of them; the default \"Run\" resets to a fresh baseline",
|
|
871
|
+
"detail.action.reuse": "↻ Reuse-run",
|
|
872
|
+
"detail.action.runTitleModel": "Run in a new session ({model})",
|
|
873
|
+
"detail.action.runTitleDefault": "Run in a new session (default model)",
|
|
874
|
+
"detail.action.run": "▶ Run",
|
|
875
|
+
"detail.action.stopConfirm": "Stop this execution session?",
|
|
876
|
+
"detail.action.stop": "Stop",
|
|
877
|
+
"detail.action.stopTitle": "Stop execution session {id} (the task returns to todo)",
|
|
878
|
+
"detail.action.stopExec": "■ Stop execution",
|
|
879
|
+
"detail.chip.nextRun": "{cron} · next {time}",
|
|
880
|
+
"detail.chip.checklist": "Checklist {done}/{total}",
|
|
881
|
+
"detail.chip.isolated": "Worktree isolation",
|
|
882
|
+
"detail.chip.holderTitle": "Click to jump to the session: {id}",
|
|
883
|
+
"detail.chip.holderStale": "Claim stale · ",
|
|
884
|
+
"detail.chip.holderBy": "Held by ",
|
|
885
|
+
"detail.chip.holderSuffix": " ↗",
|
|
886
|
+
"detail.chip.trashed": "Deleted, pending purge",
|
|
887
|
+
"detail.sub.line": "Updated {time} · last change by {who}",
|
|
888
|
+
"detail.updatedBy.system": "⚙️ System",
|
|
889
|
+
"detail.updatedBy.user": "👤 User",
|
|
890
|
+
"detail.field.description": "Description",
|
|
891
|
+
"detail.field.prompt": "Execution prompt",
|
|
892
|
+
"detail.move.to": "Move to → {status}",
|
|
893
|
+
"detail.move.confirmDoneUnchecked": "{n} checklist items are still unchecked — confirm done?",
|
|
894
|
+
"detail.move.confirmDone": "Confirm done?",
|
|
895
|
+
"detail.move.confirm": "Confirm",
|
|
896
|
+
"detail.blocked.unmark": "✓ Unblock",
|
|
897
|
+
"detail.blocked.mark": "⛔ Mark blocked",
|
|
898
|
+
"detail.release.title": "Release {id}’s claim: the task returns to todo (the holding session may still be working — make sure it has stopped first)",
|
|
899
|
+
"detail.release.button": "🔓 Release claim",
|
|
900
|
+
"detail.comments.title": "Comments",
|
|
901
|
+
"detail.comments.empty": "No comments yet — the agent reports changes and verification here at handoff",
|
|
902
|
+
"detail.comments.user": "User",
|
|
903
|
+
"detail.composer.placeholder": "Leave a comment as the user (the agent reads it before starting)…",
|
|
904
|
+
"detail.composer.send": "Post",
|
|
905
|
+
"detail.exec.title": "Executions",
|
|
906
|
+
"detail.exec.prunedTitle": "{n} older execution records were pruned at the retention cap",
|
|
907
|
+
"detail.exec.pruned": "+{n} pruned",
|
|
908
|
+
"detail.exec.trigger.manual": "Manual",
|
|
909
|
+
"detail.exec.trigger.scheduled": "Scheduled",
|
|
910
|
+
"detail.exec.openTitle": "Click to open the execution session: {id}",
|
|
911
|
+
"detail.danger.delete": "🗑 Delete (mark for purge)",
|
|
912
|
+
"detail.danger.purgeConfirm": "Physical purge is irreversible",
|
|
913
|
+
"detail.danger.purgeGo": "Purge",
|
|
914
|
+
"detail.danger.purge": "🔥 Purge physically (confirm required)",
|
|
915
|
+
"form.title.create": "New task",
|
|
916
|
+
"form.title.edit": "Edit task",
|
|
917
|
+
"form.subtitle.create": "Push onto the board; sessions in the project can claim and run it",
|
|
918
|
+
"form.subtitle.edit": "Adjust the task content and execution configuration",
|
|
919
|
+
"form.field.title": "Title",
|
|
920
|
+
"form.field.titlePlaceholder": "One line: what should be done",
|
|
921
|
+
"form.field.project": "Project",
|
|
922
|
+
"form.field.model": "Model (default = session default model)",
|
|
923
|
+
"form.field.modelDefault": "Default model",
|
|
924
|
+
"form.model.option": "{name} ({provider})",
|
|
925
|
+
"form.field.effort": "Reasoning effort",
|
|
926
|
+
"form.field.effortTitle": "Set the model’s reasoning effort (e.g. low/medium/high); default = follow the model/provider default",
|
|
927
|
+
"form.effort.follow": "Follow model default",
|
|
928
|
+
"form.effort.low": "Low (low)",
|
|
929
|
+
"form.effort.medium": "Medium (medium)",
|
|
930
|
+
"form.effort.high": "High (high)",
|
|
931
|
+
"form.effort.none": "Off (none)",
|
|
932
|
+
"form.field.preset": "Execution preset",
|
|
933
|
+
"form.field.presetTitle": "The execution session is composed from this preset (tool set and persona); default = the deployment default preset",
|
|
934
|
+
"form.preset.follow": "Follow deployment default",
|
|
935
|
+
"form.preset.defaultTag": " (deployment default)",
|
|
936
|
+
"form.field.urgency": "Urgency",
|
|
937
|
+
"form.urgency.urgent": "Urgent",
|
|
938
|
+
"form.urgency.urgentHint": "Handle first",
|
|
939
|
+
"form.urgency.normal": "Normal",
|
|
940
|
+
"form.urgency.normalHint": "Normal scheduling",
|
|
941
|
+
"form.urgency.relaxed": "Relaxed",
|
|
942
|
+
"form.urgency.relaxedHint": "When there is time",
|
|
943
|
+
"form.field.description": "Description",
|
|
944
|
+
"form.field.descriptionOptional": "Description (optional)",
|
|
945
|
+
"form.desc.placeholder": "Requirement details, background, acceptance criteria…",
|
|
946
|
+
"form.field.prompt": "Execution prompt (the actual prompt = title + description + prompt)",
|
|
947
|
+
"form.field.promptOptional": "Execution prompt (optional; the actual prompt = title + description + prompt)",
|
|
948
|
+
"form.prompt.placeholder": "Extra instructions appended after \"title + task description\" and sent to the execution session. Template variables: {{lastExecution}} (last execution result), {{lastComments}} (latest 3 comments)",
|
|
949
|
+
"form.field.mode": "Execution mode",
|
|
950
|
+
"form.mode.claim": "🤝 Claim-based",
|
|
951
|
+
"form.mode.claimHint": "Sessions in the project claim it",
|
|
952
|
+
"form.mode.scheduled": "⏰ Scheduled",
|
|
953
|
+
"form.mode.scheduledHint": "Starts automatically on schedule",
|
|
954
|
+
"form.field.cron": "Cron expression",
|
|
955
|
+
"form.cron.placeholder": "min hour day month weekday",
|
|
956
|
+
"form.cron.daily": "Daily 09:00",
|
|
957
|
+
"form.cron.hourly": "Hourly",
|
|
958
|
+
"form.cron.every10min": "Every 10 minutes",
|
|
959
|
+
"form.cron.weekly": "Mondays 09:00",
|
|
960
|
+
"form.cron.next": "Next {time}",
|
|
961
|
+
"form.field.isolation": "Execution isolation",
|
|
962
|
+
"form.iso.locked": "The task has execution history; isolation is locked",
|
|
963
|
+
"form.iso.lockedShort": "Locked (cannot change once execution has started)",
|
|
964
|
+
"form.iso.nonGit": "This project is not a git repository",
|
|
965
|
+
"form.iso.worktree": "🌿 Worktree isolation",
|
|
966
|
+
"form.iso.worktreeTitle": "Each execution runs on its own worktree branch",
|
|
967
|
+
"form.iso.worktreeHint": "Isolated branch task/title+ID, no cross-contamination",
|
|
968
|
+
"form.iso.none": "📁 Run in the project directory",
|
|
969
|
+
"form.iso.noneTitle": "Run directly in the project directory (no git)",
|
|
970
|
+
"form.iso.noneHintNonGit": "Not a git repository; will run in the project directory",
|
|
971
|
+
"form.iso.noneHint": "No git; works directly in the project directory",
|
|
972
|
+
"form.iso.nonGitNote": "This project is not a git repository; the task will run in its directory (still created with the default configuration; the runtime degrades automatically)",
|
|
973
|
+
"form.field.checklist": "Acceptance checklist (DoD)",
|
|
974
|
+
"form.field.checklistOptional": "Acceptance checklist (DoD, optional)",
|
|
975
|
+
"form.check.itemPlaceholder": "Checklist item {n} (definition of done)",
|
|
976
|
+
"form.check.removeTitle": "Remove this checklist item",
|
|
977
|
+
"form.check.add": "+ Add checklist item",
|
|
978
|
+
"form.check.checkedTitle": "Checked state is preserved on save (currently checked by: {who})",
|
|
979
|
+
"form.check.notCheckedYet": "not checked yet",
|
|
980
|
+
"form.check.hintCreate": "{n} items; the execution session works through them and checks each off; unfinished items are highlighted at review",
|
|
981
|
+
"form.check.hintEdit": "{checked}/{total} checked (saving replaces the whole list; checked state is preserved)",
|
|
982
|
+
"form.hint.needTitle": "Enter a title",
|
|
983
|
+
"form.hint.needProject": "Select a project",
|
|
984
|
+
"form.hint.cronBad": "Invalid cron expression (min hour day month weekday)",
|
|
985
|
+
"form.hint.nextRun": "Next run {time}",
|
|
986
|
+
"form.hint.saveVersion": "On save: v{v} → v{next}",
|
|
987
|
+
"form.hint.createClaim": "After creation, sessions in the project can claim and run it",
|
|
988
|
+
"form.action.runBlockedTitle": "The task is running; cannot start another",
|
|
989
|
+
"form.action.runBusyTitle": "Submitting…",
|
|
990
|
+
"form.action.runTitle": "Save, then immediately start an execution (new session)",
|
|
991
|
+
"form.action.run": "⚡ Run now",
|
|
992
|
+
"form.action.save": "Save changes",
|
|
993
|
+
"form.action.create": "Create task",
|
|
994
|
+
"tpl.aria": "Manage templates",
|
|
995
|
+
"tpl.title": "Task templates",
|
|
996
|
+
"tpl.subtitle": "Templates from the New Task ▼ menu: rename / delete / use directly; \"Save as template\" in the task detail pane adds new ones",
|
|
997
|
+
"tpl.empty": "No templates yet — click \"Save as template\" in a task detail pane to capture a common configuration",
|
|
998
|
+
"tpl.name.aria": "Template name {name}",
|
|
999
|
+
"tpl.builtin": "Built-in",
|
|
1000
|
+
"tpl.custom": "Custom",
|
|
1001
|
+
"tpl.meta.checklist": " · {n} checklist items",
|
|
1002
|
+
"tpl.rename.title": "Save rename",
|
|
1003
|
+
"tpl.rename.button": "Rename",
|
|
1004
|
+
"tpl.use.title": "Open the new-task form with this template",
|
|
1005
|
+
"tpl.use.button": "Use",
|
|
1006
|
+
"tpl.delete.title": "Delete this template",
|
|
1007
|
+
"tpl.renamed": "Template renamed",
|
|
1008
|
+
"tpl.foot.hint": "Templates are stored with the ledger in the DSH home directory and survive upgrades",
|
|
1009
|
+
"imp.aria": "Import ledger",
|
|
1010
|
+
"imp.title": "Import ledger",
|
|
1011
|
+
"imp.subtitle": "Pick an exported JSON backup: preview first, then merge or replace everything",
|
|
1012
|
+
"imp.parseError": "The file is not valid JSON",
|
|
1013
|
+
"imp.note": "The ⬇ JSON export is a same-format backup and can be imported to restore; the file’s schemaVersion must match the current version.",
|
|
1014
|
+
"imp.previewing": "Previewing…",
|
|
1015
|
+
"imp.stat.create": "New",
|
|
1016
|
+
"imp.stat.overwrite": "Overwrite (same id)",
|
|
1017
|
+
"imp.stat.invalid": "Invalid (skipped)",
|
|
1018
|
+
"imp.sec.create": "New tasks",
|
|
1019
|
+
"imp.sec.overwrite": "Overwritten tasks (whole-card replacement, execution history and comments included)",
|
|
1020
|
+
"imp.sec.invalid": "Invalid entries (not imported)",
|
|
1021
|
+
"imp.noId": "(no id)",
|
|
1022
|
+
"imp.mode.merge": "⊕ Merge",
|
|
1023
|
+
"imp.mode.mergeHint": "Adds new + overwrites by id; everything else untouched",
|
|
1024
|
+
"imp.mode.replace": "💣 Replace all",
|
|
1025
|
+
"imp.mode.replaceHint": "Clears the current ledger and adopts the imported file (automatic backup first)",
|
|
1026
|
+
"imp.result.replace": "Replace complete: imported {n} tasks ({total} former tasks backed up)",
|
|
1027
|
+
"imp.result.merge": "Merge complete: {n} added, {m} overwritten",
|
|
1028
|
+
"imp.foot.replaceConfirm": "⚠ Click again to confirm the full replacement (irreversible; a backup has been taken)",
|
|
1029
|
+
"imp.foot.replaceNeedConfirm": "Full replacement requires a second confirmation",
|
|
1030
|
+
"imp.foot.mergeHint": "Merge writes only the tasks listed in the preview",
|
|
1031
|
+
"imp.action.run": "Import",
|
|
1032
|
+
"imp.action.confirmReplace": "Confirm replace",
|
|
1033
|
+
"set.aria": "Board settings",
|
|
1034
|
+
"set.title": "Board settings",
|
|
1035
|
+
"set.subtitle": "Global defaults for new tasks and session sync",
|
|
1036
|
+
"set.iso.heading": "Default execution isolation",
|
|
1037
|
+
"set.iso.noneHint": "No git; works directly in the project directory (factory default)",
|
|
1038
|
+
"set.iso.worktreeHint": "Each execution runs on its own worktree branch (task/title+ID), isolated from the others",
|
|
1039
|
+
"set.iso.current": "Currently saved default: {current}. Affects only tasks created hereafter; existing tasks keep their creation-time choice, and non-git projects still degrade to the project directory at run time.",
|
|
1040
|
+
"set.sync.heading": "Auto-sync workspace sessions",
|
|
1041
|
+
"set.sync.off.name": "🚫 Sync off",
|
|
1042
|
+
"set.sync.off.title": "Manage only tasks created and triggered in the task board",
|
|
1043
|
+
"set.sync.off.hint": "Board tasks only (factory default)",
|
|
1044
|
+
"set.sync.on.name": "🔄 Auto-include sessions",
|
|
1045
|
+
"set.sync.on.title": "Sessions created directly in workspaces also appear on the board: In progress while running, In review automatically when done",
|
|
1046
|
+
"set.sync.on.hint": "Tracks runs and enters review on completion",
|
|
1047
|
+
"set.sync.stateOn": "On: sessions created and run directly in workspaces automatically become board cards and flow to the \"In review\" column when done.",
|
|
1048
|
+
"set.sync.stateOff": "Off: only tasks created and triggered inside the board appear on the board.",
|
|
1049
|
+
"set.foot.dirty": "Unsaved changes",
|
|
1050
|
+
"set.foot.clean": "Matches the current board settings",
|
|
1051
|
+
"set.action.save": "Save settings",
|
|
1052
|
+
"form.field.permission": "Execution permission",
|
|
1053
|
+
"form.perm.write": "Workspace write",
|
|
1054
|
+
"form.perm.writeHint": "Read-write access to the workspace and temp directories (recommended default)",
|
|
1055
|
+
"form.perm.readOnly": "Read-only",
|
|
1056
|
+
"form.perm.readOnlyHint": "View and search only; no file changes or external commands",
|
|
1057
|
+
"form.perm.fullAccess": "Full access",
|
|
1058
|
+
"form.perm.fullAccessHint": "Unrestricted: whole-disk access and external commands",
|
|
1059
|
+
"form.perm.defaultTag": " (default)",
|
|
1060
|
+
"set.perm.heading": "Default execution permission",
|
|
1061
|
+
"set.perm.writeName": "📁 Workspace write (factory default)",
|
|
1062
|
+
"set.perm.writeHint": "Read-write access to the workspace and temp directories; writes need no extra confirmation",
|
|
1063
|
+
"set.perm.readOnlyName": "🔒 Read-only",
|
|
1064
|
+
"set.perm.readOnlyHint": "Read-only viewing and search only; no file changes or destructive commands",
|
|
1065
|
+
"set.perm.fullName": "⚡ Full access",
|
|
1066
|
+
"set.perm.fullHint": "Unrestricted: whole-disk access and external system commands",
|
|
1067
|
+
"set.perm.current": "Currently saved default: {current}. The execution permission preset applied to new tasks.",
|
|
1068
|
+
"card.badge.permReadOnly": "🔒 Read-only",
|
|
1069
|
+
"card.badge.permReadOnlyTitle": "Permission: read-only",
|
|
1070
|
+
"card.badge.permFull": "⚡ Full access",
|
|
1071
|
+
"card.badge.permFullTitle": "Permission: full access",
|
|
1072
|
+
"detail.chip.permReadOnly": "Read-only",
|
|
1073
|
+
"detail.chip.permFull": "Full access",
|
|
1074
|
+
"detail.chip.permWrite": "Workspace write",
|
|
1075
|
+
"tpl.meta.permReadOnly": "read-only",
|
|
1076
|
+
"tpl.meta.permFull": "full access",
|
|
1077
|
+
"md.imageAlt": "Image {n}",
|
|
1078
|
+
"md.imageTitle": "Click to view full size ({alt})",
|
|
1079
|
+
"md.lightboxAlt": "Full-size preview",
|
|
1080
|
+
"md.closePreview": "Close preview",
|
|
1081
|
+
"slash.aria": "Quick commands and skills",
|
|
1082
|
+
"slash.title": "Quick command and skill completion",
|
|
1083
|
+
"slash.hint": "↑↓ navigate · Enter / Tab pick · Esc close",
|
|
1084
|
+
"slash.badge.command": "⚡ command",
|
|
1085
|
+
"slash.badge.skill": "🧩 skill",
|
|
1086
|
+
"slash.tipA": "💡 Type",
|
|
1087
|
+
"slash.tipB": "to autocomplete slash commands and agent skills",
|
|
1088
|
+
"slash.cmd.goal.desc": "Autonomously drive long-horizon goals to completion",
|
|
1089
|
+
"slash.cmd.goal.hint": "<goal description>",
|
|
1090
|
+
"slash.cmd.schedule.desc": "Set one-off or recurring cron schedules",
|
|
1091
|
+
"slash.cmd.schedule.hint": "<time or expression>",
|
|
1092
|
+
"slash.cmd.plan.desc": "Draft a step-by-step plan and get user confirmation before acting",
|
|
1093
|
+
"slash.cmd.browser.desc": "Launch a browser for page interaction and live search",
|
|
1094
|
+
"slash.cmd.grill-me.desc": "Align requirements and design intent through one-question-at-a-time interviews",
|
|
1095
|
+
"slash.cmd.teamwork-preview.desc": "Multi-agent collaboration and team workflow preview",
|
|
1096
|
+
"slash.cmd.learn.desc": "Capture solutions and new rules into the knowledge base",
|
|
1097
|
+
"slash.cmd.review.desc": "Multi-axis code review (correctness, architecture, security)",
|
|
1098
|
+
"slash.cmd.security.desc": "Security hardening and vulnerability scanning",
|
|
1099
|
+
"slash.cmd.permission.desc": "Switch the session permission level (read-only / workspace-write / full-access)",
|
|
1100
|
+
"slash.cmd.permission.hint": "<preset>",
|
|
1101
|
+
"slash.skill.frontend-ui-engineering": "Build production-grade, accessible frontend interfaces and components",
|
|
1102
|
+
"slash.skill.api-and-interface-design": "Design stable, well-bounded REST / RPC interfaces",
|
|
1103
|
+
"slash.skill.test-driven-development": "Test-driven development (TDD): unit and integration tests",
|
|
1104
|
+
"slash.skill.debugging-and-error-recovery": "Systematically locate bug root causes and recover",
|
|
1105
|
+
"slash.skill.performance-optimization": "Frontend/backend performance tuning and query optimization",
|
|
1106
|
+
"slash.skill.ci-cd-and-automation": "Build automation, CI/CD pipelines and quality gates",
|
|
1107
|
+
"slash.skill.code-review-and-quality": "Multi-axis code review and refactoring guidance",
|
|
1108
|
+
"slash.skill.code-simplification": "Simplify complex logic for readability and maintainability",
|
|
1109
|
+
"slash.skill.context-engineering": "Optimize context structure and prompt engineering",
|
|
1110
|
+
"slash.skill.doubt-driven-development": "Adversarial, doubt-driven review of core logic",
|
|
1111
|
+
"slash.skill.git-workflow-and-versioning": "Git workflow, branching, semantic versioning and changelogs",
|
|
1112
|
+
"slash.skill.idea-refine": "Refine ideas and test hypotheses via divergent-convergent thinking",
|
|
1113
|
+
"slash.skill.incremental-implementation": "Deliver multi-file changes in small, incremental steps",
|
|
1114
|
+
"slash.skill.interview-me": "Deep interviews to surface true intent",
|
|
1115
|
+
"slash.skill.memory-leak-debugging": "Diagnose JavaScript/Node.js memory leaks",
|
|
1116
|
+
"slash.skill.observability-and-instrumentation": "Add logging, metrics and distributed tracing",
|
|
1117
|
+
"slash.skill.planning-and-task-breakdown": "Break complex requirements into ordered, executable tasks",
|
|
1118
|
+
"slash.skill.security-and-hardening": "Harden against vulnerabilities; input filtering and auth hardening",
|
|
1119
|
+
"slash.skill.shipping-and-launch": "Pre-launch checklists and rollback strategies",
|
|
1120
|
+
"slash.skill.source-driven-development": "Design from authoritative docs and source code",
|
|
1121
|
+
"slash.skill.spec-driven-development": "Write clear technical specs before coding",
|
|
1122
|
+
"slash.skill.using-agent-skills": "Discover and invoke agent skills dynamically"
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
//#endregion
|
|
1126
|
+
//#region src/client/i18n/runtime.ts
|
|
1127
|
+
/**
|
|
1128
|
+
* Taskboard i18n runtime: the zh/en dictionary lookup plus the locale
|
|
1129
|
+
* SOURCE adapter.
|
|
1130
|
+
*
|
|
1131
|
+
* Locale source, in priority order:
|
|
1132
|
+
* 1. The DSH locale service (ctx.get('locale'), the same plugin that backs
|
|
1133
|
+
* 设置 → 通用设置 → 语言). Consumed through a NARROW structural face and
|
|
1134
|
+
* attached SOFTLY — the plugin must keep working on compositions where
|
|
1135
|
+
* the service is absent, so 'locale' is deliberately NOT in the client
|
|
1136
|
+
* inject list (a hard inject would wait forever there).
|
|
1137
|
+
* 2. A local fallback: <html lang> when the DSH locale plugin maintains it
|
|
1138
|
+
* (it points <html lang> at the active locale), else navigator.language,
|
|
1139
|
+
* else 'en' (matches DSH: zh only when something asked for Chinese).
|
|
1140
|
+
*
|
|
1141
|
+
* Preference WRITES are never made here: switching languages is the DSH
|
|
1142
|
+
* settings page's job; this module only reads.
|
|
1143
|
+
*
|
|
1144
|
+
* React re-render: useT() subscribes via useSyncExternalStore; the snapshot
|
|
1145
|
+
* object is replaced only on locale change, so renders are stable.
|
|
1146
|
+
*
|
|
1147
|
+
* @module dsh-taskboard/client/i18n/runtime
|
|
1148
|
+
*/
|
|
1149
|
+
const DICTS = {
|
|
1150
|
+
zh,
|
|
1151
|
+
en
|
|
1152
|
+
};
|
|
1153
|
+
let unsubscribeService;
|
|
1154
|
+
let snapshot = {
|
|
1155
|
+
active: detectFallbackLocale(),
|
|
1156
|
+
revision: 0
|
|
1157
|
+
};
|
|
1158
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1159
|
+
function isLocaleId(value) {
|
|
1160
|
+
return value === "zh" || value === "en";
|
|
1161
|
+
}
|
|
1162
|
+
/** Derive the fallback locale without the DSH service (zh only when asked). */
|
|
1163
|
+
function detectFallbackLocale() {
|
|
1164
|
+
try {
|
|
1165
|
+
if (typeof document !== "undefined") {
|
|
1166
|
+
const lang = document.documentElement.lang;
|
|
1167
|
+
if (typeof lang === "string" && lang.length > 0) {
|
|
1168
|
+
const lower = lang.toLowerCase();
|
|
1169
|
+
if (lower.startsWith("zh")) return "zh";
|
|
1170
|
+
if (lower.startsWith("en")) return "en";
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
} catch {}
|
|
1174
|
+
try {
|
|
1175
|
+
if (typeof navigator !== "undefined") return (navigator.language ?? "en").toLowerCase().startsWith("zh") ? "zh" : "en";
|
|
1176
|
+
} catch {}
|
|
1177
|
+
return "en";
|
|
1178
|
+
}
|
|
1179
|
+
function publish(next) {
|
|
1180
|
+
if (next.active === snapshot.active && next.revision === snapshot.revision) return;
|
|
1181
|
+
snapshot = next;
|
|
1182
|
+
for (const fn of listeners) fn();
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Attach the DSH locale service (call from the client entry's apply with
|
|
1186
|
+
* ctx.get('locale')). Absent/malformed services are ignored — the fallback
|
|
1187
|
+
* detection stays in charge.
|
|
1188
|
+
* @param localeService - the ctx 'locale' service, when provided.
|
|
1189
|
+
*/
|
|
1190
|
+
function initI18n(localeService) {
|
|
1191
|
+
const face = localeService;
|
|
1192
|
+
if (face === null || face === void 0 || typeof face.getSnapshot !== "function" || typeof face.subscribe !== "function") {
|
|
1193
|
+
publish({
|
|
1194
|
+
active: detectFallbackLocale(),
|
|
1195
|
+
revision: 0
|
|
1196
|
+
});
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
const sync = () => {
|
|
1200
|
+
try {
|
|
1201
|
+
const s = face.getSnapshot();
|
|
1202
|
+
publish({
|
|
1203
|
+
active: isLocaleId(s.active) ? s.active : detectFallbackLocale(),
|
|
1204
|
+
revision: s.revision
|
|
1205
|
+
});
|
|
1206
|
+
} catch {}
|
|
1207
|
+
};
|
|
1208
|
+
sync();
|
|
1209
|
+
unsubscribeService = face.subscribe(sync);
|
|
1210
|
+
}
|
|
1211
|
+
/** Detach the service and return to fallback detection (tests, dispose). */
|
|
1212
|
+
function disposeI18n() {
|
|
1213
|
+
try {
|
|
1214
|
+
unsubscribeService?.();
|
|
1215
|
+
} catch {}
|
|
1216
|
+
unsubscribeService = void 0;
|
|
1217
|
+
publish({
|
|
1218
|
+
active: detectFallbackLocale(),
|
|
1219
|
+
revision: 0
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
/** Subscribe-side store face for useSyncExternalStore. */
|
|
1223
|
+
const localeStore = {
|
|
1224
|
+
getSnapshot() {
|
|
1225
|
+
return snapshot;
|
|
1226
|
+
},
|
|
1227
|
+
subscribe(fn) {
|
|
1228
|
+
listeners.add(fn);
|
|
1229
|
+
return () => {
|
|
1230
|
+
listeners.delete(fn);
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
};
|
|
1234
|
+
const PLACEHOLDER = /\{(\w+)\}/g;
|
|
1235
|
+
/** Translate through the active dictionary, {name} placeholders substituted. */
|
|
1236
|
+
const translate = (key, params) => {
|
|
1237
|
+
const template = DICTS[snapshot.active][key] ?? DICTS.en[key] ?? key;
|
|
1238
|
+
if (params === void 0) return template;
|
|
1239
|
+
return template.replace(PLACEHOLDER, (match, name) => {
|
|
1240
|
+
const value = params[name];
|
|
1241
|
+
return value === void 0 ? match : String(value);
|
|
1242
|
+
});
|
|
1243
|
+
};
|
|
1244
|
+
/**
|
|
1245
|
+
* React hook: subscribes the component to locale changes and returns the
|
|
1246
|
+
* translate function (stable identity — it reads the active locale at call
|
|
1247
|
+
* time, so any re-render triggered by the subscription renders fresh text).
|
|
1248
|
+
*/
|
|
1249
|
+
function useT() {
|
|
1250
|
+
(0, react.useSyncExternalStore)(localeStore.subscribe, localeStore.getSnapshot);
|
|
1251
|
+
return translate;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
265
1254
|
//#endregion
|
|
266
1255
|
//#region src/client/controller.ts
|
|
267
1256
|
/** localStorage key for persisted view state (filters + sort). */
|
|
@@ -510,6 +1499,38 @@ window.__ModuleLoader__.load({
|
|
|
510
1499
|
return this.catalogFaces.presets;
|
|
511
1500
|
}
|
|
512
1501
|
/**
|
|
1502
|
+
* Fetch model catalog: prefers installed runtime face, falls back to Taskboard client API (0.5.5).
|
|
1503
|
+
*/
|
|
1504
|
+
async fetchModelCatalog() {
|
|
1505
|
+
if (this.catalogFaces.models !== void 0) try {
|
|
1506
|
+
const list = await this.catalogFaces.models();
|
|
1507
|
+
if (list !== void 0 && list.length > 0) return list;
|
|
1508
|
+
} catch {}
|
|
1509
|
+
try {
|
|
1510
|
+
return (await this.client.modelCatalog()).models ?? [];
|
|
1511
|
+
} catch {
|
|
1512
|
+
return [];
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Fetch preset roster: prefers installed runtime face, falls back to Taskboard client API (0.5.5).
|
|
1517
|
+
*/
|
|
1518
|
+
async fetchPresetCatalog() {
|
|
1519
|
+
if (this.catalogFaces.presets !== void 0) try {
|
|
1520
|
+
const roster = await this.catalogFaces.presets();
|
|
1521
|
+
if (roster !== void 0 && roster.presets !== void 0 && roster.presets.length > 0) return roster;
|
|
1522
|
+
} catch {}
|
|
1523
|
+
try {
|
|
1524
|
+
const res = await this.client.modelCatalog();
|
|
1525
|
+
return {
|
|
1526
|
+
presets: res.presets ?? [],
|
|
1527
|
+
...res.defaultPresetId !== void 0 ? { defaultId: res.defaultPresetId } : {}
|
|
1528
|
+
};
|
|
1529
|
+
} catch {
|
|
1530
|
+
return { presets: [] };
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
513
1534
|
* Jump to an execution's session (open it in the GUI). On success the board
|
|
514
1535
|
* closes so the conversation shows; a deleted-or-archived session reports
|
|
515
1536
|
* 'missing' for the caller to prompt about.
|
|
@@ -771,7 +1792,7 @@ window.__ModuleLoader__.load({
|
|
|
771
1792
|
async duplicate(task) {
|
|
772
1793
|
try {
|
|
773
1794
|
await this.client.create({
|
|
774
|
-
title:
|
|
1795
|
+
title: task.title.slice(0, 196) + translate("shared.duplicate.suffix"),
|
|
775
1796
|
workspaceId: task.workspaceId,
|
|
776
1797
|
urgency: task.urgency,
|
|
777
1798
|
description: task.description.length > 0 ? task.description : void 0,
|
|
@@ -783,6 +1804,7 @@ window.__ModuleLoader__.load({
|
|
|
783
1804
|
model: task.model,
|
|
784
1805
|
isolation: task.isolation,
|
|
785
1806
|
...task.presetId !== void 0 ? { presetId: task.presetId } : {},
|
|
1807
|
+
...task.permission !== void 0 ? { permission: task.permission } : {},
|
|
786
1808
|
...task.checklist !== void 0 && task.checklist.length > 0 ? { checklist: task.checklist.map((i) => i.text) } : {}
|
|
787
1809
|
});
|
|
788
1810
|
await this.refresh();
|
|
@@ -853,10 +1875,19 @@ window.__ModuleLoader__.load({
|
|
|
853
1875
|
model: task.model,
|
|
854
1876
|
isolation: task.isolation,
|
|
855
1877
|
...task.presetId !== void 0 ? { presetId: task.presetId } : {},
|
|
1878
|
+
...task.permission !== void 0 ? { permission: task.permission } : {},
|
|
856
1879
|
...task.checklist !== void 0 && task.checklist.length > 0 ? { checklist: task.checklist.map((i) => i.text) } : {}
|
|
857
1880
|
}
|
|
858
1881
|
});
|
|
859
1882
|
}
|
|
1883
|
+
/** Load prompt completions (skills + slash commands) from host (0.5.5). */
|
|
1884
|
+
async fetchPromptCompletions() {
|
|
1885
|
+
try {
|
|
1886
|
+
return await this.client.promptCompletions();
|
|
1887
|
+
} catch {
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
860
1891
|
/** Open the import modal. */
|
|
861
1892
|
openImport() {
|
|
862
1893
|
this.setState({ importOpen: true });
|
|
@@ -1399,6 +2430,10 @@ window.__ModuleLoader__.load({
|
|
|
1399
2430
|
box-shadow: var(--dsw-shadow-lv3, 0 12px 32px rgba(0,0,0,.18));
|
|
1400
2431
|
animation: dsh-atb-pop .16s ease;
|
|
1401
2432
|
}
|
|
2433
|
+
.dsh-atb-taskform-modal {
|
|
2434
|
+
width: min(960px, calc(100vw - 40px));
|
|
2435
|
+
max-height: calc(100vh - 50px);
|
|
2436
|
+
}
|
|
1402
2437
|
@keyframes dsh-atb-pop { from { opacity: 0; transform: translateY(8px) scale(.98); } }
|
|
1403
2438
|
.dsh-atb-modal-head {
|
|
1404
2439
|
display: flex; align-items: center; gap: 10px;
|
|
@@ -1422,6 +2457,59 @@ window.__ModuleLoader__.load({
|
|
|
1422
2457
|
padding: 13px 16px; overflow-y: auto;
|
|
1423
2458
|
display: grid; grid-template-columns: 1fr 1fr; gap: 11px 10px;
|
|
1424
2459
|
}
|
|
2460
|
+
.dsh-atb-taskform-body {
|
|
2461
|
+
padding: 14px 18px;
|
|
2462
|
+
display: grid;
|
|
2463
|
+
grid-template-columns: 1.05fr 1.15fr;
|
|
2464
|
+
gap: 18px;
|
|
2465
|
+
}
|
|
2466
|
+
.dsh-atb-form-col {
|
|
2467
|
+
display: flex;
|
|
2468
|
+
flex-direction: column;
|
|
2469
|
+
gap: 11px;
|
|
2470
|
+
min-width: 0;
|
|
2471
|
+
}
|
|
2472
|
+
.dsh-atb-form-left {
|
|
2473
|
+
border-right: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.14));
|
|
2474
|
+
padding-right: 18px;
|
|
2475
|
+
}
|
|
2476
|
+
.dsh-atb-form-right {
|
|
2477
|
+
display: flex;
|
|
2478
|
+
flex-direction: column;
|
|
2479
|
+
gap: 14px;
|
|
2480
|
+
}
|
|
2481
|
+
.dsh-atb-form-right .dsh-atb-field {
|
|
2482
|
+
flex: 1;
|
|
2483
|
+
display: flex;
|
|
2484
|
+
flex-direction: column;
|
|
2485
|
+
}
|
|
2486
|
+
.dsh-atb-form-right .dsh-atb-prompt-wrap {
|
|
2487
|
+
flex: 1;
|
|
2488
|
+
display: flex;
|
|
2489
|
+
flex-direction: column;
|
|
2490
|
+
}
|
|
2491
|
+
.dsh-atb-form-right .dsh-atb-prompt-inner {
|
|
2492
|
+
flex: 1;
|
|
2493
|
+
display: flex;
|
|
2494
|
+
flex-direction: column;
|
|
2495
|
+
}
|
|
2496
|
+
.dsh-atb-form-right .dsh-atb-prompt-input {
|
|
2497
|
+
flex: 1;
|
|
2498
|
+
min-height: 130px;
|
|
2499
|
+
resize: vertical;
|
|
2500
|
+
}
|
|
2501
|
+
.dsh-atb-form-subgrid {
|
|
2502
|
+
display: grid;
|
|
2503
|
+
grid-template-columns: 1fr 1fr;
|
|
2504
|
+
gap: 10px;
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
@media (max-width: 768px) {
|
|
2508
|
+
.dsh-atb-taskform-modal { width: calc(100vw - 20px); }
|
|
2509
|
+
.dsh-atb-taskform-body { grid-template-columns: 1fr; gap: 14px; padding: 12px 14px; }
|
|
2510
|
+
.dsh-atb-form-left { border-right: none; padding-right: 0; }
|
|
2511
|
+
.dsh-atb-form-right .dsh-atb-prompt-input { min-height: 90px; }
|
|
2512
|
+
}
|
|
1425
2513
|
.dsh-atb-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
|
|
1426
2514
|
.dsh-atb-field[data-span="full"] { grid-column: 1 / -1; }
|
|
1427
2515
|
.dsh-atb-field-label {
|
|
@@ -1731,6 +2819,80 @@ window.__ModuleLoader__.load({
|
|
|
1731
2819
|
.dsh-atb-set { max-width: 460px; width: min(460px, 92vw); }
|
|
1732
2820
|
.dsh-atb-set .dsh-atb-mode-picker { margin-top: 8px; }
|
|
1733
2821
|
.dsh-atb-set .dsh-atb-isolation-note { margin-top: 10px; }
|
|
2822
|
+
|
|
2823
|
+
/* ---------- 0.5.5 SlashPromptInput & Permission Picker ---------- */
|
|
2824
|
+
.dsh-atb-perm-picker { display: grid; grid-template-columns: repeat(3, 1fr); gap: 7px; margin-top: 4px; }
|
|
2825
|
+
.dsh-atb-perm-opt {
|
|
2826
|
+
display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
|
|
2827
|
+
padding: 8px 10px; border-radius: 9px; cursor: pointer; text-align: left;
|
|
2828
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.35));
|
|
2829
|
+
background: transparent; color: inherit;
|
|
2830
|
+
transition: border-color .12s ease, background .12s ease;
|
|
2831
|
+
}
|
|
2832
|
+
.dsh-atb-perm-name { display: flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 600; }
|
|
2833
|
+
.dsh-atb-perm-hint { font-size: 10.5px; color: var(--dsw-alias-label-tertiary, gray); line-height: 1.35; }
|
|
2834
|
+
.dsh-atb-perm-opt:hover { border-color: var(--dsw-alias-label-tertiary, rgba(128,128,128,.6)); }
|
|
2835
|
+
.dsh-atb-perm-opt[data-on="true"] {
|
|
2836
|
+
border-color: var(--dsw-alias-brand-primary, #1f2328);
|
|
2837
|
+
background: color-mix(in srgb, var(--dsw-alias-brand-primary, #1f2328) 9%, transparent);
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
.dsh-atb-prompt-wrap {
|
|
2841
|
+
display: flex; flex-direction: column; gap: 6px; position: relative; width: 100%;
|
|
2842
|
+
border-radius: 9px; transition: border-color .12s ease;
|
|
2843
|
+
}
|
|
2844
|
+
.dsh-atb-prompt-wrap[data-drag-over="true"] {
|
|
2845
|
+
outline: 2px dashed var(--dsw-alias-brand-primary, #1f2328);
|
|
2846
|
+
background: color-mix(in srgb, var(--dsw-alias-brand-primary, #1f2328) 6%, transparent);
|
|
2847
|
+
}
|
|
2848
|
+
.dsh-atb-prompt-inner { position: relative; width: 100%; }
|
|
2849
|
+
.dsh-atb-prompt-input {
|
|
2850
|
+
width: 100%; box-sizing: border-box; font: inherit; font-size: 13px; line-height: 1.5;
|
|
2851
|
+
padding: 7px 10px; border-radius: 8px; resize: vertical; min-height: 68px;
|
|
2852
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.35));
|
|
2853
|
+
background: var(--dsw-specific-input-major, transparent); color: var(--dsw-alias-label-primary, inherit);
|
|
2854
|
+
}
|
|
2855
|
+
.dsh-atb-prompt-input:focus {
|
|
2856
|
+
outline: none; border-color: var(--dsw-alias-brand-primary, #1f2328);
|
|
2857
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary, #1f2328) 18%, transparent);
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
/* Slash Autocomplete Popup */
|
|
2861
|
+
.dsh-atb-slash-popup {
|
|
2862
|
+
position: absolute; left: 0; bottom: calc(100% + 6px); width: 100%; max-height: 240px; z-index: 100;
|
|
2863
|
+
display: flex; flex-direction: column; overflow: hidden; border-radius: 10px;
|
|
2864
|
+
background: var(--dsw-alias-bg-overlay, #fff); color: var(--dsw-alias-label-primary, inherit);
|
|
2865
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.28));
|
|
2866
|
+
box-shadow: var(--dsw-shadow-lv3, 0 10px 28px rgba(0,0,0,.22));
|
|
2867
|
+
}
|
|
2868
|
+
.dsh-atb-slash-head {
|
|
2869
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
2870
|
+
padding: 6px 10px; border-bottom: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.15));
|
|
2871
|
+
background: var(--dsw-alias-bg-elevated, rgba(128,128,128,.06));
|
|
2872
|
+
}
|
|
2873
|
+
.dsh-atb-slash-title { font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-secondary, gray); }
|
|
2874
|
+
.dsh-atb-slash-hint { font-size: 10px; color: var(--dsw-alias-label-tertiary, gray); }
|
|
2875
|
+
.dsh-atb-slash-list { overflow-y: auto; max-height: 200px; display: flex; flex-direction: column; padding: 4px; }
|
|
2876
|
+
.dsh-atb-slash-item {
|
|
2877
|
+
display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px;
|
|
2878
|
+
cursor: pointer; font-size: 12px; transition: background .1s ease;
|
|
2879
|
+
}
|
|
2880
|
+
.dsh-atb-slash-item[data-active="true"], .dsh-atb-slash-item:hover {
|
|
2881
|
+
background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.14));
|
|
2882
|
+
}
|
|
2883
|
+
.dsh-atb-slash-badge {
|
|
2884
|
+
font-size: 10px; font-weight: 600; padding: 1px 5px; border-radius: 4px; flex-shrink: 0;
|
|
2885
|
+
}
|
|
2886
|
+
.dsh-atb-slash-badge[data-kind="command"] { background: rgba(217,130,43,.15); color: #d9822b; }
|
|
2887
|
+
.dsh-atb-slash-badge[data-kind="skill"] { background: rgba(142,78,198,.15); color: #a06ce0; }
|
|
2888
|
+
.dsh-atb-slash-name { font-weight: 600; font-family: monospace; font-size: 12.5px; }
|
|
2889
|
+
.dsh-atb-slash-param { font-size: 11px; color: var(--dsw-alias-label-tertiary, gray); font-family: monospace; }
|
|
2890
|
+
.dsh-atb-slash-desc { font-size: 11px; color: var(--dsw-alias-label-secondary, gray); margin-left: auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 45%; }
|
|
2891
|
+
|
|
2892
|
+
/* Prompt Foot Toolbar */
|
|
2893
|
+
.dsh-atb-prompt-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
2894
|
+
.dsh-atb-prompt-tip { font-size: 11px; color: var(--dsw-alias-label-tertiary, gray); }
|
|
2895
|
+
.dsh-atb-prompt-tip code { font-size: 10.5px; padding: 1px 4px; border-radius: 4px; background: rgba(128,128,128,.14); }
|
|
1734
2896
|
`;
|
|
1735
2897
|
/** Style element id (stable since 0.1.x: hook for tests and debugging). */
|
|
1736
2898
|
const STYLE_ID = "dsh-taskboard-styles";
|
|
@@ -1816,8 +2978,8 @@ window.__ModuleLoader__.load({
|
|
|
1816
2978
|
entry.type = "button";
|
|
1817
2979
|
entry.dataset.dshAtbEntry = "";
|
|
1818
2980
|
entry.className = "dsh-atb-entry";
|
|
1819
|
-
entry.setAttribute("aria-label", "
|
|
1820
|
-
entry.innerHTML = `<span class="dsh-atb-entry-icon">${ICON}</span><span class="dsh-atb-entry-label"
|
|
2981
|
+
entry.setAttribute("aria-label", translate("shared.entry.aria"));
|
|
2982
|
+
entry.innerHTML = `<span class="dsh-atb-entry-icon">${ICON}</span><span class="dsh-atb-entry-label">${translate("shared.entry.label")}</span><span class="dsh-atb-entry-stats"></span>`;
|
|
1821
2983
|
entry.addEventListener("click", () => {
|
|
1822
2984
|
controller.toggleBoard();
|
|
1823
2985
|
});
|
|
@@ -1915,7 +3077,11 @@ window.__ModuleLoader__.load({
|
|
|
1915
3077
|
setRollValue(slots[0], todo);
|
|
1916
3078
|
setRollValue(slots[1], inProgress);
|
|
1917
3079
|
setRollValue(slots[2], inReview);
|
|
1918
|
-
stats.title =
|
|
3080
|
+
stats.title = translate("shared.stats.title", {
|
|
3081
|
+
todo,
|
|
3082
|
+
doing: inProgress,
|
|
3083
|
+
review: inReview
|
|
3084
|
+
});
|
|
1919
3085
|
};
|
|
1920
3086
|
return update;
|
|
1921
3087
|
}
|
|
@@ -1997,12 +3163,19 @@ window.__ModuleLoader__.load({
|
|
|
1997
3163
|
syncStats();
|
|
1998
3164
|
};
|
|
1999
3165
|
const unsubscribe = controller.subscribe(syncActive);
|
|
3166
|
+
const unsubscribeLocale = localeStore.subscribe(() => {
|
|
3167
|
+
entry.setAttribute("aria-label", translate("shared.entry.aria"));
|
|
3168
|
+
const label = entry.querySelector(".dsh-atb-entry-label");
|
|
3169
|
+
if (label !== null) label.textContent = translate("shared.entry.label");
|
|
3170
|
+
syncStats();
|
|
3171
|
+
});
|
|
2000
3172
|
syncActive();
|
|
2001
3173
|
tryPlace();
|
|
2002
3174
|
return () => {
|
|
2003
3175
|
clearInterval(retry);
|
|
2004
3176
|
waitObserver.disconnect();
|
|
2005
3177
|
rootObserver.disconnect();
|
|
3178
|
+
unsubscribeLocale();
|
|
2006
3179
|
unsubscribe();
|
|
2007
3180
|
entry.remove();
|
|
2008
3181
|
};
|
|
@@ -2017,53 +3190,53 @@ window.__ModuleLoader__.load({
|
|
|
2017
3190
|
* @module dsh-taskboard/shared/version
|
|
2018
3191
|
*/
|
|
2019
3192
|
/** The package version (must equal package.json "version"). */
|
|
2020
|
-
const PLUGIN_VERSION = "0.
|
|
3193
|
+
const PLUGIN_VERSION = "0.6.0";
|
|
2021
3194
|
|
|
2022
3195
|
//#endregion
|
|
2023
3196
|
//#region src/client/board/labels.ts
|
|
2024
|
-
/** Column
|
|
2025
|
-
const
|
|
2026
|
-
backlog: "
|
|
2027
|
-
todo: "
|
|
2028
|
-
in_progress: "
|
|
2029
|
-
in_review: "
|
|
2030
|
-
done: "
|
|
2031
|
-
canceled: "
|
|
2032
|
-
archived: "
|
|
3197
|
+
/** Column header keys on the five-column main board (+ secondary tab). */
|
|
3198
|
+
const COLUMN_KEYS = {
|
|
3199
|
+
backlog: "status.column.backlog",
|
|
3200
|
+
todo: "status.column.todo",
|
|
3201
|
+
in_progress: "status.column.in_progress",
|
|
3202
|
+
in_review: "status.column.in_review",
|
|
3203
|
+
done: "status.column.done",
|
|
3204
|
+
canceled: "status.column.canceled",
|
|
3205
|
+
archived: "status.column.archived"
|
|
2033
3206
|
};
|
|
2034
|
-
/** Status pill
|
|
3207
|
+
/** Status pill keys (detail pane) — historical wording kept verbatim:
|
|
2035
3208
|
* terminal states read short here, the column headers carry the full forms. */
|
|
2036
|
-
const
|
|
2037
|
-
backlog: "
|
|
2038
|
-
todo: "
|
|
2039
|
-
in_progress: "
|
|
2040
|
-
in_review: "
|
|
2041
|
-
done: "
|
|
2042
|
-
canceled: "
|
|
2043
|
-
archived: "
|
|
3209
|
+
const STATUS_KEYS = {
|
|
3210
|
+
backlog: "status.pill.backlog",
|
|
3211
|
+
todo: "status.pill.todo",
|
|
3212
|
+
in_progress: "status.pill.in_progress",
|
|
3213
|
+
in_review: "status.pill.in_review",
|
|
3214
|
+
done: "status.pill.done",
|
|
3215
|
+
canceled: "status.pill.canceled",
|
|
3216
|
+
archived: "status.pill.archived"
|
|
2044
3217
|
};
|
|
2045
|
-
/** Move-button
|
|
2046
|
-
const
|
|
2047
|
-
backlog: "
|
|
2048
|
-
todo: "
|
|
2049
|
-
in_progress: "
|
|
2050
|
-
in_review: "
|
|
2051
|
-
done: "
|
|
2052
|
-
canceled: "
|
|
2053
|
-
archived: "
|
|
3218
|
+
/** Move-button verb keys (shorter than the pill text). */
|
|
3219
|
+
const MOVE_KEYS = {
|
|
3220
|
+
backlog: "status.move.backlog",
|
|
3221
|
+
todo: "status.move.todo",
|
|
3222
|
+
in_progress: "status.move.in_progress",
|
|
3223
|
+
in_review: "status.move.in_review",
|
|
3224
|
+
done: "status.move.done",
|
|
3225
|
+
canceled: "status.move.canceled",
|
|
3226
|
+
archived: "status.move.archived"
|
|
2054
3227
|
};
|
|
2055
|
-
/** Urgency chip
|
|
2056
|
-
const
|
|
2057
|
-
urgent: "
|
|
2058
|
-
normal: "
|
|
2059
|
-
relaxed: "
|
|
3228
|
+
/** Urgency chip keys. */
|
|
3229
|
+
const URGENCY_KEYS = {
|
|
3230
|
+
urgent: "urgency.urgent",
|
|
3231
|
+
normal: "urgency.normal",
|
|
3232
|
+
relaxed: "urgency.relaxed"
|
|
2060
3233
|
};
|
|
2061
|
-
/** Execution outcome
|
|
2062
|
-
const
|
|
2063
|
-
running: "
|
|
2064
|
-
succeeded: "
|
|
2065
|
-
failed: "
|
|
2066
|
-
cancelled: "
|
|
3234
|
+
/** Execution outcome keys. */
|
|
3235
|
+
const OUTCOME_KEYS = {
|
|
3236
|
+
running: "outcome.running",
|
|
3237
|
+
succeeded: "outcome.succeeded",
|
|
3238
|
+
failed: "outcome.failed",
|
|
3239
|
+
cancelled: "outcome.cancelled"
|
|
2067
3240
|
};
|
|
2068
3241
|
|
|
2069
3242
|
//#endregion
|
|
@@ -2113,6 +3286,7 @@ window.__ModuleLoader__.load({
|
|
|
2113
3286
|
* @param onAlert - show an alert message (replaces native alert).
|
|
2114
3287
|
*/
|
|
2115
3288
|
function TaskCard({ task, controller, draggable = false, now, onAlert }) {
|
|
3289
|
+
const t = useT();
|
|
2116
3290
|
const [rejectOpen, setRejectOpen] = (0, react.useState)(false);
|
|
2117
3291
|
const [note, setNote] = (0, react.useState)("");
|
|
2118
3292
|
const last = task.executions.length > 0 ? task.executions[task.executions.length - 1] : void 0;
|
|
@@ -2139,7 +3313,7 @@ window.__ModuleLoader__.load({
|
|
|
2139
3313
|
onDragStart: (e) => {
|
|
2140
3314
|
if (running !== void 0) {
|
|
2141
3315
|
e.preventDefault();
|
|
2142
|
-
const msg =
|
|
3316
|
+
const msg = t("card.drag.running", { title: task.title });
|
|
2143
3317
|
if (onAlert !== void 0) onAlert(msg);
|
|
2144
3318
|
else alert(msg);
|
|
2145
3319
|
return;
|
|
@@ -2169,17 +3343,17 @@ window.__ModuleLoader__.load({
|
|
|
2169
3343
|
children: [
|
|
2170
3344
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2171
3345
|
className: "dsh-atb-badge",
|
|
2172
|
-
children:
|
|
3346
|
+
children: t(URGENCY_KEYS[task.urgency])
|
|
2173
3347
|
}),
|
|
2174
3348
|
task.blocked && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2175
3349
|
className: "dsh-atb-badge",
|
|
2176
3350
|
"data-kind": "blocked",
|
|
2177
|
-
children: "
|
|
3351
|
+
children: t("shared.blocked")
|
|
2178
3352
|
}),
|
|
2179
3353
|
stale && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2180
3354
|
className: "dsh-atb-badge",
|
|
2181
3355
|
"data-kind": "stale",
|
|
2182
|
-
children: "
|
|
3356
|
+
children: t("card.badge.stale")
|
|
2183
3357
|
}),
|
|
2184
3358
|
task.execution.mode === "scheduled" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2185
3359
|
className: "dsh-atb-badge",
|
|
@@ -2188,13 +3362,25 @@ window.__ModuleLoader__.load({
|
|
|
2188
3362
|
}),
|
|
2189
3363
|
task.model !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2190
3364
|
className: "dsh-atb-badge",
|
|
2191
|
-
title:
|
|
3365
|
+
title: t("card.badge.modelTitle", { model: task.model.provider + "/" + task.model.model }) + (task.model.reasoningEffort !== void 0 ? t("card.badge.modelEffort", { effort: task.model.reasoningEffort }) : ""),
|
|
2192
3366
|
children: [task.model.model, task.model.reasoningEffort !== void 0 ? ` (${task.model.reasoningEffort})` : ""]
|
|
2193
3367
|
}),
|
|
3368
|
+
task.permission === "read-only" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3369
|
+
className: "dsh-atb-badge",
|
|
3370
|
+
"data-kind": "blocked",
|
|
3371
|
+
title: t("card.badge.permReadOnlyTitle"),
|
|
3372
|
+
children: t("card.badge.permReadOnly")
|
|
3373
|
+
}),
|
|
3374
|
+
task.permission === "danger-full-access" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3375
|
+
className: "dsh-atb-badge",
|
|
3376
|
+
"data-kind": "urgent",
|
|
3377
|
+
title: t("card.badge.permFullTitle"),
|
|
3378
|
+
children: t("card.badge.permFull")
|
|
3379
|
+
}),
|
|
2194
3380
|
task.checklist !== void 0 && task.checklist.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2195
3381
|
className: "dsh-atb-badge",
|
|
2196
3382
|
"data-kind": task.status === "in_review" && task.checklist.some((i) => !i.checked) ? "blocked" : "checklist",
|
|
2197
|
-
title: task.status === "in_review" && task.checklist.some((i) => !i.checked) ? "
|
|
3383
|
+
title: task.status === "in_review" && task.checklist.some((i) => !i.checked) ? t("card.badge.checklistReview") : t("card.badge.checklist"),
|
|
2198
3384
|
children: [
|
|
2199
3385
|
"☑ ",
|
|
2200
3386
|
task.checklist.filter((i) => i.checked).length,
|
|
@@ -2205,30 +3391,30 @@ window.__ModuleLoader__.load({
|
|
|
2205
3391
|
task.status === "done" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2206
3392
|
className: "dsh-atb-badge",
|
|
2207
3393
|
"data-kind": "done",
|
|
2208
|
-
children: "
|
|
3394
|
+
children: t("status.pill.done")
|
|
2209
3395
|
}),
|
|
2210
3396
|
last !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2211
3397
|
className: "dsh-atb-badge",
|
|
2212
3398
|
"data-kind": last.outcome === "running" ? "running" : last.outcome,
|
|
2213
|
-
children:
|
|
3399
|
+
children: t(OUTCOME_KEYS[last.outcome] ?? last.outcome)
|
|
2214
3400
|
}),
|
|
2215
3401
|
targetSessionId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2216
3402
|
type: "button",
|
|
2217
3403
|
className: "dsh-atb-card-session",
|
|
2218
|
-
title:
|
|
3404
|
+
title: t("card.session.jumpTitle", { id: targetSessionId }),
|
|
2219
3405
|
onClick: (e) => {
|
|
2220
3406
|
e.stopPropagation();
|
|
2221
3407
|
controller.openSession(targetSessionId).then((result) => {
|
|
2222
3408
|
if (result === "missing") {
|
|
2223
|
-
const msg =
|
|
3409
|
+
const msg = t("card.session.missing", { id: shortId$1(targetSessionId) });
|
|
2224
3410
|
if (onAlert !== void 0) onAlert(msg);
|
|
2225
3411
|
else alert(msg);
|
|
2226
3412
|
} else if (result === "archived") {
|
|
2227
|
-
const msg =
|
|
3413
|
+
const msg = t("card.session.archived", { id: shortId$1(targetSessionId) });
|
|
2228
3414
|
if (onAlert !== void 0) onAlert(msg);
|
|
2229
3415
|
else alert(msg);
|
|
2230
3416
|
} else if (result === "unavailable") {
|
|
2231
|
-
const msg =
|
|
3417
|
+
const msg = t("card.session.unavailable", { id: targetSessionId });
|
|
2232
3418
|
if (onAlert !== void 0) onAlert(msg);
|
|
2233
3419
|
else alert(msg);
|
|
2234
3420
|
}
|
|
@@ -2244,7 +3430,7 @@ window.__ModuleLoader__.load({
|
|
|
2244
3430
|
task.trashedAt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2245
3431
|
className: "dsh-atb-badge",
|
|
2246
3432
|
"data-kind": "trashed",
|
|
2247
|
-
children: "
|
|
3433
|
+
children: t("card.badge.pendingPurge")
|
|
2248
3434
|
}),
|
|
2249
3435
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2250
3436
|
style: { marginLeft: "auto" },
|
|
@@ -2259,7 +3445,7 @@ window.__ModuleLoader__.load({
|
|
|
2259
3445
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2260
3446
|
className: "dsh-atb-input dsh-atb-quick-note",
|
|
2261
3447
|
value: note,
|
|
2262
|
-
placeholder: "
|
|
3448
|
+
placeholder: t("card.reject.placeholder"),
|
|
2263
3449
|
autoFocus: true,
|
|
2264
3450
|
spellCheck: false,
|
|
2265
3451
|
onChange: (e) => setNote(e.target.value),
|
|
@@ -2276,7 +3462,7 @@ window.__ModuleLoader__.load({
|
|
|
2276
3462
|
className: "dsh-atb-quickbtn",
|
|
2277
3463
|
"data-act": "reject-confirm",
|
|
2278
3464
|
onClick: submitReject,
|
|
2279
|
-
children: "
|
|
3465
|
+
children: t("card.reject.confirm")
|
|
2280
3466
|
}),
|
|
2281
3467
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2282
3468
|
type: "button",
|
|
@@ -2286,7 +3472,7 @@ window.__ModuleLoader__.load({
|
|
|
2286
3472
|
setRejectOpen(false);
|
|
2287
3473
|
setNote("");
|
|
2288
3474
|
},
|
|
2289
|
-
children: "
|
|
3475
|
+
children: t("shared.cancel")
|
|
2290
3476
|
})
|
|
2291
3477
|
]
|
|
2292
3478
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2296,16 +3482,16 @@ window.__ModuleLoader__.load({
|
|
|
2296
3482
|
type: "button",
|
|
2297
3483
|
className: "dsh-atb-quickbtn",
|
|
2298
3484
|
"data-act": "done",
|
|
2299
|
-
title: "
|
|
3485
|
+
title: t("card.action.doneTitle"),
|
|
2300
3486
|
onClick: () => void controller.move(task.id, task.version, "done"),
|
|
2301
|
-
children: "
|
|
3487
|
+
children: t("card.action.done")
|
|
2302
3488
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2303
3489
|
type: "button",
|
|
2304
3490
|
className: "dsh-atb-quickbtn",
|
|
2305
3491
|
"data-act": "reject",
|
|
2306
|
-
title: "
|
|
3492
|
+
title: t("card.action.rejectTitle"),
|
|
2307
3493
|
onClick: () => setRejectOpen(true),
|
|
2308
|
-
children: "
|
|
3494
|
+
children: t("card.action.reject")
|
|
2309
3495
|
})]
|
|
2310
3496
|
}))
|
|
2311
3497
|
]
|
|
@@ -2334,6 +3520,7 @@ window.__ModuleLoader__.load({
|
|
|
2334
3520
|
};
|
|
2335
3521
|
}
|
|
2336
3522
|
function AlertModal({ message, onClose }) {
|
|
3523
|
+
const t = useT();
|
|
2337
3524
|
(0, react.useEffect)(() => {
|
|
2338
3525
|
const handler = (e) => {
|
|
2339
3526
|
if (e.key === "Escape") onClose();
|
|
@@ -2361,7 +3548,7 @@ window.__ModuleLoader__.load({
|
|
|
2361
3548
|
className: "dsh-atb-btn",
|
|
2362
3549
|
"data-primary": "true",
|
|
2363
3550
|
onClick: onClose,
|
|
2364
|
-
children: "
|
|
3551
|
+
children: t("alert.ok")
|
|
2365
3552
|
})
|
|
2366
3553
|
]
|
|
2367
3554
|
})
|
|
@@ -2416,6 +3603,58 @@ window.__ModuleLoader__.load({
|
|
|
2416
3603
|
}), children]
|
|
2417
3604
|
});
|
|
2418
3605
|
}
|
|
3606
|
+
/** Render markdown text with embedded clickable images and lightbox preview. */
|
|
3607
|
+
function MarkdownContent({ text }) {
|
|
3608
|
+
const t = useT();
|
|
3609
|
+
const [lightboxUrl, setLightboxUrl] = (0, react.useState)(null);
|
|
3610
|
+
const regex = /!\[(.*?)\]\(((?:data:image\/[^)]+)|(?:https?:\/\/[^)]+)|(?:[^)]+\.(?:png|jpg|jpeg|gif|webp|svg)))\)/gi;
|
|
3611
|
+
const parts = [];
|
|
3612
|
+
let lastIndex = 0;
|
|
3613
|
+
let match;
|
|
3614
|
+
let count = 0;
|
|
3615
|
+
while ((match = regex.exec(text)) !== null) {
|
|
3616
|
+
if (match.index > lastIndex) parts.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text.slice(lastIndex, match.index) }, `txt-${lastIndex}`));
|
|
3617
|
+
const alt = match[1] || t("md.imageAlt", { n: ++count });
|
|
3618
|
+
const url = match[2] ?? "";
|
|
3619
|
+
parts.push(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3620
|
+
className: "dsh-atb-detail-img-wrap",
|
|
3621
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
3622
|
+
src: url,
|
|
3623
|
+
alt,
|
|
3624
|
+
className: "dsh-atb-detail-img",
|
|
3625
|
+
onClick: () => setLightboxUrl(url),
|
|
3626
|
+
title: t("md.imageTitle", { alt })
|
|
3627
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3628
|
+
className: "dsh-atb-detail-img-caption",
|
|
3629
|
+
children: alt
|
|
3630
|
+
})]
|
|
3631
|
+
}, `img-${match.index}`));
|
|
3632
|
+
lastIndex = regex.lastIndex;
|
|
3633
|
+
}
|
|
3634
|
+
if (lastIndex < text.length) parts.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text.slice(lastIndex) }, `txt-${lastIndex}`));
|
|
3635
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3636
|
+
className: "dsh-atb-markdown-body",
|
|
3637
|
+
children: parts
|
|
3638
|
+
}), lightboxUrl !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3639
|
+
className: "dsh-atb-lightbox-backdrop",
|
|
3640
|
+
onClick: () => setLightboxUrl(null),
|
|
3641
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3642
|
+
className: "dsh-atb-lightbox-content",
|
|
3643
|
+
onClick: (e) => e.stopPropagation(),
|
|
3644
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
3645
|
+
src: lightboxUrl,
|
|
3646
|
+
alt: t("md.lightboxAlt"),
|
|
3647
|
+
className: "dsh-atb-lightbox-img"
|
|
3648
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3649
|
+
type: "button",
|
|
3650
|
+
className: "dsh-atb-lightbox-close",
|
|
3651
|
+
title: t("md.closePreview"),
|
|
3652
|
+
onClick: () => setLightboxUrl(null),
|
|
3653
|
+
children: "✕"
|
|
3654
|
+
})]
|
|
3655
|
+
})
|
|
3656
|
+
})] });
|
|
3657
|
+
}
|
|
2419
3658
|
/** The most recent execution carrying isolation facts, newest first. */
|
|
2420
3659
|
function latestIsolated(task) {
|
|
2421
3660
|
return [...task.executions].reverse().find((e) => e.isolation !== void 0 || e.worktreePath !== void 0 || e.isolationNote !== void 0);
|
|
@@ -2437,6 +3676,7 @@ window.__ModuleLoader__.load({
|
|
|
2437
3676
|
* @param spec - what to show: one commit hash, or one changed path.
|
|
2438
3677
|
*/
|
|
2439
3678
|
function DiffView({ controller, task, execution, commit, path }) {
|
|
3679
|
+
const t = useT();
|
|
2440
3680
|
const [state, setState] = (0, react.useState)({ loading: true });
|
|
2441
3681
|
(0, react.useEffect)(() => {
|
|
2442
3682
|
let alive = true;
|
|
@@ -2473,20 +3713,20 @@ window.__ModuleLoader__.load({
|
|
|
2473
3713
|
children: [
|
|
2474
3714
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2475
3715
|
className: "dsh-atb-diffview-title",
|
|
2476
|
-
children: commit !== void 0 ?
|
|
3716
|
+
children: commit !== void 0 ? t("diff.commit", { hash: shortHash(commit) }) : t("diff.file", { path: path ?? "" })
|
|
2477
3717
|
}),
|
|
2478
3718
|
state.loading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2479
3719
|
className: "dsh-atb-diffview-hint",
|
|
2480
|
-
children: "
|
|
3720
|
+
children: t("shared.loading")
|
|
2481
3721
|
}),
|
|
2482
3722
|
state.truncated === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2483
3723
|
className: "dsh-atb-diffview-hint",
|
|
2484
|
-
children: "
|
|
3724
|
+
children: t("diff.truncated")
|
|
2485
3725
|
})
|
|
2486
3726
|
]
|
|
2487
3727
|
}), state.failed === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2488
3728
|
className: "dsh-atb-diffview-error",
|
|
2489
|
-
children: "
|
|
3729
|
+
children: t("diff.failed")
|
|
2490
3730
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
2491
3731
|
className: "dsh-atb-diffview-pre",
|
|
2492
3732
|
children: state.diff ?? ""
|
|
@@ -2498,6 +3738,7 @@ window.__ModuleLoader__.load({
|
|
|
2498
3738
|
* per row; unchecked items highlight while the task sits in in_review.
|
|
2499
3739
|
*/
|
|
2500
3740
|
function ChecklistBlock({ task, controller }) {
|
|
3741
|
+
const t = useT();
|
|
2501
3742
|
const items = task.checklist ?? [];
|
|
2502
3743
|
if (items.length === 0) return null;
|
|
2503
3744
|
const { done, total } = checklistProgress(task);
|
|
@@ -2508,7 +3749,7 @@ window.__ModuleLoader__.load({
|
|
|
2508
3749
|
"data-kind": "checklist",
|
|
2509
3750
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2510
3751
|
className: "dsh-atb-fieldcard-label",
|
|
2511
|
-
children: ["
|
|
3752
|
+
children: [t("checklist.title"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2512
3753
|
className: "dsh-atb-cl-progress",
|
|
2513
3754
|
"data-tone": reviewing && unchecked > 0 ? "bad" : void 0,
|
|
2514
3755
|
children: [
|
|
@@ -2516,7 +3757,7 @@ window.__ModuleLoader__.load({
|
|
|
2516
3757
|
done,
|
|
2517
3758
|
"/",
|
|
2518
3759
|
total,
|
|
2519
|
-
reviewing && unchecked > 0 ?
|
|
3760
|
+
reviewing && unchecked > 0 ? t("checklist.unchecked", { n: unchecked }) : done === total ? t("checklist.allDone") : ""
|
|
2520
3761
|
]
|
|
2521
3762
|
})]
|
|
2522
3763
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -2537,10 +3778,10 @@ window.__ModuleLoader__.load({
|
|
|
2537
3778
|
}),
|
|
2538
3779
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2539
3780
|
className: "dsh-atb-cl-meta",
|
|
2540
|
-
children: [item.checked ? `${item.checkedBy === "user" ? "
|
|
3781
|
+
children: [item.checked ? `${item.checkedBy === "user" ? t("checklist.byUser") : `🤖 ${shortId(item.checkedBy)}`} · ${fmtTime(item.checkedAt)}` : t("checklist.uncheckedItem"), item.note !== void 0 && item.note.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2541
3782
|
className: "dsh-atb-cl-note",
|
|
2542
3783
|
title: item.note,
|
|
2543
|
-
children:
|
|
3784
|
+
children: t("checklist.evidence", { note: item.note })
|
|
2544
3785
|
})]
|
|
2545
3786
|
})
|
|
2546
3787
|
]
|
|
@@ -2553,6 +3794,7 @@ window.__ModuleLoader__.load({
|
|
|
2553
3794
|
* carries one, rendered section by section for the reviewer.
|
|
2554
3795
|
*/
|
|
2555
3796
|
function ReportBlock({ task }) {
|
|
3797
|
+
const t = useT();
|
|
2556
3798
|
const execution = [...task.executions].reverse().find((e) => e.report !== void 0);
|
|
2557
3799
|
const report = execution?.report;
|
|
2558
3800
|
if (execution === void 0 || report === void 0) return null;
|
|
@@ -2572,23 +3814,23 @@ window.__ModuleLoader__.load({
|
|
|
2572
3814
|
children: [
|
|
2573
3815
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2574
3816
|
className: "dsh-atb-fieldcard-label",
|
|
2575
|
-
children: ["
|
|
3817
|
+
children: [t("report.title"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2576
3818
|
className: "dsh-atb-cl-progress",
|
|
2577
|
-
children:
|
|
3819
|
+
children: t("report.submitted", { time: fmtTime(execution.endedAt ?? execution.startedAt) })
|
|
2578
3820
|
})]
|
|
2579
3821
|
}),
|
|
2580
3822
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2581
3823
|
className: "dsh-atb-rpt-summary",
|
|
2582
3824
|
children: report.summary
|
|
2583
3825
|
}),
|
|
2584
|
-
section("
|
|
2585
|
-
section("
|
|
2586
|
-
section("
|
|
3826
|
+
section(t("report.changedFiles"), report.changedFiles),
|
|
3827
|
+
section(t("report.checks"), report.checks),
|
|
3828
|
+
section(t("report.artifacts"), report.artifacts),
|
|
2587
3829
|
report.risk.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2588
3830
|
className: "dsh-atb-rpt-sec",
|
|
2589
3831
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2590
3832
|
className: "dsh-atb-rpt-label",
|
|
2591
|
-
children: "
|
|
3833
|
+
children: t("report.risk")
|
|
2592
3834
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2593
3835
|
className: "dsh-atb-rpt-risk",
|
|
2594
3836
|
children: report.risk
|
|
@@ -2603,6 +3845,7 @@ window.__ModuleLoader__.load({
|
|
|
2603
3845
|
* remove worktree — plan §3.3).
|
|
2604
3846
|
*/
|
|
2605
3847
|
function IsolationBlock({ task, controller }) {
|
|
3848
|
+
const t = useT();
|
|
2606
3849
|
const { alert: showAlert, el: alertEl } = useAlert();
|
|
2607
3850
|
const [confirmMerge, setConfirmMerge] = (0, react.useState)(false);
|
|
2608
3851
|
const [confirmRemove, setConfirmRemove] = (0, react.useState)(null);
|
|
@@ -2617,8 +3860,8 @@ window.__ModuleLoader__.load({
|
|
|
2617
3860
|
controller.mergeBranch(task.id).then((result) => {
|
|
2618
3861
|
setBusy(false);
|
|
2619
3862
|
setConfirmMerge(false);
|
|
2620
|
-
if (!result.ok) showAlert(
|
|
2621
|
-
else if (result.noop === true) showAlert("
|
|
3863
|
+
if (!result.ok) showAlert(t("iso.merge.failed", { error: result.error }));
|
|
3864
|
+
else if (result.noop === true) showAlert(t("iso.merge.noop"));
|
|
2622
3865
|
});
|
|
2623
3866
|
};
|
|
2624
3867
|
const doRemove = (deleteBranch) => {
|
|
@@ -2626,8 +3869,8 @@ window.__ModuleLoader__.load({
|
|
|
2626
3869
|
controller.removeWorktree(task.id, deleteBranch).then((result) => {
|
|
2627
3870
|
setBusy(false);
|
|
2628
3871
|
setConfirmRemove(null);
|
|
2629
|
-
if (!result.ok) showAlert(
|
|
2630
|
-
else if (result.branchError !== void 0) showAlert(
|
|
3872
|
+
if (!result.ok) showAlert(t("iso.remove.failed", { error: result.error }));
|
|
3873
|
+
else if (result.branchError !== void 0) showAlert(t("iso.remove.branchFailed", { error: result.branchError }));
|
|
2631
3874
|
});
|
|
2632
3875
|
};
|
|
2633
3876
|
if (execution.isolation !== "worktree" || execution.worktreePath === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2636,11 +3879,11 @@ window.__ModuleLoader__.load({
|
|
|
2636
3879
|
children: [
|
|
2637
3880
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2638
3881
|
className: "dsh-atb-fieldcard-label",
|
|
2639
|
-
children: "
|
|
3882
|
+
children: t("iso.title")
|
|
2640
3883
|
}),
|
|
2641
3884
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2642
3885
|
className: "dsh-atb-iso-none",
|
|
2643
|
-
children: ["
|
|
3886
|
+
children: [t("iso.none"), execution.isolationNote !== void 0 ? ` · ${execution.isolationNote}` : ""]
|
|
2644
3887
|
}),
|
|
2645
3888
|
alertEl
|
|
2646
3889
|
]
|
|
@@ -2655,7 +3898,7 @@ window.__ModuleLoader__.load({
|
|
|
2655
3898
|
children: [
|
|
2656
3899
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2657
3900
|
className: "dsh-atb-fieldcard-label",
|
|
2658
|
-
children: "
|
|
3901
|
+
children: t("iso.worktreeTitle")
|
|
2659
3902
|
}),
|
|
2660
3903
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2661
3904
|
className: "dsh-atb-iso-facts",
|
|
@@ -2663,24 +3906,22 @@ window.__ModuleLoader__.load({
|
|
|
2663
3906
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2664
3907
|
className: "dsh-atb-iso-fact",
|
|
2665
3908
|
title: execution.worktreePath,
|
|
2666
|
-
children: ["🌿 分支 ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: execution.branch ?? task.branch })]
|
|
2667
|
-
}),
|
|
2668
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2669
|
-
className: "dsh-atb-iso-fact",
|
|
2670
3909
|
children: [
|
|
2671
|
-
"
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
shortHash(execution.headCommit)
|
|
3910
|
+
t("iso.branch"),
|
|
3911
|
+
" ",
|
|
3912
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: execution.branch ?? task.branch })
|
|
2675
3913
|
]
|
|
2676
3914
|
}),
|
|
2677
|
-
|
|
3915
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2678
3916
|
className: "dsh-atb-iso-fact",
|
|
2679
|
-
children:
|
|
2680
|
-
|
|
2681
|
-
execution.
|
|
2682
|
-
|
|
2683
|
-
|
|
3917
|
+
children: t("iso.baseline", {
|
|
3918
|
+
base: shortHash(execution.baseCommit),
|
|
3919
|
+
head: shortHash(execution.headCommit)
|
|
3920
|
+
})
|
|
3921
|
+
}),
|
|
3922
|
+
execution.changedFiles !== void 0 && execution.changedFiles > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3923
|
+
className: "dsh-atb-iso-fact",
|
|
3924
|
+
children: t("iso.changed", { n: execution.changedFiles })
|
|
2684
3925
|
}),
|
|
2685
3926
|
execution.diffStat !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2686
3927
|
className: "dsh-atb-iso-fact",
|
|
@@ -2697,7 +3938,7 @@ window.__ModuleLoader__.load({
|
|
|
2697
3938
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2698
3939
|
type: "button",
|
|
2699
3940
|
className: "dsh-atb-iso-commit-btn",
|
|
2700
|
-
title: "
|
|
3941
|
+
title: t("iso.commit.openTitle"),
|
|
2701
3942
|
onClick: () => setOpenDiff(openDiff?.commit === c.hash ? null : { commit: c.hash }),
|
|
2702
3943
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: shortHash(c.hash) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: c.subject })]
|
|
2703
3944
|
}), openDiff?.commit === c.hash && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DiffView, {
|
|
@@ -2706,17 +3947,13 @@ window.__ModuleLoader__.load({
|
|
|
2706
3947
|
execution,
|
|
2707
3948
|
commit: c.hash
|
|
2708
3949
|
})]
|
|
2709
|
-
}, c.hash)), commitTotal > 10 && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3950
|
+
}, c.hash)), commitTotal > 10 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2710
3951
|
className: "dsh-atb-iso-more",
|
|
2711
|
-
children:
|
|
2712
|
-
"… 共 ",
|
|
2713
|
-
commitTotal,
|
|
2714
|
-
" 个提交"
|
|
2715
|
-
]
|
|
3952
|
+
children: t("iso.commits.more", { n: commitTotal })
|
|
2716
3953
|
})]
|
|
2717
3954
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2718
3955
|
className: "dsh-atb-iso-nocommit",
|
|
2719
|
-
children: "
|
|
3956
|
+
children: t("iso.nocommit")
|
|
2720
3957
|
}),
|
|
2721
3958
|
dirtyTotal > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2722
3959
|
className: "dsh-atb-iso-dirty",
|
|
@@ -2725,12 +3962,7 @@ window.__ModuleLoader__.load({
|
|
|
2725
3962
|
type: "button",
|
|
2726
3963
|
className: "dsh-atb-iso-dirty-toggle",
|
|
2727
3964
|
onClick: () => setDirtyOpen(!dirtyOpen),
|
|
2728
|
-
children: [
|
|
2729
|
-
"⚠ 有 ",
|
|
2730
|
-
dirtyTotal,
|
|
2731
|
-
" 处未提交修改(合并前请让 agent 提交,或手动处理)",
|
|
2732
|
-
dirtyOpen ? " ▲" : " ▼ 查看文件"
|
|
2733
|
-
]
|
|
3965
|
+
children: [t("iso.dirty.toggle", { n: dirtyTotal }), dirtyOpen ? t("iso.dirty.collapse") : t("iso.dirty.expand")]
|
|
2734
3966
|
}),
|
|
2735
3967
|
dirtyOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2736
3968
|
className: "dsh-atb-iso-dirty-files",
|
|
@@ -2739,7 +3971,7 @@ window.__ModuleLoader__.load({
|
|
|
2739
3971
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2740
3972
|
type: "button",
|
|
2741
3973
|
className: "dsh-atb-iso-dirty-file",
|
|
2742
|
-
title: "
|
|
3974
|
+
title: t("iso.dirty.openTitle"),
|
|
2743
3975
|
onClick: () => setOpenDiff(openDiff?.path === filePath ? null : { path: filePath }),
|
|
2744
3976
|
children: [
|
|
2745
3977
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: line.slice(0, 2) }),
|
|
@@ -2747,13 +3979,9 @@ window.__ModuleLoader__.load({
|
|
|
2747
3979
|
filePath
|
|
2748
3980
|
]
|
|
2749
3981
|
}, `${line}-${index}`);
|
|
2750
|
-
}), dirtyTotal > 30 && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3982
|
+
}), dirtyTotal > 30 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2751
3983
|
className: "dsh-atb-iso-more",
|
|
2752
|
-
children:
|
|
2753
|
-
"… 共 ",
|
|
2754
|
-
dirtyTotal,
|
|
2755
|
-
" 处(完整列表见任务台账)"
|
|
2756
|
-
]
|
|
3984
|
+
children: t("iso.dirty.more", { n: dirtyTotal })
|
|
2757
3985
|
})]
|
|
2758
3986
|
}),
|
|
2759
3987
|
openDiff?.path !== void 0 && dirtyOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DiffView, {
|
|
@@ -2769,13 +3997,13 @@ window.__ModuleLoader__.load({
|
|
|
2769
3997
|
children: [
|
|
2770
3998
|
running ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2771
3999
|
className: "dsh-atb-iso-hint",
|
|
2772
|
-
children: "
|
|
4000
|
+
children: t("iso.hint.running")
|
|
2773
4001
|
}) : confirmMerge ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2774
4002
|
className: "dsh-atb-confirm",
|
|
2775
4003
|
children: [
|
|
2776
4004
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2777
4005
|
className: "dsh-atb-confirm-label",
|
|
2778
|
-
children: "
|
|
4006
|
+
children: t("iso.merge.confirm")
|
|
2779
4007
|
}),
|
|
2780
4008
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2781
4009
|
type: "button",
|
|
@@ -2783,45 +4011,45 @@ window.__ModuleLoader__.load({
|
|
|
2783
4011
|
"data-primary": "true",
|
|
2784
4012
|
disabled: busy,
|
|
2785
4013
|
onClick: doMerge,
|
|
2786
|
-
children: "
|
|
4014
|
+
children: t("iso.merge.go")
|
|
2787
4015
|
}),
|
|
2788
4016
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2789
4017
|
type: "button",
|
|
2790
4018
|
className: "dsh-atb-btn",
|
|
2791
4019
|
onClick: () => setConfirmMerge(false),
|
|
2792
|
-
children: "
|
|
4020
|
+
children: t("shared.cancel")
|
|
2793
4021
|
})
|
|
2794
4022
|
]
|
|
2795
4023
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2796
4024
|
type: "button",
|
|
2797
4025
|
className: "dsh-atb-btn",
|
|
2798
4026
|
disabled: busy,
|
|
2799
|
-
title: "
|
|
4027
|
+
title: t("iso.merge.title"),
|
|
2800
4028
|
onClick: () => setConfirmMerge(true),
|
|
2801
|
-
children: "
|
|
4029
|
+
children: t("iso.merge.button")
|
|
2802
4030
|
}),
|
|
2803
4031
|
!running && (confirmRemove === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2804
4032
|
type: "button",
|
|
2805
4033
|
className: "dsh-atb-btn",
|
|
2806
4034
|
"data-danger": "true",
|
|
2807
4035
|
disabled: busy,
|
|
2808
|
-
title: "
|
|
4036
|
+
title: t("iso.remove.wtTitle"),
|
|
2809
4037
|
onClick: () => setConfirmRemove("wt"),
|
|
2810
|
-
children: "
|
|
4038
|
+
children: t("iso.remove.wt")
|
|
2811
4039
|
}), task.branch !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2812
4040
|
type: "button",
|
|
2813
4041
|
className: "dsh-atb-btn",
|
|
2814
4042
|
"data-danger": "true",
|
|
2815
4043
|
disabled: busy,
|
|
2816
|
-
title: "
|
|
4044
|
+
title: t("iso.remove.wtbTitle"),
|
|
2817
4045
|
onClick: () => setConfirmRemove("wtb"),
|
|
2818
|
-
children: "
|
|
4046
|
+
children: t("iso.remove.wtb")
|
|
2819
4047
|
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2820
4048
|
className: "dsh-atb-confirm",
|
|
2821
4049
|
children: [
|
|
2822
4050
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2823
4051
|
className: "dsh-atb-confirm-label",
|
|
2824
|
-
children: confirmRemove === "wtb" ? "
|
|
4052
|
+
children: confirmRemove === "wtb" ? t("iso.remove.confirmWtb") : t("iso.remove.confirmWt")
|
|
2825
4053
|
}),
|
|
2826
4054
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2827
4055
|
type: "button",
|
|
@@ -2829,19 +4057,19 @@ window.__ModuleLoader__.load({
|
|
|
2829
4057
|
"data-danger": "true",
|
|
2830
4058
|
disabled: busy,
|
|
2831
4059
|
onClick: () => doRemove(confirmRemove === "wtb"),
|
|
2832
|
-
children: "
|
|
4060
|
+
children: t("shared.confirmDelete")
|
|
2833
4061
|
}),
|
|
2834
4062
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2835
4063
|
type: "button",
|
|
2836
4064
|
className: "dsh-atb-btn",
|
|
2837
4065
|
onClick: () => setConfirmRemove(null),
|
|
2838
|
-
children: "
|
|
4066
|
+
children: t("shared.cancel")
|
|
2839
4067
|
})
|
|
2840
4068
|
]
|
|
2841
4069
|
})),
|
|
2842
4070
|
!running && confirmRemove === null && !confirmMerge && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2843
4071
|
className: "dsh-atb-iso-hint",
|
|
2844
|
-
children: "
|
|
4072
|
+
children: t("iso.hint.keep")
|
|
2845
4073
|
})
|
|
2846
4074
|
]
|
|
2847
4075
|
}),
|
|
@@ -2856,6 +4084,7 @@ window.__ModuleLoader__.load({
|
|
|
2856
4084
|
* @param now - current epoch ms (stale-claim highlight).
|
|
2857
4085
|
*/
|
|
2858
4086
|
function TaskDetail({ task, controller, now }) {
|
|
4087
|
+
const t = useT();
|
|
2859
4088
|
const [comment, setComment] = (0, react.useState)("");
|
|
2860
4089
|
const [confirmDone, setConfirmDone] = (0, react.useState)(false);
|
|
2861
4090
|
const [confirmPurge, setConfirmPurge] = (0, react.useState)(false);
|
|
@@ -2879,9 +4108,9 @@ window.__ModuleLoader__.load({
|
|
|
2879
4108
|
/** Jump to an execution's session; prompt precisely when it cannot open. */
|
|
2880
4109
|
const jumpToSession = (sessionId) => {
|
|
2881
4110
|
controller.openSession(sessionId).then((result) => {
|
|
2882
|
-
if (result === "missing") showAlert(
|
|
2883
|
-
else if (result === "archived") showAlert(
|
|
2884
|
-
else if (result === "unavailable") showAlert(
|
|
4111
|
+
if (result === "missing") showAlert(t("card.session.missing", { id: shortId(sessionId) }));
|
|
4112
|
+
else if (result === "archived") showAlert(t("card.session.archived", { id: shortId(sessionId) }));
|
|
4113
|
+
else if (result === "unavailable") showAlert(t("card.session.unavailable", { id: sessionId }));
|
|
2885
4114
|
});
|
|
2886
4115
|
};
|
|
2887
4116
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2898,7 +4127,7 @@ window.__ModuleLoader__.load({
|
|
|
2898
4127
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: task.title }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2899
4128
|
className: "dsh-atb-statuspill",
|
|
2900
4129
|
"data-status": task.status,
|
|
2901
|
-
children:
|
|
4130
|
+
children: t(STATUS_KEYS[task.status] ?? task.status)
|
|
2902
4131
|
})]
|
|
2903
4132
|
}),
|
|
2904
4133
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2906,7 +4135,7 @@ window.__ModuleLoader__.load({
|
|
|
2906
4135
|
children: [
|
|
2907
4136
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Chip, {
|
|
2908
4137
|
tone: task.urgency,
|
|
2909
|
-
children: ["● ",
|
|
4138
|
+
children: ["● ", t(URGENCY_KEYS[task.urgency] ?? task.urgency)]
|
|
2910
4139
|
}),
|
|
2911
4140
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
|
|
2912
4141
|
icon: "📁",
|
|
@@ -2914,35 +4143,32 @@ window.__ModuleLoader__.load({
|
|
|
2914
4143
|
}),
|
|
2915
4144
|
task.model !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Chip, {
|
|
2916
4145
|
icon: "✦",
|
|
2917
|
-
title:
|
|
4146
|
+
title: t("card.badge.modelTitle", { model: task.model.provider + "/" + task.model.model }) + (task.model.reasoningEffort !== void 0 ? t("card.badge.modelEffort", { effort: task.model.reasoningEffort }) : ""),
|
|
2918
4147
|
children: [task.model.model, task.model.reasoningEffort !== void 0 ? ` · ${task.model.reasoningEffort}` : ""]
|
|
2919
4148
|
}),
|
|
2920
4149
|
task.presetId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
|
|
2921
4150
|
icon: "🎛",
|
|
2922
4151
|
children: task.presetId
|
|
2923
4152
|
}),
|
|
2924
|
-
task.execution.mode === "scheduled" && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
4153
|
+
task.execution.mode === "scheduled" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
|
|
2925
4154
|
icon: "⏰",
|
|
2926
|
-
children:
|
|
2927
|
-
task.execution.cron,
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
]
|
|
4155
|
+
children: t("detail.chip.nextRun", {
|
|
4156
|
+
cron: task.execution.cron ?? "",
|
|
4157
|
+
time: fmtTime(task.execution.nextRunAt)
|
|
4158
|
+
})
|
|
2931
4159
|
}),
|
|
2932
4160
|
task.blocked && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
|
|
2933
4161
|
icon: "⛔",
|
|
2934
4162
|
tone: "urgent",
|
|
2935
|
-
children: "
|
|
4163
|
+
children: t("shared.blocked")
|
|
2936
4164
|
}),
|
|
2937
|
-
task.checklist !== void 0 && task.checklist.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
4165
|
+
task.checklist !== void 0 && task.checklist.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
|
|
2938
4166
|
icon: "☑",
|
|
2939
4167
|
tone: task.status === "in_review" && task.checklist.some((i) => !i.checked) ? "urgent" : void 0,
|
|
2940
|
-
children:
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
task.checklist.length
|
|
2945
|
-
]
|
|
4168
|
+
children: t("detail.chip.checklist", {
|
|
4169
|
+
done: checklistProgress(task).done,
|
|
4170
|
+
total: task.checklist.length
|
|
4171
|
+
})
|
|
2946
4172
|
}),
|
|
2947
4173
|
task.branch !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Chip, {
|
|
2948
4174
|
icon: "🌿",
|
|
@@ -2951,40 +4177,52 @@ window.__ModuleLoader__.load({
|
|
|
2951
4177
|
}),
|
|
2952
4178
|
(task.isolation === void 0 || task.isolation === "worktree") && task.branch === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
|
|
2953
4179
|
icon: "🌿",
|
|
2954
|
-
children: "
|
|
4180
|
+
children: t("detail.chip.isolated")
|
|
4181
|
+
}),
|
|
4182
|
+
task.permission === "read-only" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
|
|
4183
|
+
icon: "🔒",
|
|
4184
|
+
tone: "urgent",
|
|
4185
|
+
children: t("detail.chip.permReadOnly")
|
|
4186
|
+
}),
|
|
4187
|
+
task.permission === "danger-full-access" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
|
|
4188
|
+
icon: "⚡",
|
|
4189
|
+
tone: "urgent",
|
|
4190
|
+
children: t("detail.chip.permFull")
|
|
4191
|
+
}),
|
|
4192
|
+
(task.permission === "workspace-write" || task.permission === void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
|
|
4193
|
+
icon: "📁",
|
|
4194
|
+
children: t("detail.chip.permWrite")
|
|
2955
4195
|
}),
|
|
2956
4196
|
holder !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2957
4197
|
type: "button",
|
|
2958
4198
|
className: "dsh-atb-chip2 dsh-atb-chip-btn",
|
|
2959
4199
|
"data-tone": stale ? "urgent" : void 0,
|
|
2960
|
-
title:
|
|
4200
|
+
title: t("detail.chip.holderTitle", { id: holder }),
|
|
2961
4201
|
onClick: () => jumpToSession(holder),
|
|
2962
4202
|
children: [
|
|
2963
4203
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2964
4204
|
className: "dsh-atb-chip2-icon",
|
|
2965
4205
|
children: stale ? "⏱" : "🤖"
|
|
2966
4206
|
}),
|
|
2967
|
-
stale ? "
|
|
4207
|
+
stale ? t("detail.chip.holderStale") : t("detail.chip.holderBy"),
|
|
2968
4208
|
shortId(holder),
|
|
2969
|
-
"
|
|
4209
|
+
t("detail.chip.holderSuffix")
|
|
2970
4210
|
]
|
|
2971
4211
|
}),
|
|
2972
4212
|
task.trashedAt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
|
|
2973
4213
|
icon: "🗑",
|
|
2974
4214
|
tone: "urgent",
|
|
2975
|
-
children: "
|
|
4215
|
+
children: t("detail.chip.trashed")
|
|
2976
4216
|
}),
|
|
2977
4217
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Chip, { children: ["v", task.version] })
|
|
2978
4218
|
]
|
|
2979
4219
|
}),
|
|
2980
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
4220
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2981
4221
|
className: "dsh-atb-detail-sub",
|
|
2982
|
-
children:
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
task.updatedBy.kind === "agent" ? `🤖 ${shortId(task.updatedBy.sessionId)}` : task.updatedBy.kind === "system" ? "⚙️ 系统" : "👤 用户"
|
|
2987
|
-
]
|
|
4222
|
+
children: t("detail.sub.line", {
|
|
4223
|
+
time: fmtTime(task.updatedAt),
|
|
4224
|
+
who: task.updatedBy.kind === "agent" ? `🤖 ${shortId(task.updatedBy.sessionId)}` : task.updatedBy.kind === "system" ? t("detail.updatedBy.system") : t("detail.updatedBy.user")
|
|
4225
|
+
})
|
|
2988
4226
|
})
|
|
2989
4227
|
]
|
|
2990
4228
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2993,56 +4231,56 @@ window.__ModuleLoader__.load({
|
|
|
2993
4231
|
targetSessionId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2994
4232
|
type: "button",
|
|
2995
4233
|
className: "dsh-atb-detail-session",
|
|
2996
|
-
title:
|
|
4234
|
+
title: t("detail.session.jumpTitle", { id: targetSessionId }),
|
|
2997
4235
|
onClick: () => jumpToSession(targetSessionId),
|
|
2998
|
-
children: "
|
|
4236
|
+
children: t("detail.session.jump")
|
|
2999
4237
|
}),
|
|
3000
4238
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3001
4239
|
type: "button",
|
|
3002
4240
|
className: "dsh-atb-detail-edit",
|
|
3003
4241
|
onClick: () => controller.openEditor(task.id),
|
|
3004
|
-
children: "
|
|
4242
|
+
children: t("detail.action.edit")
|
|
3005
4243
|
}),
|
|
3006
4244
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3007
4245
|
type: "button",
|
|
3008
4246
|
className: "dsh-atb-detail-edit",
|
|
3009
|
-
title: "
|
|
4247
|
+
title: t("detail.action.duplicateTitle"),
|
|
3010
4248
|
disabled: actionBusy,
|
|
3011
4249
|
onClick: () => runAction(() => controller.duplicate(task)),
|
|
3012
|
-
children: "
|
|
4250
|
+
children: t("detail.action.duplicate")
|
|
3013
4251
|
}),
|
|
3014
4252
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3015
4253
|
type: "button",
|
|
3016
4254
|
className: "dsh-atb-detail-edit",
|
|
3017
|
-
title: "
|
|
4255
|
+
title: t("detail.action.saveTplTitle"),
|
|
3018
4256
|
disabled: actionBusy,
|
|
3019
4257
|
onClick: () => runAction(async () => {
|
|
3020
|
-
if (await controller.saveAsTemplate(task)) showAlert("
|
|
4258
|
+
if (await controller.saveAsTemplate(task)) showAlert(t("detail.action.saveTplDone"));
|
|
3021
4259
|
}),
|
|
3022
|
-
children: "
|
|
4260
|
+
children: t("detail.action.saveTpl")
|
|
3023
4261
|
}),
|
|
3024
4262
|
canRun && task.branch !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3025
4263
|
type: "button",
|
|
3026
4264
|
className: "dsh-atb-detail-run",
|
|
3027
|
-
title: "
|
|
4265
|
+
title: t("detail.action.reuseTitle"),
|
|
3028
4266
|
disabled: actionBusy,
|
|
3029
4267
|
onClick: () => runAction(() => controller.run(task.id, true)),
|
|
3030
|
-
children: "
|
|
4268
|
+
children: t("detail.action.reuse")
|
|
3031
4269
|
}),
|
|
3032
4270
|
canRun && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3033
4271
|
type: "button",
|
|
3034
4272
|
className: "dsh-atb-detail-run",
|
|
3035
|
-
title: task.model !== void 0 ?
|
|
4273
|
+
title: task.model !== void 0 ? t("detail.action.runTitleModel", { model: task.model.model }) : t("detail.action.runTitleDefault"),
|
|
3036
4274
|
disabled: actionBusy,
|
|
3037
4275
|
onClick: () => runAction(() => controller.run(task.id)),
|
|
3038
|
-
children: "
|
|
4276
|
+
children: t("detail.action.run")
|
|
3039
4277
|
}),
|
|
3040
4278
|
runningExecution !== void 0 && (confirmCancel ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3041
4279
|
className: "dsh-atb-confirm",
|
|
3042
4280
|
children: [
|
|
3043
4281
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3044
4282
|
className: "dsh-atb-confirm-label",
|
|
3045
|
-
children: "
|
|
4283
|
+
children: t("detail.action.stopConfirm")
|
|
3046
4284
|
}),
|
|
3047
4285
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3048
4286
|
type: "button",
|
|
@@ -3052,27 +4290,27 @@ window.__ModuleLoader__.load({
|
|
|
3052
4290
|
controller.cancel(task.id);
|
|
3053
4291
|
setConfirmCancel(false);
|
|
3054
4292
|
},
|
|
3055
|
-
children: "
|
|
4293
|
+
children: t("detail.action.stop")
|
|
3056
4294
|
}),
|
|
3057
4295
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3058
4296
|
type: "button",
|
|
3059
4297
|
className: "dsh-atb-btn",
|
|
3060
4298
|
onClick: () => setConfirmCancel(false),
|
|
3061
|
-
children: "
|
|
4299
|
+
children: t("shared.cancel")
|
|
3062
4300
|
})
|
|
3063
4301
|
]
|
|
3064
4302
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3065
4303
|
type: "button",
|
|
3066
4304
|
className: "dsh-atb-detail-run",
|
|
3067
4305
|
"data-danger": "true",
|
|
3068
|
-
title:
|
|
4306
|
+
title: t("detail.action.stopTitle", { id: runningExecution.sessionId ?? "" }),
|
|
3069
4307
|
onClick: () => setConfirmCancel(true),
|
|
3070
|
-
children: "
|
|
4308
|
+
children: t("detail.action.stopExec")
|
|
3071
4309
|
})),
|
|
3072
4310
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3073
4311
|
type: "button",
|
|
3074
4312
|
className: "dsh-atb-detail-close",
|
|
3075
|
-
"aria-label": "
|
|
4313
|
+
"aria-label": t("shared.close"),
|
|
3076
4314
|
onClick: () => controller.select(void 0),
|
|
3077
4315
|
children: "✕"
|
|
3078
4316
|
})
|
|
@@ -3083,10 +4321,10 @@ window.__ModuleLoader__.load({
|
|
|
3083
4321
|
className: "dsh-atb-fieldcard",
|
|
3084
4322
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3085
4323
|
className: "dsh-atb-fieldcard-label",
|
|
3086
|
-
children: "
|
|
4324
|
+
children: t("detail.field.description")
|
|
3087
4325
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3088
4326
|
className: "dsh-atb-desc",
|
|
3089
|
-
children: task.description
|
|
4327
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownContent, { text: task.description })
|
|
3090
4328
|
})]
|
|
3091
4329
|
}),
|
|
3092
4330
|
task.prompt.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -3094,10 +4332,10 @@ window.__ModuleLoader__.load({
|
|
|
3094
4332
|
"data-kind": "prompt",
|
|
3095
4333
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3096
4334
|
className: "dsh-atb-fieldcard-label",
|
|
3097
|
-
children: "
|
|
4335
|
+
children: t("detail.field.prompt")
|
|
3098
4336
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3099
4337
|
className: "dsh-atb-promptbox",
|
|
3100
|
-
children: task.prompt
|
|
4338
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownContent, { text: task.prompt })
|
|
3101
4339
|
})]
|
|
3102
4340
|
}),
|
|
3103
4341
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IsolationBlock, {
|
|
@@ -3120,7 +4358,7 @@ window.__ModuleLoader__.load({
|
|
|
3120
4358
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3121
4359
|
className: "dsh-atb-confirm-label",
|
|
3122
4360
|
"data-tone": unchecked > 0 ? "bad" : void 0,
|
|
3123
|
-
children: unchecked > 0 ?
|
|
4361
|
+
children: unchecked > 0 ? t("detail.move.confirmDoneUnchecked", { n: unchecked }) : t("detail.move.confirmDone")
|
|
3124
4362
|
}),
|
|
3125
4363
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3126
4364
|
type: "button",
|
|
@@ -3130,42 +4368,42 @@ window.__ModuleLoader__.load({
|
|
|
3130
4368
|
controller.move(task.id, task.version, "done");
|
|
3131
4369
|
setConfirmDone(false);
|
|
3132
4370
|
},
|
|
3133
|
-
children: "
|
|
4371
|
+
children: t("detail.move.confirm")
|
|
3134
4372
|
}),
|
|
3135
4373
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3136
4374
|
type: "button",
|
|
3137
4375
|
className: "dsh-atb-btn",
|
|
3138
4376
|
onClick: () => setConfirmDone(false),
|
|
3139
|
-
children: "
|
|
4377
|
+
children: t("shared.cancel")
|
|
3140
4378
|
})
|
|
3141
4379
|
]
|
|
3142
|
-
}, to) : /* @__PURE__ */ (0, react_jsx_runtime.
|
|
4380
|
+
}, to) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3143
4381
|
type: "button",
|
|
3144
4382
|
className: "dsh-atb-movebtn",
|
|
3145
4383
|
"data-to": to,
|
|
3146
4384
|
onClick: () => setConfirmDone(true),
|
|
3147
|
-
children:
|
|
3148
|
-
}, to) : /* @__PURE__ */ (0, react_jsx_runtime.
|
|
4385
|
+
children: t("detail.move.to", { status: t(MOVE_KEYS[to]) })
|
|
4386
|
+
}, to) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3149
4387
|
type: "button",
|
|
3150
4388
|
className: "dsh-atb-movebtn",
|
|
3151
4389
|
"data-to": to,
|
|
3152
4390
|
onClick: () => void controller.move(task.id, task.version, to),
|
|
3153
|
-
children:
|
|
4391
|
+
children: t("detail.move.to", { status: t(MOVE_KEYS[to]) })
|
|
3154
4392
|
}, to)),
|
|
3155
4393
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3156
4394
|
type: "button",
|
|
3157
4395
|
className: "dsh-atb-movebtn",
|
|
3158
4396
|
"data-to": "blocked",
|
|
3159
4397
|
onClick: () => void controller.toggleBlocked(task),
|
|
3160
|
-
children: task.blocked ? "
|
|
4398
|
+
children: task.blocked ? t("detail.blocked.unmark") : t("detail.blocked.mark")
|
|
3161
4399
|
}),
|
|
3162
4400
|
holder !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3163
4401
|
type: "button",
|
|
3164
4402
|
className: "dsh-atb-movebtn",
|
|
3165
4403
|
"data-to": "release",
|
|
3166
|
-
title:
|
|
4404
|
+
title: t("detail.release.title", { id: holder }),
|
|
3167
4405
|
onClick: () => void controller.move(task.id, task.version, "todo"),
|
|
3168
|
-
children: "
|
|
4406
|
+
children: t("detail.release.button")
|
|
3169
4407
|
})
|
|
3170
4408
|
]
|
|
3171
4409
|
})
|
|
@@ -3173,13 +4411,13 @@ window.__ModuleLoader__.load({
|
|
|
3173
4411
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3174
4412
|
className: "dsh-atb-section",
|
|
3175
4413
|
children: [
|
|
3176
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h4", { children: ["
|
|
4414
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h4", { children: [t("detail.comments.title"), task.comments.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3177
4415
|
className: "dsh-atb-count2",
|
|
3178
4416
|
children: task.comments.length
|
|
3179
4417
|
})] }),
|
|
3180
4418
|
task.comments.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3181
4419
|
className: "dsh-atb-empty2",
|
|
3182
|
-
children: "
|
|
4420
|
+
children: t("detail.comments.empty")
|
|
3183
4421
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3184
4422
|
className: "dsh-atb-commentlist",
|
|
3185
4423
|
children: task.comments.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -3192,7 +4430,7 @@ window.__ModuleLoader__.load({
|
|
|
3192
4430
|
className: "dsh-atb-bubble-main",
|
|
3193
4431
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3194
4432
|
className: "dsh-atb-bubble-meta",
|
|
3195
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: c.threadId !== void 0 ? `agent ${shortId(c.threadId)}` : "
|
|
4433
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: c.threadId !== void 0 ? `agent ${shortId(c.threadId)}` : t("detail.comments.user") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: fmtTime(c.createdAt) })]
|
|
3196
4434
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3197
4435
|
className: "dsh-atb-bubble-body",
|
|
3198
4436
|
children: c.body
|
|
@@ -3205,7 +4443,7 @@ window.__ModuleLoader__.load({
|
|
|
3205
4443
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
3206
4444
|
className: "dsh-atb-composer-input",
|
|
3207
4445
|
value: comment,
|
|
3208
|
-
placeholder: "
|
|
4446
|
+
placeholder: t("detail.composer.placeholder"),
|
|
3209
4447
|
onChange: (e) => setComment(e.target.value),
|
|
3210
4448
|
onKeyDown: (e) => {
|
|
3211
4449
|
if ((e.ctrlKey || e.metaKey) && e.key === "Enter" && comment.trim().length > 0) controller.comment(task.id, comment).then((ok) => {
|
|
@@ -3221,7 +4459,7 @@ window.__ModuleLoader__.load({
|
|
|
3221
4459
|
if (ok) setComment("");
|
|
3222
4460
|
});
|
|
3223
4461
|
},
|
|
3224
|
-
children: "
|
|
4462
|
+
children: t("detail.composer.send")
|
|
3225
4463
|
})]
|
|
3226
4464
|
})
|
|
3227
4465
|
]
|
|
@@ -3229,19 +4467,15 @@ window.__ModuleLoader__.load({
|
|
|
3229
4467
|
task.executions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3230
4468
|
className: "dsh-atb-section",
|
|
3231
4469
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h4", { children: [
|
|
3232
|
-
"
|
|
4470
|
+
t("detail.exec.title"),
|
|
3233
4471
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3234
4472
|
className: "dsh-atb-count2",
|
|
3235
4473
|
children: task.executions.length
|
|
3236
4474
|
}),
|
|
3237
|
-
task.executionsPruned !== void 0 && task.executionsPruned > 0 && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
4475
|
+
task.executionsPruned !== void 0 && task.executionsPruned > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3238
4476
|
className: "dsh-atb-count2",
|
|
3239
|
-
title:
|
|
3240
|
-
children:
|
|
3241
|
-
"+",
|
|
3242
|
-
task.executionsPruned,
|
|
3243
|
-
" 已清理"
|
|
3244
|
-
]
|
|
4477
|
+
title: t("detail.exec.prunedTitle", { n: task.executionsPruned }),
|
|
4478
|
+
children: t("detail.exec.pruned", { n: task.executionsPruned })
|
|
3245
4479
|
})
|
|
3246
4480
|
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3247
4481
|
className: "dsh-atb-execlist",
|
|
@@ -3254,12 +4488,12 @@ window.__ModuleLoader__.load({
|
|
|
3254
4488
|
}),
|
|
3255
4489
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3256
4490
|
className: "dsh-atb-exec-trigger",
|
|
3257
|
-
children: e.trigger === "manual" ? "
|
|
4491
|
+
children: e.trigger === "manual" ? t("detail.exec.trigger.manual") : t("detail.exec.trigger.scheduled")
|
|
3258
4492
|
}),
|
|
3259
4493
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3260
4494
|
className: "dsh-atb-exec-outcome",
|
|
3261
4495
|
"data-outcome": e.outcome,
|
|
3262
|
-
children:
|
|
4496
|
+
children: t(OUTCOME_KEYS[e.outcome] ?? e.outcome)
|
|
3263
4497
|
}),
|
|
3264
4498
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3265
4499
|
className: "dsh-atb-exec-time",
|
|
@@ -3268,7 +4502,7 @@ window.__ModuleLoader__.load({
|
|
|
3268
4502
|
e.sessionId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3269
4503
|
type: "button",
|
|
3270
4504
|
className: "dsh-atb-exec-session",
|
|
3271
|
-
title:
|
|
4505
|
+
title: t("detail.exec.openTitle", { id: e.sessionId }),
|
|
3272
4506
|
onClick: () => jumpToSession(e.sessionId),
|
|
3273
4507
|
children: [
|
|
3274
4508
|
"🤖 ",
|
|
@@ -3292,13 +4526,13 @@ window.__ModuleLoader__.load({
|
|
|
3292
4526
|
className: "dsh-atb-btn",
|
|
3293
4527
|
"data-danger": "true",
|
|
3294
4528
|
onClick: () => void controller.remove(task.id, task.version, false),
|
|
3295
|
-
children: "
|
|
4529
|
+
children: t("detail.danger.delete")
|
|
3296
4530
|
}) : confirmPurge ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3297
4531
|
className: "dsh-atb-confirm",
|
|
3298
4532
|
children: [
|
|
3299
4533
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3300
4534
|
className: "dsh-atb-confirm-label",
|
|
3301
|
-
children: "
|
|
4535
|
+
children: t("detail.danger.purgeConfirm")
|
|
3302
4536
|
}),
|
|
3303
4537
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3304
4538
|
type: "button",
|
|
@@ -3308,13 +4542,13 @@ window.__ModuleLoader__.load({
|
|
|
3308
4542
|
controller.remove(task.id, task.version, true);
|
|
3309
4543
|
setConfirmPurge(false);
|
|
3310
4544
|
},
|
|
3311
|
-
children: "
|
|
4545
|
+
children: t("detail.danger.purgeGo")
|
|
3312
4546
|
}),
|
|
3313
4547
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3314
4548
|
type: "button",
|
|
3315
4549
|
className: "dsh-atb-btn",
|
|
3316
4550
|
onClick: () => setConfirmPurge(false),
|
|
3317
|
-
children: "
|
|
4551
|
+
children: t("shared.cancel")
|
|
3318
4552
|
})
|
|
3319
4553
|
]
|
|
3320
4554
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -3322,7 +4556,7 @@ window.__ModuleLoader__.load({
|
|
|
3322
4556
|
className: "dsh-atb-btn",
|
|
3323
4557
|
"data-danger": "true",
|
|
3324
4558
|
onClick: () => setConfirmPurge(true),
|
|
3325
|
-
children: "
|
|
4559
|
+
children: t("detail.danger.purge")
|
|
3326
4560
|
})
|
|
3327
4561
|
}),
|
|
3328
4562
|
alertEl
|
|
@@ -3330,6 +4564,384 @@ window.__ModuleLoader__.load({
|
|
|
3330
4564
|
});
|
|
3331
4565
|
}
|
|
3332
4566
|
|
|
4567
|
+
//#endregion
|
|
4568
|
+
//#region src/client/board/SlashPromptInput.tsx
|
|
4569
|
+
/**
|
|
4570
|
+
* SlashPromptInput: Rich text input component for task description & execution prompt.
|
|
4571
|
+
* Features:
|
|
4572
|
+
* - Slash autocomplete popup for commands and skills with keyboard navigation.
|
|
4573
|
+
* - Clean text editing without image base64 pollution.
|
|
4574
|
+
*
|
|
4575
|
+
* @module dsh-taskboard/client/board/SlashPromptInput
|
|
4576
|
+
*/
|
|
4577
|
+
/** Default built-in slash commands (descriptions resolve through t at render,
|
|
4578
|
+
* so they follow the GUI language live; host-provided items override by name). */
|
|
4579
|
+
const defaultCommands = (t) => [
|
|
4580
|
+
{
|
|
4581
|
+
name: "goal",
|
|
4582
|
+
kind: "command",
|
|
4583
|
+
description: t("slash.cmd.goal.desc"),
|
|
4584
|
+
hint: t("slash.cmd.goal.hint")
|
|
4585
|
+
},
|
|
4586
|
+
{
|
|
4587
|
+
name: "schedule",
|
|
4588
|
+
kind: "command",
|
|
4589
|
+
description: t("slash.cmd.schedule.desc"),
|
|
4590
|
+
hint: t("slash.cmd.schedule.hint")
|
|
4591
|
+
},
|
|
4592
|
+
{
|
|
4593
|
+
name: "plan",
|
|
4594
|
+
kind: "command",
|
|
4595
|
+
description: t("slash.cmd.plan.desc")
|
|
4596
|
+
},
|
|
4597
|
+
{
|
|
4598
|
+
name: "browser",
|
|
4599
|
+
kind: "command",
|
|
4600
|
+
description: t("slash.cmd.browser.desc")
|
|
4601
|
+
},
|
|
4602
|
+
{
|
|
4603
|
+
name: "grill-me",
|
|
4604
|
+
kind: "command",
|
|
4605
|
+
description: t("slash.cmd.grill-me.desc")
|
|
4606
|
+
},
|
|
4607
|
+
{
|
|
4608
|
+
name: "teamwork-preview",
|
|
4609
|
+
kind: "command",
|
|
4610
|
+
description: t("slash.cmd.teamwork-preview.desc")
|
|
4611
|
+
},
|
|
4612
|
+
{
|
|
4613
|
+
name: "learn",
|
|
4614
|
+
kind: "command",
|
|
4615
|
+
description: t("slash.cmd.learn.desc")
|
|
4616
|
+
},
|
|
4617
|
+
{
|
|
4618
|
+
name: "review",
|
|
4619
|
+
kind: "command",
|
|
4620
|
+
description: t("slash.cmd.review.desc")
|
|
4621
|
+
},
|
|
4622
|
+
{
|
|
4623
|
+
name: "security",
|
|
4624
|
+
kind: "command",
|
|
4625
|
+
description: t("slash.cmd.security.desc")
|
|
4626
|
+
},
|
|
4627
|
+
{
|
|
4628
|
+
name: "permission",
|
|
4629
|
+
kind: "command",
|
|
4630
|
+
description: t("slash.cmd.permission.desc"),
|
|
4631
|
+
hint: t("slash.cmd.permission.hint")
|
|
4632
|
+
}
|
|
4633
|
+
];
|
|
4634
|
+
/** Default built-in skills (descriptions resolve through t at render). */
|
|
4635
|
+
const defaultSkills = (t) => [
|
|
4636
|
+
{
|
|
4637
|
+
name: "frontend-ui-engineering",
|
|
4638
|
+
kind: "skill",
|
|
4639
|
+
description: t("slash.skill.frontend-ui-engineering")
|
|
4640
|
+
},
|
|
4641
|
+
{
|
|
4642
|
+
name: "api-and-interface-design",
|
|
4643
|
+
kind: "skill",
|
|
4644
|
+
description: t("slash.skill.api-and-interface-design")
|
|
4645
|
+
},
|
|
4646
|
+
{
|
|
4647
|
+
name: "test-driven-development",
|
|
4648
|
+
kind: "skill",
|
|
4649
|
+
description: t("slash.skill.test-driven-development")
|
|
4650
|
+
},
|
|
4651
|
+
{
|
|
4652
|
+
name: "debugging-and-error-recovery",
|
|
4653
|
+
kind: "skill",
|
|
4654
|
+
description: t("slash.skill.debugging-and-error-recovery")
|
|
4655
|
+
},
|
|
4656
|
+
{
|
|
4657
|
+
name: "performance-optimization",
|
|
4658
|
+
kind: "skill",
|
|
4659
|
+
description: t("slash.skill.performance-optimization")
|
|
4660
|
+
},
|
|
4661
|
+
{
|
|
4662
|
+
name: "ci-cd-and-automation",
|
|
4663
|
+
kind: "skill",
|
|
4664
|
+
description: t("slash.skill.ci-cd-and-automation")
|
|
4665
|
+
},
|
|
4666
|
+
{
|
|
4667
|
+
name: "code-review-and-quality",
|
|
4668
|
+
kind: "skill",
|
|
4669
|
+
description: t("slash.skill.code-review-and-quality")
|
|
4670
|
+
},
|
|
4671
|
+
{
|
|
4672
|
+
name: "code-simplification",
|
|
4673
|
+
kind: "skill",
|
|
4674
|
+
description: t("slash.skill.code-simplification")
|
|
4675
|
+
},
|
|
4676
|
+
{
|
|
4677
|
+
name: "context-engineering",
|
|
4678
|
+
kind: "skill",
|
|
4679
|
+
description: t("slash.skill.context-engineering")
|
|
4680
|
+
},
|
|
4681
|
+
{
|
|
4682
|
+
name: "doubt-driven-development",
|
|
4683
|
+
kind: "skill",
|
|
4684
|
+
description: t("slash.skill.doubt-driven-development")
|
|
4685
|
+
},
|
|
4686
|
+
{
|
|
4687
|
+
name: "git-workflow-and-versioning",
|
|
4688
|
+
kind: "skill",
|
|
4689
|
+
description: t("slash.skill.git-workflow-and-versioning")
|
|
4690
|
+
},
|
|
4691
|
+
{
|
|
4692
|
+
name: "idea-refine",
|
|
4693
|
+
kind: "skill",
|
|
4694
|
+
description: t("slash.skill.idea-refine")
|
|
4695
|
+
},
|
|
4696
|
+
{
|
|
4697
|
+
name: "incremental-implementation",
|
|
4698
|
+
kind: "skill",
|
|
4699
|
+
description: t("slash.skill.incremental-implementation")
|
|
4700
|
+
},
|
|
4701
|
+
{
|
|
4702
|
+
name: "interview-me",
|
|
4703
|
+
kind: "skill",
|
|
4704
|
+
description: t("slash.skill.interview-me")
|
|
4705
|
+
},
|
|
4706
|
+
{
|
|
4707
|
+
name: "memory-leak-debugging",
|
|
4708
|
+
kind: "skill",
|
|
4709
|
+
description: t("slash.skill.memory-leak-debugging")
|
|
4710
|
+
},
|
|
4711
|
+
{
|
|
4712
|
+
name: "observability-and-instrumentation",
|
|
4713
|
+
kind: "skill",
|
|
4714
|
+
description: t("slash.skill.observability-and-instrumentation")
|
|
4715
|
+
},
|
|
4716
|
+
{
|
|
4717
|
+
name: "planning-and-task-breakdown",
|
|
4718
|
+
kind: "skill",
|
|
4719
|
+
description: t("slash.skill.planning-and-task-breakdown")
|
|
4720
|
+
},
|
|
4721
|
+
{
|
|
4722
|
+
name: "security-and-hardening",
|
|
4723
|
+
kind: "skill",
|
|
4724
|
+
description: t("slash.skill.security-and-hardening")
|
|
4725
|
+
},
|
|
4726
|
+
{
|
|
4727
|
+
name: "shipping-and-launch",
|
|
4728
|
+
kind: "skill",
|
|
4729
|
+
description: t("slash.skill.shipping-and-launch")
|
|
4730
|
+
},
|
|
4731
|
+
{
|
|
4732
|
+
name: "source-driven-development",
|
|
4733
|
+
kind: "skill",
|
|
4734
|
+
description: t("slash.skill.source-driven-development")
|
|
4735
|
+
},
|
|
4736
|
+
{
|
|
4737
|
+
name: "spec-driven-development",
|
|
4738
|
+
kind: "skill",
|
|
4739
|
+
description: t("slash.skill.spec-driven-development")
|
|
4740
|
+
},
|
|
4741
|
+
{
|
|
4742
|
+
name: "using-agent-skills",
|
|
4743
|
+
kind: "skill",
|
|
4744
|
+
description: t("slash.skill.using-agent-skills")
|
|
4745
|
+
}
|
|
4746
|
+
];
|
|
4747
|
+
/**
|
|
4748
|
+
* Rich prompt textarea with / autocomplete for slash commands & skills.
|
|
4749
|
+
*/
|
|
4750
|
+
function SlashPromptInput({ value, onChange, controller, placeholder, rows = 4, maxLength = 8e3, disabled = false, autoFocus = false, className, ariaLabel }) {
|
|
4751
|
+
const t = useT();
|
|
4752
|
+
const textareaRef = (0, react.useRef)(null);
|
|
4753
|
+
const [hostCompletions, setHostCompletions] = (0, react.useState)(void 0);
|
|
4754
|
+
const completions = (0, react.useMemo)(() => {
|
|
4755
|
+
const merge = (defaults, host) => {
|
|
4756
|
+
const map = /* @__PURE__ */ new Map();
|
|
4757
|
+
for (const d of defaults) map.set(d.name, d);
|
|
4758
|
+
for (const h of host ?? []) map.set(h.name, h);
|
|
4759
|
+
return Array.from(map.values());
|
|
4760
|
+
};
|
|
4761
|
+
return {
|
|
4762
|
+
commands: merge(defaultCommands(t), hostCompletions?.commands),
|
|
4763
|
+
skills: merge(defaultSkills(t), hostCompletions?.skills)
|
|
4764
|
+
};
|
|
4765
|
+
}, [t, hostCompletions]);
|
|
4766
|
+
const [popupOpen, setPopupOpen] = (0, react.useState)(false);
|
|
4767
|
+
const [slashQuery, setSlashQuery] = (0, react.useState)("");
|
|
4768
|
+
const [slashStart, setSlashStart] = (0, react.useState)(-1);
|
|
4769
|
+
const [selectedIndex, setSelectedIndex] = (0, react.useState)(0);
|
|
4770
|
+
(0, react.useEffect)(() => {
|
|
4771
|
+
if (controller === void 0) return;
|
|
4772
|
+
let alive = true;
|
|
4773
|
+
controller.fetchPromptCompletions().then((res) => {
|
|
4774
|
+
if (!alive || res === void 0) return;
|
|
4775
|
+
setHostCompletions({
|
|
4776
|
+
commands: res.commands.map((c) => ({
|
|
4777
|
+
...c,
|
|
4778
|
+
kind: "command"
|
|
4779
|
+
})),
|
|
4780
|
+
skills: res.skills.map((s) => ({
|
|
4781
|
+
...s,
|
|
4782
|
+
kind: "skill"
|
|
4783
|
+
}))
|
|
4784
|
+
});
|
|
4785
|
+
});
|
|
4786
|
+
return () => {
|
|
4787
|
+
alive = false;
|
|
4788
|
+
};
|
|
4789
|
+
}, [controller]);
|
|
4790
|
+
const filteredItems = (0, react.useMemo)(() => {
|
|
4791
|
+
const q = slashQuery.toLowerCase().trim();
|
|
4792
|
+
const all = [...completions.commands, ...completions.skills];
|
|
4793
|
+
if (q.length === 0) return all;
|
|
4794
|
+
return all.filter((item) => item.name.toLowerCase().includes(q) || item.description !== void 0 && item.description.toLowerCase().includes(q));
|
|
4795
|
+
}, [completions, slashQuery]);
|
|
4796
|
+
(0, react.useEffect)(() => {
|
|
4797
|
+
if (selectedIndex >= filteredItems.length) setSelectedIndex(Math.max(0, filteredItems.length - 1));
|
|
4798
|
+
}, [filteredItems.length, selectedIndex]);
|
|
4799
|
+
const checkSlashTrigger = () => {
|
|
4800
|
+
const el = textareaRef.current;
|
|
4801
|
+
if (el === null) return;
|
|
4802
|
+
const pos = el.selectionStart;
|
|
4803
|
+
const currentText = el.value.slice(0, pos);
|
|
4804
|
+
const lastSlash = currentText.lastIndexOf("/");
|
|
4805
|
+
if (lastSlash >= 0) {
|
|
4806
|
+
const charBefore = lastSlash > 0 ? currentText[lastSlash - 1] ?? "\n" : "\n";
|
|
4807
|
+
const isWordStart = /\s/.test(charBefore) || lastSlash === 0;
|
|
4808
|
+
const queryPart = currentText.slice(lastSlash + 1);
|
|
4809
|
+
const noWhitespaceInQuery = !/\s/.test(queryPart);
|
|
4810
|
+
if (isWordStart && noWhitespaceInQuery) {
|
|
4811
|
+
setSlashStart(lastSlash);
|
|
4812
|
+
setSlashQuery(queryPart);
|
|
4813
|
+
setPopupOpen(true);
|
|
4814
|
+
return;
|
|
4815
|
+
}
|
|
4816
|
+
}
|
|
4817
|
+
setPopupOpen(false);
|
|
4818
|
+
};
|
|
4819
|
+
const applyCompletion = (item) => {
|
|
4820
|
+
const el = textareaRef.current;
|
|
4821
|
+
if (el === null || slashStart < 0) return;
|
|
4822
|
+
const pos = el.selectionStart;
|
|
4823
|
+
const before = value.slice(0, slashStart);
|
|
4824
|
+
const after = value.slice(pos);
|
|
4825
|
+
const inserted = `/${item.name} `;
|
|
4826
|
+
onChange(before + inserted + after);
|
|
4827
|
+
setPopupOpen(false);
|
|
4828
|
+
setTimeout(() => {
|
|
4829
|
+
if (textareaRef.current !== null) {
|
|
4830
|
+
const nextPos = slashStart + inserted.length;
|
|
4831
|
+
textareaRef.current.focus();
|
|
4832
|
+
textareaRef.current.setSelectionRange(nextPos, nextPos);
|
|
4833
|
+
}
|
|
4834
|
+
}, 0);
|
|
4835
|
+
};
|
|
4836
|
+
const handleKeyDown = (e) => {
|
|
4837
|
+
if (popupOpen && filteredItems.length > 0) {
|
|
4838
|
+
if (e.key === "ArrowDown") {
|
|
4839
|
+
e.preventDefault();
|
|
4840
|
+
setSelectedIndex((prev) => (prev + 1) % filteredItems.length);
|
|
4841
|
+
return;
|
|
4842
|
+
}
|
|
4843
|
+
if (e.key === "ArrowUp") {
|
|
4844
|
+
e.preventDefault();
|
|
4845
|
+
setSelectedIndex((prev) => (prev - 1 + filteredItems.length) % filteredItems.length);
|
|
4846
|
+
return;
|
|
4847
|
+
}
|
|
4848
|
+
if (e.key === "Enter" || e.key === "Tab") {
|
|
4849
|
+
const picked = filteredItems[selectedIndex];
|
|
4850
|
+
if (picked !== void 0) {
|
|
4851
|
+
e.preventDefault();
|
|
4852
|
+
applyCompletion(picked);
|
|
4853
|
+
return;
|
|
4854
|
+
}
|
|
4855
|
+
}
|
|
4856
|
+
if (e.key === "Escape") {
|
|
4857
|
+
e.preventDefault();
|
|
4858
|
+
setPopupOpen(false);
|
|
4859
|
+
return;
|
|
4860
|
+
}
|
|
4861
|
+
}
|
|
4862
|
+
};
|
|
4863
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4864
|
+
className: `dsh-atb-prompt-wrap ${className ?? ""}`,
|
|
4865
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4866
|
+
className: "dsh-atb-prompt-inner",
|
|
4867
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
4868
|
+
ref: textareaRef,
|
|
4869
|
+
className: "dsh-atb-prompt-input",
|
|
4870
|
+
value,
|
|
4871
|
+
rows,
|
|
4872
|
+
maxLength,
|
|
4873
|
+
disabled,
|
|
4874
|
+
autoFocus,
|
|
4875
|
+
placeholder,
|
|
4876
|
+
"aria-label": ariaLabel,
|
|
4877
|
+
onChange: (e) => {
|
|
4878
|
+
onChange(e.target.value);
|
|
4879
|
+
checkSlashTrigger();
|
|
4880
|
+
},
|
|
4881
|
+
onKeyUp: checkSlashTrigger,
|
|
4882
|
+
onClick: checkSlashTrigger,
|
|
4883
|
+
onKeyDown: handleKeyDown
|
|
4884
|
+
}), popupOpen && filteredItems.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4885
|
+
className: "dsh-atb-slash-popup",
|
|
4886
|
+
role: "listbox",
|
|
4887
|
+
"aria-label": t("slash.aria"),
|
|
4888
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4889
|
+
className: "dsh-atb-slash-head",
|
|
4890
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4891
|
+
className: "dsh-atb-slash-title",
|
|
4892
|
+
children: t("slash.title")
|
|
4893
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4894
|
+
className: "dsh-atb-slash-hint",
|
|
4895
|
+
children: t("slash.hint")
|
|
4896
|
+
})]
|
|
4897
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4898
|
+
className: "dsh-atb-slash-list",
|
|
4899
|
+
children: filteredItems.map((item, idx) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4900
|
+
role: "option",
|
|
4901
|
+
"aria-selected": idx === selectedIndex,
|
|
4902
|
+
className: "dsh-atb-slash-item",
|
|
4903
|
+
"data-active": idx === selectedIndex ? "true" : void 0,
|
|
4904
|
+
"data-kind": item.kind,
|
|
4905
|
+
onClick: () => applyCompletion(item),
|
|
4906
|
+
onMouseEnter: () => setSelectedIndex(idx),
|
|
4907
|
+
children: [
|
|
4908
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4909
|
+
className: "dsh-atb-slash-badge",
|
|
4910
|
+
"data-kind": item.kind,
|
|
4911
|
+
children: item.kind === "command" ? t("slash.badge.command") : t("slash.badge.skill")
|
|
4912
|
+
}),
|
|
4913
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4914
|
+
className: "dsh-atb-slash-name",
|
|
4915
|
+
children: ["/", item.name]
|
|
4916
|
+
}),
|
|
4917
|
+
item.hint && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4918
|
+
className: "dsh-atb-slash-param",
|
|
4919
|
+
children: item.hint
|
|
4920
|
+
}),
|
|
4921
|
+
item.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4922
|
+
className: "dsh-atb-slash-desc",
|
|
4923
|
+
children: item.description
|
|
4924
|
+
})
|
|
4925
|
+
]
|
|
4926
|
+
}, `${item.kind}-${item.name}`))
|
|
4927
|
+
})]
|
|
4928
|
+
})]
|
|
4929
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4930
|
+
className: "dsh-atb-prompt-foot",
|
|
4931
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4932
|
+
className: "dsh-atb-prompt-tip",
|
|
4933
|
+
children: [
|
|
4934
|
+
t("slash.tipA"),
|
|
4935
|
+
" ",
|
|
4936
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: "/" }),
|
|
4937
|
+
" ",
|
|
4938
|
+
t("slash.tipB")
|
|
4939
|
+
]
|
|
4940
|
+
})
|
|
4941
|
+
})]
|
|
4942
|
+
});
|
|
4943
|
+
}
|
|
4944
|
+
|
|
3333
4945
|
//#endregion
|
|
3334
4946
|
//#region src/client/board/TaskFormModal.tsx
|
|
3335
4947
|
/**
|
|
@@ -3370,43 +4982,64 @@ window.__ModuleLoader__.load({
|
|
|
3370
4982
|
else localStorage.setItem(LAST_MODEL_KEY, JSON.stringify(model));
|
|
3371
4983
|
} catch {}
|
|
3372
4984
|
}
|
|
3373
|
-
/** Urgency segmented options with a one-line hint each. */
|
|
3374
|
-
const
|
|
4985
|
+
/** Urgency segmented options with a one-line hint each (translated per render). */
|
|
4986
|
+
const urgencyOptions = (t) => [
|
|
3375
4987
|
{
|
|
3376
4988
|
value: "urgent",
|
|
3377
|
-
label: "
|
|
3378
|
-
hint: "
|
|
4989
|
+
label: t("form.urgency.urgent"),
|
|
4990
|
+
hint: t("form.urgency.urgentHint")
|
|
3379
4991
|
},
|
|
3380
4992
|
{
|
|
3381
4993
|
value: "normal",
|
|
3382
|
-
label: "
|
|
3383
|
-
hint: "
|
|
4994
|
+
label: t("form.urgency.normal"),
|
|
4995
|
+
hint: t("form.urgency.normalHint")
|
|
3384
4996
|
},
|
|
3385
4997
|
{
|
|
3386
4998
|
value: "relaxed",
|
|
3387
|
-
label: "
|
|
3388
|
-
hint: "
|
|
4999
|
+
label: t("form.urgency.relaxed"),
|
|
5000
|
+
hint: t("form.urgency.relaxedHint")
|
|
3389
5001
|
}
|
|
3390
5002
|
];
|
|
3391
|
-
/** Cron presets offered in the scheduled mode. */
|
|
3392
|
-
const
|
|
5003
|
+
/** Cron presets offered in the scheduled mode (translated per render). */
|
|
5004
|
+
const cronPresets = (t) => [
|
|
3393
5005
|
{
|
|
3394
|
-
label: "
|
|
5006
|
+
label: t("form.cron.daily"),
|
|
3395
5007
|
cron: "0 9 * * *"
|
|
3396
5008
|
},
|
|
3397
5009
|
{
|
|
3398
|
-
label: "
|
|
5010
|
+
label: t("form.cron.hourly"),
|
|
3399
5011
|
cron: "0 * * * *"
|
|
3400
5012
|
},
|
|
3401
5013
|
{
|
|
3402
|
-
label: "
|
|
5014
|
+
label: t("form.cron.every10min"),
|
|
3403
5015
|
cron: "*/10 * * * *"
|
|
3404
5016
|
},
|
|
3405
5017
|
{
|
|
3406
|
-
label: "
|
|
5018
|
+
label: t("form.cron.weekly"),
|
|
3407
5019
|
cron: "0 9 * * 1"
|
|
3408
5020
|
}
|
|
3409
5021
|
];
|
|
5022
|
+
/** Permission presets aligned with DSH (translated per render). */
|
|
5023
|
+
const permissionOptions = (t) => [
|
|
5024
|
+
{
|
|
5025
|
+
value: "workspace-write",
|
|
5026
|
+
label: t("form.perm.write"),
|
|
5027
|
+
hint: t("form.perm.writeHint"),
|
|
5028
|
+
icon: "📁"
|
|
5029
|
+
},
|
|
5030
|
+
{
|
|
5031
|
+
value: "read-only",
|
|
5032
|
+
label: t("form.perm.readOnly"),
|
|
5033
|
+
hint: t("form.perm.readOnlyHint"),
|
|
5034
|
+
icon: "🔒"
|
|
5035
|
+
},
|
|
5036
|
+
{
|
|
5037
|
+
value: "danger-full-access",
|
|
5038
|
+
label: t("form.perm.fullAccess"),
|
|
5039
|
+
hint: t("form.perm.fullAccessHint"),
|
|
5040
|
+
icon: "⚡"
|
|
5041
|
+
}
|
|
5042
|
+
];
|
|
3410
5043
|
/** Field shell: label + control, optionally spanning the full grid row. */
|
|
3411
5044
|
function Field({ label, required = false, full = false, children }) {
|
|
3412
5045
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
@@ -3427,6 +5060,7 @@ window.__ModuleLoader__.load({
|
|
|
3427
5060
|
* replaces the whole list on save).
|
|
3428
5061
|
*/
|
|
3429
5062
|
function ChecklistEditor({ rows, onChange, editing }) {
|
|
5063
|
+
const t = useT();
|
|
3430
5064
|
const setRow = (index, patch) => {
|
|
3431
5065
|
onChange(rows.map((row, i) => i === index ? {
|
|
3432
5066
|
...row,
|
|
@@ -3444,21 +5078,21 @@ window.__ModuleLoader__.load({
|
|
|
3444
5078
|
type: "checkbox",
|
|
3445
5079
|
className: "dsh-atb-cke-box",
|
|
3446
5080
|
checked: row.checked,
|
|
3447
|
-
title:
|
|
5081
|
+
title: t("form.check.checkedTitle", { who: row.checkedBy ?? t("form.check.notCheckedYet") }),
|
|
3448
5082
|
onChange: (e) => setRow(index, { checked: e.target.checked })
|
|
3449
5083
|
}),
|
|
3450
5084
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3451
5085
|
className: "dsh-atb-cke-text",
|
|
3452
5086
|
value: row.text,
|
|
3453
5087
|
maxLength: 200,
|
|
3454
|
-
placeholder:
|
|
5088
|
+
placeholder: t("form.check.itemPlaceholder", { n: index + 1 }),
|
|
3455
5089
|
spellCheck: false,
|
|
3456
5090
|
onChange: (e) => setRow(index, { text: e.target.value })
|
|
3457
5091
|
}),
|
|
3458
5092
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3459
5093
|
type: "button",
|
|
3460
5094
|
className: "dsh-atb-cke-del",
|
|
3461
|
-
title: "
|
|
5095
|
+
title: t("form.check.removeTitle"),
|
|
3462
5096
|
onClick: () => onChange(rows.filter((_, i) => i !== index)),
|
|
3463
5097
|
children: "✕"
|
|
3464
5098
|
})
|
|
@@ -3471,11 +5105,14 @@ window.__ModuleLoader__.load({
|
|
|
3471
5105
|
text: "",
|
|
3472
5106
|
checked: false
|
|
3473
5107
|
}]),
|
|
3474
|
-
children: "
|
|
5108
|
+
children: t("form.check.add")
|
|
3475
5109
|
}),
|
|
3476
5110
|
rows.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3477
5111
|
className: "dsh-atb-cke-hint",
|
|
3478
|
-
children: editing ?
|
|
5112
|
+
children: editing ? t("form.check.hintEdit", {
|
|
5113
|
+
checked,
|
|
5114
|
+
total: rows.length
|
|
5115
|
+
}) : t("form.check.hintCreate", { n: rows.length })
|
|
3479
5116
|
})
|
|
3480
5117
|
]
|
|
3481
5118
|
});
|
|
@@ -3489,6 +5126,7 @@ window.__ModuleLoader__.load({
|
|
|
3489
5126
|
* @param task - the task being edited (create mode when absent).
|
|
3490
5127
|
*/
|
|
3491
5128
|
function TaskFormModal({ controller, task }) {
|
|
5129
|
+
const t = useT();
|
|
3492
5130
|
const state = controller.getSnapshot();
|
|
3493
5131
|
const prefill = state.templatePrefill;
|
|
3494
5132
|
const editing = task !== void 0;
|
|
@@ -3510,6 +5148,7 @@ window.__ModuleLoader__.load({
|
|
|
3510
5148
|
const [presetId, setPresetId] = (0, react.useState)(initialPreset);
|
|
3511
5149
|
const [presets, setPresets] = (0, react.useState)([]);
|
|
3512
5150
|
const [presetDefault, setPresetDefault] = (0, react.useState)(void 0);
|
|
5151
|
+
const [permission, setPermission] = (0, react.useState)(task?.permission ?? (prefill?.permission ? asPermission(prefill.permission) : defaultPermissionOf(state.ledger.settings)));
|
|
3513
5152
|
const [isolation, setIsolation] = (0, react.useState)(task?.isolation ?? (prefill?.isolation === "none" ? "none" : prefill?.isolation === "worktree" ? "worktree" : defaultIsolationOf(state.ledger.settings)));
|
|
3514
5153
|
const [checkRows, setCheckRows] = (0, react.useState)(task?.checklist !== void 0 && task.checklist.length > 0 ? task.checklist.map((i) => ({ ...i })) : (prefill?.checklist ?? []).map((text) => ({
|
|
3515
5154
|
text,
|
|
@@ -3526,14 +5165,10 @@ window.__ModuleLoader__.load({
|
|
|
3526
5165
|
return () => document.removeEventListener("keydown", onKey);
|
|
3527
5166
|
}, [controller]);
|
|
3528
5167
|
(0, react.useEffect)(() => {
|
|
3529
|
-
|
|
3530
|
-
if (face === void 0) return;
|
|
3531
|
-
face().then(setCatalog).catch(() => setCatalog([]));
|
|
5168
|
+
controller.fetchModelCatalog().then(setCatalog).catch(() => setCatalog([]));
|
|
3532
5169
|
}, [controller]);
|
|
3533
5170
|
(0, react.useEffect)(() => {
|
|
3534
|
-
|
|
3535
|
-
if (face === void 0) return;
|
|
3536
|
-
face().then((roster) => {
|
|
5171
|
+
controller.fetchPresetCatalog().then((roster) => {
|
|
3537
5172
|
setPresets(roster.presets);
|
|
3538
5173
|
setPresetDefault(roster.defaultId);
|
|
3539
5174
|
if (!editing && initialPreset === "" && roster.defaultId !== void 0) setPresetId(roster.defaultId);
|
|
@@ -3599,6 +5234,7 @@ window.__ModuleLoader__.load({
|
|
|
3599
5234
|
model: picked ?? null,
|
|
3600
5235
|
...isolationOut !== void 0 && !isolationLocked ? { isolation: isolationOut } : {},
|
|
3601
5236
|
presetId: presetOut ?? null,
|
|
5237
|
+
permission,
|
|
3602
5238
|
checklist: rows.length > 0 ? rows : null
|
|
3603
5239
|
}) : controller.create({
|
|
3604
5240
|
title,
|
|
@@ -3613,6 +5249,7 @@ window.__ModuleLoader__.load({
|
|
|
3613
5249
|
model: picked,
|
|
3614
5250
|
...isolationOut !== void 0 ? { isolation: isolationOut } : {},
|
|
3615
5251
|
...presetOut !== void 0 ? { presetId: presetOut } : {},
|
|
5252
|
+
permission,
|
|
3616
5253
|
...rows.length > 0 ? { checklist: rows.map((r) => r.text) } : {}
|
|
3617
5254
|
})).catch(() => void 0).finally(() => setBusy(false));
|
|
3618
5255
|
};
|
|
@@ -3640,6 +5277,7 @@ window.__ModuleLoader__.load({
|
|
|
3640
5277
|
model: picked ?? null,
|
|
3641
5278
|
...isolationOut !== void 0 && !isolationLocked ? { isolation: isolationOut } : {},
|
|
3642
5279
|
presetId: presetOut ?? null,
|
|
5280
|
+
permission,
|
|
3643
5281
|
checklist: rows.length > 0 ? rows : null
|
|
3644
5282
|
})) await controller.run(task.id);
|
|
3645
5283
|
} else {
|
|
@@ -3656,24 +5294,28 @@ window.__ModuleLoader__.load({
|
|
|
3656
5294
|
model: picked,
|
|
3657
5295
|
...isolationOut !== void 0 ? { isolation: isolationOut } : {},
|
|
3658
5296
|
...presetOut !== void 0 ? { presetId: presetOut } : {},
|
|
5297
|
+
permission,
|
|
3659
5298
|
...rows.length > 0 ? { checklist: rows.map((r) => r.text) } : {}
|
|
3660
5299
|
});
|
|
3661
5300
|
if (id !== void 0) await controller.run(id);
|
|
3662
5301
|
}
|
|
3663
5302
|
})().catch(() => void 0).finally(() => setBusy(false));
|
|
3664
5303
|
};
|
|
3665
|
-
const hint = !valid ? title.trim().length === 0 ? "
|
|
5304
|
+
const hint = !valid ? title.trim().length === 0 ? t("form.hint.needTitle") : workspaceId === "" ? t("form.hint.needProject") : t("form.hint.cronBad") : mode === "scheduled" && nextRun !== null ? t("form.hint.nextRun", { time: fmtTime(nextRun) }) : editing ? t("form.hint.saveVersion", {
|
|
5305
|
+
v: task.version,
|
|
5306
|
+
next: task.version + 1
|
|
5307
|
+
}) : t("form.hint.createClaim");
|
|
3666
5308
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3667
5309
|
className: "dsh-atb-modal-backdrop",
|
|
3668
5310
|
onClick: (e) => {
|
|
3669
5311
|
if (e.target === e.currentTarget) controller.closeForm();
|
|
3670
5312
|
},
|
|
3671
5313
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3672
|
-
className: "dsh-atb-modal",
|
|
5314
|
+
className: "dsh-atb-modal dsh-atb-taskform-modal",
|
|
3673
5315
|
"data-mode": editing ? "edit" : "create",
|
|
3674
5316
|
role: "dialog",
|
|
3675
5317
|
"aria-modal": "true",
|
|
3676
|
-
"aria-label": editing ? "
|
|
5318
|
+
"aria-label": editing ? t("form.title.edit") : t("form.title.create"),
|
|
3677
5319
|
children: [
|
|
3678
5320
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3679
5321
|
className: "dsh-atb-modal-head",
|
|
@@ -3684,271 +5326,307 @@ window.__ModuleLoader__.load({
|
|
|
3684
5326
|
}),
|
|
3685
5327
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3686
5328
|
className: "dsh-atb-modal-headtext",
|
|
3687
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: editing ? "
|
|
5329
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: editing ? t("form.title.edit") : t("form.title.create") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: editing ? t("form.subtitle.edit") : t("form.subtitle.create") })]
|
|
3688
5330
|
}),
|
|
3689
5331
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3690
5332
|
type: "button",
|
|
3691
5333
|
className: "dsh-atb-modal-close",
|
|
3692
|
-
"aria-label": "
|
|
5334
|
+
"aria-label": t("shared.close"),
|
|
3693
5335
|
onClick: () => controller.closeForm(),
|
|
3694
5336
|
children: "✕"
|
|
3695
5337
|
})
|
|
3696
5338
|
]
|
|
3697
5339
|
}),
|
|
3698
5340
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3699
|
-
className: "dsh-atb-modal-body",
|
|
3700
|
-
children: [
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
onChange: (e) => {
|
|
3730
|
-
const val = e.target.value;
|
|
3731
|
-
setModel(val);
|
|
3732
|
-
if (val === "") setReasoningEffort("");
|
|
3733
|
-
else {
|
|
3734
|
-
const pm = JSON.parse(val);
|
|
3735
|
-
const cm = catalog.find((m) => m.provider === pm.provider && m.model === pm.model);
|
|
3736
|
-
if (cm?.reasoning?.defaultEffort !== void 0) setReasoningEffort(cm.reasoning.defaultEffort);
|
|
3737
|
-
else setReasoningEffort("");
|
|
3738
|
-
}
|
|
3739
|
-
},
|
|
3740
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3741
|
-
value: "",
|
|
3742
|
-
children: "默认模型"
|
|
3743
|
-
}), catalog.map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3744
|
-
value: JSON.stringify({
|
|
3745
|
-
provider: m.provider,
|
|
3746
|
-
model: m.model
|
|
3747
|
-
}),
|
|
3748
|
-
children: [
|
|
3749
|
-
m.name ?? m.model,
|
|
3750
|
-
"(",
|
|
3751
|
-
m.provider,
|
|
3752
|
-
")"
|
|
3753
|
-
]
|
|
3754
|
-
}, `${m.provider}/${m.model}`))]
|
|
3755
|
-
})
|
|
3756
|
-
}),
|
|
3757
|
-
parsedModel !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3758
|
-
label: "思考强度(Reasoning Effort)",
|
|
3759
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3760
|
-
value: reasoningEffort,
|
|
3761
|
-
onChange: (e) => setReasoningEffort(e.target.value),
|
|
3762
|
-
title: "设置模型的思考强度(如 low/medium/high);默认 = 跟随模型/提供商默认",
|
|
3763
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3764
|
-
value: "",
|
|
3765
|
-
children: ["跟随模型默认", modelReasoning?.defaultEffort !== void 0 ? `(当前:${modelReasoning.efforts.find((ef) => ef.id === modelReasoning.defaultEffort)?.name ?? modelReasoning.defaultEffort})` : ""]
|
|
3766
|
-
}), modelReasoning !== void 0 && modelReasoning.efforts.length > 0 ? modelReasoning.efforts.map((eff) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3767
|
-
value: eff.id,
|
|
3768
|
-
children: [eff.name, eff.description ? ` (${eff.description})` : ""]
|
|
3769
|
-
}, eff.id)) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3770
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3771
|
-
value: "low",
|
|
3772
|
-
children: "低 (low)"
|
|
5341
|
+
className: "dsh-atb-modal-body dsh-atb-taskform-body",
|
|
5342
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5343
|
+
className: "dsh-atb-form-col dsh-atb-form-left",
|
|
5344
|
+
children: [
|
|
5345
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
5346
|
+
label: t("form.field.title"),
|
|
5347
|
+
required: true,
|
|
5348
|
+
full: true,
|
|
5349
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5350
|
+
ref: titleRef,
|
|
5351
|
+
value: title,
|
|
5352
|
+
onChange: (e) => setTitle(e.target.value),
|
|
5353
|
+
placeholder: t("form.field.titlePlaceholder"),
|
|
5354
|
+
maxLength: 200
|
|
5355
|
+
})
|
|
5356
|
+
}),
|
|
5357
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5358
|
+
className: "dsh-atb-form-subgrid",
|
|
5359
|
+
children: [
|
|
5360
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
5361
|
+
label: t("form.field.project"),
|
|
5362
|
+
required: true,
|
|
5363
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
5364
|
+
value: workspaceId,
|
|
5365
|
+
onChange: (e) => setWorkspaceId(e.target.value),
|
|
5366
|
+
children: state.workspaces.map((ws) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5367
|
+
value: ws.id,
|
|
5368
|
+
children: ws.title || ws.path
|
|
5369
|
+
}, ws.id))
|
|
5370
|
+
})
|
|
3773
5371
|
}),
|
|
3774
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
3775
|
-
|
|
3776
|
-
children:
|
|
5372
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
5373
|
+
label: t("form.field.model"),
|
|
5374
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
5375
|
+
value: model,
|
|
5376
|
+
onChange: (e) => {
|
|
5377
|
+
const val = e.target.value;
|
|
5378
|
+
setModel(val);
|
|
5379
|
+
if (val === "") setReasoningEffort("");
|
|
5380
|
+
else {
|
|
5381
|
+
const pm = JSON.parse(val);
|
|
5382
|
+
const cm = catalog.find((m) => m.provider === pm.provider && m.model === pm.model);
|
|
5383
|
+
if (cm?.reasoning?.defaultEffort !== void 0) setReasoningEffort(cm.reasoning.defaultEffort);
|
|
5384
|
+
else setReasoningEffort("");
|
|
5385
|
+
}
|
|
5386
|
+
},
|
|
5387
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5388
|
+
value: "",
|
|
5389
|
+
children: t("form.field.modelDefault")
|
|
5390
|
+
}), catalog.map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5391
|
+
value: JSON.stringify({
|
|
5392
|
+
provider: m.provider,
|
|
5393
|
+
model: m.model
|
|
5394
|
+
}),
|
|
5395
|
+
children: t("form.model.option", {
|
|
5396
|
+
name: m.name ?? m.model,
|
|
5397
|
+
provider: m.provider
|
|
5398
|
+
})
|
|
5399
|
+
}, `${m.provider}/${m.model}`))]
|
|
5400
|
+
})
|
|
3777
5401
|
}),
|
|
3778
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
3779
|
-
|
|
3780
|
-
children:
|
|
5402
|
+
parsedModel !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
5403
|
+
label: t("form.field.effort"),
|
|
5404
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
5405
|
+
value: reasoningEffort,
|
|
5406
|
+
onChange: (e) => setReasoningEffort(e.target.value),
|
|
5407
|
+
title: t("form.field.effortTitle"),
|
|
5408
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
5409
|
+
value: "",
|
|
5410
|
+
children: [t("form.effort.follow"), modelReasoning?.defaultEffort !== void 0 ? t("shared.current", { name: modelReasoning.efforts.find((ef) => ef.id === modelReasoning.defaultEffort)?.name ?? modelReasoning.defaultEffort }) : ""]
|
|
5411
|
+
}), modelReasoning !== void 0 && modelReasoning.efforts.length > 0 ? modelReasoning.efforts.map((eff) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
5412
|
+
value: eff.id,
|
|
5413
|
+
children: [eff.name, eff.description ? ` (${eff.description})` : ""]
|
|
5414
|
+
}, eff.id)) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
5415
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5416
|
+
value: "low",
|
|
5417
|
+
children: t("form.effort.low")
|
|
5418
|
+
}),
|
|
5419
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5420
|
+
value: "medium",
|
|
5421
|
+
children: t("form.effort.medium")
|
|
5422
|
+
}),
|
|
5423
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5424
|
+
value: "high",
|
|
5425
|
+
children: t("form.effort.high")
|
|
5426
|
+
}),
|
|
5427
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5428
|
+
value: "none",
|
|
5429
|
+
children: t("form.effort.none")
|
|
5430
|
+
})
|
|
5431
|
+
] })]
|
|
5432
|
+
})
|
|
3781
5433
|
}),
|
|
3782
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
3783
|
-
|
|
3784
|
-
children:
|
|
5434
|
+
presets.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
5435
|
+
label: t("form.field.preset"),
|
|
5436
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
5437
|
+
value: presetId,
|
|
5438
|
+
onChange: (e) => setPresetId(e.target.value),
|
|
5439
|
+
title: t("form.field.presetTitle"),
|
|
5440
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
5441
|
+
value: "",
|
|
5442
|
+
children: [t("form.preset.follow"), presetDefault !== void 0 ? t("shared.current", { name: presets.find((p) => p.id === presetDefault)?.name ?? presetDefault }) : ""]
|
|
5443
|
+
}), presets.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
5444
|
+
value: p.id,
|
|
5445
|
+
children: [p.name ?? p.id, p.id === presetDefault ? t("form.preset.defaultTag") : ""]
|
|
5446
|
+
}, p.id))]
|
|
5447
|
+
})
|
|
3785
5448
|
})
|
|
3786
|
-
]
|
|
3787
|
-
})
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
5449
|
+
]
|
|
5450
|
+
}),
|
|
5451
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
5452
|
+
label: t("form.field.urgency"),
|
|
5453
|
+
full: true,
|
|
5454
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5455
|
+
className: "dsh-atb-urgency-picker",
|
|
5456
|
+
children: urgencyOptions(t).map((o) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5457
|
+
type: "button",
|
|
5458
|
+
className: "dsh-atb-urgency-opt",
|
|
5459
|
+
"data-urgency": o.value,
|
|
5460
|
+
"data-on": urgency === o.value,
|
|
5461
|
+
onClick: () => setUrgency(o.value),
|
|
5462
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5463
|
+
className: "dsh-atb-urgency-name",
|
|
5464
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5465
|
+
className: "dsh-atb-dot",
|
|
5466
|
+
"data-urgency": o.value
|
|
5467
|
+
}), o.label]
|
|
5468
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5469
|
+
className: "dsh-atb-urgency-hint",
|
|
5470
|
+
children: o.hint
|
|
5471
|
+
})]
|
|
5472
|
+
}, o.value))
|
|
5473
|
+
})
|
|
5474
|
+
}),
|
|
5475
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
5476
|
+
label: t("form.field.permission"),
|
|
5477
|
+
full: true,
|
|
5478
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5479
|
+
className: "dsh-atb-perm-picker",
|
|
5480
|
+
children: permissionOptions(t).map((opt) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5481
|
+
type: "button",
|
|
5482
|
+
className: "dsh-atb-perm-opt",
|
|
5483
|
+
"data-on": permission === opt.value,
|
|
5484
|
+
onClick: () => setPermission(opt.value),
|
|
5485
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5486
|
+
className: "dsh-atb-perm-name",
|
|
5487
|
+
children: [
|
|
5488
|
+
opt.icon,
|
|
5489
|
+
" ",
|
|
5490
|
+
opt.label,
|
|
5491
|
+
opt.value === "workspace-write" ? t("form.perm.defaultTag") : ""
|
|
5492
|
+
]
|
|
5493
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5494
|
+
className: "dsh-atb-perm-hint",
|
|
5495
|
+
children: opt.hint
|
|
5496
|
+
})]
|
|
5497
|
+
}, opt.value))
|
|
5498
|
+
})
|
|
5499
|
+
}),
|
|
5500
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
5501
|
+
label: t("form.field.mode"),
|
|
5502
|
+
full: true,
|
|
5503
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5504
|
+
className: "dsh-atb-mode-picker",
|
|
5505
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5506
|
+
type: "button",
|
|
5507
|
+
className: "dsh-atb-mode-opt",
|
|
5508
|
+
"data-on": mode === "claim",
|
|
5509
|
+
onClick: () => setMode("claim"),
|
|
3817
5510
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3818
|
-
className: "dsh-atb-
|
|
3819
|
-
|
|
3820
|
-
}),
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
5511
|
+
className: "dsh-atb-mode-name",
|
|
5512
|
+
children: t("form.mode.claim")
|
|
5513
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5514
|
+
className: "dsh-atb-mode-hint",
|
|
5515
|
+
children: t("form.mode.claimHint")
|
|
5516
|
+
})]
|
|
5517
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5518
|
+
type: "button",
|
|
5519
|
+
className: "dsh-atb-mode-opt",
|
|
5520
|
+
"data-on": mode === "scheduled",
|
|
5521
|
+
onClick: () => setMode("scheduled"),
|
|
5522
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5523
|
+
className: "dsh-atb-mode-name",
|
|
5524
|
+
children: t("form.mode.scheduled")
|
|
5525
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5526
|
+
className: "dsh-atb-mode-hint",
|
|
5527
|
+
children: t("form.mode.scheduledHint")
|
|
5528
|
+
})]
|
|
3824
5529
|
})]
|
|
3825
|
-
}
|
|
3826
|
-
})
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
full: true,
|
|
3849
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3850
|
-
className: "dsh-atb-mode-picker",
|
|
3851
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3852
|
-
type: "button",
|
|
3853
|
-
className: "dsh-atb-mode-opt",
|
|
3854
|
-
"data-on": mode === "claim",
|
|
3855
|
-
onClick: () => setMode("claim"),
|
|
3856
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3857
|
-
className: "dsh-atb-mode-name",
|
|
3858
|
-
children: "🤝 认领制"
|
|
3859
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3860
|
-
className: "dsh-atb-mode-hint",
|
|
3861
|
-
children: "项目内会话认领"
|
|
5530
|
+
})
|
|
5531
|
+
}),
|
|
5532
|
+
mode === "scheduled" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Field, {
|
|
5533
|
+
label: t("form.field.cron"),
|
|
5534
|
+
required: true,
|
|
5535
|
+
full: true,
|
|
5536
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5537
|
+
className: cronBad ? "dsh-atb-input-bad" : void 0,
|
|
5538
|
+
value: cron,
|
|
5539
|
+
onChange: (e) => setCron(e.target.value),
|
|
5540
|
+
placeholder: t("form.cron.placeholder"),
|
|
5541
|
+
spellCheck: false
|
|
5542
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5543
|
+
className: "dsh-atb-cron-presets",
|
|
5544
|
+
children: [cronPresets(t).map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5545
|
+
type: "button",
|
|
5546
|
+
className: "dsh-atb-cron-preset",
|
|
5547
|
+
"data-on": cron.trim() === p.cron,
|
|
5548
|
+
onClick: () => setCron(p.cron),
|
|
5549
|
+
children: p.label
|
|
5550
|
+
}, p.cron)), !cronBad && nextRun !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5551
|
+
className: "dsh-atb-cron-next",
|
|
5552
|
+
children: t("form.cron.next", { time: fmtTime(nextRun) })
|
|
3862
5553
|
})]
|
|
3863
|
-
})
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
5554
|
+
})]
|
|
5555
|
+
}),
|
|
5556
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Field, {
|
|
5557
|
+
label: t("form.field.isolation"),
|
|
5558
|
+
full: true,
|
|
5559
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5560
|
+
className: "dsh-atb-mode-picker",
|
|
5561
|
+
"data-disabled": isolationDisabled ? "true" : void 0,
|
|
5562
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5563
|
+
type: "button",
|
|
5564
|
+
className: "dsh-atb-mode-opt",
|
|
5565
|
+
"data-on": isolation === "worktree",
|
|
5566
|
+
disabled: isolationDisabled,
|
|
5567
|
+
title: isolationLocked ? t("form.iso.locked") : !gitOk ? t("form.iso.nonGit") : t("form.iso.worktreeTitle"),
|
|
5568
|
+
onClick: () => setIsolation("worktree"),
|
|
5569
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5570
|
+
className: "dsh-atb-mode-name",
|
|
5571
|
+
children: t("form.iso.worktree")
|
|
5572
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5573
|
+
className: "dsh-atb-mode-hint",
|
|
5574
|
+
children: isolationLocked ? t("form.iso.lockedShort") : !gitOk ? t("form.iso.nonGit") : t("form.iso.worktreeHint")
|
|
5575
|
+
})]
|
|
5576
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5577
|
+
type: "button",
|
|
5578
|
+
className: "dsh-atb-mode-opt",
|
|
5579
|
+
"data-on": isolation === "none",
|
|
5580
|
+
disabled: isolationDisabled,
|
|
5581
|
+
title: isolationLocked ? t("form.iso.locked") : t("form.iso.noneTitle"),
|
|
5582
|
+
onClick: () => setIsolation("none"),
|
|
5583
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5584
|
+
className: "dsh-atb-mode-name",
|
|
5585
|
+
children: t("form.iso.none")
|
|
5586
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5587
|
+
className: "dsh-atb-mode-hint",
|
|
5588
|
+
children: isolationLocked ? t("form.iso.lockedShort") : !gitOk ? t("form.iso.noneHintNonGit") : t("form.iso.noneHint")
|
|
5589
|
+
})]
|
|
3874
5590
|
})]
|
|
5591
|
+
}), !gitOk && !isolationLocked && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5592
|
+
className: "dsh-atb-isolation-note",
|
|
5593
|
+
children: t("form.iso.nonGitNote")
|
|
3875
5594
|
})]
|
|
5595
|
+
}),
|
|
5596
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
5597
|
+
label: editing ? t("form.field.checklist") : t("form.field.checklistOptional"),
|
|
5598
|
+
full: true,
|
|
5599
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChecklistEditor, {
|
|
5600
|
+
rows: checkRows,
|
|
5601
|
+
onChange: setCheckRows,
|
|
5602
|
+
editing
|
|
5603
|
+
})
|
|
3876
5604
|
})
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3883
|
-
className: cronBad ? "dsh-atb-input-bad" : void 0,
|
|
3884
|
-
value: cron,
|
|
3885
|
-
onChange: (e) => setCron(e.target.value),
|
|
3886
|
-
placeholder: "分 时 日 月 周",
|
|
3887
|
-
spellCheck: false
|
|
3888
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3889
|
-
className: "dsh-atb-cron-presets",
|
|
3890
|
-
children: [CRON_PRESETS.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3891
|
-
type: "button",
|
|
3892
|
-
className: "dsh-atb-cron-preset",
|
|
3893
|
-
"data-on": cron.trim() === p.cron,
|
|
3894
|
-
onClick: () => setCron(p.cron),
|
|
3895
|
-
children: p.label
|
|
3896
|
-
}, p.cron)), !cronBad && nextRun !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3897
|
-
className: "dsh-atb-cron-next",
|
|
3898
|
-
children: ["下次 ", fmtTime(nextRun)]
|
|
3899
|
-
})]
|
|
3900
|
-
})]
|
|
3901
|
-
}),
|
|
3902
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Field, {
|
|
3903
|
-
label: "执行隔离",
|
|
5605
|
+
]
|
|
5606
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5607
|
+
className: "dsh-atb-form-col dsh-atb-form-right",
|
|
5608
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
5609
|
+
label: editing ? t("form.field.description") : t("form.field.descriptionOptional"),
|
|
3904
5610
|
full: true,
|
|
3905
|
-
children:
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
onClick: () => setIsolation("worktree"),
|
|
3915
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3916
|
-
className: "dsh-atb-mode-name",
|
|
3917
|
-
children: "🌿 Worktree 隔离"
|
|
3918
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3919
|
-
className: "dsh-atb-mode-hint",
|
|
3920
|
-
children: isolationLocked ? "已锁定(执行开始后不可更改)" : !gitOk ? "当前项目非 git 仓库" : "独立分支 task/标题+ID,互不污染"
|
|
3921
|
-
})]
|
|
3922
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3923
|
-
type: "button",
|
|
3924
|
-
className: "dsh-atb-mode-opt",
|
|
3925
|
-
"data-on": isolation === "none",
|
|
3926
|
-
disabled: isolationDisabled,
|
|
3927
|
-
title: isolationLocked ? "任务已有执行记录,隔离方式已锁定" : "直接在项目目录执行(不使用 git)",
|
|
3928
|
-
onClick: () => setIsolation("none"),
|
|
3929
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3930
|
-
className: "dsh-atb-mode-name",
|
|
3931
|
-
children: "📁 原目录执行"
|
|
3932
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3933
|
-
className: "dsh-atb-mode-hint",
|
|
3934
|
-
children: isolationLocked ? "已锁定(执行开始后不可更改)" : !gitOk ? "当前项目非 git 仓库,将在原目录执行" : "不使用 git,直接在项目目录工作"
|
|
3935
|
-
})]
|
|
3936
|
-
})]
|
|
3937
|
-
}), !gitOk && !isolationLocked && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3938
|
-
className: "dsh-atb-isolation-note",
|
|
3939
|
-
children: "当前项目非 git 仓库,将在原目录执行(任务仍按默认配置创建,运行时自动降级)"
|
|
3940
|
-
})]
|
|
3941
|
-
}),
|
|
3942
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3943
|
-
label: editing ? "验收清单(DoD)" : "验收清单(DoD,可选)",
|
|
5611
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SlashPromptInput, {
|
|
5612
|
+
value: description,
|
|
5613
|
+
onChange: setDescription,
|
|
5614
|
+
controller,
|
|
5615
|
+
rows: 7,
|
|
5616
|
+
placeholder: t("form.desc.placeholder")
|
|
5617
|
+
})
|
|
5618
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
5619
|
+
label: editing ? t("form.field.prompt") : t("form.field.promptOptional"),
|
|
3944
5620
|
full: true,
|
|
3945
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
3946
|
-
|
|
3947
|
-
onChange:
|
|
3948
|
-
|
|
5621
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SlashPromptInput, {
|
|
5622
|
+
value: prompt,
|
|
5623
|
+
onChange: setPrompt,
|
|
5624
|
+
controller,
|
|
5625
|
+
rows: 7,
|
|
5626
|
+
placeholder: t("form.prompt.placeholder")
|
|
3949
5627
|
})
|
|
3950
|
-
})
|
|
3951
|
-
]
|
|
5628
|
+
})]
|
|
5629
|
+
})]
|
|
3952
5630
|
}),
|
|
3953
5631
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3954
5632
|
className: "dsh-atb-modal-foot",
|
|
@@ -3963,15 +5641,15 @@ window.__ModuleLoader__.load({
|
|
|
3963
5641
|
type: "button",
|
|
3964
5642
|
className: "dsh-atb-btn",
|
|
3965
5643
|
onClick: () => controller.closeForm(),
|
|
3966
|
-
children: "
|
|
5644
|
+
children: t("shared.cancel")
|
|
3967
5645
|
}),
|
|
3968
5646
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3969
5647
|
type: "button",
|
|
3970
5648
|
className: "dsh-atb-btn",
|
|
3971
5649
|
disabled: !valid || runBlocked || busy,
|
|
3972
|
-
title: runBlocked ? "
|
|
5650
|
+
title: runBlocked ? t("form.action.runBlockedTitle") : busy ? t("form.action.runBusyTitle") : t("form.action.runTitle"),
|
|
3973
5651
|
onClick: submitAndRun,
|
|
3974
|
-
children: "
|
|
5652
|
+
children: t("form.action.run")
|
|
3975
5653
|
}),
|
|
3976
5654
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3977
5655
|
type: "button",
|
|
@@ -3979,7 +5657,7 @@ window.__ModuleLoader__.load({
|
|
|
3979
5657
|
"data-primary": "true",
|
|
3980
5658
|
disabled: !valid || busy,
|
|
3981
5659
|
onClick: submit,
|
|
3982
|
-
children: editing ? "
|
|
5660
|
+
children: editing ? t("form.action.save") : t("form.action.create")
|
|
3983
5661
|
})
|
|
3984
5662
|
]
|
|
3985
5663
|
})]
|
|
@@ -4000,15 +5678,15 @@ window.__ModuleLoader__.load({
|
|
|
4000
5678
|
*
|
|
4001
5679
|
* @module dsh-taskboard/client/board/SettingsModal
|
|
4002
5680
|
*/
|
|
4003
|
-
/** The isolation options with one-line hints (mirrors the task form). */
|
|
4004
|
-
const
|
|
5681
|
+
/** The isolation options with one-line hints (mirrors the task form; translated per render). */
|
|
5682
|
+
const isolationOptions = (t) => [{
|
|
4005
5683
|
value: "none",
|
|
4006
|
-
name: "
|
|
4007
|
-
hint: "
|
|
5684
|
+
name: t("form.iso.none"),
|
|
5685
|
+
hint: t("set.iso.noneHint")
|
|
4008
5686
|
}, {
|
|
4009
5687
|
value: "worktree",
|
|
4010
|
-
name: "
|
|
4011
|
-
hint: "
|
|
5688
|
+
name: t("form.iso.worktree"),
|
|
5689
|
+
hint: t("set.iso.worktreeHint")
|
|
4012
5690
|
}];
|
|
4013
5691
|
/**
|
|
4014
5692
|
* The 看板设置 modal: reads the live ledger settings, stages a local draft,
|
|
@@ -4016,11 +5694,21 @@ window.__ModuleLoader__.load({
|
|
|
4016
5694
|
* @param controller - the board controller.
|
|
4017
5695
|
*/
|
|
4018
5696
|
function SettingsModal({ controller }) {
|
|
4019
|
-
const
|
|
4020
|
-
const
|
|
4021
|
-
const
|
|
5697
|
+
const t = useT();
|
|
5698
|
+
const state = controller.getSnapshot();
|
|
5699
|
+
const currentIso = state.ledger.settings?.defaultIsolation ?? "none";
|
|
5700
|
+
const currentSync = defaultSyncExternalSessionsOf(state.ledger.settings);
|
|
5701
|
+
const currentPerm = defaultPermissionOf(state.ledger.settings);
|
|
5702
|
+
const [draftIso, setDraftIso] = (0, react.useState)(currentIso);
|
|
5703
|
+
const [draftSync, setDraftSync] = (0, react.useState)(currentSync);
|
|
5704
|
+
const [draftPerm, setDraftPerm] = (0, react.useState)(currentPerm);
|
|
5705
|
+
const dirty = draftIso !== currentIso || draftSync !== currentSync || draftPerm !== currentPerm;
|
|
4022
5706
|
const save = () => {
|
|
4023
|
-
controller.updateSettings({
|
|
5707
|
+
controller.updateSettings({
|
|
5708
|
+
defaultIsolation: draftIso,
|
|
5709
|
+
syncExternalSessions: draftSync,
|
|
5710
|
+
defaultPermission: draftPerm
|
|
5711
|
+
}).then((ok) => {
|
|
4024
5712
|
if (ok) controller.closeSettings();
|
|
4025
5713
|
});
|
|
4026
5714
|
};
|
|
@@ -4033,7 +5721,7 @@ window.__ModuleLoader__.load({
|
|
|
4033
5721
|
className: "dsh-atb-modal dsh-atb-set",
|
|
4034
5722
|
role: "dialog",
|
|
4035
5723
|
"aria-modal": "true",
|
|
4036
|
-
"aria-label": "
|
|
5724
|
+
"aria-label": t("set.aria"),
|
|
4037
5725
|
children: [
|
|
4038
5726
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4039
5727
|
className: "dsh-atb-modal-head",
|
|
@@ -4044,70 +5732,162 @@ window.__ModuleLoader__.load({
|
|
|
4044
5732
|
}),
|
|
4045
5733
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4046
5734
|
className: "dsh-atb-modal-headtext",
|
|
4047
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "
|
|
5735
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("set.title") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("set.subtitle") })]
|
|
4048
5736
|
}),
|
|
4049
5737
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4050
5738
|
type: "button",
|
|
4051
5739
|
className: "dsh-atb-modal-close",
|
|
4052
|
-
"aria-label": "
|
|
5740
|
+
"aria-label": t("shared.close"),
|
|
4053
5741
|
onClick: () => controller.closeSettings(),
|
|
4054
5742
|
children: "✕"
|
|
4055
5743
|
})
|
|
4056
5744
|
]
|
|
4057
5745
|
}),
|
|
4058
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
5746
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4059
5747
|
className: "dsh-atb-modal-body",
|
|
4060
|
-
children:
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
5748
|
+
children: [
|
|
5749
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5750
|
+
className: "dsh-atb-diag-sec",
|
|
5751
|
+
children: [
|
|
5752
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("set.iso.heading") }),
|
|
5753
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5754
|
+
className: "dsh-atb-mode-picker",
|
|
5755
|
+
children: isolationOptions(t).map((o) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5756
|
+
type: "button",
|
|
5757
|
+
className: "dsh-atb-mode-opt",
|
|
5758
|
+
"data-on": draftIso === o.value,
|
|
5759
|
+
title: o.hint,
|
|
5760
|
+
onClick: () => setDraftIso(o.value),
|
|
5761
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5762
|
+
className: "dsh-atb-mode-name",
|
|
5763
|
+
children: o.name
|
|
5764
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5765
|
+
className: "dsh-atb-mode-hint",
|
|
5766
|
+
children: o.hint
|
|
5767
|
+
})]
|
|
5768
|
+
}, o.value))
|
|
5769
|
+
}),
|
|
5770
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5771
|
+
className: "dsh-atb-isolation-note",
|
|
5772
|
+
children: t("set.iso.current", { current: currentIso === "worktree" ? t("form.iso.worktree") : t("form.iso.none") })
|
|
5773
|
+
})
|
|
5774
|
+
]
|
|
5775
|
+
}),
|
|
5776
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5777
|
+
className: "dsh-atb-diag-sec",
|
|
5778
|
+
children: [
|
|
5779
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("set.sync.heading") }),
|
|
5780
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5781
|
+
className: "dsh-atb-mode-picker",
|
|
5782
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5783
|
+
type: "button",
|
|
5784
|
+
className: "dsh-atb-mode-opt",
|
|
5785
|
+
"data-on": !draftSync,
|
|
5786
|
+
title: t("set.sync.off.title"),
|
|
5787
|
+
onClick: () => setDraftSync(false),
|
|
5788
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5789
|
+
className: "dsh-atb-mode-name",
|
|
5790
|
+
children: t("set.sync.off.name")
|
|
5791
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5792
|
+
className: "dsh-atb-mode-hint",
|
|
5793
|
+
children: t("set.sync.off.hint")
|
|
5794
|
+
})]
|
|
5795
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5796
|
+
type: "button",
|
|
5797
|
+
className: "dsh-atb-mode-opt",
|
|
5798
|
+
"data-on": draftSync,
|
|
5799
|
+
title: t("set.sync.on.title"),
|
|
5800
|
+
onClick: () => setDraftSync(true),
|
|
5801
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5802
|
+
className: "dsh-atb-mode-name",
|
|
5803
|
+
children: t("set.sync.on.name")
|
|
5804
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5805
|
+
className: "dsh-atb-mode-hint",
|
|
5806
|
+
children: t("set.sync.on.hint")
|
|
5807
|
+
})]
|
|
4078
5808
|
})]
|
|
4079
|
-
},
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
5809
|
+
}),
|
|
5810
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5811
|
+
className: "dsh-atb-isolation-note",
|
|
5812
|
+
children: currentSync ? t("set.sync.stateOn") : t("set.sync.stateOff")
|
|
5813
|
+
})
|
|
5814
|
+
]
|
|
5815
|
+
}),
|
|
5816
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5817
|
+
className: "dsh-atb-diag-sec",
|
|
5818
|
+
children: [
|
|
5819
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("set.perm.heading") }),
|
|
5820
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5821
|
+
className: "dsh-atb-perm-picker",
|
|
5822
|
+
children: [
|
|
5823
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5824
|
+
type: "button",
|
|
5825
|
+
className: "dsh-atb-perm-opt",
|
|
5826
|
+
"data-on": draftPerm === "workspace-write",
|
|
5827
|
+
onClick: () => setDraftPerm("workspace-write"),
|
|
5828
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5829
|
+
className: "dsh-atb-perm-name",
|
|
5830
|
+
children: t("set.perm.writeName")
|
|
5831
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5832
|
+
className: "dsh-atb-perm-hint",
|
|
5833
|
+
children: t("set.perm.writeHint")
|
|
5834
|
+
})]
|
|
5835
|
+
}),
|
|
5836
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5837
|
+
type: "button",
|
|
5838
|
+
className: "dsh-atb-perm-opt",
|
|
5839
|
+
"data-on": draftPerm === "read-only",
|
|
5840
|
+
onClick: () => setDraftPerm("read-only"),
|
|
5841
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5842
|
+
className: "dsh-atb-perm-name",
|
|
5843
|
+
children: t("set.perm.readOnlyName")
|
|
5844
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5845
|
+
className: "dsh-atb-perm-hint",
|
|
5846
|
+
children: t("set.perm.readOnlyHint")
|
|
5847
|
+
})]
|
|
5848
|
+
}),
|
|
5849
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5850
|
+
type: "button",
|
|
5851
|
+
className: "dsh-atb-perm-opt",
|
|
5852
|
+
"data-on": draftPerm === "danger-full-access",
|
|
5853
|
+
onClick: () => setDraftPerm("danger-full-access"),
|
|
5854
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5855
|
+
className: "dsh-atb-perm-name",
|
|
5856
|
+
children: t("set.perm.fullName")
|
|
5857
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5858
|
+
className: "dsh-atb-perm-hint",
|
|
5859
|
+
children: t("set.perm.fullHint")
|
|
5860
|
+
})]
|
|
5861
|
+
})
|
|
5862
|
+
]
|
|
5863
|
+
}),
|
|
5864
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5865
|
+
className: "dsh-atb-isolation-note",
|
|
5866
|
+
children: t("set.perm.current", { current: currentPerm === "read-only" ? t("set.perm.readOnlyName") : currentPerm === "danger-full-access" ? t("set.perm.fullName") : t("set.perm.writeName") })
|
|
5867
|
+
})
|
|
5868
|
+
]
|
|
5869
|
+
})
|
|
5870
|
+
]
|
|
4091
5871
|
}),
|
|
4092
5872
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4093
5873
|
className: "dsh-atb-modal-foot",
|
|
4094
5874
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4095
5875
|
className: "dsh-atb-modal-hint",
|
|
4096
|
-
children: dirty ? "
|
|
5876
|
+
children: dirty ? t("set.foot.dirty") : t("set.foot.clean")
|
|
4097
5877
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4098
5878
|
className: "dsh-atb-modal-footbtns",
|
|
4099
5879
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4100
5880
|
type: "button",
|
|
4101
5881
|
className: "dsh-atb-btn",
|
|
4102
5882
|
onClick: () => controller.closeSettings(),
|
|
4103
|
-
children: "
|
|
5883
|
+
children: t("shared.cancel")
|
|
4104
5884
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4105
5885
|
type: "button",
|
|
4106
5886
|
className: "dsh-atb-btn",
|
|
4107
5887
|
"data-primary": "true",
|
|
4108
5888
|
disabled: !dirty,
|
|
4109
5889
|
onClick: save,
|
|
4110
|
-
children: "
|
|
5890
|
+
children: t("set.action.save")
|
|
4111
5891
|
})]
|
|
4112
5892
|
})]
|
|
4113
5893
|
})
|
|
@@ -4145,6 +5925,7 @@ window.__ModuleLoader__.load({
|
|
|
4145
5925
|
* @param controller - the controller.
|
|
4146
5926
|
*/
|
|
4147
5927
|
function ImportModal({ controller }) {
|
|
5928
|
+
const t = useT();
|
|
4148
5929
|
const [fileName, setFileName] = (0, react.useState)("");
|
|
4149
5930
|
const [parsed, setParsed] = (0, react.useState)(null);
|
|
4150
5931
|
const [parseError, setParseError] = (0, react.useState)(void 0);
|
|
@@ -4173,7 +5954,7 @@ window.__ModuleLoader__.load({
|
|
|
4173
5954
|
if (p !== void 0) setPlan(p);
|
|
4174
5955
|
});
|
|
4175
5956
|
} catch {
|
|
4176
|
-
setParseError("
|
|
5957
|
+
setParseError(t("imp.parseError"));
|
|
4177
5958
|
}
|
|
4178
5959
|
});
|
|
4179
5960
|
};
|
|
@@ -4189,7 +5970,13 @@ window.__ModuleLoader__.load({
|
|
|
4189
5970
|
setBusy(false);
|
|
4190
5971
|
setConfirmReplace(false);
|
|
4191
5972
|
if (r === void 0) return;
|
|
4192
|
-
setResult(r.mode === "replace" ?
|
|
5973
|
+
setResult(r.mode === "replace" ? t("imp.result.replace", {
|
|
5974
|
+
n: r.created + r.overwritten,
|
|
5975
|
+
total: r.replacedTotal ?? 0
|
|
5976
|
+
}) : t("imp.result.merge", {
|
|
5977
|
+
n: r.created,
|
|
5978
|
+
m: r.overwritten
|
|
5979
|
+
}));
|
|
4193
5980
|
});
|
|
4194
5981
|
};
|
|
4195
5982
|
const close = () => controller.closeImport();
|
|
@@ -4202,7 +5989,7 @@ window.__ModuleLoader__.load({
|
|
|
4202
5989
|
className: "dsh-atb-modal dsh-atb-imp",
|
|
4203
5990
|
role: "dialog",
|
|
4204
5991
|
"aria-modal": "true",
|
|
4205
|
-
"aria-label": "
|
|
5992
|
+
"aria-label": t("imp.aria"),
|
|
4206
5993
|
children: [
|
|
4207
5994
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4208
5995
|
className: "dsh-atb-modal-head",
|
|
@@ -4213,12 +6000,12 @@ window.__ModuleLoader__.load({
|
|
|
4213
6000
|
}),
|
|
4214
6001
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4215
6002
|
className: "dsh-atb-modal-headtext",
|
|
4216
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "
|
|
6003
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("imp.title") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("imp.subtitle") })]
|
|
4217
6004
|
}),
|
|
4218
6005
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4219
6006
|
type: "button",
|
|
4220
6007
|
className: "dsh-atb-modal-close",
|
|
4221
|
-
"aria-label": "
|
|
6008
|
+
"aria-label": t("shared.close"),
|
|
4222
6009
|
onClick: close,
|
|
4223
6010
|
children: "✕"
|
|
4224
6011
|
})
|
|
@@ -4241,7 +6028,7 @@ window.__ModuleLoader__.load({
|
|
|
4241
6028
|
}),
|
|
4242
6029
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4243
6030
|
className: "dsh-atb-imp-note",
|
|
4244
|
-
children: "
|
|
6031
|
+
children: t("imp.note")
|
|
4245
6032
|
}),
|
|
4246
6033
|
parseError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4247
6034
|
className: "dsh-atb-imp-error",
|
|
@@ -4249,7 +6036,7 @@ window.__ModuleLoader__.load({
|
|
|
4249
6036
|
}),
|
|
4250
6037
|
plan === void 0 && parseError === void 0 && fileName.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4251
6038
|
className: "dsh-atb-empty2",
|
|
4252
|
-
children: "
|
|
6039
|
+
children: t("imp.previewing")
|
|
4253
6040
|
}),
|
|
4254
6041
|
plan !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4255
6042
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -4258,37 +6045,37 @@ window.__ModuleLoader__.load({
|
|
|
4258
6045
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4259
6046
|
className: "dsh-atb-imp-stat",
|
|
4260
6047
|
"data-tone": "ok",
|
|
4261
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: plan.create.length }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "
|
|
6048
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: plan.create.length }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("imp.stat.create") })]
|
|
4262
6049
|
}),
|
|
4263
6050
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4264
6051
|
className: "dsh-atb-imp-stat",
|
|
4265
6052
|
"data-tone": "warn",
|
|
4266
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: plan.overwrite.length }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "
|
|
6053
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: plan.overwrite.length }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("imp.stat.overwrite") })]
|
|
4267
6054
|
}),
|
|
4268
6055
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4269
6056
|
className: "dsh-atb-imp-stat",
|
|
4270
6057
|
"data-tone": plan.invalid.length > 0 ? "bad" : void 0,
|
|
4271
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: plan.invalid.length }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "
|
|
6058
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: plan.invalid.length }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("imp.stat.invalid") })]
|
|
4272
6059
|
})
|
|
4273
6060
|
]
|
|
4274
6061
|
}),
|
|
4275
6062
|
plan.create.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4276
6063
|
className: "dsh-atb-imp-sec",
|
|
4277
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: "
|
|
6064
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("imp.sec.create") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4278
6065
|
className: "dsh-atb-imp-list",
|
|
4279
6066
|
children: plan.create.map((r) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlanRow, { row: r }, r.id))
|
|
4280
6067
|
})]
|
|
4281
6068
|
}),
|
|
4282
6069
|
plan.overwrite.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4283
6070
|
className: "dsh-atb-imp-sec",
|
|
4284
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: "
|
|
6071
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("imp.sec.overwrite") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4285
6072
|
className: "dsh-atb-imp-list",
|
|
4286
6073
|
children: plan.overwrite.map((r) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlanRow, { row: r }, r.id))
|
|
4287
6074
|
})]
|
|
4288
6075
|
}),
|
|
4289
6076
|
plan.invalid.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4290
6077
|
className: "dsh-atb-imp-sec",
|
|
4291
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: "
|
|
6078
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("imp.sec.invalid") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4292
6079
|
className: "dsh-atb-imp-list",
|
|
4293
6080
|
children: plan.invalid.map((r, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4294
6081
|
className: "dsh-atb-imp-row",
|
|
@@ -4296,7 +6083,7 @@ window.__ModuleLoader__.load({
|
|
|
4296
6083
|
title: r.id ?? "",
|
|
4297
6084
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4298
6085
|
className: "dsh-atb-imp-row-title",
|
|
4299
|
-
children: r.id ?? "
|
|
6086
|
+
children: r.id ?? t("imp.noId")
|
|
4300
6087
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4301
6088
|
className: "dsh-atb-imp-row-status",
|
|
4302
6089
|
children: r.reason
|
|
@@ -4316,10 +6103,10 @@ window.__ModuleLoader__.load({
|
|
|
4316
6103
|
},
|
|
4317
6104
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4318
6105
|
className: "dsh-atb-mode-name",
|
|
4319
|
-
children: "
|
|
6106
|
+
children: t("imp.mode.merge")
|
|
4320
6107
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4321
6108
|
className: "dsh-atb-mode-hint",
|
|
4322
|
-
children: "
|
|
6109
|
+
children: t("imp.mode.mergeHint")
|
|
4323
6110
|
})]
|
|
4324
6111
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4325
6112
|
type: "button",
|
|
@@ -4328,10 +6115,10 @@ window.__ModuleLoader__.load({
|
|
|
4328
6115
|
onClick: () => setMode("replace"),
|
|
4329
6116
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4330
6117
|
className: "dsh-atb-mode-name",
|
|
4331
|
-
children: "
|
|
6118
|
+
children: t("imp.mode.replace")
|
|
4332
6119
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4333
6120
|
className: "dsh-atb-mode-hint",
|
|
4334
|
-
children: "
|
|
6121
|
+
children: t("imp.mode.replaceHint")
|
|
4335
6122
|
})]
|
|
4336
6123
|
})]
|
|
4337
6124
|
}),
|
|
@@ -4346,14 +6133,14 @@ window.__ModuleLoader__.load({
|
|
|
4346
6133
|
className: "dsh-atb-modal-foot",
|
|
4347
6134
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4348
6135
|
className: "dsh-atb-modal-hint",
|
|
4349
|
-
children: mode === "replace" ? confirmReplace ? "
|
|
6136
|
+
children: mode === "replace" ? confirmReplace ? t("imp.foot.replaceConfirm") : t("imp.foot.replaceNeedConfirm") : t("imp.foot.mergeHint")
|
|
4350
6137
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4351
6138
|
className: "dsh-atb-modal-footbtns",
|
|
4352
6139
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4353
6140
|
type: "button",
|
|
4354
6141
|
className: "dsh-atb-btn",
|
|
4355
6142
|
onClick: close,
|
|
4356
|
-
children: result !== void 0 ? "
|
|
6143
|
+
children: result !== void 0 ? t("shared.close") : t("shared.cancel")
|
|
4357
6144
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4358
6145
|
type: "button",
|
|
4359
6146
|
className: "dsh-atb-btn",
|
|
@@ -4361,7 +6148,7 @@ window.__ModuleLoader__.load({
|
|
|
4361
6148
|
"data-danger": mode === "replace" && confirmReplace ? "true" : void 0,
|
|
4362
6149
|
disabled: plan === void 0 || busy,
|
|
4363
6150
|
onClick: commit,
|
|
4364
|
-
children: mode === "replace" && confirmReplace ? "
|
|
6151
|
+
children: mode === "replace" && confirmReplace ? t("imp.action.confirmReplace") : t("imp.action.run")
|
|
4365
6152
|
})]
|
|
4366
6153
|
})]
|
|
4367
6154
|
})
|
|
@@ -4384,6 +6171,7 @@ window.__ModuleLoader__.load({
|
|
|
4384
6171
|
* @param controller - the controller.
|
|
4385
6172
|
*/
|
|
4386
6173
|
function TemplateManager({ controller }) {
|
|
6174
|
+
const t = useT();
|
|
4387
6175
|
const state = controller.getSnapshot();
|
|
4388
6176
|
const [edits, setEdits] = (0, react.useState)({});
|
|
4389
6177
|
const [confirmId, setConfirmId] = (0, react.useState)(void 0);
|
|
@@ -4405,7 +6193,7 @@ window.__ModuleLoader__.load({
|
|
|
4405
6193
|
delete next[id];
|
|
4406
6194
|
return next;
|
|
4407
6195
|
});
|
|
4408
|
-
showAlert("
|
|
6196
|
+
showAlert(t("tpl.renamed"));
|
|
4409
6197
|
}
|
|
4410
6198
|
});
|
|
4411
6199
|
};
|
|
@@ -4418,7 +6206,7 @@ window.__ModuleLoader__.load({
|
|
|
4418
6206
|
className: "dsh-atb-modal dsh-atb-tplm",
|
|
4419
6207
|
role: "dialog",
|
|
4420
6208
|
"aria-modal": "true",
|
|
4421
|
-
"aria-label": "
|
|
6209
|
+
"aria-label": t("tpl.aria"),
|
|
4422
6210
|
children: [
|
|
4423
6211
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4424
6212
|
className: "dsh-atb-modal-head",
|
|
@@ -4429,12 +6217,12 @@ window.__ModuleLoader__.load({
|
|
|
4429
6217
|
}),
|
|
4430
6218
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4431
6219
|
className: "dsh-atb-modal-headtext",
|
|
4432
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "
|
|
6220
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("tpl.title") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("tpl.subtitle") })]
|
|
4433
6221
|
}),
|
|
4434
6222
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4435
6223
|
type: "button",
|
|
4436
6224
|
className: "dsh-atb-modal-close",
|
|
4437
|
-
"aria-label": "
|
|
6225
|
+
"aria-label": t("shared.close"),
|
|
4438
6226
|
onClick: close,
|
|
4439
6227
|
children: "✕"
|
|
4440
6228
|
})
|
|
@@ -4444,33 +6232,34 @@ window.__ModuleLoader__.load({
|
|
|
4444
6232
|
className: "dsh-atb-modal-body",
|
|
4445
6233
|
children: state.templates.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4446
6234
|
className: "dsh-atb-empty2",
|
|
4447
|
-
children: "
|
|
6235
|
+
children: t("tpl.empty")
|
|
4448
6236
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4449
6237
|
className: "dsh-atb-tplm-list",
|
|
4450
|
-
children: state.templates.map((
|
|
6238
|
+
children: state.templates.map((tpl) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4451
6239
|
className: "dsh-atb-tplm-row",
|
|
4452
6240
|
children: [
|
|
4453
6241
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4454
6242
|
className: "dsh-atb-tplm-name",
|
|
4455
|
-
value: nameOf(
|
|
6243
|
+
value: nameOf(tpl.id, tpl.name),
|
|
4456
6244
|
maxLength: 60,
|
|
4457
6245
|
spellCheck: false,
|
|
4458
|
-
"aria-label":
|
|
6246
|
+
"aria-label": t("tpl.name.aria", { name: tpl.name }),
|
|
4459
6247
|
onChange: (e) => setEdits((prev) => ({
|
|
4460
6248
|
...prev,
|
|
4461
|
-
[
|
|
6249
|
+
[tpl.id]: e.target.value
|
|
4462
6250
|
})),
|
|
4463
6251
|
onKeyDown: (e) => {
|
|
4464
|
-
if (e.key === "Enter") save(
|
|
6252
|
+
if (e.key === "Enter") save(tpl.id, nameOf(tpl.id, tpl.name));
|
|
4465
6253
|
}
|
|
4466
6254
|
}),
|
|
4467
6255
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4468
6256
|
className: "dsh-atb-tplm-meta",
|
|
4469
|
-
title: `${
|
|
6257
|
+
title: `${tpl.builtin === true ? t("tpl.builtin") : t("tpl.custom")}${tpl.task.checklist !== void 0 && tpl.task.checklist.length > 0 ? t("tpl.meta.checklist", { n: tpl.task.checklist.length }) : ""}${tpl.task.urgency !== void 0 ? ` · ${tpl.task.urgency}` : ""}${tpl.task.permission !== void 0 ? ` · ${t("shared.permission")}: ${tpl.task.permission}` : ""}`,
|
|
4470
6258
|
children: [
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
6259
|
+
tpl.builtin === true ? t("tpl.builtin") : t("tpl.custom"),
|
|
6260
|
+
tpl.task.checklist !== void 0 && tpl.task.checklist.length > 0 ? t("tpl.meta.checklist", { n: tpl.task.checklist.length }) : "",
|
|
6261
|
+
tpl.task.urgency !== void 0 ? ` · ${tpl.task.urgency}` : "",
|
|
6262
|
+
tpl.task.permission !== void 0 && tpl.task.permission !== "workspace-write" ? ` · ${tpl.task.permission === "read-only" ? t("tpl.meta.permReadOnly") : t("tpl.meta.permFull")}` : ""
|
|
4474
6263
|
]
|
|
4475
6264
|
}),
|
|
4476
6265
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -4479,61 +6268,61 @@ window.__ModuleLoader__.load({
|
|
|
4479
6268
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4480
6269
|
type: "button",
|
|
4481
6270
|
className: "dsh-atb-btn",
|
|
4482
|
-
disabled: nameOf(
|
|
4483
|
-
title: "
|
|
4484
|
-
onClick: () => save(
|
|
4485
|
-
children: "
|
|
6271
|
+
disabled: nameOf(tpl.id, tpl.name) === tpl.name || nameOf(tpl.id, tpl.name).trim().length === 0,
|
|
6272
|
+
title: t("tpl.rename.title"),
|
|
6273
|
+
onClick: () => save(tpl.id, nameOf(tpl.id, tpl.name)),
|
|
6274
|
+
children: t("tpl.rename.button")
|
|
4486
6275
|
}),
|
|
4487
6276
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4488
6277
|
type: "button",
|
|
4489
6278
|
className: "dsh-atb-btn",
|
|
4490
|
-
title: "
|
|
6279
|
+
title: t("tpl.use.title"),
|
|
4491
6280
|
onClick: () => {
|
|
4492
6281
|
close();
|
|
4493
|
-
controller.newFromTemplate(
|
|
6282
|
+
controller.newFromTemplate(tpl.task);
|
|
4494
6283
|
},
|
|
4495
|
-
children: "
|
|
6284
|
+
children: t("tpl.use.button")
|
|
4496
6285
|
}),
|
|
4497
|
-
confirmId ===
|
|
6286
|
+
confirmId === tpl.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4498
6287
|
type: "button",
|
|
4499
6288
|
className: "dsh-atb-btn",
|
|
4500
6289
|
"data-danger": "true",
|
|
4501
6290
|
onClick: () => {
|
|
4502
|
-
controller.deleteTemplate(
|
|
6291
|
+
controller.deleteTemplate(tpl.id);
|
|
4503
6292
|
setConfirmId(void 0);
|
|
4504
6293
|
},
|
|
4505
|
-
children: "
|
|
6294
|
+
children: t("shared.confirmDelete")
|
|
4506
6295
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4507
6296
|
type: "button",
|
|
4508
6297
|
className: "dsh-atb-btn",
|
|
4509
6298
|
onClick: () => setConfirmId(void 0),
|
|
4510
|
-
children: "
|
|
6299
|
+
children: t("shared.cancel")
|
|
4511
6300
|
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4512
6301
|
type: "button",
|
|
4513
6302
|
className: "dsh-atb-btn",
|
|
4514
6303
|
"data-danger": "true",
|
|
4515
|
-
title: "
|
|
4516
|
-
onClick: () => setConfirmId(
|
|
6304
|
+
title: t("tpl.delete.title"),
|
|
6305
|
+
onClick: () => setConfirmId(tpl.id),
|
|
4517
6306
|
children: "🗑"
|
|
4518
6307
|
})
|
|
4519
6308
|
]
|
|
4520
6309
|
})
|
|
4521
6310
|
]
|
|
4522
|
-
},
|
|
6311
|
+
}, tpl.id))
|
|
4523
6312
|
})
|
|
4524
6313
|
}),
|
|
4525
6314
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4526
6315
|
className: "dsh-atb-modal-foot",
|
|
4527
6316
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4528
6317
|
className: "dsh-atb-modal-hint",
|
|
4529
|
-
children: "
|
|
6318
|
+
children: t("tpl.foot.hint")
|
|
4530
6319
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4531
6320
|
className: "dsh-atb-modal-footbtns",
|
|
4532
6321
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4533
6322
|
type: "button",
|
|
4534
6323
|
className: "dsh-atb-btn",
|
|
4535
6324
|
onClick: close,
|
|
4536
|
-
children: "
|
|
6325
|
+
children: t("shared.close")
|
|
4537
6326
|
})
|
|
4538
6327
|
})]
|
|
4539
6328
|
})
|
|
@@ -4573,6 +6362,7 @@ window.__ModuleLoader__.load({
|
|
|
4573
6362
|
* @param controller - the controller.
|
|
4574
6363
|
*/
|
|
4575
6364
|
function TaskBoard({ controller }) {
|
|
6365
|
+
const t = useT();
|
|
4576
6366
|
const state = (0, react.useSyncExternalStore)((cb) => controller.subscribe(cb), () => controller.getSnapshot());
|
|
4577
6367
|
const [now, setNow] = (0, react.useState)(() => Date.now());
|
|
4578
6368
|
(0, react.useEffect)(() => {
|
|
@@ -4594,15 +6384,14 @@ window.__ModuleLoader__.load({
|
|
|
4594
6384
|
children: [
|
|
4595
6385
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
4596
6386
|
className: "dsh-atb-title",
|
|
4597
|
-
children: "
|
|
6387
|
+
children: t("board.title")
|
|
4598
6388
|
}),
|
|
4599
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
6389
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4600
6390
|
className: "dsh-atb-count",
|
|
4601
|
-
children:
|
|
4602
|
-
live.length,
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
]
|
|
6391
|
+
children: t("board.count.tasks", {
|
|
6392
|
+
n: live.length,
|
|
6393
|
+
rev: state.ledger.revision
|
|
6394
|
+
})
|
|
4606
6395
|
}),
|
|
4607
6396
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4608
6397
|
className: "dsh-atb-newmenu",
|
|
@@ -4615,7 +6404,7 @@ window.__ModuleLoader__.load({
|
|
|
4615
6404
|
setNewMenuOpen(next);
|
|
4616
6405
|
if (next) controller.prepareTemplateMenu();
|
|
4617
6406
|
},
|
|
4618
|
-
children: "
|
|
6407
|
+
children: t("board.action.newTask")
|
|
4619
6408
|
}), newMenuOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4620
6409
|
className: "dsh-atb-newmenu-backdrop",
|
|
4621
6410
|
onClick: closeMenu
|
|
@@ -4629,7 +6418,7 @@ window.__ModuleLoader__.load({
|
|
|
4629
6418
|
closeMenu();
|
|
4630
6419
|
controller.setComposer(true);
|
|
4631
6420
|
},
|
|
4632
|
-
children: "
|
|
6421
|
+
children: t("board.action.blankTask")
|
|
4633
6422
|
}),
|
|
4634
6423
|
state.templates.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4635
6424
|
type: "button",
|
|
@@ -4649,7 +6438,7 @@ window.__ModuleLoader__.load({
|
|
|
4649
6438
|
closeMenu();
|
|
4650
6439
|
controller.openTemplateManager();
|
|
4651
6440
|
},
|
|
4652
|
-
children: "
|
|
6441
|
+
children: t("board.action.manageTemplates")
|
|
4653
6442
|
})
|
|
4654
6443
|
]
|
|
4655
6444
|
})] })]
|
|
@@ -4658,7 +6447,7 @@ window.__ModuleLoader__.load({
|
|
|
4658
6447
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4659
6448
|
className: "dsh-atb-input dsh-atb-search",
|
|
4660
6449
|
value: state.search,
|
|
4661
|
-
placeholder: "
|
|
6450
|
+
placeholder: t("board.search.placeholder"),
|
|
4662
6451
|
spellCheck: false,
|
|
4663
6452
|
onChange: (e) => controller.setSearch(e.target.value)
|
|
4664
6453
|
}),
|
|
@@ -4668,7 +6457,7 @@ window.__ModuleLoader__.load({
|
|
|
4668
6457
|
onChange: (e) => controller.setWorkspaceFilter(e.target.value === "" ? void 0 : e.target.value),
|
|
4669
6458
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4670
6459
|
value: "",
|
|
4671
|
-
children: "
|
|
6460
|
+
children: t("board.filter.allProjects")
|
|
4672
6461
|
}), state.workspaces.map((ws) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4673
6462
|
value: ws.id,
|
|
4674
6463
|
children: ws.title || ws.path
|
|
@@ -4677,28 +6466,28 @@ window.__ModuleLoader__.load({
|
|
|
4677
6466
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
4678
6467
|
className: "dsh-atb-select",
|
|
4679
6468
|
value: state.sortBy,
|
|
4680
|
-
title: "
|
|
6469
|
+
title: t("board.sort.title"),
|
|
4681
6470
|
onChange: (e) => controller.setSortBy(e.target.value),
|
|
4682
6471
|
children: [
|
|
4683
6472
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4684
6473
|
value: "default",
|
|
4685
|
-
children: "
|
|
6474
|
+
children: t("board.sort.default")
|
|
4686
6475
|
}),
|
|
4687
6476
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4688
6477
|
value: "updated",
|
|
4689
|
-
children: "
|
|
6478
|
+
children: t("board.sort.updated")
|
|
4690
6479
|
}),
|
|
4691
6480
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4692
6481
|
value: "urgency",
|
|
4693
|
-
children: "
|
|
6482
|
+
children: t("board.sort.urgency")
|
|
4694
6483
|
}),
|
|
4695
6484
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4696
6485
|
value: "created",
|
|
4697
|
-
children: "
|
|
6486
|
+
children: t("board.sort.created")
|
|
4698
6487
|
}),
|
|
4699
6488
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4700
6489
|
value: "title",
|
|
4701
|
-
children: "
|
|
6490
|
+
children: t("board.sort.byTitle")
|
|
4702
6491
|
})
|
|
4703
6492
|
]
|
|
4704
6493
|
}),
|
|
@@ -4715,43 +6504,43 @@ window.__ModuleLoader__.load({
|
|
|
4715
6504
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4716
6505
|
className: "dsh-atb-dot",
|
|
4717
6506
|
"data-urgency": u
|
|
4718
|
-
}),
|
|
6507
|
+
}), t(URGENCY_KEYS[u])]
|
|
4719
6508
|
}, u)),
|
|
4720
6509
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4721
6510
|
type: "button",
|
|
4722
6511
|
className: "dsh-atb-btn",
|
|
4723
6512
|
onClick: () => controller.toggleSecondary(),
|
|
4724
|
-
children: state.secondaryOpen ? "
|
|
6513
|
+
children: state.secondaryOpen ? t("board.action.backToBoard") : t("board.action.otherTasks")
|
|
4725
6514
|
}),
|
|
4726
6515
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4727
6516
|
type: "button",
|
|
4728
6517
|
className: "dsh-atb-btn",
|
|
4729
|
-
title: "
|
|
6518
|
+
title: t("board.action.settingsTitle"),
|
|
4730
6519
|
onClick: () => controller.openSettings(),
|
|
4731
|
-
children: "
|
|
6520
|
+
children: t("board.action.settings")
|
|
4732
6521
|
}),
|
|
4733
6522
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4734
6523
|
type: "button",
|
|
4735
6524
|
className: "dsh-atb-btn",
|
|
4736
|
-
title: "
|
|
6525
|
+
title: t("board.action.diagTitle"),
|
|
4737
6526
|
onClick: () => controller.openDiagnostics(),
|
|
4738
|
-
children: "
|
|
6527
|
+
children: t("board.action.diag")
|
|
4739
6528
|
}),
|
|
4740
6529
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4741
6530
|
type: "button",
|
|
4742
6531
|
className: "dsh-atb-btn",
|
|
4743
|
-
title: "
|
|
6532
|
+
title: t("board.action.importTitle"),
|
|
4744
6533
|
onClick: () => controller.openImport(),
|
|
4745
|
-
children: "
|
|
6534
|
+
children: t("board.action.import")
|
|
4746
6535
|
}),
|
|
4747
6536
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4748
6537
|
className: "dsh-atb-newmenu",
|
|
4749
6538
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4750
6539
|
type: "button",
|
|
4751
6540
|
className: "dsh-atb-btn",
|
|
4752
|
-
title: "
|
|
6541
|
+
title: t("board.action.exportTitle"),
|
|
4753
6542
|
onClick: () => setExportOpen(!exportOpen),
|
|
4754
|
-
children: "
|
|
6543
|
+
children: t("board.action.export")
|
|
4755
6544
|
}), exportOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4756
6545
|
className: "dsh-atb-newmenu-backdrop",
|
|
4757
6546
|
onClick: closeExport
|
|
@@ -4760,21 +6549,21 @@ window.__ModuleLoader__.load({
|
|
|
4760
6549
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4761
6550
|
type: "button",
|
|
4762
6551
|
className: "dsh-atb-newmenu-opt",
|
|
4763
|
-
title: "
|
|
6552
|
+
title: t("board.export.jsonTitle"),
|
|
4764
6553
|
onClick: () => {
|
|
4765
6554
|
closeExport();
|
|
4766
6555
|
controller.exportJson();
|
|
4767
6556
|
},
|
|
4768
|
-
children: "
|
|
6557
|
+
children: t("board.export.json")
|
|
4769
6558
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4770
6559
|
type: "button",
|
|
4771
6560
|
className: "dsh-atb-newmenu-opt",
|
|
4772
|
-
title: "
|
|
6561
|
+
title: t("board.export.csvTitle"),
|
|
4773
6562
|
onClick: () => {
|
|
4774
6563
|
closeExport();
|
|
4775
6564
|
controller.exportCsv();
|
|
4776
6565
|
},
|
|
4777
|
-
children: "
|
|
6566
|
+
children: t("board.export.csv")
|
|
4778
6567
|
})]
|
|
4779
6568
|
})] })]
|
|
4780
6569
|
}),
|
|
@@ -4818,7 +6607,10 @@ window.__ModuleLoader__.load({
|
|
|
4818
6607
|
const task = state.ledger.tasks.find((t) => t.id === id);
|
|
4819
6608
|
if (task === void 0 || task.status === status) return;
|
|
4820
6609
|
if (!canTransition(task.status, status)) {
|
|
4821
|
-
showAlert(
|
|
6610
|
+
showAlert(t("board.drag.forbidden", {
|
|
6611
|
+
from: t(COLUMN_KEYS[task.status]),
|
|
6612
|
+
to: t(COLUMN_KEYS[status])
|
|
6613
|
+
}));
|
|
4822
6614
|
return;
|
|
4823
6615
|
}
|
|
4824
6616
|
controller.move(id, task.version, status);
|
|
@@ -4830,7 +6622,7 @@ window.__ModuleLoader__.load({
|
|
|
4830
6622
|
className: "dsh-atb-dot",
|
|
4831
6623
|
"data-status": status
|
|
4832
6624
|
}),
|
|
4833
|
-
|
|
6625
|
+
t(COLUMN_KEYS[status]),
|
|
4834
6626
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4835
6627
|
className: "dsh-atb-colcount",
|
|
4836
6628
|
children: columnTasks.length
|
|
@@ -4846,7 +6638,7 @@ window.__ModuleLoader__.load({
|
|
|
4846
6638
|
onAlert: showAlert
|
|
4847
6639
|
}, task.id)), columnTasks.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4848
6640
|
className: "dsh-atb-empty",
|
|
4849
|
-
children: "
|
|
6641
|
+
children: t("board.empty")
|
|
4850
6642
|
})]
|
|
4851
6643
|
})]
|
|
4852
6644
|
}, status);
|
|
@@ -4874,6 +6666,7 @@ window.__ModuleLoader__.load({
|
|
|
4874
6666
|
}
|
|
4875
6667
|
/** ⚙ Health-diagnostics panel (plan §3.6): ledger basics + orphan worktrees + one-click cleanup. */
|
|
4876
6668
|
function DiagnosticsPanel({ controller }) {
|
|
6669
|
+
const t = useT();
|
|
4877
6670
|
const state = controller.getSnapshot();
|
|
4878
6671
|
const diag = state.diagnostics;
|
|
4879
6672
|
const wsName = (id) => {
|
|
@@ -4889,7 +6682,7 @@ window.__ModuleLoader__.load({
|
|
|
4889
6682
|
className: "dsh-atb-modal dsh-atb-diag",
|
|
4890
6683
|
role: "dialog",
|
|
4891
6684
|
"aria-modal": "true",
|
|
4892
|
-
"aria-label": "
|
|
6685
|
+
"aria-label": t("diag.title"),
|
|
4893
6686
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4894
6687
|
className: "dsh-atb-modal-head",
|
|
4895
6688
|
children: [
|
|
@@ -4899,12 +6692,12 @@ window.__ModuleLoader__.load({
|
|
|
4899
6692
|
}),
|
|
4900
6693
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4901
6694
|
className: "dsh-atb-modal-headtext",
|
|
4902
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "
|
|
6695
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("diag.title") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("diag.subtitle") })]
|
|
4903
6696
|
}),
|
|
4904
6697
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4905
6698
|
type: "button",
|
|
4906
6699
|
className: "dsh-atb-modal-close",
|
|
4907
|
-
"aria-label": "
|
|
6700
|
+
"aria-label": t("shared.close"),
|
|
4908
6701
|
onClick: () => controller.closeDiagnostics(),
|
|
4909
6702
|
children: "✕"
|
|
4910
6703
|
})
|
|
@@ -4913,38 +6706,38 @@ window.__ModuleLoader__.load({
|
|
|
4913
6706
|
className: "dsh-atb-modal-body",
|
|
4914
6707
|
children: diag === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4915
6708
|
className: "dsh-atb-empty2",
|
|
4916
|
-
children: "
|
|
6709
|
+
children: t("shared.loading")
|
|
4917
6710
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4918
6711
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4919
6712
|
className: "dsh-atb-diag-grid",
|
|
4920
6713
|
children: [
|
|
4921
6714
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4922
6715
|
className: "dsh-atb-diag-item",
|
|
4923
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: diag.revision }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "
|
|
6716
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: diag.revision }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("diag.revision") })]
|
|
4924
6717
|
}),
|
|
4925
6718
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4926
6719
|
className: "dsh-atb-diag-item",
|
|
4927
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: diag.tasks }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "
|
|
6720
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: diag.tasks }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("diag.tasks") })]
|
|
4928
6721
|
}),
|
|
4929
6722
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4930
6723
|
className: "dsh-atb-diag-item",
|
|
4931
6724
|
"data-bad": diag.staleRunning > 0 ? "true" : void 0,
|
|
4932
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: diag.staleRunning }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "
|
|
6725
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: diag.staleRunning }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("diag.running") })]
|
|
4933
6726
|
}),
|
|
4934
6727
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4935
6728
|
className: "dsh-atb-diag-item",
|
|
4936
6729
|
"data-bad": diag.orphanWorktrees.length > 0 ? "true" : void 0,
|
|
4937
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: diag.orphanWorktrees.length }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "
|
|
6730
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: diag.orphanWorktrees.length }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("diag.orphans") })]
|
|
4938
6731
|
})
|
|
4939
6732
|
]
|
|
4940
6733
|
}),
|
|
4941
6734
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4942
6735
|
className: "dsh-atb-diag-sec",
|
|
4943
6736
|
children: [
|
|
4944
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: "
|
|
6737
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("diag.orphans.heading") }),
|
|
4945
6738
|
diag.orphanWorktrees.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4946
6739
|
className: "dsh-atb-empty2",
|
|
4947
|
-
children: "
|
|
6740
|
+
children: t("diag.orphans.none")
|
|
4948
6741
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4949
6742
|
className: "dsh-atb-diag-orphans",
|
|
4950
6743
|
children: diag.orphanWorktrees.map((o) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -4962,21 +6755,21 @@ window.__ModuleLoader__.load({
|
|
|
4962
6755
|
className: "dsh-atb-btn",
|
|
4963
6756
|
"data-danger": "true",
|
|
4964
6757
|
onClick: () => void controller.cleanupOrphan(o.workspaceId, o.taskId),
|
|
4965
|
-
children: "
|
|
6758
|
+
children: t("diag.orphans.cleanup")
|
|
4966
6759
|
})]
|
|
4967
6760
|
}, o.path))
|
|
4968
6761
|
}),
|
|
4969
6762
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4970
6763
|
className: "dsh-atb-empty2",
|
|
4971
|
-
children: "
|
|
6764
|
+
children: t("diag.orphans.hint")
|
|
4972
6765
|
})
|
|
4973
6766
|
]
|
|
4974
6767
|
}),
|
|
4975
6768
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4976
6769
|
className: "dsh-atb-diag-sec",
|
|
4977
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: "gitignore
|
|
6770
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("diag.gitignore.heading") }), (diag.gitIgnoreSuggestions ?? []).length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4978
6771
|
className: "dsh-atb-empty2",
|
|
4979
|
-
children: "
|
|
6772
|
+
children: t("diag.gitignore.none")
|
|
4980
6773
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4981
6774
|
className: "dsh-atb-diag-orphans",
|
|
4982
6775
|
children: diag.gitIgnoreSuggestions.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -4986,9 +6779,11 @@ window.__ModuleLoader__.load({
|
|
|
4986
6779
|
title: s.workspacePath,
|
|
4987
6780
|
children: [
|
|
4988
6781
|
wsName(s.workspaceId),
|
|
4989
|
-
" ·
|
|
6782
|
+
" · ",
|
|
6783
|
+
t("diag.gitignore.suggestA"),
|
|
6784
|
+
" ",
|
|
4990
6785
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: ".dsh-worktrees/" }),
|
|
4991
|
-
"
|
|
6786
|
+
t("diag.gitignore.suggestB")
|
|
4992
6787
|
]
|
|
4993
6788
|
})
|
|
4994
6789
|
}, s.workspaceId))
|
|
@@ -5001,22 +6796,23 @@ window.__ModuleLoader__.load({
|
|
|
5001
6796
|
}
|
|
5002
6797
|
/** Secondary tab: tasks grouped into canceled / archived / trashed columns. */
|
|
5003
6798
|
function SecondaryTab({ controller, tasks }) {
|
|
6799
|
+
const t = useT();
|
|
5004
6800
|
const trashed = tasks.filter((t) => t.trashedAt !== void 0);
|
|
5005
6801
|
const archived = tasks.filter((t) => t.trashedAt === void 0 && t.status === "archived");
|
|
5006
6802
|
const canceled = tasks.filter((t) => t.trashedAt === void 0 && t.status === "canceled");
|
|
5007
6803
|
const groups = [
|
|
5008
6804
|
{
|
|
5009
|
-
label: "
|
|
6805
|
+
label: t("status.column.canceled"),
|
|
5010
6806
|
dot: "canceled",
|
|
5011
6807
|
rows: canceled
|
|
5012
6808
|
},
|
|
5013
6809
|
{
|
|
5014
|
-
label: "
|
|
6810
|
+
label: t("status.column.archived"),
|
|
5015
6811
|
dot: "archived",
|
|
5016
6812
|
rows: archived
|
|
5017
6813
|
},
|
|
5018
6814
|
{
|
|
5019
|
-
label: "
|
|
6815
|
+
label: t("board.group.trashed"),
|
|
5020
6816
|
dot: "trashed",
|
|
5021
6817
|
rows: trashed
|
|
5022
6818
|
}
|
|
@@ -5025,7 +6821,7 @@ window.__ModuleLoader__.load({
|
|
|
5025
6821
|
className: "dsh-atb-secondary",
|
|
5026
6822
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5027
6823
|
className: "dsh-atb-empty",
|
|
5028
|
-
children: "
|
|
6824
|
+
children: t("board.secondary.empty")
|
|
5029
6825
|
})
|
|
5030
6826
|
});
|
|
5031
6827
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -5052,7 +6848,7 @@ window.__ModuleLoader__.load({
|
|
|
5052
6848
|
controller
|
|
5053
6849
|
}, task.id)), group.rows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5054
6850
|
className: "dsh-atb-empty",
|
|
5055
|
-
children: "
|
|
6851
|
+
children: t("board.empty")
|
|
5056
6852
|
})]
|
|
5057
6853
|
})]
|
|
5058
6854
|
}, group.label))
|
|
@@ -5208,37 +7004,126 @@ window.__ModuleLoader__.load({
|
|
|
5208
7004
|
function apply(ctx) {
|
|
5209
7005
|
try {
|
|
5210
7006
|
injectStyles();
|
|
5211
|
-
|
|
5212
|
-
const
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
7007
|
+
initI18n(ctx.get?.("locale"));
|
|
7008
|
+
const client = createClient();
|
|
7009
|
+
const controller = new BoardController(client);
|
|
7010
|
+
controller.installModelCatalog(async () => {
|
|
7011
|
+
try {
|
|
7012
|
+
const modelDirs = ctx.get?.("modelDirectories") ?? ctx.modelDirectories;
|
|
7013
|
+
if (modelDirs?.catalog?.load !== void 0) {
|
|
7014
|
+
const res = await modelDirs.catalog.load();
|
|
7015
|
+
if (res?.groups !== void 0 && res.groups.length > 0) {
|
|
7016
|
+
const out = [];
|
|
7017
|
+
for (const group of res.groups) for (const model of group.models) out.push({
|
|
7018
|
+
provider: group.id,
|
|
7019
|
+
model: model.id,
|
|
7020
|
+
name: model.name,
|
|
7021
|
+
...model.description !== void 0 ? { description: model.description } : {},
|
|
7022
|
+
...model.reasoning !== void 0 ? { reasoning: model.reasoning } : {}
|
|
7023
|
+
});
|
|
7024
|
+
if (out.length > 0) return out;
|
|
7025
|
+
}
|
|
7026
|
+
}
|
|
7027
|
+
} catch {}
|
|
7028
|
+
try {
|
|
7029
|
+
const remote = ctx.get?.("remote") ?? ctx.remote;
|
|
7030
|
+
if (remote?.session?.modelCatalog !== void 0) {
|
|
7031
|
+
const res = await remote.session.modelCatalog();
|
|
7032
|
+
if (res.ok && res.value?.groups !== void 0 && res.value.groups.length > 0) {
|
|
7033
|
+
const out = [];
|
|
7034
|
+
for (const group of res.value.groups) for (const model of group.models) out.push({
|
|
7035
|
+
provider: group.id,
|
|
7036
|
+
model: model.id,
|
|
7037
|
+
name: model.name,
|
|
7038
|
+
...model.description !== void 0 ? { description: model.description } : {},
|
|
7039
|
+
...model.reasoning !== void 0 ? { reasoning: model.reasoning } : {}
|
|
7040
|
+
});
|
|
7041
|
+
if (out.length > 0) return out;
|
|
7042
|
+
}
|
|
7043
|
+
}
|
|
7044
|
+
if (remote?.llm?.models !== void 0) {
|
|
7045
|
+
const res = await remote.llm.models({});
|
|
7046
|
+
if (res.result.ok && res.result.value?.groups !== void 0 && res.result.value.groups.length > 0) {
|
|
7047
|
+
const out = [];
|
|
7048
|
+
for (const group of res.result.value.groups) for (const model of group.models) out.push({
|
|
7049
|
+
provider: group.id,
|
|
7050
|
+
model: model.id,
|
|
7051
|
+
name: model.name,
|
|
7052
|
+
...model.description !== void 0 ? { description: model.description } : {},
|
|
7053
|
+
...model.reasoning !== void 0 ? { reasoning: model.reasoning } : {}
|
|
7054
|
+
});
|
|
7055
|
+
if (out.length > 0) return out;
|
|
7056
|
+
}
|
|
7057
|
+
}
|
|
7058
|
+
} catch {}
|
|
7059
|
+
try {
|
|
7060
|
+
const connection = ctx.get?.("connection") ?? ctx.connection;
|
|
7061
|
+
if (connection?.api?.llm?.models !== void 0) {
|
|
7062
|
+
const res = await connection.api.llm.models({});
|
|
7063
|
+
if (res.result.ok && res.result.value?.groups !== void 0 && res.result.value.groups.length > 0) {
|
|
7064
|
+
const out = [];
|
|
7065
|
+
for (const group of res.result.value.groups) for (const model of group.models) out.push({
|
|
7066
|
+
provider: group.id,
|
|
7067
|
+
model: model.id,
|
|
7068
|
+
name: model.name,
|
|
7069
|
+
...model.description !== void 0 ? { description: model.description } : {},
|
|
7070
|
+
...model.reasoning !== void 0 ? { reasoning: model.reasoning } : {}
|
|
7071
|
+
});
|
|
7072
|
+
if (out.length > 0) return out;
|
|
7073
|
+
}
|
|
7074
|
+
}
|
|
7075
|
+
} catch {}
|
|
7076
|
+
try {
|
|
7077
|
+
const res = await client.modelCatalog();
|
|
7078
|
+
if (res.models !== void 0 && res.models.length > 0) return res.models;
|
|
7079
|
+
} catch {}
|
|
7080
|
+
return [];
|
|
7081
|
+
});
|
|
7082
|
+
controller.installPresetRoster(async () => {
|
|
7083
|
+
try {
|
|
7084
|
+
const remote = ctx.get?.("remote") ?? ctx.remote;
|
|
7085
|
+
if (remote?.agentPresets?.list !== void 0) {
|
|
7086
|
+
const res = await remote.agentPresets.list();
|
|
7087
|
+
const rawPresets = res.ok === true ? res.value.presets : res.result?.ok === true ? res.result.value.presets : void 0;
|
|
7088
|
+
if (rawPresets !== void 0 && rawPresets.length > 0) {
|
|
7089
|
+
const presets = rawPresets.map((p) => ({
|
|
7090
|
+
id: p.id,
|
|
7091
|
+
name: p.name
|
|
7092
|
+
}));
|
|
7093
|
+
const def = rawPresets.find((p) => p.isDefault);
|
|
7094
|
+
return {
|
|
7095
|
+
presets,
|
|
7096
|
+
...def !== void 0 ? { defaultId: def.id } : {}
|
|
7097
|
+
};
|
|
7098
|
+
}
|
|
7099
|
+
}
|
|
7100
|
+
} catch {}
|
|
7101
|
+
try {
|
|
7102
|
+
const connection = ctx.get?.("connection") ?? ctx.connection;
|
|
7103
|
+
if (connection?.api?.agentPresets?.list !== void 0) {
|
|
7104
|
+
const res = await connection.api.agentPresets.list({});
|
|
7105
|
+
if (res.result.ok && res.result.value?.presets !== void 0 && res.result.value.presets.length > 0) {
|
|
7106
|
+
const presets = res.result.value.presets.map((p) => ({
|
|
7107
|
+
id: p.id,
|
|
7108
|
+
name: p.name
|
|
7109
|
+
}));
|
|
7110
|
+
const def = res.result.value.presets.find((p) => p.isDefault);
|
|
7111
|
+
return {
|
|
7112
|
+
presets,
|
|
7113
|
+
...def !== void 0 ? { defaultId: def.id } : {}
|
|
7114
|
+
};
|
|
7115
|
+
}
|
|
7116
|
+
}
|
|
7117
|
+
} catch {}
|
|
7118
|
+
try {
|
|
7119
|
+
const res = await client.modelCatalog();
|
|
7120
|
+
if (res.presets !== void 0 && res.presets.length > 0) return {
|
|
7121
|
+
presets: res.presets,
|
|
7122
|
+
...res.defaultPresetId !== void 0 ? { defaultId: res.defaultPresetId } : {}
|
|
5239
7123
|
};
|
|
5240
|
-
}
|
|
5241
|
-
|
|
7124
|
+
} catch {}
|
|
7125
|
+
return { presets: [] };
|
|
7126
|
+
});
|
|
5242
7127
|
controller.installSessionJumper(createSessionJumper({
|
|
5243
7128
|
getSessions: () => ctx.get?.("sessions"),
|
|
5244
7129
|
getWorkspaces: () => ctx.get?.("workspaces")
|
|
@@ -5254,6 +7139,7 @@ window.__ModuleLoader__.load({
|
|
|
5254
7139
|
ctx.effect?.(() => () => {
|
|
5255
7140
|
for (const d of disposers.splice(0)) d();
|
|
5256
7141
|
controller.dispose();
|
|
7142
|
+
disposeI18n();
|
|
5257
7143
|
}, "dsh-taskboard: client mount");
|
|
5258
7144
|
} catch (error) {
|
|
5259
7145
|
console.error("[dsh-taskboard] client half failed to start:", error);
|