DeepSeek-Reasonix 转录流渲染器原子切换#9777验收合同全解析从 ResizeObserver 所有权到原生尾部确认【免费下载链接】DeepSeek-ReasonixDeepSeek-native AI coding agent for your terminal. Engineered around prefix-cache stability — leave it running.项目地址: https://gitcode.com/GitHub_Trending/de/DeepSeek-Reasonix导读本文深入解读 DeepSeek-Reasonix 桌面端 Transcript对话转录流渲染器#9777切换cutover的验收合同该合同定义了一套严格的正确性门槛用于以原子方式用新一代渲染器替代遗留引擎而不放松既有质量阈值。文章将结合 docs/TRANSCRIPT_ACCEPTANCE_9777.md 原文以及desktop/frontend/src/lib与desktop/frontend/src/__tests__下的真实源码实现拆解 generation 围栏、ResizeObserver 所有权、唯一滚动写入者、before-paint 几何提交、跨平台原生回放与几何驱动的完成判定帮助读者理解这类“渲染器原子替换”项目在可维护性与可验收性上的工程实践。一、背景#9777 在做什么为什么要“原子替换”在 DeepSeek-Reasonix 这类面向终端与桌面的 AI 编程代理中Transcript转录流是模型推理过程、工具调用结果与最终回答的主要呈现面。旧引擎承载滚动、虚拟化与测量逻辑多年当渲染器需要整体重构时最危险的做法是“渐进式混用”——新旧两套滚动写入逻辑并存会导致几何竞争、回跳、空白帧与尾部不可达等问题。因此 #9777 的渲染器 slice 采取原子替换策略渲染管线整体切换到新实现中间不保留“第二套滚动写入者”。文档明确指出“Native gestures keep authority; no second scroll writer is added”原生手势保持权威不新增第二个滚动写入者。这一原则在源码中有直接对应物TranscriptViewportWriter的类注释写着“The only production owner allowed to mutate the native Transcript scroll position”——它是生产代码中唯一被允许改写 Transcript 原生滚动位置的类所有 adapter、手势与事务都必须经由它路由见 transcriptViewportWriter.ts。围绕这一核心切换包内含五个关键技术机制正是本文后续逐节展开的主线mounted-block ResizeObserver 所有权谁挂载、谁观察、谁撤销generation-fenced 冷测量按代围栏隔离的几何测量已消耗的原生滚轮行程的退役retirement of consumed native wheel travel每次绘制前的一次 before-paint 几何提交用于 prefix 几何与锚点校正旧队列几何不得覆盖该提交防止竞态回写。二、ResizeObserver 所有权观察者的生命周期与撤销语义2.1 为什么“disconnect”本身不够原生 WebViewWKWebView、WebView2、WebKitGTK中ResizeObserver.disconnect()只能阻止后续回调派发无法撤销已经排队但尚未派发的回调。如果渲染器切换时只做 disconnect旧观察者的排队投递仍可能在新渲染器接管后触发向新几何提交写入过期数据。transcriptGeometryObserver.ts的第一行注释就点明了这一点“Disconnect alone does not revoke already queued ResizeObserver deliveries.”因此该模块同时依赖两层护栏generation 比对回调执行时检查generation kernel.generation代不一致立即丢弃before-paint 排队取消已登记的 paint 回调可被cancelFrame()主动取消dispose 时会调用cancelFrame?.()。源码见 transcriptGeometryObserver.ts。其核心流程为ResizeObserver通知到来时若当前代有效且没有已挂起的帧先执行before()再通过kernel.afterCurrentGenerationPaint()把commit()排到当前代的下一次绘制之前从而保证测量结果与绘制提交属于同一代。2.2 测试如何验证“过期代不能发布”transcript-native-viewport.test.tsx 完整演练了这套生命周期初始 surface 观察原生滚动scrollTop更新到 100kernel.generation后旧代的事件派发被忽略observedTop仍为 100替换 surface 后同一 DOM 上的新订阅重新生效滚动到 10000 被观察到旧观察者被 disconnectoldObserver.disconnected true旧观察者手动notify()时新代无法发布仍为 10000新订阅可发布当前几何组件卸载时新观察者也正确断开。这组断言精确对应文档中“Old queued geometry cannot overwrite that commit”旧队列几何不能覆盖该提交的验收要求。三、Generation 围栏冷测量与每次提交的“代”校验3.1 TranscriptKernel 中的 generation 语义TranscriptKernel是滚动几何状态机transcriptKernel.tsgeneration是一个单调递增的计数器replaceSurface()每次被调用都会generationValue 1并返回新代afterCurrentGenerationPaint(callback)只会在“当前代未变”时才执行回调代已变化则丢弃advanceGeometry()也校验代号旧代的几何推进请求无效。配合 useTranscriptKernel.ts 中replaceSurface(sessionKey)的调用时机会话/选项卡切换、surface 重建可以理解“generation-fenced cold measurements”的含义渲染器切换后所有冷测量cold measurements即对尚未虚拟化区域的首次几何测定都必须在新的代号下进行任何携带旧代号的测量结果都不能进入新渲染器的提交路径。3.2 写入侧的代校验一次写入四个拒绝理由TranscriptViewportWriter.write()是唯一滚动写入入口它按顺序执行四道防线transcriptViewportWriter.tsno-viewport未 attach 元素时拒绝stale-generation请求的 generation 与当前不符时拒绝——这是对旧队列几何的直接拦截user-gesturefrozen原生手势/拖拽滚轮期间冻结写入时拒绝invalid-geometryscrollHeight/clientHeight非有限值时拒绝。此外写入被限制在[0, scrollHeight - clientHeight]区间内最终落地位置与请求位置偏差超过4px会被判定为native-clamp原生钳制这一 4px 阈值正是文档中“4px displacement and tail limits”在写入端的体现。四、唯一滚动写入者与原生手势权威4.1 一次 before-paint commit旧几何不能覆盖文档要求切换后“one before-paint commit for prefix geometry and anchor correction”绘制前对 prefix 几何与锚点校正做一次提交。在 transcriptWindowGeometry.ts 中有对应注释An adapter-approved batch is a before-paint transaction. Retaining the older prefix would defer safe offscreen growth until native travel brings it into view.即被 adapter 批准的批次本身就是一次 before-paint 事务若继续沿用旧 prefix安全离屏增长会被推迟到原生行程把它带入视口。而commitTranscriptWindowGeometry在measurementCommit有效时会基于本批次重建 prefix从而保证画出来的几何永远来自最新提交。4.2 原生手势期间冻结写入原生滚动条拖拽、滚轮持续输入、键盘翻页等用户手势期间写入者进入frozen状态任何程序化写入返回user-gesture拒绝。指针手势在 useTranscriptKernel.ts 中被分为两类pointerGestureRef 1内容区指针拖拽如选择pointerGestureRef 2原生滚动条 thumb 拖拽clientX rect.right - 18判定此时写入者被冻结。同时NATIVE_GESTURE_IDLE_MS 320定义了原生手势租约的空闲判定窗口useTranscriptKernel.ts因为“原生 WebView 可能在平台合成器仍是权威滚动持有者时在合并后的滚轮批次之间留下短暂间隙”租约必须跨越该边界直到原生流真正空闲后才提交延迟几何。onScroll中只要kernel.nativeGestureLeaseActive为真就持续renewGestureLease()让浏览器最终位置保持权威。五、窗口化渲染与 mounted-block 上限5.1 MAX_MOUNTED_COMPLETED_BLOCKS 40窗口化渲染windowed rendering必须保留 mounted-block 上限。源码在 transcriptWindowGeometry.ts 定义了export const MAX_MOUNTED_COMPLETED_BLOCKS 40;即同一时刻最多挂载 40 个“已完成”的转录块。covered判定要求range.items.length residentCount MAX_MOUNTED_COMPLETED_BLOCKS不满足时回退到mode: full全量渲染路径而不是突破上限继续窗口化。5.2 覆盖判定与 retained / reconstructed 回退transcriptWindowRange.ts 定义了窗口范围提交的四种来源sourcecandidateadapter 提供的候选范围retained保留上一份仍能覆盖视口的不可变范围避免 WebView 在“更新滚动位置已绘制”后投递过期范围通知时抖动reconstructed大跨度原生跳转使候选与旧范围都失效时从 TanStack 虚拟器的 prefix 尺寸账本同步重建unavailable任何覆盖方案都不可用时绝不绘制未覆盖原生视口的范围由 adapter 走全 DOM 安全路径兜底。coversColdViewport用“冷启动区间 ∩ 视口区间”逐块扫描任何缝隙都会导致覆盖失败——这正是验收中“painted overlap”绘制重叠与“最终尾部距离”在渲染侧的保障基础。六、验收范围从选择保留到流式尾部可达文档列出的验收面包括selection选择、question navigation问题导航、history prepend历史前置、async content growth异步内容增长、process/reasoning disclosure过程/推理披露、A-to-B-to-A replacementA-B-A 替换、streaming tail reachability流式尾部可达性与 manual-reading ownership手动阅读所有权。在源码层面对应的机制选择保留useTranscriptSelectionRetention.ts 与测试 transcript-selection-retention.test.tsx 中“Transcript resets its scroll generation before the selection hooks own effect”的注释说明代号重置发生在选择钩子副作用之前保证切换后选择锚点不被旧代状态污染历史前置prependuseTranscriptKernel.ts 中beginStructural(prepend)会先置prependAwaitingGeometryRef.current true等待几何提交完成前不执行锚点校正避免前插内容导致的几何错位流式尾部跟随tail-follow模式通过kernel.scrollToTail()与写入者owner tail-follow的pinTail写入完成transcriptViewportWriter.tsisAtBottom判定采用scrollHeight - clientHeight - scrollTop 4的 4px 阈值手动阅读所有权用户滚离底部时endUserGesture()结束手势、intent 从tail切换为reader此后不再自动回尾直到用户再次调用scrollToBottom()。七、跨平台原生验证浏览器模拟无法替代文档强调本地生产回放local production replay覆盖Chromium 与 Playwright WebKit而平台工作流platform workflow必须额外在实际宿主上运行macOS WKWebView、Windows WebView2、Linux WebKitGTK。理由在文档中直言“Browser emulation cannot replace these native checks”浏览器模拟无法替代这些原生检查——因为只有真实宿主才具备文档所引用的平台看门狗与原生合成行为平台看门狗/预算说明GTK45 秒总看门狗原生滚动/尾部确认的总时长上限WKWebView225 秒交互看门狗macOS 交互时长上限WebView260 秒交互预算Windows 交互时长上限全平台4px 位移与尾部限制落地位置与请求位置偏差、距尾部距离全平台零空白帧渲染期间不允许出现无内容帧全平台有界挂载mounted-block 上限源码值为 40验收合同明确规定现有正确性阈值不得为了通过切换而放宽“Existing correctness thresholds must not be relaxed to qualify the cutover”。同时文档也对自己做了边界声明App 源绑定命令source-bound commands与内存资格memory qualification是独立 slice本渲染器验收不认证整个 App 的堆驻留或原始集成 PR交付状态以各子 PR 的实时检查为准本文件记录的是契约而非永久的绿色 CI 声明。八、几何驱动的原生完成判定八事件批次与两次物理尾部确认原生滚动完成native finish不是按固定事件数计数而是几何驱动的文档原话After the sustained input phase, hosts send batches of eight native events until two observations confirm the physical tail. There is no fixed finishing-event count that assumes estimated height stayed constant.翻译为工程约束即持续输入阶段结束后宿主hosts以每批八个原生事件的粒度继续投递直到两次独立观察都确认到达物理尾部physical tail为止不存在“固定完成事件数 假设估算高度不变”的捷径。之所以如此设计是因为虚拟化渲染中scrollHeight是测量后的估算值异步内容增长streaming、prepend会随时改变它若用固定事件数判定“已完成”在高度变化时会产生虚假完成或尾部不可达。几何驱动的方案把判定依据绑定在真实滚动位置的连续两次观察上天然免疫估算高度漂移。九、面向合并流程的验收纪律文档最后规定了切换的合并纪律可归纳为三点每个子 PR 必须通过自己当前 head 的检查pass its own current-head checks不允许用历史结果替代合并前必须集成两个父级settings设置与 pure-model纯模型父 PRLive 子 PR 检查是交付状态的权威本文档仅记录契约不代表对 CI 结果的永久声明。这套“契约 实时检查”的双轨机制既给重构团队提供稳定的验收基线又避免文档因年代久远而与实际 CI 状态脱节。十、小结从这份验收合同能学到什么#9777的 Transcript cutover 验收合同是一个值得参考的“渲染器原子替换”模板其工程要点可以沉淀为四条原则单一写入者任何滚动位置变更都必须流经唯一的TranscriptViewportWriter杜绝新旧实现并存期间的二次写入竞争代围栏 before-paint 提交用generation隔离过期测量用afterCurrentGenerationPaint把几何提交锁定到当前代的绘制前一刻并主动取消已排队的旧回调原生权威用户手势期间冻结程序化写入以 320ms 空闲窗口跨越平台合成器的批次间隙最终位置以浏览器/WebView 为准阈值不妥协4px 位移/尾部限制、零空白帧、有界挂载40 blocks在切换前后保持同一标准跨平台Chromium、WebKit、WKWebView、WebView2、WebKitGTK逐一验证浏览器模拟不得替代真实宿主检查。对于任何正在重构长列表/虚拟滚动渲染器的团队docs/TRANSCRIPT_ACCEPTANCE_9777.md 这份合同连同 transcriptViewportWriter.ts、transcriptGeometryObserver.ts、transcriptWindowRange.ts 及其配套测试构成了一套可直接对照落地的完整验收范式。【免费下载链接】DeepSeek-ReasonixDeepSeek-native AI coding agent for your terminal. Engineered around prefix-cache stability — leave it running.项目地址: https://gitcode.com/GitHub_Trending/de/DeepSeek-Reasonix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
