fix(web): 消息目录点第一个圆点误高亮第二个 + bump 0.20.4
跳转锚点(block:center)与活跃判定锚点(顶线 80px)不一致:第一轮 上方无内容无法居中、被钉到顶端,短轮时下一轮卡片顶也落进 80px 带内 → 越界高亮第二个圆点。改跳转为 block:start(同锚点)+ .msg scroll-margin-top:16px,活跃容差 80→24 对齐。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
247a887cd6
commit
d412aa6b24
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。
|
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。
|
||||||
|
|
||||||
最后更新:2026-06-22(前端修复:定时弹窗 z-index 遮挡 + 登录 focus 引用错 id + bump 0.20.3)
|
最后更新:2026-06-23(前端修复:消息目录点第一个圆点误高亮第二个 + bump 0.20.4)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -21,6 +21,12 @@
|
||||||
|
|
||||||
## 已完成关键能力
|
## 已完成关键能力
|
||||||
|
|
||||||
|
### 2026-06-23 / 消息目录定位错位修复(bump 0.20.4)
|
||||||
|
|
||||||
|
- 现象:点右侧圆点轨道**第一个**圆点,活跃高亮常落到**第二个**。根因是两套锚点不一致——`jumpToMessage` 用 `block:"center"` 居中,但第一轮上方无内容无法居中、被钉到顶端;而 `updateActiveOutlineDot` 按「顶线 80px 容差」判活跃轮,第一轮短时下一轮卡片顶也落进 80px 带内 → 越界高亮第二个圆点(滚动监听又覆盖了 jumpToMessage 的显式 setActiveOutlineIdx)。
|
||||||
|
- 修复:跳转改 `block:"start"`(顶部对齐,与活跃判定同锚点)+ `.msg` 加 `scroll-margin-top:16px` 留呼吸;活跃容差 80→24 与之对齐,贴顶短轮判到自己不越界。
|
||||||
|
- 文件:`web/static/js/chat.js`(`jumpToMessage` / `updateActiveOutlineDot`)、`web/static/dev.html`(`.msg` CSS);`core/__init__.py` 0.20.3→0.20.4。
|
||||||
|
|
||||||
### 2026-06-22 / 前端两处 bug 修复(bump 0.20.3)
|
### 2026-06-22 / 前端两处 bug 修复(bump 0.20.3)
|
||||||
|
|
||||||
- 定时弹窗"被遮挡":`#crons-modal` 漏了 z-index,退回基础 `.modal`(无 z-index)被 z-index:5 的侧栏/面板盖住;补 `z-index: 112` 与兄弟只读 modal(`#skills-modal`/`#memory-modal`)对齐。排查用 node 加 DOM mock 跑通整条前端模块图,确认 `hd-crons` 绑定确实执行(排除了"按钮没绑事件"),定位到纯 CSS 层叠问题。
|
- 定时弹窗"被遮挡":`#crons-modal` 漏了 z-index,退回基础 `.modal`(无 z-index)被 z-index:5 的侧栏/面板盖住;补 `z-index: 112` 与兄弟只读 modal(`#skills-modal`/`#memory-modal`)对齐。排查用 node 加 DOM mock 跑通整条前端模块图,确认 `hd-crons` 绑定确实执行(排除了"按钮没绑事件"),定位到纯 CSS 层叠问题。
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
|
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
|
||||||
# 改版本只动这一行。
|
# 改版本只动这一行。
|
||||||
__version__ = "0.20.3"
|
__version__ = "0.20.4"
|
||||||
|
|
|
||||||
|
|
@ -591,7 +591,7 @@
|
||||||
.ol-dot.active::after { background: var(--accent); width: 20px; }
|
.ol-dot.active::after { background: var(--accent); width: 20px; }
|
||||||
/* 阅读宽度:assistant/system/tool 限到 ~48rem(约 60-80 字/行,长文不至于满屏铺开难回扫);
|
/* 阅读宽度:assistant/system/tool 限到 ~48rem(约 60-80 字/行,长文不至于满屏铺开难回扫);
|
||||||
user 气泡更窄(36rem)。宽屏下提升可读性,窄屏 92% 仍生效(min 取小者) */
|
user 气泡更窄(36rem)。宽屏下提升可读性,窄屏 92% 仍生效(min 取小者) */
|
||||||
.msg { border: 1px solid var(--border); border-radius: var(--r-md); padding: 8px 12px; max-width: min(92%, 48rem); animation: msg-in .22s cubic-bezier(.2,.7,.2,1); }
|
.msg { border: 1px solid var(--border); border-radius: var(--r-md); padding: 8px 12px; max-width: min(92%, 48rem); animation: msg-in .22s cubic-bezier(.2,.7,.2,1); scroll-margin-top: 16px; }
|
||||||
.msg.user { background: var(--user-bg); align-self: flex-end; max-width: min(92%, 36rem); }
|
.msg.user { background: var(--user-bg); align-self: flex-end; max-width: min(92%, 36rem); }
|
||||||
.msg.assistant, .msg.system, .msg.tool, .msg.error { background: var(--asst-bg); align-self: flex-start; }
|
.msg.assistant, .msg.system, .msg.tool, .msg.error { background: var(--asst-bg); align-self: flex-start; }
|
||||||
.msg.error { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
|
.msg.error { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
|
||||||
|
|
|
||||||
|
|
@ -532,7 +532,10 @@ async function jumpToMessage(idx) {
|
||||||
card = wrap.querySelector(`.msg[data-idx="${idx}"]`);
|
card = wrap.querySelector(`.msg[data-idx="${idx}"]`);
|
||||||
}
|
}
|
||||||
if (!card) return;
|
if (!card) return;
|
||||||
card.scrollIntoView({ behavior: "smooth", block: "center" });
|
// 顶部对齐(非居中):第一轮上方无内容无法居中、会被钉到顶端,而 updateActiveOutlineDot
|
||||||
|
// 按「顶线」判活跃轮 —— 两套锚点必须一致,否则贴顶时活跃圆点会越界到下一轮。
|
||||||
|
// .msg 的 scroll-margin-top 给卡片留一点上方呼吸空间。
|
||||||
|
card.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
card.classList.add("msg-jump-flash");
|
card.classList.add("msg-jump-flash");
|
||||||
setTimeout(() => card.classList.remove("msg-jump-flash"), 1200);
|
setTimeout(() => card.classList.remove("msg-jump-flash"), 1200);
|
||||||
setActiveOutlineIdx(idx);
|
setActiveOutlineIdx(idx);
|
||||||
|
|
@ -604,7 +607,9 @@ function updateActiveOutlineDot() {
|
||||||
for (const it of (state.outline || [])) {
|
for (const it of (state.outline || [])) {
|
||||||
const card = wrap.querySelector(`.msg[data-idx="${it.idx}"]`);
|
const card = wrap.querySelector(`.msg[data-idx="${it.idx}"]`);
|
||||||
if (!card) continue;
|
if (!card) continue;
|
||||||
if (card.getBoundingClientRect().top - top <= 80) activeIdx = it.idx;
|
// 容差与 .msg 的 scroll-margin-top(16px)对齐:贴顶的短第一轮判到自己,不越界
|
||||||
|
// 到下一轮(80px 太宽:短轮次时下一轮卡片顶也落进带内 → 误高亮第二个圆点)。
|
||||||
|
if (card.getBoundingClientRect().top - top <= 24) activeIdx = it.idx;
|
||||||
else break; // outline 升序,首个落在视口下方的之后都更靠下
|
else break; // outline 升序,首个落在视口下方的之后都更靠下
|
||||||
}
|
}
|
||||||
if (activeIdx != null) setActiveOutlineIdx(activeIdx);
|
if (activeIdx != null) setActiveOutlineIdx(activeIdx);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue