Dashboard: 修复日志频繁回到顶部 + 展开日志被定时刷新关闭 #40

Closed
opened 2026-09-01 11:37:53 +08:00 by tao.chen · 1 comment
Owner

static/app.js 两个问题:

  1. 执行中日志频繁跳到顶部: renderRunning 每 1.5s 都用 innerHTML 重建 #running-body(包含
    ),导致 scrollTop 归零后再 scrollTop=scrollHeight,因 CSS scroll-behavior:smooth + layout 未完成而表现为先回到顶部再到底部,且若用户曾向上滚动查看历史也会被强制拉回底部。
  2. 执行历史中展开日志被关闭: renderHistory 每 4s 用 innerHTML 重建 #history-list,展开的
     被重置为 display:none,按钮文字也被重置为"查看日志"。

期望行为:

  • 仅当任务身份(script|repo|ref|startedAt)变化时才重建 #running-body 的 DOM 结构;同一任务只更新
    .textContent。
  • 自动滚动改为"贴底跟随":若用户当前已在底部(误差 ≤24px)或本次是新任务,则在 rAF 中 scrollTop=scrollHeight;否则保持用户滚动位置不变。
  • 点击"自动滚动"按钮开启时,立即把滚动条拉到最新位置。
  • 用 state.expandedLogs:Set 记录展开状态,renderHistory 后按集合恢复每个 log-cell 的 display 与按钮文字。

Task: fumwhk1zoqb4033a0dj6u2z7

static/app.js 两个问题: 1. 执行中日志频繁跳到顶部: renderRunning 每 1.5s 都用 innerHTML 重建 #running-body(包含 <pre id="live-log">),导致 scrollTop 归零后再 scrollTop=scrollHeight,因 CSS scroll-behavior:smooth + layout 未完成而表现为先回到顶部再到底部,且若用户曾向上滚动查看历史也会被强制拉回底部。 2. 执行历史中展开日志被关闭: renderHistory 每 4s 用 innerHTML 重建 #history-list,展开的 <pre id="log-${id}"> 被重置为 display:none,按钮文字也被重置为"查看日志"。 期望行为: - 仅当任务身份(script|repo|ref|startedAt)变化时才重建 #running-body 的 DOM 结构;同一任务只更新 <pre>.textContent。 - 自动滚动改为"贴底跟随":若用户当前已在底部(误差 ≤24px)或本次是新任务,则在 rAF 中 scrollTop=scrollHeight;否则保持用户滚动位置不变。 - 点击"自动滚动"按钮开启时,立即把滚动条拉到最新位置。 - 用 state.expandedLogs:Set<recordId> 记录展开状态,renderHistory 后按集合恢复每个 log-cell 的 display 与按钮文字。 --- <sub>Task: fumwhk1zoqb4033a0dj6u2z7</sub>
tao.chen added the priority:mediumstatus:in-progress labels 2026-09-01 11:38:06 +08:00
tao.chen added status:done and removed status:in-progress labels 2026-09-01 11:48:31 +08:00
Author
Owner

tao.chen commented:

实施 & 验证

变更范围: 仅 static/app.js(255 → 299 行,+65/-20)。

关键改动:

  1. state 新增 expandedLogs: Set<id>lastLiveKey 两个字段。
  2. renderRunning()state.lastLiveKey !== liveKey 时才用 innerHTML 重建 #running-body;同一任务仅更新 #live-log.textContent,DOM 节点稳定。
  3. 自动滚动改为"贴底跟随":更新前算 distanceFromBottom = scrollHeight - scrollTop - clientHeight,≤24 视为贴底,在 rAF 中 scrollTop = scrollHeight;否则保留用户位置。
  4. #log-autoscroll 按钮开启时,在 rAF 中立即把 #live-log 滚到底。
  5. renderHistory()state.expandedLogs 作为单一事实源,re-render 后按集合恢复 <pre> 显示与按钮文字;.toggle-log 按钮新增 data-id="${r.id}"

验证:

  • node --check static/app.js → 通过
  • go build . → 通过(embed.FS 已重新嵌入更新后的 app.js)
  • jsdom 运行时实测(Issue 1):
    • 同一任务连续 5 次 pollQueue,body.innerHTML 重建次数 = 0
    • 用户上滚(dFB=640)后再轮询,scrollTop 保持 0,不被拉回
    • dFB=40(>24)时不滚动;dFB=20(≤24)时贴底
  • 静态分析(Issue 2):expandedLogs Set 在 renderHistory 中完整使用,toggle-log 按钮已暴露 data-id,re-render 后恢复展开/收起状态
  • Codex 报告 31/31 项 jsdom 功能测试覆盖全部 acceptance 条目
  • 无 console 报错,无新依赖,未改 public API 或 CSS/HTML/Go 文件
**tao.chen** commented: > ## 实施 & 验证 **变更范围**: 仅 `static/app.js`(255 → 299 行,+65/-20)。 **关键改动**: 1. `state` 新增 `expandedLogs: Set<id>` 与 `lastLiveKey` 两个字段。 2. `renderRunning()` 在 `state.lastLiveKey !== liveKey` 时才用 `innerHTML` 重建 `#running-body`;同一任务仅更新 `#live-log.textContent`,DOM 节点稳定。 3. 自动滚动改为"贴底跟随":更新前算 `distanceFromBottom = scrollHeight - scrollTop - clientHeight`,≤24 视为贴底,在 rAF 中 `scrollTop = scrollHeight`;否则保留用户位置。 4. `#log-autoscroll` 按钮开启时,在 rAF 中立即把 `#live-log` 滚到底。 5. `renderHistory()` 用 `state.expandedLogs` 作为单一事实源,re-render 后按集合恢复 `<pre>` 显示与按钮文字;`.toggle-log` 按钮新增 `data-id="${r.id}"`。 **验证**: - `node --check static/app.js` → 通过 - `go build .` → 通过(`embed.FS` 已重新嵌入更新后的 `app.js`) - jsdom 运行时实测(Issue 1): - 同一任务连续 5 次 `pollQueue`,`body.innerHTML` 重建次数 = 0 - 用户上滚(`dFB=640`)后再轮询,`scrollTop` 保持 0,不被拉回 - `dFB=40`(>24)时不滚动;`dFB=20`(≤24)时贴底 - 静态分析(Issue 2):`expandedLogs` Set 在 renderHistory 中完整使用,toggle-log 按钮已暴露 `data-id`,re-render 后恢复展开/收起状态 - Codex 报告 31/31 项 jsdom 功能测试覆盖全部 acceptance 条目 - 无 console 报错,无新依赖,未改 public API 或 CSS/HTML/Go 文件
Sign in to join this conversation.