添加取消选中实体按钮

- 在左侧工具栏拖拽/选择下方新增“取消选中”按钮,提供等同 Esc 的可点击入口。

- 将 VideoWorkspace 的取消选中信号传入 CanvasArea,统一清空 mask 选区、临时绘制状态和顶点选择。

- 修正 Canvas 本地选区与全局 selectedMaskIds 的同步,避免取消后旧本地选区被重新发布。

- 补充 ToolsPalette、CanvasArea 回归测试,覆盖实体按钮位置、回调和 clearSelectionSignal 行为。

- 更新 README、AGENTS 与前端审计/需求冻结/设计冻结/测试计划/交互状态机文档。
This commit is contained in:
2026-05-04 04:09:32 +08:00
parent 141dd4ce4b
commit 87b82b882f
12 changed files with 120 additions and 24 deletions

View File

@@ -503,6 +503,7 @@ export function VideoWorkspace({ onNavigateToAI }: { onNavigateToAI?: () => void
const redoMasks = useStore((state) => state.redoMasks);
const [isSaving, setIsSaving] = useState(false);
const [isExporting, setIsExporting] = useState(false);
const [clearSelectionSignal, setClearSelectionSignal] = useState(0);
const [isExportMenuOpen, setIsExportMenuOpen] = useState(false);
const [exportScope, setExportScope] = useState<ExportScope>('current');
const [exportOutputs, setExportOutputs] = useState<SegmentationExportOutput[]>([
@@ -924,6 +925,11 @@ export function VideoWorkspace({ onNavigateToAI }: { onNavigateToAI?: () => void
});
}, [currentFrame, currentFrameNumber, executeClearCurrentMasks]);
const handleClearSelection = useCallback(() => {
setSelectedMaskIds([]);
setClearSelectionSignal((value) => value + 1);
}, [setSelectedMaskIds]);
const handleDeleteSelectedMasks = useCallback(async (requestedMaskIds?: string[]) => {
if (!currentFrame) return;
const latestMasks = useStore.getState().masks;
@@ -2269,6 +2275,7 @@ export function VideoWorkspace({ onNavigateToAI }: { onNavigateToAI?: () => void
onTriggerAI={onNavigateToAI}
onAutoPropagate={() => void handleAutoPropagate()}
onImportGtMask={() => gtMaskInputRef.current?.click()}
onClearSelection={handleClearSelection}
onDeleteMasks={handleDeleteSelectedMasks}
onClearMasks={handleClearCurrentFrameMasks}
canAutoPropagate={Boolean(currentProject?.id && currentFrame?.id) && !isSaving && !isExporting && !isImportingGt}
@@ -2284,6 +2291,7 @@ export function VideoWorkspace({ onNavigateToAI }: { onNavigateToAI?: () => void
frame={currentFrame}
currentFrameNumber={currentFrameNumber || 0}
totalFrames={totalFrames}
clearSelectionSignal={clearSelectionSignal}
onRequestDeleteMasks={(maskIds) => void handleDeleteSelectedMasks(maskIds)}
onRequestBooleanFrameRange={handleBooleanFrameRangeRequest}
onBooleanOperationStart={clearPendingBooleanRangeSelection}