qwen-code Web Shell 全局轮次导航(Global Turn Navigation)Phase 3:连续滚动与紧凑虚拟化 Turn Rail 的设计与实现

发布时间:2026/9/15 17:24:54
qwen-code Web Shell 全局轮次导航(Global Turn Navigation)Phase 3:连续滚动与紧凑虚拟化 Turn Rail 的设计与实现 qwen-code Web Shell 全局轮次导航Global Turn NavigationPhase 3连续滚动与紧凑虚拟化 Turn Rail 的设计与实现【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code导读本文围绕 qwen-code 开源仓库中 Web Shell 全局轮次导航 Phase 3 设计文档展开讲解 Web Shell 如何在保留原有连续向上滚动体验的同时引入一条覆盖全部持久化轮次turn的左侧全局导航轨rail并对未加载的远端历史内容做到按需加载。读完本文你将掌握 Phase 3 的核心决策、useTranscriptViewport/TranscriptViewport/GlobalTurnNavigation的组件职责划分、紧凑 rail 的虚拟化与键盘交互设计以及历史范围边界滚动、阅读锚点保持、错误重试与能力降级机制并可直接对照仓库源码继续深入。一、背景从已加载轮次导航到全局轮次导航在 Phase 3 之前Web Shell 的历史导航经历了两个阶段基线Phase 1 之前侧边轮次轨完全由当前已加载的messages数组派生getSessionTimelineEntries(messages)导航完整性受制于浏览器内的 transcript 内存与 50,000 block / 字节预算未加载或被驱逐的轮次在导航轨上天然缺失。Phase 1 / 2Adaemon 侧新增会话级稀疏 turn 索引GET /session/:id/turn-index与基于快照的随机访问读取atRecordId但 Web Shell 客户端尚无法消费。Phase 2B实现了有界历史视口bounded historical viewport但交互形态是显式历史快照工具栏explicit historical snapshot toolbar用顺序翻页控件替代了原先自然的向上滚动。Phase 3 的目标正是纠正 Phase 2B 的交互取舍用户希望找回原来的连续滚动体验同时获得一条覆盖每一个持久化轮次的左侧导航轨远端 transcript 内容按需加载。这一形态取代了 Phase 2B 面向用户的快照控制Phase 2B 中的有界存储与隔离物化isolated materialization机制则继续保留作为内部基础设施。Phase 3 与整体设计的关系见 父级设计文档涵盖 turn 分类、稀疏索引、turn-index 协议、锚定 transcript 协议、身份不变量等其客户端数据层见 Phase 2 设计历史视口整合见 Phase 2B 设计。二、核心设计决策连续滚动 全局导航轨而非快照工具栏Phase 3 设计文档明确了以下关键决策保留普通向上翻页默认的TranscriptViewport把现有的 liveMessageList分页 props 原样透传维持原有的 follow-bottom、向上加载、错误处理与 jump-to-latest 行为。能力探测capability detection绝不能禁用普通历史加载——即使 daemon 支持全局导航旧的向上滚动路径仍然可用。全局 rail 数据来自导航 storerail 使用导航 store 暴露的effective turn count有效轮次数并对可见的 ordinal 槽位惰性加载元数据而不是一次性把全部元数据塞进浏览器。按需加载远端内容点击已加载轮次时将 source block ID 映射到当前消息 ID复用MessageList既有的滚动与高亮行为点击远端轮次时调用locateOrdinal把准入后的有界历史范围呈现在同一消息区域。历史内容与 live transcript 严格隔离历史内容不会混入 live transcript也不会喂给权限、Todo、任务等业务消费方同时不显示任何快照标题栏或翻页工具栏。降级路径对于没有 turn 索引的 daemon保留既有的仅已加载导航loaded-only rail作为回退。这些决策在 turn-navigation-store.ts 的DaemonTurnNavigationSnapshot中得到印证其mode取值为legacy | loading | ready | degradedfallbackReason覆盖unsupported | too_large | initial_error——即能力缺失、索引超限、初始失败三种降级情形都有明确的状态表达。三、组件与职责划分Phase 3 文档把实现拆分为四个职责清晰的组件层组件职责useTranscriptViewport拥有本地选择意图selection intent、范围固定range pinning、加载与待滚动目标状态TranscriptViewport整合全局 rail、恢复阅读锚点、观察历史滚动边界全局 rail 组件GlobalTurnNavigation复用共享按钮与主题 token限制渲染的 ordinal 槽位数量导航 storeturn-navigation-store仅在内部增加取消支持cancellation support不改 daemon 路由、持久化格式、SDK 协议与 reducer 语义3.1 useTranscriptViewport历史视口的本地大脑useTranscriptViewport.ts 是历史视口的状态中枢通过useSyncExternalStore订阅导航 store 的两个快照getViewportSnapshot()历史视口快照sessionId、revision、pages、rangesgetSnapshot()导航快照mode、totalTurns、effectiveTurnCount、indexPages、provisionalTurns、selected 等。其核心能力包括selectOrdinal(ordinal)完整的远端轮次选择状态机。先判断目标 ordinal 是否为provisionalTurns中的临时条目此时直接使用本地 loaded-block 定位绝不发起持久化锚定请求否则调用store.locateViewportOrdinal(ordinal, request, releaseAnchor)。每次选择都会intent.current生成一个 token只有isCurrent()通过token 未过期且 revision 未变的请求才允许提交从代码层面落实只有最新的选择请求可以提交这一设计约束。load(direction, beforeAdmit)沿历史范围的 older/newer 方向加载边界。对newer方向的 live 边界通过store.captureLiveBoundary()捕获 live 旧边界必要时走loadViewportBoundary的reopenLiveBoundary路径见下文。returnToLive()/continueLive(blockId?)回到 live。continueLive支持携带重叠阅读锚点实现从历史回到 live 时尽量停留在同一阅读位置。pin(sourceBlockId)把某个 source block 所在的历史页注册为当前视口的保留锚点setViewportAnchor(viewportId, pageId)防止该页被驱逐。canContinueLive只有历史范围的新边界是 live 边界、且存在 live 重叠hasLiveOverlap或捕获的beforeRecordId与视口记录的 live 边界一致时才允许直接续接 live——避免把live 修剪后产生的空洞伪装成无缝衔接。useTranscriptViewport还通过viewKeysessionId:revision:rangeId或sessionId:live区分视图身份变更 source/range/revision 会重置瞬时滚动状态而范围内追加不会重挂载。3.2 TranscriptViewport整合与锚点恢复TranscriptViewport.tsx 是历史视口的容器组件当导航模式为ready/loading且effectiveTurnCount SESSION_TIMELINE_MIN_VISIBLE_ENTRIES时渲染GlobalTurnNavigation并通过useChatNavigationVisible依据容器宽度决定是否展示窄布局隐藏。在历史模式下向MessageList注入一组冻结视口 propsfrozenViewport: true、hasOlderHistory: false、onLoadOlderHistory: undefined、transcriptReloadPaused: true并清空编辑/重发/分支/重试/欢迎头等 live 专属行为——这正是历史片段不暴露行级变更控件的实现。阅读锚点ReadingAnchorcapture()捕获当前首个可见内容行及其像素偏移含 tool-call 子锚点refreshAnchor在边界加载前刷新锚点restore循环最多 8 帧在布局稳定后按 rowKey / sourceBlockIds / callId 恢复滚动位置。历史模式restoring.current期间忽略自身滚动事件避免恢复滚动触发再加载的震荡。边界观察loadAtEdge在滚动距顶部/底部不足 200px 时触发 older/newer 加载live 边界且可续接时调用continueLive重叠锚点优先否则load(newer)走reopenLiveBoundary恢复路径。历史模式且无onCanScrollToBottomChange时底部居中渲染返回最新按钮加载/错误状态在顶部居中渲染错误伴随重试按钮。3.3 GlobalTurnNavigation64px 紧凑 rail 的虚拟化实现GlobalTurnNavigation.tsx 实现设计文档要求的紧凑 rail 呈现虚拟化ROW_HEIGHT 1616px 槽位间距、OVERSCAN 4。count state.effectiveTurnCount作为虚拟化总行数只渲染[start, end)区间内的按钮外层ol用height: count * ROW_HEIGHT的大 spacer 表示完整逻辑高度——10,000 轮次不会创建 10,000 个按钮与父设计中rail DOM 复杂度 O(可见 tick overscan)的性能模型一致。占位与惰性加载从state.indexPages构建entries映射对可见区间内缺失的 ordinal按WEB_SHELL_TURN_INDEX_PAGE_SIZE归并出缺失页调用store.loadOrdinal(...)批量补取元数据。加载失败时渲染重试按钮。Tooltip 预览每个按钮外包Tooltiphover / 键盘聚焦时显示轮次标题timeline.turnPrefix label与可选的公开助手预览detail。hover 只读取已存在的元数据不发起 transcript 内容请求——点击才按需定位远端内容。键盘导航Home/End 跳到首/末轮次ArrowUp/Down、PageUp/PageDown 按 ordinal 或按视口高度步进移动焦点按钮用pendingFocusdata-turn-ordinal查询在虚拟化重渲染后恢复。无障碍aria-posinset{ordinal 1}、aria-setsize{count}描述逻辑集合而非已挂载子集aria-currentlocation标记当前选中 tick。3.4 导航 store仅增加内部取消支持turn-navigation-store.ts 是 rail 与历史视口的数据后端locateOrdinal:797起执行远端定位先loadOrdinal保证元数据页存在检查locations缓存命中则直接复用已 admission 的历史页并pageTable.select否则以atRecordId turnIdsnapshot发起锚定 transcript 读取并用 sessionEpoch / chainEpoch / generation 三重校验丢弃过期响应。loadViewportBoundary:1159起处理历史范围的边界续读当newer边界为 live 且无 live 重叠时先readFreshHead获取新的 index 快照再reopenLiveBoundary以捕获的beforeRecordId重建边界最后走loadBoundary——这就是设计文档live 修剪移除衔接边后必须显式恢复而不是静默跳到新 live 尾部的实现。retry()按错误 operationindex / locate / older / newer精确重试索引错误重取 head 或 ordinal定位错误重试当前 selected ordinal边界错误重试对应 range 的边界并遵守retryable标志。四、选择与加载流程loaded turn 与 distant turn 的分流Phase 3 文档将选择行为定义为一条清晰的状态机路径useTranscriptViewport.selectOrdinal与 store 的locateOrdinal共同实现捕获请求代次generation若选中的 ordinal 是占位符先拉取其元数据页仅当代次仍匹配时继续。解析 canonical 条目provisional live 条目live:promptId/shell:eventId始终走本地 loaded-block 定位绝不发起持久化锚定请求。已加载轮次turnId命中定位器映射通过sourceRecordIds交集关联 canonicalturnId与当前渲染 block直接滚动到该 block 并复用既有 locate 高亮行为。远端轮次以atRecordIdturnId 该条目的索引快照请求 transcript 页在隔离的临时 store中规范化后仅当整页符合预算时才作为不可变历史页准入。失效规则用户输入、再次选择、返回最新、会话或 owner 变更都会使挂起的导航失效只有最新一次选择可以提交。失败跳转保留当前可见页并暴露重试。live 保持SSE 持续连接新事件继续写入 liveTail但视口脱离后不自动跟随跳至最新随时可用。这里可以对照父设计中的身份不变量turnId是持久化用户记录的 UUID而非 promptId 或数组索引ordinal仅是快照内的零基位置用于排序、虚拟布局与无障碍绝不作为随机读定位符。五、历史范围的边界滚动与锚点保持Phase 3 文档对在历史范围内连续滚动提出了明确的体验要求并由TranscriptViewport的滚动捕获机制实现边界加载在历史范围的顶部/底部滚动时加载相邻边界页同时保持可见 block 与 tool-call 锚点不变。TranscriptViewport在滚动事件进入边界 200px 内时先capture()保存锚点再发起加载避免虚拟行未挂载时丢失阅读位置。jump-to-latest 全程可用历史阅读的任何时刻返回最新都可用scrollToBottom在历史/加载态下被重定向为returnToLive()。回到 live 边界到达 live 边界时优先使用重叠阅读锚点continueLive(overlap ? source : ...)回到 live 内容无重叠时对 newer 方向走loadViewportBoundary的readFreshHead reopenLiveBoundary显式恢复路径而非静默跳过空洞。错误保留内容边界加载失败时保留当前内容并暴露内联重试history.viewErrorhistory.retry绝不静默跳过缺口。useTranscriptViewport返回的retry()会记忆上次失败动作{ ordinal }或{ direction }重试时精确重放。六、错误处理、降级与限制Phase 3 文档明确以下边界条件全部历史指全部 ordinal 槽位而非仅缓存的索引页rail 的完整性来自底层索引totalTurns索引页与 transcript 缓存各自保持既有预算。远端跳转不加载中间历史也不替换 live reducer 状态一次跳转 一个有界 transcript 页的网络 规范化成本流式成本与历史页数无关。分屏保持既有紧凑策略窄布局与既有 split panes 中 rail 隐藏兼容 compact-layout 政策。能力降级daemon 无session_turn_navigation能力或 transcript 超过 256 MiB 索引上限时回退到 loaded-only railmode: legacy/degraded索引请求瞬时失败时transcript 保持可用rail 显示占位 tick 与重试入口。快照失效transcript_snapshot_unavailable409、rewind/branch、链重写等事件会使快照绑定页失效并显式重取绝不静默重锚定到错误的活动链。七、验证与测试Phase 3 设计文档将验证分为两层聚焦组件测试useTranscriptViewport/GlobalTurnNavigation/TranscriptViewport的单元测试覆盖选择状态机、占位加载、边界加载、取消与降级对应测试文件包括 GlobalTurnNavigation.test.tsx、TranscriptViewport.test.tsx、TranscriptViewport.scroll.test.tsx 与 TranscriptViewport.pending.test.tsx。浏览器测试验证 rail 的 hover/focus 预览、键盘进入、响应式可见性、数千轮次虚拟化以及 loaded/distant 选择同时验证恢复的普通滚动路径ordinary scroll path在全局 rail 启用后依然工作——即能力探测不得禁用普通向上加载这一承诺的回归保障。需要说明的是依据父设计文档Phase 3 的前端功能与范围验证已完成但集成浏览器到真实 daemon 的生命周期验收仍属独立里程碑与实现、合并状态分别跟踪。八、快速上手如何查看与运行本功能位于 Web Shell 包内所有实现文件均在packages/web-shell/client/下。可沿以下路径快速定位设计 Phase 3 设计 → 父级设计 → Phase 2B 历史视口 → Phase 2 客户端数据层数据后端 turn-navigation-store.ts导航快照、定位、边界加载、viewpoint pinning状态中枢 useTranscriptViewport.ts视口容器 TranscriptViewport.tsx全局 rail GlobalTurnNavigation.tsx 及其样式复用自 MessageList.module.css 中的既有 timeline tick 主题 token运行 Web Shell 开发环境后打开一个持久化轮次足够多的会话超过SESSION_TIMELINE_MIN_VISIBLE_ENTRIES阈值即可在左侧看到紧凑 tick railhover 查看轮次预览点击远端 tick 观察历史范围按需加载与返回最新按钮滚动历史范围顶部/底部可验证边界续载与锚点保持。九、总结Phase 3 的最终交互形态可以概括为一句话普通向上滚动原样保留全局 turn rail 以紧凑 tick hover 预览呈现远端内容点击按需加载历史与 live 严格隔离任何时刻可一键返回最新。它把导航完整性从 transcript 内存中解放出来——rail 覆盖全部持久化轮次却只需常驻 O(可见 tick overscan) 的 DOM 与有界的索引页缓存同时它不新增任何 daemon 路由、持久化格式或 SDK 协议变更仅通过useTranscriptViewport、TranscriptViewport、GlobalTurnNavigation三个前端组件的组合与导航 store 的内部取消支持完成。对于任何希望为长会话终端界面提供全局可导航 本地有界体验的工程师而言这份设计与实现都是值得对照研读的参考案例。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询