style(web): 列表状态灯挪到文件夹行左侧,数据行 space-between 均匀分布(bump 0.38.8)

终态徽章 + 运行圆点放进文件夹行行首(无文件夹行回落数据行,patch 逻辑同规则
找 host);底部数据行剩纯数据均匀铺开,时间自然落行尾。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-07-03 16:58:56 +08:00
parent 259dde502d
commit d24165a2fe
4 changed files with 22 additions and 13 deletions

View File

@ -21,6 +21,9 @@
## 已完成关键能力
### 2026-07-03 / web 列表状态灯挪到文件夹行左侧,数据行均匀分布(bump 0.38.8)
用户建议:状态放文件夹名左侧、时间那行正常分布。落地:终态徽章 + 运行圆点挪进文件夹行行首(`● 📁 ppt4`,行首左上区最先被扫到;无文件夹行的 task 回落到数据行行首,`syncTaskRowRunIndicator` 按同规则找 host:`.wd-line` 优先、`.meta.stats` 兜底);底部数据行只剩纯数据(skill/条/tok/时间),改 `justify-content:space-between` 均匀铺开,时间自然落行尾。改 `web/static/js/chat.js` + `web/static/dev.html`
### 2026-07-03 / web 列表 meta 行数字组改靠左跟排——修 active 静默后的左侧"缺口"(bump 0.38.7)
用户发截图:0.38.6 active 徽章静默后,无 skill 的行(列表主体)meta 行左槽空了,数字组(条/tok)又被 `.num.right-group{margin-left:auto}` 整组挤右,中间留出一块像缺了东西。修:数字组改靠左跟排填上左槽,只有 time-ago 锚行尾(`margin-left:auto` 移到 time-ago);模板删掉已无意义的 `right-group` class。"条/tok"跨行对齐由原有 min-width+右对齐槽位保持。改 `web/static/dev.html` + `web/static/js/chat.js`

View File

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

View File

@ -552,10 +552,13 @@
align-items: baseline; font-variant-numeric: tabular-nums; }
.task-row .meta > * { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.task-row .meta .badge { flex-shrink: 0; }
/* 数字组靠左跟排(active 徽章静默后左槽常空,整组右挤会留出一块"缺口");
固定 min-width + 右对齐让"条/tok"跨行对齐;只有时间锚在行尾 */
/* 底部数据行(stats):状态已挪去文件夹行,剩纯数据均匀铺开,时间自然落行尾;
固定 min-width + 右对齐让"条/tok"跨行对齐 */
.task-row .meta.stats { justify-content: space-between; }
.task-row .meta .num { flex-shrink: 0; text-align: right; min-width: 44px; }
.task-row .meta .time-ago { flex-shrink: 0; text-align: right; min-width: 64px; margin-left: auto; }
.task-row .meta .time-ago { flex-shrink: 0; text-align: right; min-width: 64px; }
/* 状态灯(终态徽章 + 运行圆点)在文件夹行(块级文本流)内:垂直居中 + 与 📁 留距 */
.task-row .wd-line .badge, .task-row .wd-line .run-ind { vertical-align: middle; margin-right: 4px; }
.task-row .badge {
display: inline-block; padding: 0 6px; border-radius: var(--r-md); font-size: 11px;
background: #eef; color: #336;

View File

@ -163,15 +163,16 @@ function runIndicatorHtml(t) {
function syncTaskRowRunIndicator(tid) {
const row = document.querySelector(`.task-row[data-tid="${CSS.escape(tid)}"]`);
if (!row) return;
const metaLine = row.querySelector(".meta:not(.muted)");
if (!metaLine) return;
const old = metaLine.querySelector(".run-ind");
// 圆点 host 与渲染时同规则:优先文件夹行,无文件夹行回落底部数据行
const host = row.querySelector(".wd-line") || row.querySelector(".meta.stats");
if (!host) return;
const old = host.querySelector(".run-ind");
if (old) old.remove();
const html = runIndicatorHtml((state.tasksById || {})[tid] || { task_id: tid });
if (!html) return;
const badge = metaLine.querySelector(".badge");
const badge = host.querySelector(".badge");
if (badge) badge.insertAdjacentHTML("afterend", html);
else metaLine.insertAdjacentHTML("afterbegin", html);
else host.insertAdjacentHTML("afterbegin", html);
}
function renderTaskList(tasks, append = false) {
@ -195,16 +196,18 @@ function renderTaskList(tasks, append = false) {
// st- 前缀防撞 .task-row.active(选中态)——status 值 "active" 不能直接当 class 用
const stCls = statusLabel ? ` st-${t.status}` : "";
const rowTitle = `${taskName}\n${t.task_id}`; // hover 出全名 + 完整 id(替代 meta 里被去掉的 id8)
// 状态(终态徽章 + 运行圆点)放文件夹行左侧——行首左上区最先被扫到;
// 无文件夹行的 task 回落到底部数据行行首(syncTaskRowRunIndicator 同规则找 host)
const indHtml = `${statusLabel ? `<span class="badge ${t.status}">${statusLabel}</span>` : ""}${runIndicatorHtml(t)}`;
// 渠道镜像 task(微信 / 企业微信)不进此列表 —— 后端 /v1/tasks 已排除,改由左栏卡片承载(loadChannelCards)
return `
<div class="task-row${active}${stCls}" data-tid="${t.task_id}" title="${escapeHtml(rowTitle)}" style="display:flex;align-items:flex-start;gap:6px;">
<div style="flex:1;min-width:0;">
<div class="desc">${escapeHtml(taskName)}</div>
${wdName ? `<div class="meta muted" title="${escapeHtml(t.working_dir || "")}" style="display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">📁 ${escapeHtml(wdName)}</div>` : ""}
${wdName ? `<div class="meta muted wd-line" title="${escapeHtml(t.working_dir || "")}" style="display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${indHtml}📁 ${escapeHtml(wdName)}</div>` : ""}
${desc ? `<div class="meta muted" title="${escapeHtml(desc)}" style="display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${escapeHtml(desc)}</div>` : ""}
<div class="meta">
${statusLabel ? `<span class="badge ${t.status}">${statusLabel}</span>` : ""}
${runIndicatorHtml(t)}
<div class="meta stats">
${wdName ? "" : indHtml}
${t.skill ? `<span class="muted" title="${escapeHtml(t.skill)}">${escapeHtml(t.skill)}</span>` : ""}
<span class="num">${t.n_messages || 0} </span>
<span class="num" title="${escapeHtml(taskUsageTooltip(t))}">${fmtTokens(t.tokens)} tok</span>