Commit Graph

2 Commits

Author SHA1 Message Date
c0e9dc1818 fix(webui): 聊天记录不再"每次都发完整记录",并修掉视口跳顶
两个都是你指出的症状,都定位到了具体代码路径。

① 「每次都发完整聊天记录」
   a) API 缺省值错了:/api/v1/chat/history 的 limit 缺省是 0 = **不限制**,
      于是任何不带 limit 的调用每次都拿到整段记录。实测(126 条):
      不带 limit 635,297 字节;现在默认只回一页 180,668 字节。
      显式 limit=0 仍可整取(逃生口)。WebUI/GUI 本来都带 limit,不受影响。
   b) 前端 30s 轮询(以及每次 SSE 报错)都直接拉一页 40 条:
      浏览器实测单次 180,813 字节。现在先做"尾巴探测"(limit=1,362 字节),
      尾巴一致就直接跳过;不一致才拉整页。

② 「聊天记录会跳到顶部」——两条会导致视口丢失的路径都堵上
   a) syncChatFromHistory 在"找不到重合点"时直接 `state.messages = serverMsgs`:
      服务端只回一页,而本地可能已经向上翻了好几页;一覆盖,容器立刻变矮,
      视口被夹回顶部,用户翻过的旧消息也凭空消失。现在只在服务端页**不短于**本地时
      才整体替换。
   b) renderChat 全量重建 innerHTML 后,仅在粘底时滚到底;非粘底(用户正在向上读)
      时位置没人管。改为重建前记住 scrollTop、非粘底时原样还回去。

浏览器实测(CDP 驱动真实页面,126 条历史):
  * 15s/30s 定时器跑满 40s:聊天区滚动位置 **0 px 变化**,未跳顶;
  * 期间 chat/history 请求:limit=1 × 2(各 362 字节)+ 首屏 limit=40 一次;
  * 控制台无报错;新增消息后轮询仍能正确并进来(尾巴探测→拉整页→合并)。

测试:新增 TestChatHistoryDefaultIsPaged(默认一页 / has_more / limit=0 整取 / 显式分页)。
顺带修测试串味:迁移用例往 os.TempDir() 写共享历史文件,会让其它用例的
NewHandler 加载到脏历史(表现为条数多 1);现在各用例用自己的临时文件。
2026-09-14 08:06:16 +08:00
764939ed90 refactor(webui): dashboard.html 6637 行拆成「外壳 + 样式 + 脚本」
前端刻意没有构建链(纯 CSS + Vanilla JS,go:embed 进二进制),所以拆法是:
外壳 dashboard.html 留 {{DASHBOARD_CSS}} / {{DASHBOARD_JS}} 两个占位符,
init() 启动时把两份资产原样填回去 —— **发出的 HTML 与拆分前逐字节一致**,
但 6637 行的单文件变成三份,便于编辑与评审。

  dashboard.html   168 行   外壳(head/body 结构 + 两个占位符)
  dashboard.css   2099 行   样式
  dashboard.js    4371 行   脚本

逐字节校验(三重):
  * 组装结果 sha256 == git HEAD 里拆分前的 dashboard.html;
  * 真实实例 GET /(带 API key)返回体 sha256 同上:0ef14b49…c053;
  * 新增 TestDashboardAssetsSplit:占位符必须存在、样式/脚本不得再内联回外壳、
    组装结果不得残留占位符且必须含样式与脚本特征串。

按行号切片时踩过一次坑并已修正:`</style>`/`</script>` 两个闭合标签被切掉
(正好少 27 字节)——正是因为当时少了逐字节校验,现在把它固化成断言。
2026-09-14 07:17:23 +08:00