Skip to content

fix(renderer): 彻底修复编辑超长消息时视口吸附顶部的 DOM 塌陷与滚动截断问题 PR 说明正文: - #221

Merged
lioensky merged 1 commit into
lioensky:mainfrom
infinite-vector:fix/message-edit-viewport-scroll-jump
Oct 1, 2026
Merged

lioensky merged 1 commit into
lioensky:mainfrom
infinite-vector:fix/message-edit-viewport-scroll-jump

Conversation

@infinite-vector

Copy link
Copy Markdown
Contributor

动机与背景 (Motivation)

在编辑长度超过视口高度的长消息时,当用户滚动至消息中下部键入字符、换行或操作光标时,视口会被强制拉回消息顶部附近的固定偏移位置。
根因分析表明:

  1. uiHelperFunctions.autoResizeTextarea 在测量高度时执行了 textarea.style.height = 'auto',导致多行文本框高度瞬间骤缩;
  2. 这一瞬间造成外层滚动容器(.chat-messages-container)总内容高度剧烈崩塌,触发 Chromium 底层 scrollTop <= scrollHeight - clientHeight 约束,无条件将 scrollTop 强制截断(Clamping)到顶部附近;
  3. 随后代码即便恢复了真实内容高度,但已被截断的 scrollTop 无法自发还原,导致视口剧烈跳动。

解决方案与架构 (Solution)

遵循“解决问题而非设置限制”的原则,不施加任何最大高度限制,长文本依然支持无界自由展开,采用双轨治本机制:

  1. 视口锚定与父级防塌陷保护 (modules/ui-helpers.js):
    • 在将 style.height 重置为 auto 之前,捕获向上最近滚动容器的真实 savedScrollTop;
    • 提取 textarea 当前高度,在父容器上临时挂载 minHeight 撑住物理空间,阻止父级滚动条空间瞬间崩塌;
    • 高度重算完成后释放临时支撑,并校验还原 savedScrollTop,维持单帧内滚动坐标的单调性与连续性。
  2. Chromium 原生排版引擎支持 (styles/chat.css):
    • 为 .message-edit-textarea 启用现代 field-sizing: content 与 overflow-y: hidden、resize: vertical,交由排版引擎原生自适应高度,免除 JS 回流抖动。
  3. 焦点保护 (modules/renderer/messageContextMenu.js):
    • 进入编辑模式瞬间调用 textarea.focus({ preventScroll: true }),杜绝因焦点捕获引发的异常位移。

验证情况 (Testing)

  • 在本地多条包含几千字 Markdown 与代码块的长消息下进行了极端打字、换行、选区拖拽与光标移动实测;
  • 无论光标位于长消息的顶部、中部还是最底部,外层聊天视口坚如磐石,完全杜绝吸顶跳动;
  • 代码审查无新增语法或作用域警告,换行符风格严格对齐。

v工程真好用,许愿式修bug真不赖。

@lioensky
lioensky merged commit c879e82 into lioensky:main Oct 1, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants