Fence session lifetimes and cancel owned previews on tool changes

This commit is contained in:
2026-11-09 22:09:30 +08:00
parent ed85546674
commit bcc11e2e84
5 changed files with 83 additions and 20 deletions
@@ -158,6 +158,7 @@ function CanvasView({ engine, board, viewId, renderers,fitLoading=true }: { engi
return id;
}
function connect(connection: Connection) {
if(engine.sessions.getSnapshot().views.get(viewId)?.tool!=='select')return;
const document = engine.getSnapshot().document, source = document.elements.get(connection.source), destination = document.elements.get(connection.target);
if (!source || !destination) return;
if([source.id,destination.id].some(id=>projectionCache.current!.getNode(id)?.type==='unsupported')){setError('此元素暂不支持创建新连线');return;}
@@ -240,7 +241,7 @@ function CanvasView({ engine, board, viewId, renderers,fitLoading=true }: { engi
onFocusCapture={event => { if ((event.target as HTMLElement).closest('input,textarea,[contenteditable="true"],[role="textbox"]')) { focusEpoch.current++; setEditorFocused(true); } }}
onBlurCapture={() => { const epoch = ++focusEpoch.current; queueMicrotask(() => { if (focusEpoch.current === epoch) setEditorFocused(false); }); }}
onMouseDownCapture={event => { if (event.button === 0 && (event.target as HTMLElement).closest('.react-flow__node')) pointerStart.current = {...flow.screenToFlowPosition({ x: event.clientX, y: event.clientY }),pointerId:pointerStart.current?.pointerId}; }}
onTouchStartCapture={event => { if (event.touches.length > 1) { engine.sessions.cancel('multi_touch'); drag.current = null; return; } const touch = event.touches[0]; if (touch && (event.target as HTMLElement).closest('.react-flow__node')) pointerStart.current = {...flow.screenToFlowPosition({ x: touch.clientX, y: touch.clientY }),pointerId:pointerStart.current?.pointerId}; }}
onTouchStartCapture={event => { if (event.touches.length > 1) { const state=engine.sessions.getSnapshot();if(state.gesture?.viewId===viewId||state.connectionGesture?.viewId===viewId)engine.sessions.cancel('multi_touch');drag.current = null;pointerStart.current=null;return; } const touch = event.touches[0]; if (touch && (event.target as HTMLElement).closest('.react-flow__node')) pointerStart.current = {...flow.screenToFlowPosition({ x: touch.clientX, y: touch.clientY }),pointerId:pointerStart.current?.pointerId}; }}
onPointerDownCapture={event => { if (event.button === 0 && (event.target as HTMLElement).closest('.react-flow__node')) pointerStart.current = {...flow.screenToFlowPosition({ x: event.clientX, y: event.clientY }),pointerId:event.pointerId}; }}
onPointerCancelCapture={event => { const currentDrag=drag.current;if(!currentDrag||currentDrag.pointerId!==undefined&&currentDrag.pointerId!==event.pointerId)return;const state=engine.sessions.getSnapshot();if(state.gesture?.viewId===viewId&&state.gesture.token===currentDrag.token)engine.sessions.cancel('pointer_cancel');drag.current=null; }}
onKeyDown={event => {
@@ -256,7 +257,7 @@ function CanvasView({ engine, board, viewId, renderers,fitLoading=true }: { engi
<NodeEditing.Provider value={nodeEditing}>
<EdgeEditing.Provider value={edgeEditing}>
<ReactFlow<ProjectionNode> elevateEdgesOnSelect onlyRenderVisibleElements={cullVisible} nodes={projection.nodes} edges={projection.edges} nodeTypes={nodeTypes} edgeTypes={edgeTypes} connectionMode={ConnectionMode.Loose}
nodesDraggable={view.tool === 'select'} panOnDrag={view.tool === 'pan' ? true : [1, 2]} selectionOnDrag={view.tool === 'select'} panOnScroll zoomOnScroll={false} minZoom={.1} maxZoom={4} deleteKeyCode={null} defaultViewport={view.viewport}
nodesConnectable={view.tool === 'select'} nodesDraggable={view.tool === 'select'} panOnDrag={view.tool === 'pan' ? true : [1, 2]} selectionOnDrag={view.tool === 'select'} panOnScroll zoomOnScroll={false} minZoom={.1} maxZoom={4} deleteKeyCode={null} defaultViewport={view.viewport}
onMoveEnd={(_, viewport) => engine.sessions.viewport(viewId, viewport)}
onNodeDragStart={(event, node, nodes) => {