Skip to content

关系图谱自动复位(1s 慢弹簧回位)+ 记忆精选卡三行折叠 + 记忆时间轴气泡对话式 - #317

Merged
21335464876 merged 6 commits into
devfrom
text
Oct 23, 2026
Merged

21335464876 merged 6 commits into
devfrom
text

Conversation

@zengjiayi819

@zengjiayi819 zengjiayi819 commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

概要

三块 UI 改进:context-room 关系图谱复位动画、记忆页精选卡、记忆页时间轴形态。

1. 关系图谱:移出画布 5s 自动复位

  • 触发模型:鼠标在关系图谱画布内时图谱绝不动作;只有移出画布满 5 秒才回位(倒计时从离开那刻才开始)
  • 只有拖拽/滚轮真正改过视口才武装复位;点击节点、3px 内的抖动不算改视口
  • 移回画布:取消待触发的复位;若 1s 补间正播到一半,立刻定在当前帧,并重新武装等下次离开补完
  • autoFitOnIdleMs = 0 可整体关闭;旧画布无补间能力时退回瞬时对准
  • 移除右上角「适应画布」按钮(RelationsPane / HomeView)

2. 复位动画:1s 慢弹簧

  • ζ 0.38 / ω 10 欠阻尼二阶阶跃响应:放大 → 缩小 → 交替衰减,1s 末精确落定
  • 弃用 pixi-viewport 的 Animate 插件:它按 Penner 签名 ease(elapsed, 0, 1, duration) 调用自定义 ease、并以「世界宽度」为插值量,此前的弹簧曲线实际被塌缩成一次瞬跳。改为共享 ticker 逐帧驱动:scale 与画面中心按弹簧值线性插值
  • 新增 cancelFitAnimation()(进画布句柄),返回是否真的停了一段在播补间

3. 复位/居中修正

  • fitView 居中改用节点几何中心;底边 24px 标签预留只参与算缩放(此前中心被带偏 12 世界像素,内容固定上偏)
  • 补间终点即精确目标,无残留偏差

4. 记忆精选卡:固定三行

  • 内容区预留三行,超出折叠(line-clamp);真被截断才显示「展开/收起」
  • 展开时卡片自然变高;折叠态即最小三行状态

5. 记忆时间轴:气泡对话式(H 方案,14 候选中选定)

  • 每条记忆改为 [时间 | 连接件 | 气泡卡片] 三列网格:时间右对齐、短横线 + 发光圆点连接到卡片
  • 气泡卡片保留原摘要卡全部能力(类型徽章 / 房间跳转 / 展开详情),圆角改左上切角气泡形
  • 修复时间被圆点遮挡:左列只显示 HH:mm(新增 formatTimeShort),完整日期悬停 title 可见、dateTime 语义保留;横线右缩 5px 与文字留出空隙
  • 全部颜色走 --cr-* 变量,暗色主题自动跟随

测试

套件 结果
room-graph-canvas-autofit(按离开触发语义重写) 8/8 ✅
context-room-relations-pane / home-graph-fit 4/4、2/2 ✅
memory-featured-expand(新增) 2/2 ✅
pixi-force-graph-renderer 25/28(3 条失败为 dev 基线已有,stash 复核一致)
context-room 全量 与 dev 基线完全一致,无新增失败
tsc 零新增错误(ThoughtsPane 既有 10 条不在本 PR 范围)
时间轴几何(headless Chrome 探针) 三列 46/16/弹性,线-点-文字有空隙,无溢出

其他

  • 70e69241 移除了误提交的 submodules/nxcoreasr gitlink(无 .gitmodules 声明,克隆会得到空目录)

zengjiayi819 added 2 commits October 23, 2026 15:40
- 精选卡内容区预留三行,超出行数用 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 基线

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查结论

整体实现质量较高:补间数学(欠阻尼二阶阶跃响应 + 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)理论上可与补间同帧竞争,但两者目标几乎一致且仅一帧闪烁,可不处理。

zengjiayi819 added 4 commits October 23, 2026 17:00
- 一行改为 [时间 | 连接件 | 气泡] 三列网格:时间右对齐与节点同水平线,
  中列短横线 + 发光圆点连接到卡片
- 气泡卡片沿用原摘要卡,仅加「左上切角」的气泡圆角(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,圆点垂直对齐到时间文字行中心
@zengjiayi819 zengjiayi819 changed the title 关系图谱自动复位(1s 慢弹簧回位)+ 记忆精选卡三行折叠 关系图谱自动复位(1s 慢弹簧回位)+ 记忆精选卡三行折叠 + 记忆时间轴气泡对话式 Oct 23, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查结论

整体实现质量高:补间数学(归一化弹簧、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

@21335464876
21335464876 merged commit cdfa915 into dev Oct 23, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants