Repository navigation
Open
Labels
area:canvasCanvas, nodes and direct manipulationCanvas, nodes and direct manipulationtype:featNew capability or behaviourNew capability or behaviour
Description
目标与范围
承接 #107「P0 画布性能与撤销/重做」。当前缺少可比较的大工程基线,反复撤销/重做会出现可感知停顿,历史操作之间夹有编辑时更明显。本 Issue 交付:可重复的大工程测量方法、基于实测的卡顿定位与修复、以及同一工程同一环境的前后对比结论。
范围限制:不重写历史机制,不改变撤销语义(只回退允许回退的工程内容,不回退远端调用与费用),不含后台渲染与多端同步议题;性能目标数值待基线数据产出后确认。
用户流程与关键规则
- 选一个代表性大工程并记录规模:节点数、连线数、素材数、嵌入媒体体积、工程文件大小,以及测量所用机器与浏览器版本。
- 测量覆盖打开、加载、选中、拖拽、连线、缩放、输入、提交生成、结果显示;撤销与重做分别测单次和连续 20 次。
- 必须包含「历史操作之间夹有编辑」的场景:撤销若干步后再编辑,future 应正确清空,不得出现重做复活旧内容或历史错位。
- 撤销/重做期间界面保持可交互;更新后保留选中、焦点、正在编辑的文本、播放头与未提交输入。
- 状态校验以服务端快照为准:反复撤销/重做、刷新、重开工程、后台任务并发完成后,画布与工程快照一致。
- 定位结论要区分前端渲染、快照与版本载荷、后端历史回放三段耗时,不把服务端排队时间算作界面卡顿。
实现影响
- 前端本地历史
src/canvas/history.ts保留最多 80 份整份文档快照,sameContent逐节点逐边比较;拖拽走 transaction 提交,是比较与拷贝开销的主要来源。 src/backend/creationClient.ts的undo()每次手势都会重新拉取完整工程快照、再读 history,并携带全部canvas_item、generation_spec、reference、editor_document版本提交命令;大工程下这是 O(n) 载荷与往返。src/canvas/CanvasWorkspace.tsx负责撤销编排队列(historyReplaying)和整份文档状态,撤销后的重渲染范围决定停顿感知。- 后端
packages/core/src/opendirector/application/creation_history.py与命令日志读取负责回放;若耗时在服务端,应在此修复而不是只用前端规避。 - 测量脚本放
tests/browser/,沿用tests/browser/reference_drag_performance.mjs的隔离 API 与 CPU profile 方式;回归用例可扩展paste_undo.mjs、canvas_selection_history.mjs与tests/test_creation_history_service.py。
验收标准
- 提供可重复执行的测量脚本与环境说明,在大工程上输出各项耗时并记录工程规模。
- 复现反复撤销/重做的停顿并给出定位证据(CPU profile 或分段耗时),说明瓶颈落在哪一段。
- 修复后同一工程、同一环境的连续 20 次撤销/重做不再出现采集到的长停顿,并给出前后数值对比。
- 撤销后再编辑的场景有回归用例覆盖:future 正确清空,不复活旧内容,历史步数不串位。
- 反复撤销/重做、刷新、重开、后台并发生成完成后,画布与工程快照一致,选中、焦点、编辑中文本、播放头与未提交输入保留。
- 现有前后端历史测试与浏览器用例通过;新增用户可见文案同时提供中英词条并通过
npm run check:i18n。 - 剩余限制与未覆盖场景记录在本 Issue 评论中。
待确认
- 代表性大工程的规模与参考硬件,作为对外比较的固定口径。
- 性能目标形态:仅记录数值,还是设定阈值并纳入 CI 门禁(需基线数据后再定)。
- 若瓶颈来自每次撤销的全量快照拉取,是否接受按需增量读取的改造。
Metadata
Metadata
Assignees
Labels
area:canvasCanvas, nodes and direct manipulationCanvas, nodes and direct manipulationtype:featNew capability or behaviourNew capability or behaviour
Activity
kunkunblueberry commentedon Nov 10, 2026
Taking this up as the performance owner, with one sequencing note before work starts.
The first acceptance item is currently blocked
The storage side of that baseline does not produce numbers today.
#260records that the nightly cold-open and delete fixtures expire their edit session during preparation, so the lane reportsSUITE/INCOMPLETEand fails instead of emittingstorage.coldOpen.snapshot,storage.coldOpen.open,storage.delete.clientSnapshotandstorage.delete.trash. I reproduced it locally at the configured 600-commit / 200-node scale one1f2246a:#270renews the fixture's lease during preparation (outside the timed region) and both scenarios then produce every tracked measurement on my machine. That is the change that makes "可比较的基线" possible at all.So the order I propose is:
#270, confirm the next nightly run emits all four storage measurements.What I can take
tests/browser/reference_drag_performance.mjs's isolation/profile pattern for the canvas interactions listed here, and pin the storage side to the nightly lane that#270restores.What I would rather not take unilaterally
The rework direction in 待确认: "若瓶颈来自每次撤销的全量快照拉取,是否接受按需增量读取的改造". That changes
creationClient.undo()'s contract and every caller, so it needs your call rather than a performance-driven decision.Also worth stating up front:
src/canvas/history.tskeeps up to 80 whole-document snapshots andsameContentcompares node by node, whileundo()re-fetches the full project snapshot per gesture. Those two facts alone predict a large-project stall, but I will not claim them as the cause until the profile says so — the issue's own requirement is that the conclusion distinguish the three layers.