# DSH 折叠插件 — 原始需求整理 > 本文档仅整理需求方(用户)在讨论中提出的原始要求与已确认的事实,不含任何实现方案。 > 用途:作为后续重新设计/实现的依据。 --- ## 一、需求目标 在 DSH Web 的会话消息流中,把每一轮**工具调用过程**折叠进一个**方框**,避免长对话被一长串 Think / Bash / Read 等过程刷屏。 ## 二、原始需求明细 ### 1. 方框(折叠容器) - 一个**方框**,默认显示**两行**: - 第一行:**工具调用**——包括 think、bash、read 之类的**所有**工具调用; - 第二行及以后:纯文字输出。 - 方框内**最多两项**的原则: - **工具一项只能显示一行**; - **纯文字输出一项可以显示全部**(不强行压成两行,完整显示)。 ### 2. 文字(content 流)的进出框规则 - 当出现真正的 content 流时,**不在方框里输出**,就在方框**外**像普通文字那样输出; - 一旦这个输出完,**又出现了工具调用**,那么**马上把这一堆输出全部收到方框里**; - 这一堆文字可以**完整**在方框里显示,不压行; - 但整体仍然作为**一项**("最多两项"原则不变:工具一项 + 文字一项)。 - 如果这个文字输出完**没有其他工具调用了**,说明这就是**最终展示的答案**: - 这部分输出**什么都不用动**,因为就是按正常文字渲染的。 ### 3. 方框 = 折叠起来的工具调用过程 - 方框的本质就是**把工具调用过程折叠起来**的容器。 ### 4. 展开 - 用户**点击方框 = 展开**: - 把里面工具调用都展开; - 但**方框还在**,里面所有的东西**还是在方框里**,只是现在是个**大方框**了; - 展开只是显示**现在这种没插件的样子**(各行按原始顺序排列),**不是**彻底展开每个工具调用或每一个 think 的内部。 ### 5. 展开后的页面定位 - 展开时用户点击展开的位置**通常是最后两行**; - 展开后,用户鼠标位置**还应该是最后两行**; - 注意:不是移动用户鼠标,而是**展开后页面定位应该怎么变**(即:展开后如何保持点击处的视口位置不跳)。 ### 6. 折叠按钮(展开态) - 展开方框后,在**方框左边**会出现一个**对向箭头**的可点击项; - 它**随着方框上下移动**,但**不会超出方框位置**; - 这就是**折叠按钮**,点击后**再次折叠**。 ### 7. 动画保留 - 工具调用时,边上的图标有**微动的小动画**,这些**都要保留**。 ### 8. 实现方式约定(用户确认过的方向) - "我只是给原来的显示套了个框,然后把多于两行的隐藏起来了"——即: - **套框**:在原有显示外面套一个框; - **隐藏**:把多于两行的内容隐藏起来; - 不移动、不克隆原有内容节点。 --- ## 三、已确认的技术事实(背景参考) 以下是在讨论中确认过的、与需求直接相关的客观事实,供重新设计时参考。 ### 1. 思考过程 vs 最终展示结果(可以区分) - **思考过程** = `reasoning` 块,DOM 上是 `data-variant="think"` 的 "Think" 折叠行; - **最终展示结果** = `text` 块,渲染成无 variant 的 markdown 正文; - 两者在数据层(`AssistantBlock.kind`)和 DOM 层都能明确区分。 ### 2. "轮"(turn)与"步"(step) - 一轮 = 多次模型输出(step)串成:模型输出 → 工具结果 → 再模型输出 → …; - 一个 step 内 `reasoning` + `text` + `tool-call` 同属一次 `assistant/message` 一起定稿。 ### 3. 整轮的"最终结果"如何定义 - 整轮最终展示结果 = **最后一个含非空正文的定稿 assistant step**; - 思考过程中出现的纯文字(工具调用之间的文字)属于**中间输出**,**不是**整轮的最终结果; - 但"是不是最终结果"是**事后才能确定**的属性——输出进行中无法实时判断(流式时无法知道后面还会不会有工具调用)。 ### 4. 核心 UI 已自带的能力(不要重复做) - 单个 Think 块、单个工具卡片**本身**已经是可折叠的 disclosure 行(默认收成一行摘要,点击展开); - 本插件要做的差异化折叠是**更高层级的聚合**(轮级/过程级),不是单卡片级。 ### 5. 会话流的 DOM 契约(供实现时核对) | 内容 | 标记 | |---|---| | 会话流容器 | `[data-chat-flow]` | | 顶层行 | `[data-chat-flow-kind]`(`assistant-step` / `tool-call` / `user` / `steering` / `turn-tail` …) | | think 行 | `[data-variant="think"]`(在 assistant-step 行内) | | 工具行 | `[data-chat-flow-kind="tool-call"]`(内含 `[data-chat-call-id]`;子调用 `[data-subcalls]`) | | 工具名 | 不在统一属性上:bash 等卡片用 `data-variant` + 卡片标题文本(无 `data-tool`) | | 滚动容器 | `[data-conversation-scroll]` | ### 6. 实现上的已知约束(踩过的坑) - 文字/工具行是 **React 渲染并管理**的节点: - **隐藏**(`display:none`)安全,React 无感; - **移动/克隆节点**会与 React 虚拟树冲突(重渲染时原节点被重建 → 出现两份/状态错乱)。 - 核心的滚动分页锚点依赖**可见**的锚点行(`[data-chat-anchor-key]`);整轮行都被隐藏时,折叠容器需要保留一个可见锚点,否则核心可能跳位置。 --- ## 四、未决问题(需求层面待确认) - "方框内默认两行" 与 "工具一项一行" 的确切关系:默认显示 = 工具概要一行 + 文字全显?(按此理解整理) - 一轮内出现**多段**中间文字时,作为"一项"是**合并显示全部**,还是有其他规则? - 折叠状态是否需要持久化(刷新/切会话后是否记住展开/折叠)? - 多轮(多个方框)同时存在时的行为边界。