完善遮罩删除范围选择

- 删除/清空已保存标注前预检后端 annotation id,跳过本地陈旧 id,避免重复 DELETE 产生 404 控制台红字

- 左侧工具栏新增 DEL 删除选中遮罩入口,调整清空遮罩弹窗文案为“清空所有传播帧”,并加入按帧范围选择入口

- 区域合并和重叠区域去除在存在传播帧时弹出当前帧/所有传播帧选择,传播帧同步后保留原 lineage metadata

- 多 polygon 或分离区域组成的 mask 选中后显示全部顶点与插点手柄,同帧传播链分散 mask 点选时联动高亮

- 调整工具栏分组分隔线位置,只在清空遮罩下方保留 tool-group-separator 测试标记

- 更新 VideoWorkspace、CanvasArea、ToolsPalette 回归测试和相关项目文档
This commit is contained in:
2026-05-03 22:14:00 +08:00
parent 275be62db5
commit 5ae1d15336
12 changed files with 215 additions and 55 deletions

View File

@@ -82,15 +82,16 @@
- 绘制工具点击已有 mask 时应继续执行当前绘制动作,不应被 mask 选择逻辑吞掉。
- 所有 polygon mask 都不显示黄色 seed point也不提供 seed point 拖动;普通手工/AI/GT mask 在画布上应保持一致的区域渲染、选择、顶点编辑、拓扑统计、边缘平滑和保存体验。
- 工具栏提供“调整多边形”工具,用户可以点击 mask 进入 polygon 顶点编辑态;按住顶点即可直接拖动并实时更新 mask 几何,不需要先单击选中顶点,已保存 mask 会标记为 dirty顶点拖拽不能冒泡成画布拖拽编辑结束后 Canvas 当前缩放和平移视口必须保持不变。
- 工具栏在“重叠区域去除”之后提供唯一的“清空遮罩”和“导入 GT Mask”入口;导入入口使用区别于普通编辑工具的紫色底色,不切换 activeTool。
- 工具栏按浅灰分隔线分组:拖拽/选择到创建圆为基础绘制组,画笔/橡皮擦为局部笔触组,区域合并/重叠区域去除/DEL/清空遮罩为布尔与删除组,导入 GT Mask/AI 智能分割为外部动作组;`data-testid="tool-group-separator"` 位于清空遮罩下方并分隔外部动作组。工作区在“清空遮罩”上方提供 `DEL` 按钮,语义等同键盘 Delete/Backspace“导入 GT Mask”入口使用区别于普通编辑工具的紫色底色不切换 activeTool。
- 顶点编辑态显示边中点插入手柄;点击边中点会在该边中间新增顶点。
- “调整多边形”工具下双击 polygon 边界时,会在最接近的线段上按双击位置新增顶点。
- 顶点编辑态下选中顶点后可用 Delete/Backspace 删除顶点,但不会让 polygon 少于三点。
- 中空 mask 必须保留外圈与内洞 ring 分组;进入“调整多边形”后,外圈和内洞都应显示可拖动顶点与边中点插入手柄,内洞顶点拖动、插点和保存后的回显都不能把 mask 变成实心。
- 选中整个 mask 且未选中具体顶点时Delete/Backspace 删除该 mask已保存 mask 同步调用后端删除接口;如果删除对象属于自动传播链或是传播 seed应同步删除同一传播链上的自动传播 mask但不能删除其他帧独立 AI 推理或人工标注 mask
- 多 polygon 或分离区域组成的同一个 mask 进入“调整多边形”后,所有分离 polygon 都应显示可拖动顶点与边中点插入手柄,不能只显示第一个主区域
- 选中整个 mask 且未选中具体顶点时Delete/Backspace 删除该 mask左侧 `DEL` 按钮复用同一删除链路。删除已保存 mask 前,前端必须用当前后端标注列表预检 `annotationId`,只对仍存在的 id 发送 `DELETE /api/ai/annotations/{id}`,避免本地陈旧 id 导致浏览器控制台出现 404 红字;如果删除对象属于自动传播链或是传播 seed应同步删除同一传播链上的自动传播 mask但不能删除其他帧独立 AI 推理或人工标注 mask。
- 撤销、重做绑定全局 `maskHistory/maskFuture`,工作区支持顶栏按钮和全局快捷键 `Ctrl/Cmd+Z``Ctrl/Cmd+Shift+Z``Ctrl/Cmd+Y`;快捷键监听应在 capture 阶段处理,并在 `event.key` 不可靠时兼容 `event.code=KeyZ/KeyY`但输入框、文本域、下拉框和可编辑文本聚焦时不能拦截AI 页支持自己的按钮;左侧工具栏不重复放置撤销/重做入口。
- 区域合并工具支持多选当前帧 mask并使用 polygon union 生成合并后的主 mask若主区域和参与区域存在同一传播链上的对应 mask合并必须同步应用到其它传播帧中对应的主区域和参与区域只删除每个已同步帧里的参与合并 mask不能把未参与本次同步的同链对象整链误删。
- 区域去除工具支持多选当前帧 mask并从第一个选中的主 mask 中扣除后续选中 mask若主区域和参与区域存在同一传播链上的对应 mask去除必须同步应用到其它传播帧中对应的主区域和参与区域参与扣除的 mask 本身保留。
- 区域合并工具支持多选当前帧 mask并使用 polygon union 生成合并后的主 mask若主区域和参与区域存在同一传播链上的对应 mask合并前必须弹出范围选择,让用户选择只处理当前帧或处理所有传播帧;选择所有传播帧时,同一次合并必须同步应用到其它传播帧中对应的主区域和参与区域,只删除每个已同步帧里的参与合并 mask不能把未参与本次同步的同链对象整链误删。
- 区域去除工具支持多选当前帧 mask并从第一个选中的主 mask 中扣除后续选中 mask若主区域和参与区域存在同一传播链上的对应 mask去除前必须弹出范围选择,让用户选择只处理当前帧或处理所有传播帧;选择所有传播帧时,同一次去除必须同步应用到其它传播帧中对应的主区域和参与区域,参与扣除的 mask 本身保留。
- 区域合并/去除同步到传播帧时必须保留传播 mask 原有 `source``source_annotation_id``source_mask_id``propagation_seed_key` 等 lineage metadata这些帧可以进入 dirty 待保存状态,但不能因为几何同步在时间轴上从自动传播帧变成人工/AI 标注帧。
- 区域合并/去除模式显示已选数量,并隐藏 polygon 编辑手柄以避免手柄抢占多选点击;第一个选中的主区域使用黄色实线轮廓,后续参与合并/扣除的区域使用红色虚线轮廓。
- 区域去除结果包含内洞时,前端保留 hole ring 并用 even-odd 规则渲染,保存时把外圈写入 `mask_data.polygons`、把每个外圈对应内洞写入 `mask_data.holes`,并用 `metadata.polygonRingCounts` 支撑前端 ring 回显。
@@ -150,7 +151,7 @@
- 当前前端保存状态按钮会保存当前项目未保存 mask并会更新已标记为 dirty 的已保存 mask。
- 如果 dirty mask 携带的本地旧 `annotationId` 在后端已经不存在,前端保存链路必须先用当前后端标注列表做存在性预检,已知缺失的 id 直接用同一几何和 metadata 重新 `POST` 创建标注;如果预检后发生并发删除导致 `PATCH` 返回 404也必须降级为重新创建并重新拉取后端标注替换本地旧 id点击“开始传播”前的参考帧保存也必须复用该容错逻辑不能因陈旧 id 中断传播。
- 保存成功后,前端会重新拉取后端标注,并用后端 saved annotation 替换本次提交的 draft mask未提交的其他 draft mask 仍保留。
- 工作区“清空遮罩”只从左侧工具栏触发;当前帧有选中 mask 时以选中 mask 为对象,没有选中时以当前帧全部 mask 为对象。若目标 mask 没有关联其它传播帧,则直接删除当前帧已保存标注并清空当前帧未保存 mask不弹确认若目标 mask 存在传播链上的其它帧结果,则弹出范围确认,用户可选择“只清当前帧”“清空传播所有帧”,也可取消。清空传播所有帧只同步清空同传播链自动传播结果,不能删除其它帧独立 AI 推理或人工标注 mask。
- 工作区“清空遮罩”只从左侧工具栏触发;当前帧有选中 mask 时以选中 mask 为对象,没有选中时以当前帧全部 mask 为对象。若目标 mask 没有关联其它传播帧,则直接删除当前帧已保存标注并清空当前帧未保存 mask不弹确认若目标 mask 存在传播链上的其它帧结果,则弹出范围确认,用户可选择“只清当前帧”“清空所有传播帧”、“按帧范围选择”或取消;按帧范围选择进入和清空片段遮罩一致的时间轴范围选择模式,并提供“清空全部”和“保留人工/AI”。清空所有传播帧只同步清空同传播链自动传播结果,不能删除其它帧独立 AI 推理或人工标注 mask。
- 工作区加载项目帧后会查询已保存标注并回显。
- 工作区支持导入 GT mask 图片,前端调用 `POST /api/ai/import-gt-mask`
- 导入 GT Mask 时,前端必须让用户选择未知 maskid 处理策略:舍弃未知类别,或导入为“未定义类别”等待后续重新命名。