Repository navigation
fix(chat): preserve latest reply through layout changes - #7377
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: actor_kind=model_agent; model=gpt-6.1-sol; provider=OpenAI; runtime_reported; reasoning_effort=max
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
详细中文评审
动机
用户在管家或 Goal 对话中缩小窗口、继续阅读回复时会遇到这个问题。 窗口从 982px 缩到 600px 时,旧版把最新回复留在可视区外;修复后仍停在末尾,用户上翻后则保留阅读位置。 在桌面和窄屏的源码及打包场景中,最新回复随布局变化保持可见,回到最新消息按钮仍可恢复跟随。 本 PR 不声称完成全量历史分页、真实 provider 投递或 App/飞书媒体双向同步。 已安装原生输入验收尚未通过;完整历史、真实媒体和 provider 验收继续由既有会话同步工作负责。
改动思路
跟随意图、实际滚动位置与 DOM 布局都属于既有共享对话组件;在这里观察尺寸并合并到一个动画帧,比新建滚动服务或持久状态更小。 本 PR 只修复对话布局变化时的阅读位置,复用原按钮和既有图片场景;不改变 Session、请求、授权或执行者。 原来的消息变化路径没有覆盖窗口变矮这种普通布局变化;只监听窗口又覆盖不了图片或展开正文引发的时间线变化。新的观察回调先检查原跟随意图,再合并到一个动画帧,执行时再次检查;组件切换或卸载时断开观察并取消帧。这样既能响应布局,也保留用户主动阅读历史的选择。无需新 Session、持久阅读位置、设置或授权确认。
| 当前状态与触发 | 处理 | 用户可见结果 |
|---|---|---|
| 停在最新回复,窗口变矮 | 观察尺寸,合并并执行跟随 | 当前回复仍可见 |
| 上翻阅读历史后缩放 | 保留原暂停跟随意图 | 阅读位置保持 |
| 点击既有回到最新消息 | 原滚动 owner 恢复跟随 | 回到当前回复 |
此表是精确头 ed2f5c9b19f2a260616b83104a681faefa826764 的源码说明;执行证据是既有源码/打包浏览器场景,表本身不充当运行证明。
具体改动
判断依据为修改前的 docs/architecture/rfcs/app-conversation-and-async-inbox-v0.zh-CN.md,spec_revision c56f55307e87110e54d9a82c868d3649280f22fb,没有用本次新补的说明反向定义验收。三个既有要求分别映射:scroll-follow(底部跟随)由尺寸观察与原消息跟随共同落实;history-read-position(上翻保持位置)由向上移动判断与帧前复查落实;return-to-latest(回到最新消息)仍沿用原按钮。新增英文与中文段落只补充这一布局边界,并明确完整同步仍待验收。
关键代码讲解
scrollToLatestMessage把当前 scroller 移到底部后,记录浏览器实际夹紧后的 scrollTop;相同函数仍服务既有最新消息按钮,避免自写入被下一次事件误判为用户上翻。PersonalWorkspacePage latest-follow effect沿用消息依赖,额外观察 viewport 和时间线。重复通知取消前一个帧,帧执行前再次检查跟随意图,清理时断开 ResizeObserver 并取消未执行帧,不创建新的流或执行器。personal-channel-scroll onScroll在接近底部时保持跟随,离开底部且实际位置向上时暂停。内容增长或视口缩小本身不会被认作用户上翻。记忆值只属于当前 DOM,随原对话切换重置。
conversation-image-request 在原图片、操作卡顺序、拒绝恢复与零持久操作断言上加入窗口缩小及阅读历史后缩放两条相反轨迹。8个文件的90行增加/5行删除中,生产增量29行,其余是原场景、双语说明与三个未经编辑的合成截图;没有提交生成 bundle、私有历史或原生日志。调用者仍是 DashboardPage 的既有共享对话组件;搜索未发现同资源的第二个滚动策略 owner,其他 ResizeObserver 测量的是不同面板。
对主干的风险
主要反例是排队中的布局帧在用户上翻后继续拉回底部。实现同时在安排与执行时检查意图,并以真实向上移动暂停;源码及打包的桌面/窄屏场景验证了这一相反路径。关闭对话不安装该观察,cleanup 断开并取消帧,未改变发送、图片准入、会话存储、委派、权限或扣额。这是默认展示修复,没有新增 opt-in/default-off 声明;不需要虚构一组开关资格测试。新增数字 ref 是类型化的临时 DOM 投影,不是第二套业务真相或关键词状态分类。
验证结果:新窗口缩小 oracle 在不可变基线先失败,修复后源码与完整 npm run build:chat 打包场景在1512和390宽度通过;TypeScript、双语 RFC、标准 exact-head premerge 的4项直接与17项选中检查零执行失败。三张PNG已看像素并检查仅IHDR/IDAT/IEND,没有文本/身份元数据,自动文本扫描通过;premerge 的 manual_review_required 保留为人工图片资格,而非自动合并许可。
两项相邻浏览器失败未掩盖:activity 在基线和头均找不到 overview tray elapsed;history recovery 在基线和复跑头均为 projection-only identity 的0!=1断言,首次头曾在重叠刷新等待超时。它们没有计为通过或已修复;本改动不触及 elapsed 渲染或历史身份投影,当前 changed invariant 由独立源码/打包正反例覆盖。第一次 Vite-only 打包缺清单,完整构建后的检查才通过。wait_for_ci=false,未查询或等待CI。远端主干更新未改这个滚动 owner 或 RFC 对;未假设旧完整历史已经合格。
我的整体评价
APPROVE,精确头 ed2f5c9b19f2a260616b83104a681faefa826764,属于 justified_increment。long_horizon 为 preserved:观察器只管理当前视图并有生命周期清理,不改变执行、排队或结果回传;user_experience 为 improved:正常缩放后无需再发消息或多一次确认即可继续读回复。已检查合成桌面修改前后与窄屏完整截图:回复正文、图片和底部输入区可同时辨认;历史仍可通过上翻访问。 未核实已安装原生输入、真实 provider、完整历史分页和真实飞书媒体双向送达。 这项修复可独立回滚与验收,原完整同步由既有工作继续;没有为了局部绿色结果声明整体完成。机制必要且范围相称,复用既有 owner/场景,无新增协议或兼容分支,也没有共享语义枚举和预算变化。自评结论不等于GitHub正式APPROVED或合并授权,需继续按现有授权核验当前头 readiness。
英文简短结论
English verdict: APPROVE — ed2f5c9b19f2a260616b83104a681faefa826764. The existing shared conversation owner now follows viewport/timeline changes without stealing intentional history reading. Base-red/head-green source and complete packaged desktop/narrow checks cover the changed invariant. Unchanged adjacent failures and native/provider/full-history gaps remain explicit; merge readiness and authority are separate.
When the conversation window becomes shorter, the newest reply can disappear below the viewport until another message arrives. Keep following the end when the viewport or timeline changes size, and preserve the reading position after the user scrolls up. The existing “回到最新消息” button resumes following.
The change stays in the shared conversation view: one coalesced animation frame and a resize observer for the viewport/timeline, with cleanup and a second follow check before scrolling. Extend the existing image-conversation scenario rather than add another runner. Update both RFC mirrors with the validated boundary.
Validation
c56f55307e87110e54d9a82c868d3649280f22fband passes with this fix, at desktop 1512px and narrow 390px widths. The same scenario verifies intentional history reading stays put, explicit latest navigation, exact image handling and rejected-draft recovery.npm run build:chatbuild and the packaged image-conversation scenario pass. An initial Vite-only build omitted the bundle manifest; that failed packaged attempt is retained, and the complete build was used for the passing run.conversation-activitycannot find the overview tray elapsed label on both base and head;conversation-history-recoveryfails its projection-only stored identity assertion on both base and a repeated head run. The first head history run instead timed out awaiting a refresh response. None is counted as passed or fixed here.wait_for_ci=false: CI was not queried or awaited.Visual evidence
Tested source revision:
ed2f5c9b19f2a260616b83104a681faefa826764. These are public-safe, synthetic browser fixtures with stub provider responses and synthetic operation cards. They are not private conversations, real financial operations or evidence of live Lark media delivery. The fixture header’s delegation-read failure is intentional. The packaged run verifies the same changed behavior after a complete production bundle build.Desktop before (1512 × 600, immutable base production view):
Desktop after (1512 × 600):
Narrow after (390 × 600):
Installed native input acceptance is still unqualified because the desktop automation input returned
noWindowsAvailabledespite a readable running App. Complete App/Lark history pagination, reply/media synchronization and live provider acceptance remain open; this layout fix does not claim their completion.