feat(serve): 调用链路面板展开/折叠 + has_more 截断标记 (#152) - #155
Conversation
build_tree_dfs 返回 (nodes, unexpanded) 闭环:预算耗尽、深度钳制、 循环 break 三种截断统一折算为未展开的有效邻居数,使假叶子与真叶子 在 JSON 中可区分(修复截断误导)。顶层 truncated/caller_count 语义 保持兼容;HTTP 与 MCP 的 trace JSON 同步输出新字段。 测试:新增 6 个截断行为测试(预算精确计数/兄弟截断/深度边界/ 真叶子不误报/祖先环防误报/builtin 过滤不计入)。 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
- 树节点 +/- 逐层折叠,默认展开 1 层(expand-depth 偏好持久化) - expand all / collapse all(清空显式状态 + 调整默认深度) - 折叠状态按目标节点作用域(currentTraceKey|pathKey),跨目标不泄漏 - has_more 节点渲染「▾ N more」注解徽章,点击复用 navigateTo 深入 - 折叠重渲染零 fetch(缓存 trace + 保留滚动位置),导航重置顶部 - localStorage JSON 读取加守卫,损坏数据不再阻断 UI 初始化 - trace 请求 depth 50→10,消除被服务端钳制的误导 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
c2j
left a comment
There was a problem hiding this comment.
Review — Overall: Approve with minor suggestions
I traced the backend counting logic through all four truncation paths (budget exhausted at a child's recursion entry / budget breaking a sibling loop / depth clamp / genuine leaf) and the has_more/more_count attribution is correct at every level: no double counting, no false positives, and the two filters (ancestors + skip_builtins) are correctly applied before effective is computed so more_count is an exact value, not an upper bound. The six new tests are meaningful and map 1:1 to real behaviors (the budget-exhaustion-at-recursion-entry case is the hardest one and it is precisely locked). The TreeNode struct has a single construction site (traverse.rs:161 — verified by grep), so the additive fields are zero-breakage, and both serializers (HTTP handlers.rs, MCP tools.rs) are updated in sync.
Frontend: the collapse key design (currentTraceKey | section.path-idx) correctly isolates state per trace target AND per tree position, which natively avoids the "duplicate node across branches" trap from issue #152. Toggling is zero-fetch (cached currentDetailData + local re-render with scroll preservation), and the delegated click ordering ensures [+] never triggers navigation. The depth=50→10 change is honest (server was silently clamping anyway). localStorage guards are robust.
Nit 1 (recommend fixing before merge) — expand/collapse-all "default depth" leaks across targets and persists
expandAllTree sets treeExpandDepth = 99 and collapseAllTree sets it to 0, and neither is reset in navigateTo/hideDetail, and both are written to localStorage.
Consequence: after clicking "collapse all" once on node A, opening node B renders B's entire tree at default depth 0 — the top-level callers/callees are all tucked behind [+] and effectively invisible. It survives page reloads too. This contradicts the PR's own "cross-target isolation" goal: the explicit collapse map (treeCollapsed) is correctly scoped per target, but the default depth leaks globally.
Suggested fix: reset treeExpandDepth to 1 on navigateTo/hideDetail (or treat expand/collapse-all as session-only and stop persisting it), so expand/collapse-all acts only on the current target.
Nit 2 (cosmetic, cheap) — leaf placeholder width vs [+]/[-] mismatch
The leaf placeholder is (~1 monospace char + trailing space) while [+]/[-] is 3 chars. For siblings at the same depth where one is expandable and one is a leaf, the node-tag/key columns shift by ~2 chars and rows look ragged. A fixed min-width (e.g. 2ch) + centering on .tree-toggle, or an equal-width placeholder, would keep the columns aligned.
Nit 3 (design, acceptable) — has_more badge hidden while node is collapsed
The if (isCollapsed) continue; skips both children and the "N more" badge, so a truncated fake-leaf at the default collapse depth only shows [+]; the user must expand before seeing "N more — click to open". Acceptable because has_more already makes the node expandable (the [+] itself signals "more below"), but if you want stronger affordance you could always render the badge for nodes with children: [] and has_more: true, independent of collapse state.
Notes (no action needed, confirmed)
caller_count/callee_countstill report the visible top-level count (smaller than reality under truncation) — pre-existing behavior, not claimed to be fixed here; the globaltruncatedbanner still covers it. Could be a follow-up issue.- Frontend has no JS test harness and CI skips
test_serve_*; manual browser acceptance is consistent with current repo state — acceptable. - README Cytoscape mismatch correctly declared out of scope in the PR body.
Overall this is a solid, well-tested implementation of issue #152. Happy to approve once Nit 1 is addressed or confirmed as an intentional global preference.
Review 修正(PR #155): - expand/collapse-all 不再修改全局默认深度(原实现写 localStorage 后跨目标泄漏、跨刷新持久,与按目标隔离目标矛盾),改为枚举当前 树的可展开节点写入作用域键(target|pathKey);默认深度回归常量 1 - .tree-toggle 固定 min-width:3ch + 居中,叶子占位与 [+]/[-] 列对齐 - 无 children 的截断假叶子折叠后仍渲染「N more」徽章,折叠态保留 深入查看入口;有 children 的节点折叠仍隐藏徽章(行为不变) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
|
感谢细致的 review,四处截断路径的追踪确认很有帮助。三个 nit 已在 92e40bd 全部处理: Nit 1 — 已修复(采纳方案变体): Nit 2 — 已修复: Nit 3 — 已采纳增强: Notes 部分无异议: |
概要
Closes #152
serve 模式详情面板的 Callers/Callees 调用链树此前一次性平铺渲染全部层级,且预算截断产生的"假叶子"(
children: [])与真叶子在 JSON 中无法区分,复杂链路不可聚焦、截断状态误导。本 PR 实现 issue 的必做 A(折叠交互)与必做 B(has_more标记),懒加载真分页(可选 D)按 issue 建议暂缓。后端(507a5dd)
TreeNode新增has_more: bool+more_count: usize(未展示直接孩子数,精确值)build_tree_dfs返回签名改为(Vec<TreeNode>, usize):邻居收集(含 ancestors/builtin 过滤)提前至 early-return 前,预算耗尽、深度钳制、循环 break 三种截断统一折算为unexpanded,push 时has_more = unexpanded > 0— 闭环覆盖且不误报(真叶子/全祖先环均effective=0)truncated/caller_count/trace_chain签名保持兼容tree_nodes_to_json)与 MCP(同名副本)同步输出新字段前端(c3d7319)
+/−逐层折叠,默认展开 1 层(codeweb-tree-expand-depth持久化)currentTraceKey|pathKey按目标作用域 — 同名节点跨分支不误伤,跨目标不泄漏(code review 修正项)has_more节点渲染「▾ N more」注解徽章(无连接符样式),点击复用现有navigateTo重新获取 500 节点预算深入查看localStorageJSON 读取加 try/catch 守卫;trace 请求depth=50→10消除被服务端钳制的误导测试
cargo test traverse— 6 个新截断行为测试 + 既有 14 个全过cargo test --features serve/cargo test --features full(跳过 CI 已知环境项)全绿cargo build --features full+clippy --features full -- -D warnings+fmt --check干净说明
assets/app.js为实际 UI 唯一真相(README 提及的 Cytoscape 画布未落地,与本需求正交)nodes/:id/callers|callees分页端点已具备,待实测超限频率再立项)、CLI/TUI 输出的 more 提示