Repository navigation
关系图谱自动复位(1s 慢弹簧回位)+ 记忆精选卡三行折叠 + 记忆时间轴气泡对话式 - #317
Conversation
- 精选卡内容区预留三行,超出行数用 line-clamp 折叠 - 真被截断时才显示「展开/收起」按钮(aria-expanded 同步) - 展开时卡片自然变高;折叠态就是最小三行状态 - zh-CN/en-US 文案 timeline.expandFeatured / timeline.collapseFeatured - 新增 tests/memory-featured-expand.test.tsx(2 条,测溢出探测与展开切换)
触发(RoomGraphCanvas): - 鼠标在画布内图谱绝不动;移出画布满 5s 才回位(离开才开始倒计时) - 拖拽/滚轮改过视口才武装;点节点、3px 内抖动不算改视口 - 移回画布取消待触发;补间播到一半回画布会立刻定住并重新武装 - autoFitOnIdleMs=0 关闭;老画布无补间能力时退回瞬时对准 - 移除右上角「适应画布」按钮,RelationsPane/HomeView 改传 autoFitOnIdleMs 补间(PixiForceGraphRenderer): - 弃用 pixi-viewport 的 Animate 插件:它按 Penner 签名 ease(elapsed,0,1,d) 调用自定义 ease,且以世界宽度为插值量——之前的弹簧曲线实际被塌缩成瞬跳 - 改为由共享 ticker 逐帧驱动:scale 与画面中心按弹簧值线性插值, 与预览页 scratch/spring-fit-demo.html 的数学一致 - F 慢弹簧(ζ0.38 / ω10,1s):放大→缩小交替衰减后精确落定 - animateFitView/cancelFitAnimation 进句柄;cancel 返回是否真停了一段在播补间 居中修正: - fitView 复位/居中改用节点几何中心;底边 24px 标签预留只参与算缩放 (此前中心被标签预留带偏 12 世界像素,内容固定上偏) - 补间终点即精确目标,无残留偏差 测试: - room-graph-canvas-autofit 重写为离开触发语义(8 条全绿) - pixi-force-graph-renderer 补真实轨迹断言(第一帧不瞬跳、峰值过冲、 回摆、1s 末精确落位、取消即停);25/28 过,3 条失败为 dev 基线
There was a problem hiding this comment.
审查结论
整体实现质量较高:补间数学(欠阻尼二阶阶跃响应 + settled 归一化,保证 1s 末精确落定)、几何中心/标签包围盒分离、指针事件状态机都经得起推敲,测试覆盖了主要语义分支。未发现安全/隐私问题、数据丢失、主路径运行时回归或构建风险,无阻塞项。以下是按严重度排序的建议:
1.(中低)autoFitOnIdleMs 的 prop JSDoc 与实现语义矛盾 — RoomGraphCanvas.tsx:41-48
prop 文档描述的是「画布内静止计时」模型:「鼠标在图谱上**静止**满 N 毫秒 → 弹性补间……期间任何 mousemove 都重新计时」;但实现(RoomGraphCanvas.tsx:199-206)里画布内根本不排倒计时——计时只在 pointerleave 后开始。PR 描述与组件内第二段注释(164-166 行)都是「离开触发」模型,即 prop 文档是错的。这是对外 prop 的契约说明,未来调用方(或按文档写测试的人)会被误导,建议改写文档与实现对齐。
2.(低)拖拽节点时可能「误武装」自动复位 — RoomGraphCanvas.tsx:215-226 vs PixiForceGraphRenderer.ts:404
外层用 3 屏幕 px 判定「改动视口」,而渲染器判定节点拖拽开始用的是 3 世界单位 阈值。当缩放 > 1 时,3 屏幕像素 < 3 世界单位:若节点拖拽的第一次移动恰好落在 3px ~ 3×scale px 之间,外层会先于 onDragNode 置位 draggingNode 而执行 armAutoFit()——视口从未变化却被武装。后果通常是良性的(回到已是目标的视野≈无操作),但在紧凑面板里复位用的 minScale 是 0.2(PixiForceGraphRenderer.ts:763),与 settle-fit 的 minScale: 1(只居中不缩小)不同,纯拖节点后离开画布 5s 图会被缩小。与「只有真正改过视口才武装」的声明不符,建议 arm 时校验视口是否真的变了,或把死区阈值按 viewport.scale 换算成世界单位。
3.(低/测试质量)「取消在播补间」用例并未真正模拟在播补间 — room-graph-canvas-autofit.test.tsx:1481-1501
canvasProps 收集的是组件 props,而不是 useImperativeHandle 暴露的句柄,因此 canvas.animateFitView?.({ durationMs: 1000 }) 恒为 no-op,「模拟补间在播」没有发生;且 mock 的 cancelFitAnimation 无条件计数,断言无法区分「取消了一段在播补间」与「本来就没有补间」(真实实现是有返回值的)。取消语义目前只靠 renderer 层测试兜底,这条用例名不副实。另:FakeViewport.animate()(pixi-force-graph-renderer.test.ts:1061-1069)在弃用 pixi-viewport Animate 插件后已无生产调用方,属残留脚手架,可一并清掉。
4.(低)死状态 pointerInside — RoomGraphCanvas.tsx:169
在 enter/down/move/leave 四处读写,但从未被读取。要么删掉,要么把「画布内绝不动作」的守卫真正建立在其上(可让 autoFitFire 开头检查 pointerInside.current 兜底)。
5.(极低)切换精选条目时 featuredClamped 有一帧脏值 — AtomicMemoryPane.tsx:402-412
换精选(id 变化)后 featuredClamped 保留上一条的测量值直到 effect 重测,新条目若未截断会闪现一帧「展开」按钮再消失。可用 useLayoutEffect 或在 id 变化时同步重置来消除。
已核实无问题的点
- 复位目标 minScale=0.2 与被移除的「适应画布」按钮行为一致,非回归;
GraphFitButton仍被 Link/Wiki/EntityFact 图谱使用,非死代码,context-room-graph-fit-buttons.test.tsx不受影响。 - 弹簧 ease 的
settled归一化保证ease(1)=1,终点无残留偏差;Math.max(0.02, scale)兜住过冲;stepFitTween位于 drawFrame 早退分支之前,revision 奇偶不阻断补间。 centerOnContent改用节点几何中心只影响居中(-12 世界像素),缩放仍用带标签预留的包围盒;全仓库仅pixi-force-graph-renderer.test.ts断言 moveCenter 坐标,且已同步更新。- i18n 两个 locale(en-US/zh-CN)均已补齐
timeline.expandFeatured/collapseFeatured;featured.content以 React 文本渲染,无注入面。 - 三行占位 CSS:
min-height: calc(1.6em * 3)以元素自身 font-size(14px,后置规则胜出)计,与 line-height 1.6 精确对齐;[data-expanded]选择器特异性 (0,2,0) 稳压末尾 clamp 规则 (0,1,0)。 - 面板 resize 触发的 settle-fit 定时器(
useForceGraphLayout.ts:115-126)理论上可与补间同帧竞争,但两者目标几乎一致且仅一帧闪烁,可不处理。
- 一行改为 [时间 | 连接件 | 气泡] 三列网格:时间右对齐与节点同水平线, 中列短横线 + 发光圆点连接到卡片 - 气泡卡片沿用原摘要卡,仅加「左上切角」的气泡圆角(4/10/10/10) - 详情面板展开时落在气泡列,不压连接件 - 移除原左侧贯穿直线与 ::before 圆点方案 - 新增 mem-tl-link 装饰节点(aria-hidden)
- 时间列改用 formatTimeShort(HH:mm):完整日期悬停 title 可见、dateTime 语义保留 (此前完整「YYYY/MM/DD HH:mm」在 44px 列里 nowrap 溢出,被中列圆点压住) - 连接件横线右侧缩进 5px、时间列右内边距 4px:线与文字不再相接 - 圆点列 16→18px,圆点垂直对齐到时间文字行中心
There was a problem hiding this comment.
审查结论
整体实现质量高:补间数学(归一化弹簧、progress>=1 精确落点、Math.max(0.02, scale) 钳住过冲)、drawFrame 里 stepFitTween 前置使脏检查必然重绘、PIXI 原生监听先于 React 冒泡 onPointerMove 触发(保证 draggingNode 先于武装判定置位)等关键点都验证无误;GraphFitButton 仍被 Wiki/EntityFact/Link 三个画布使用,未产生死代码。未发现安全、隐私、构建或数据丢失风险,无阻塞项。以下按严重度列出 4 条非阻塞发现:
1.(低)放大画布后拖拽节点仍会误武装自动复位
RoomGraphCanvas.tsx:218-223 用 3 屏幕 px 死区判定「改了视口」,而 PixiForceGraphRenderer.ts:404,425 的节点拖拽要移动 3 世界单位 才触发 onNodeDrag(进而置 draggingNode = true)。缩放 > 1 时 3px 屏幕距离 < 3 世界单位,shell 的死区先越过 → 此时 draggingNode 还未置位 → armAutoFit() 被调用。结果:用户只是拖了个节点(视口没动),离开画布 5s 后视口仍会弹回,违背 PR 描述的「拖的是节点 → 视口没变,不武装」。缩放 ≤ 1 时两边阈值等价且 PIXI 先派发,无此问题。建议在 handlePointerEnd 里校正:若 draggingNode.current 为真且期间未发生 pan,则解除武装。
2.(低)en-US 下时间列 "02:30 PM" 仍可能溢出 44px 列
useMemoryData.ts:202-207 的 formatTimeShort 未固定 hour12:zh-CN 产出 14:30,en-US 默认产出 02:30 PM(约 8 字符)。而 MemoryPage.css 中 .mem-tl-time 列宽 44px、white-space: nowrap; overflow: visible、10px 字号,en-US 会向左溢出数像素——这正是最后一条 commit(94394fde)想修的「时间列被遮挡」问题的温和复现。建议 { hour12: false }(与 ConnectorConsolePage.tsx:299 的做法一致)或按 locale 加宽列。
3.(低)featuredClamped 溢出探测只跟随 window resize,桌面端布局变化不触发
AtomicMemoryPane.tsx:403-412 只在挂载/精选变化时量一次 scrollHeight vs clientHeight,之后仅监听 window resize。但桌面应用里主导航折叠(App.tsx:1068 的 data-nav-collapsed)、分栏拖动、字体异步加载都会改变换行而不派发 window resize → 探测过期:内容可能已被三行 clamp 却不出现「展开」入口(用户被截断且无法看全文)。建议改用 ResizeObserver 观察 featuredContentRef 元素本身。
4.(极低)pointerInside 为只写不读的死状态
RoomGraphCanvas.tsx:169,193,200,208,216 的 pointerInside 从未被读取——「鼠标在画布内图谱不动」的实际保障来自「倒计时只在 handlePointerLeave 排」这一结构。建议删除,避免误导后续维护者以为存在 in-canvas 分支。
其余核对过且无问题的点:RelationsPane.tsx JSX 标签内的 // 注释为合法语法;.mem-featured-content[data-expanded](0,2,0)能完整覆盖末尾的 line-clamp 规则(0,1,0);后置的 .mem-featured-content { font-size: 14px } 与 min-height: calc(1.6em * 3) 的 em 单位自洽;now 为可选依赖,wiki/entity/link 画布的 PIXI_DEPENDENCIES 路径不受影响;两个改写后的测试(home-graph-fit / relations-pane)与按钮移除语义匹配,context-room-graph-fit-buttons.test.tsx 覆盖的其他画布按钮不受影响。
🤖 Generated with Claude Code
概要
三块 UI 改进:context-room 关系图谱复位动画、记忆页精选卡、记忆页时间轴形态。
1. 关系图谱:移出画布 5s 自动复位
autoFitOnIdleMs = 0可整体关闭;旧画布无补间能力时退回瞬时对准2. 复位动画:1s 慢弹簧
ease(elapsed, 0, 1, duration)调用自定义 ease、并以「世界宽度」为插值量,此前的弹簧曲线实际被塌缩成一次瞬跳。改为共享 ticker 逐帧驱动:scale 与画面中心按弹簧值线性插值cancelFitAnimation()(进画布句柄),返回是否真的停了一段在播补间3. 复位/居中修正
4. 记忆精选卡:固定三行
5. 记忆时间轴:气泡对话式(H 方案,14 候选中选定)
formatTimeShort),完整日期悬停 title 可见、dateTime语义保留;横线右缩 5px 与文字留出空隙--cr-*变量,暗色主题自动跟随测试
其他
70e69241移除了误提交的submodules/nxcoreasrgitlink(无 .gitmodules 声明,克隆会得到空目录)