Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions emrg/gui/renderer/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -467,9 +467,11 @@ const App = (() => {
} else if (res.result === "running") {
Chat.addSystemMessage(_t("app.taskRunning", { n }));
showToast(_t("app.taskRunning", { n }), { type: "info", durationMs: 4000 });
await Dialogs.refreshTaskListIfOpen(); // rant 2026-08-21T17:41:23:面板开着则立即刷新
} else {
Chat.addSystemMessage(_t("app.triggered", { n }));
showToast(_t("app.triggered", { n }), { type: "success" });
await Dialogs.refreshTaskListIfOpen(); // rant 2026-08-21T17:41:23:面板开着则立即刷新
}
} catch (e) {
Chat.addSystemMessage(_t("app.triggerFailed", { msg: e.message }));
Expand Down
11 changes: 11 additions & 0 deletions emrg/gui/renderer/js/dialogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,14 @@ const Dialogs = (() => {
return wrap;
}

async function refreshTaskListIfOpen() {
// rant 2026-08-21T17:41:23:触发任务后表格不刷新(状态/上次运行/倒计时滞后)。
// 聊天 /trigger 路径复用:任务面板开着才重渲染;没开就不做(renderTaskList 无谓开销 + 倒计时定时器)。
if (App.state.activeView === "tasks") {
await renderTaskList();
}
}

async function renderTaskList() {
const list = $("task-list");
if (!list) return; // 元素缺失(测试桩)时忽略
Expand Down Expand Up @@ -713,9 +721,11 @@ const Dialogs = (() => {
} else if (res && res.result === "running") {
Chat.addSystemMessage(_t("app.taskRunning", { n: t.name }));
showToast(_t("app.taskRunning", { n: t.name }), { type: "info", durationMs: 4000 });
await renderTaskList(); // rant 2026-08-21T17:41:23:触发后立即刷新表格(状态/上次运行/倒计时不滞后)
} else {
Chat.addSystemMessage(_t("app.triggered", { n: t.name }));
showToast(_t("app.triggered", { n: t.name }), { type: "success" });
await renderTaskList(); // rant 2026-08-21T17:41:23:触发成功后立即刷新表格
}
} catch (e) {
Chat.addSystemMessage(_t("app.triggerFailed", { msg: e.message }));
Expand Down Expand Up @@ -1573,6 +1583,7 @@ const Dialogs = (() => {
initTaskManagement, // rant 18:23:15 P3:定时任务/自定义类型管理初始化
loadTaskMeta, // rant 18:23:15 P3:任务/类型元数据加载(面板打开/测试复用)
renderTaskList, // rant 18:23:15 P3:任务列表渲染(测试/刷新复用)
refreshTaskListIfOpen, // rant 2026-08-21T17:41:23:任务面板开着才重渲染(聊天 /trigger 路径复用)
formatCountdown, // rant 10:36:39:倒计时格式化(≤60s "43s" / ≤1h "1m23s" / >1h "1h05m";测试复用)
updateTaskCountdowns, // rant 10:36:39:1s tick 更新倒计时文本(测试直接调用模拟走秒)
startTaskCountdown, // rant 10:36:39:启动 1s 倒计时(幂等;renderTaskList 自动调用)
Expand Down
25 changes: 20 additions & 5 deletions emrg/gui/test/renderer.smoke.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2797,16 +2797,22 @@ test("P3:新增任务表单 —— 间隔 <60 客户端拒绝;≥60 提交 t

test("rant 2026-08-15T09:20:27/09:23:10:面板操作反馈走全局 toast(任意视图可见)+ Trigger 三态语义", async () => {
const triggerCalls = [];
let listCalls = 0;
let nightlyRunning = false; // rant 2026-08-21T17:41:23:状态化 mock —— 触发后 nightly 转为 running
const { ctx, els } = makeSandbox({
listTasks: async () => [
{ name: "emrg-task", type: "evolution", running: true, interval: 60 },
{ name: "nightly", type: "custom", running: false, interval: 3600 },
],
listTasks: async () => {
listCalls += 1;
return [
{ name: "emrg-task", type: "evolution", running: true, interval: 60 },
{ name: "nightly", type: "custom", running: nightlyRunning, interval: 3600 },
];
},
listProjects: async () => [{ name: "emrg", path: "/p/emrg" }],
triggerTask: async (payload) => {
triggerCalls.push(payload);
if (payload.name === "emrg-task") return { name: "emrg-task", result: "running", detail: "task is currently executing" };
if (payload.name === "missing") return { error: "task 'missing' not found" };
nightlyRunning = true; // 触发成功 → daemon 侧状态变化(mock 模拟下次 listTasks 返回 running)
return { name: "nightly", result: "triggered", detail: "next run moved to immediately" };
},
});
Expand All @@ -2822,22 +2828,31 @@ test("rant 2026-08-15T09:20:27/09:23:10:面板操作反馈走全局 toast(
const btns1 = rows[1].querySelectorAll(".model-action-btn");
assert.strictEqual(btns0[0].disabled, true, "running 任务触发按钮应禁用");
assert.strictEqual(btns1[0].disabled, false, "空闲任务触发按钮可用");
const callsBefore = listCalls;
// 触发空闲任务 → triggered → success toast(面板视图下 toast 可见)
btns1[0].click();
await tick();
assert.strictEqual(triggerCalls.length, 1, "触发应调用 triggerTask");
assert.strictEqual(triggerCalls[0].name, "nightly");
assert.strictEqual(els["toast"].classList.contains("toast-success"), true, "triggered → success toast");
assert.ok(String(els["toast-msg"].textContent).includes("nightly"), "toast 消息含任务名");
// rant 2026-08-21T17:41:23:触发成功后表格必须立即刷新 —— listTasks 被重拉 + nightly 行出现 running 徽标
assert.ok(listCalls >= callsBefore + 1, "触发后应重新拉取任务列表(renderTaskList)");
const rows2 = vm.runInContext('Array.from(document.getElementById("task-list").children).filter((c) => c.className.includes("task-row"))', ctx);
assert.strictEqual(rows2.length, 2, "刷新后仍两行");
assert.strictEqual(rows2[1].querySelectorAll(".task-running-badge").length, 1, "触发后 nightly 行应显示 running 徽标");
assert.strictEqual(rows2[1].querySelectorAll(".model-action-btn")[0].disabled, true, "触发后 nightly 触发按钮应禁用");
// /trigger 路径:running → info toast(不再假成功)
await vm.runInContext('App.doTrigger("emrg-task")', ctx);
await tick();
assert.strictEqual(els["toast"].classList.contains("toast-info"), true, "running → info toast");
assert.ok(String(els["toast-msg"].textContent).includes("emrg-task"), "info toast 含任务名");
// 触发失败 → error toast
// 触发失败 → error toast(不刷新列表)
const callsBeforeErr = listCalls;
vm.runInContext('App.doTrigger("missing")', ctx);
await tick();
assert.strictEqual(els["toast"].classList.contains("toast-error"), true, "error → error toast");
assert.strictEqual(listCalls, callsBeforeErr, "触发失败不应重拉列表");
});

// ── rant 2026-08-15T10:36:39:任务状态 + 下次运行倒计时 ──
Expand Down
Loading