style(web): 运行态标识精简为纯脉冲圆点——文案收进 hover title(bump 0.38.5)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-07-03 16:43:58 +08:00
parent 640bd0a1a3
commit 7e6159af48
4 changed files with 18 additions and 13 deletions

View File

@ -21,6 +21,9 @@
## 已完成关键能力 ## 已完成关键能力
### 2026-07-03 / web 运行态标识精简为纯脉冲圆点(bump 0.38.5)
用户反馈「运行中」等文字让列表 meta 行太拥挤。标识收成一个 7px 带色脉冲圆点(绿=运行中/橙=停止中/红=出错),文案全部移进 hover title(error 仍带 run_error 详情);圆点在 baseline 对齐的 meta 行里补 `align-self:center`。改 `web/static/js/chat.js` + `web/static/dev.html`
### 2026-07-03 / web 后台 running task 自动挂 SSE——运行态标识刷新页面后也实时(bump 0.38.4) ### 2026-07-03 / web 后台 running task 自动挂 SSE——运行态标识刷新页面后也实时(bump 0.38.4)
0.38.3 留的边界:刷新页面(liveRuns 清空)或 run 由别的标签页/渠道启动时,列表标识只是服务端快照,run 跑完没人通知前端,会一直挂「运行中」。用户点出方向:别轮询,直接复用 SSE。改法:`loadTaskList` 收尾新增 `subscribeRunningRows`——列表带出的 running/cancelling 行,本地未订阅的自动 `ensureRunningTaskSubscribed` 挂上事件流(上限 4 条后台流,防 HTTP/1.1 同源连接数被占满;超限行标识仍显示只是不自动清),done/error 走 fetchSse 现有收尾(清 liveRuns + 就地清标识 + 重拉列表),全程实时零轮询。配套两处:`ensureRunningTaskSubscribed` 的 cancelling/workingDir 从"读全局 state.taskMeta"改为调用方传 seed(taskMeta 或列表行)——后台 task 的媒体产物 rel 解析必须用各自 working_dir;`renderLiveRunIfVisible` 只在订阅的是选中 task 时才调(后台订阅不碰对话区,否则重挂卡 + 强制滚底误伤正看着的对话)。附带收益:刷新后切进 running task,直播卡带着后台累计的文字直接可见(renderMessages 收尾 renderLiveRunIfVisible 挂卡)。只改 `web/static/js/chat.js` 0.38.3 留的边界:刷新页面(liveRuns 清空)或 run 由别的标签页/渠道启动时,列表标识只是服务端快照,run 跑完没人通知前端,会一直挂「运行中」。用户点出方向:别轮询,直接复用 SSE。改法:`loadTaskList` 收尾新增 `subscribeRunningRows`——列表带出的 running/cancelling 行,本地未订阅的自动 `ensureRunningTaskSubscribed` 挂上事件流(上限 4 条后台流,防 HTTP/1.1 同源连接数被占满;超限行标识仍显示只是不自动清),done/error 走 fetchSse 现有收尾(清 liveRuns + 就地清标识 + 重拉列表),全程实时零轮询。配套两处:`ensureRunningTaskSubscribed` 的 cancelling/workingDir 从"读全局 state.taskMeta"改为调用方传 seed(taskMeta 或列表行)——后台 task 的媒体产物 rel 解析必须用各自 working_dir;`renderLiveRunIfVisible` 只在订阅的是选中 task 时才调(后台订阅不碰对话区,否则重挂卡 + 强制滚底误伤正看着的对话)。附带收益:刷新后切进 running task,直播卡带着后台累计的文字直接可见(renderMessages 收尾 renderLiveRunIfVisible 挂卡)。只改 `web/static/js/chat.js`

View File

@ -1,3 +1,3 @@
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
# 改版本只动这一行。 # 改版本只动这一行。
__version__ = "0.38.4" __version__ = "0.38.5"

View File

@ -560,13 +560,14 @@
display: inline-block; padding: 0 6px; border-radius: var(--r-md); font-size: 11px; display: inline-block; padding: 0 6px; border-radius: var(--r-md); font-size: 11px;
background: #eef; color: #336; background: #eef; color: #336;
} }
/* 运行态标识(run_status):running 绿脉冲点、cancelling 橙、error 红(hover 出 run_error)。 /* 运行态标识(run_status):纯脉冲圆点,running 绿、cancelling 橙、error 红,文案在 hover title
数据源 = /v1/tasks 行 run_status + 本地 liveRuns 叠加;run 开始/停止就地 patch,结束随列表重拉清掉 */ (meta 行拥挤,不放文字)。数据源 = /v1/tasks 行 run_status + 本地 liveRuns 叠加;
.task-row .meta .run-ind { flex-shrink: 0; display: inline-flex; align-items: center; gap: 4px; } run 开始/停止就地 patch,结束随列表重拉清掉 */
.task-row .meta .run-ind { flex-shrink: 0; display: inline-flex; align-self: center; }
.run-ind.running { color: var(--c-green); } .run-ind.running { color: var(--c-green); }
.run-ind.cancelling { color: var(--c-orange); } .run-ind.cancelling { color: var(--c-orange); }
.run-ind.error { color: var(--c-red); } .run-ind.error { color: var(--c-red); }
.run-ind .run-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; } .run-ind .run-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.run-ind.running .run-dot, .run-ind.cancelling .run-dot { animation: run-pulse 1.2s ease-in-out infinite; } .run-ind.running .run-dot, .run-ind.cancelling .run-dot { animation: run-pulse 1.2s ease-in-out infinite; }
@keyframes run-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } } @keyframes run-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
.badge.completed { background: var(--c-green-bg); color: var(--c-green); } .badge.completed { background: var(--c-green-bg); color: var(--c-green); }

View File

@ -142,18 +142,19 @@ function taskRunState(t) {
return t.run_status || "idle"; return t.run_status || "idle";
} }
const RUN_IND = { // 只渲一个带色脉冲圆点(绿=运行中/橙=停止中/红=出错),文案收进 hover title——
running: { label: "运行中", title: "正在执行(调用工具 / 回复中)" }, // 列表 meta 行本就拥挤,多两个字就挤断行
cancelling: { label: "停止中", title: "正在停止" }, const RUN_IND_TITLE = {
error: { label: "出错", title: "" }, // title 动态填 run_error running: "运行中(调用工具 / 回复中)",
cancelling: "停止中",
error: "", // 动态填 run_error
}; };
function runIndicatorHtml(t) { function runIndicatorHtml(t) {
const rs = taskRunState(t); const rs = taskRunState(t);
const cfg = RUN_IND[rs]; if (!(rs in RUN_IND_TITLE)) return ""; // idle → 不显示
if (!cfg) return ""; // idle → 不显示 const title = rs === "error" ? (t.run_error || "上次执行出错") : RUN_IND_TITLE[rs];
const title = rs === "error" ? (t.run_error || "上次执行出错") : cfg.title; return `<span class="run-ind ${rs}" title="${escapeHtml(title)}"><span class="run-dot"></span></span>`;
return `<span class="run-ind ${rs}" title="${escapeHtml(title)}"><span class="run-dot"></span>${cfg.label}</span>`;
} }
// run 开始 / 停止请求后就地刷新对应列表行的运行态标识(不重拉列表 — loadTaskList reset // run 开始 / 停止请求后就地刷新对应列表行的运行态标识(不重拉列表 — loadTaskList reset