双层镜像 Reserve 防抖与即时框选复制
重放完整内容时用 Reserve 双层镜像锁住最终高度、不让滚动条跳动,以及用户即时框选复制底线。
双层镜像 Reserve 防抖与即时框选复制
在 AI 原生交互中,最容易被忽视的体验底线是视口稳定性和用户随时复制已经生成文字的自由。
流式生成中的滚动条癫痫缺陷
在传统的流式聊天界面中,随着大模型不断吐字,内容容器每秒增高数像素。 这导致浏览器滚动条不断伸缩颤抖,已生成的内容在视口内上下晃动,用户甚至无法将视线稳定在正在阅读的上一行。
双层镜像 Reserve 占位几何原理
TxStreamText 与 TxStreamElement 都用双层镜像占位(The Reserve Layer Technique):
- 活动呈现层(Live Layer):可见、可读、可操作,随节拍器平滑吐字。
- 预占位镜像层(Reserve Layer):在重放或已知完整尺寸的交互中,挂载
aria-hidden="true"、inert与visibility: hidden。它预先占据完整网格尺寸,使得外层容器高度完全锁定。
TxStreamElement 的两层还会去掉首个子项的 margin-top 与末个子项的 margin-bottom,让占位层与它撑住的回答量出同样的高度;TxStreamText 是行内文字,用 inline-grid 叠放两层,没有外边距要修。TxCodeStream 的 reserve 不用镜像:完整代码的行数已知,直接按总行数撑住代码区。这只在重放已完成的内容时生效,而且 reserve 默认关闭:真正实时流式输出时最终高度还不知道,容器仍会随内容增高。
示例加载中...
动画绝不阻断阅读与复制
许多华而不实的产品给流式段落挂上全屏蒙版或 user-select: none,强制用户“等打字动画演完才能复制”。
TuffEx 将这视为极其粗暴的设计反模式!
- 文字即刻落定(Instant DOM Availability):每个词元落地到 DOM 的瞬间,它就是一个标准的文本节点。用户在任何时刻均可随时用鼠标框选并按下
Cmd+C复制。 - 零事件拦截:微渐显特效必须严格限制在视觉表现层,绝不允许拦截任何点击、双击与拖拽手势。
交互中断跳过(Skip on Interrupt)
用户如果正在专注工作,往往没有耐心等待几十秒的打字效果:
- TuffEx 本身不监听滚动、点击或 ESC,何时跳过由宿主决定:
TxStreamText与TxStreamElement都通过 ref 暴露skip(),宿主在用户快速滚动、点击底部区域或按下 ESC 时调用它即可。 skip()是同步的:调用当下就显示全部内容、不再播放入场,不必等下一帧,控制权立即回到用户手里。- 减弱动态偏好(Reduced Motion)开启时,节拍器与动效自动全部旁路,始终保持最纯粹的极速输出通道。