From d54a67662bdbc1da7057940e1850ee748a520adb Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Wed, 26 Aug 2026 23:10:20 +0200 Subject: [PATCH] task(A4.1): wire dispatcher to canvas - doc bridge, selection bridge, activeTool sync --- frontend/src/components/CanvasArea.tsx | 41 ++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/CanvasArea.tsx b/frontend/src/components/CanvasArea.tsx index df4c164..f4cdbff 100644 --- a/frontend/src/components/CanvasArea.tsx +++ b/frontend/src/components/CanvasArea.tsx @@ -14,6 +14,9 @@ import { GroupManager } from '../tools/modification/GroupTool'; import { formatCoordinate } from '../utils/format'; import { InteractionDispatcher } from '../interaction/dispatcher'; import { logger } from '../utils/logger'; +import { CADDocument } from '../kernel/document/CADDocument'; +import { YjsDocument } from '../crdt/YjsDocument'; +import { pluginRegistry } from '../plugins'; const CanvasArea: React.FC = ({ cursorPos, viewMode, onViewChange, gridEnabled, orthoEnabled, snapEnabled, @@ -29,6 +32,8 @@ const CanvasArea: React.FC = ({ const renderEngineRef = useRef(null); const interactionRef = useRef(null); const dispatcherRef = useRef(null); + /** Bereits an onElementCreated gemeldete V2-Element-IDs (verhindert Doppelmeldung bei jedem onChanged-Frame). */ + const announcedV2Ids = useRef>(new Set()); const spatialIndexRef = useRef(null); const layerManagerRef = useRef(null); const selectionEngineRef = useRef(null); @@ -47,17 +52,38 @@ const CanvasArea: React.FC = ({ canvas, zoomPan, renderEngine, snapEngine, selectionEngine, spatialIndex, layerManager, ); - // ── Task A2: V2-Tool-Dispatcher hinter Feature-Flag ── - // Flag VITE_TOOL_DISPATCHER=v2 (in .env / Shell): aktiviert den neuen - // InteractionDispatcher parallel zur Legacy InteractionEngine. - // Tools mit handlers werden dann über die Plugin-API v2 geroutet. + // ── Task A2/A4: V2-Tool-Dispatcher hinter Feature-Flag ── + // Flag VITE_TOOL_DISPATCHER=v2: aktiviert den InteractionDispatcher UND + // verdrahtet ihn mit einem lokalen CADDocument + SelectionBridge (Task A4.1). if (import.meta.env.VITE_TOOL_DISPATCHER === 'v2') { + const ydoc = new YjsDocument(); + ydoc.data.layers.set('layer-0', { + id: 'layer-0', name: 'Standard', visible: true, locked: false, + color: '#ffffff', lineType: 'solid', transparency: 0, sortOrder: 0, parentId: null, + } as never); + const v2Doc = new CADDocument(ydoc); + // Committete V2-Elemente fließen über den etablierten Kanal in UI/State: + v2Doc.onChanged(() => { + for (const el of v2Doc.getAllElements()) { + if (!announcedV2Ids.current.has(el.id)) { + announcedV2Ids.current.add(el.id); + onElementCreated(el); + } + } + }); + dispatcherRef.current = new InteractionDispatcher(canvas, { zoomPan, setStatus: (msg) => { - /* StatusBar-Bridge folgt in A3 */ + /* StatusBar-Bridge folgt in A4 */ }, setPreview: (el) => renderEngine.setPreviewElement(el as CADElement | null), + getDoc: () => v2Doc, + getSelectionBridge: () => ({ + getIds: () => Array.from(selectionEngine.getSelectedIds()), + setIds: (ids) => selectionEngine.selectByIds(ids, false), + hitTest: (wx, wy, tol) => renderEngine.hitTest(wx, wy, tol), + }), }); logger.info('InteractionDispatcher v2 aktiviert (VITE_TOOL_DISPATCHER=v2)'); } @@ -249,6 +275,11 @@ const CanvasArea: React.FC = ({ const interaction = interactionRef.current; if (!interaction) return; interaction.setTool(activeTool as ToolType); + // Task A4.1: V2-Werkzeuge zusätzlich an den Dispatcher routen (Flag aktiv). + // Legacy bleibt parallel aktiv für noch nicht migrierte Tools. + dispatcherRef.current?.setActive( + pluginRegistry.getToolV2(activeTool) ? activeTool : null, + ); // eslint-disable-next-line react-hooks/exhaustive-deps }, [activeTool]);