feat: global block library tree + grouping tool — 19 files, 588 tests green

This commit is contained in:
A0 Orchestrator
2026-07-01 21:02:31 +02:00
parent 405e55e818
commit 7b19a99b24
20 changed files with 2099 additions and 79 deletions
+66 -1
View File
@@ -583,8 +583,37 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
}, []);
const handleCommandTrigger = useCallback((msg: string) => {
// Route group/ungroup actions from keyboard shortcuts
if (msg === 'group') {
const gm = groupManagerRef.current;
const ids = selectedElementIdsRef.current;
if (ids.length < 2) {
setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Gruppieren: Mindestens 2 Elemente auswählen', type: 'info' }]);
return;
}
const group = gm.createGroup(ids);
setGroups(gm.getGroups());
collab.setGroup(group);
setCommandHistory((prev) => [...prev, { prefix: '·', text: `Gruppe erstellt: ${group.name} (${ids.length} Elemente)`, type: 'info' }]);
return;
}
if (msg === 'ungroup') {
const gm = groupManagerRef.current;
const allGroups = gm.getGroups();
if (allGroups.length === 0) {
setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Degruppieren: Keine Gruppe vorhanden', type: 'info' }]);
return;
}
const selIds = selectedElementIdsRef.current;
let targetGroup = allGroups.find(g => g.elementIds.some(id => selIds.includes(id)));
if (!targetGroup) targetGroup = allGroups[allGroups.length - 1];
gm.ungroup(targetGroup.id);
setGroups(gm.getGroups());
setCommandHistory((prev) => [...prev, { prefix: '·', text: `Gruppe aufgelöst: ${targetGroup.name}`, type: 'info' }]);
return;
}
setCommandHistory((prev) => [...prev, { prefix: '·', text: msg, type: 'info' }]);
}, []);
}, [collab]);
// Block management handlers
const handleRenameBlock = useCallback((id: string, name: string) => {
@@ -892,6 +921,38 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
return;
}
// ─── Group/Ungroup actions ───
if (action === 'group') {
const gm = groupManagerRef.current;
const ids = selectedElementIdsRef.current;
if (ids.length < 2) {
setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Gruppieren: Mindestens 2 Elemente auswählen', type: 'info' }]);
return;
}
const group = gm.createGroup(ids);
const newGroups = gm.getGroups();
setGroups(newGroups);
collab.setGroup(group);
setCommandHistory((prev) => [...prev, { prefix: '·', text: `Gruppe erstellt: ${group.name} (${ids.length} Elemente)`, type: 'info' }]);
return;
}
if (action === 'ungroup') {
const gm = groupManagerRef.current;
const allGroups = gm.getGroups();
if (allGroups.length === 0) {
setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Degruppieren: Keine Gruppe vorhanden', type: 'info' }]);
return;
}
// Find the group that contains any selected element
const selIds = selectedElementIdsRef.current;
let targetGroup = allGroups.find(g => g.elementIds.some(id => selIds.includes(id)));
if (!targetGroup) targetGroup = allGroups[allGroups.length - 1];
gm.ungroup(targetGroup.id);
setGroups(gm.getGroups());
setCommandHistory((prev) => [...prev, { prefix: '·', text: `Gruppe aufgelöst: ${targetGroup.name}`, type: 'info' }]);
return;
}
// ─── Insert actions (set active tool) ───
if (action === 'insert-line') { setActiveTool('line'); setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Linie-Werkzeug aktiv', type: 'info' }]); return; }
if (action === 'insert-rect') { setActiveTool('rect'); setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Rechteck-Werkzeug aktiv', type: 'info' }]); return; }
@@ -1429,6 +1490,8 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
onDeleteBlock={handleDeleteBlock}
onSvgImport={handleSvgImport}
onSaveGroupAsBlock={handleSaveGroupAsBlock}
onGroup={() => handleRibbonAction('group')}
onUngroup={() => handleRibbonAction('ungroup')}
/>
<CanvasArea
cursorPos={cursorPos}
@@ -1461,6 +1524,7 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
selectedTemplate={selectedTemplate}
bgConfig={bgConfig}
remoteCursors={collab.cursors}
groups={groups}
/>
<RightSidebar
activePanel={activeRightPanel}
@@ -1499,6 +1563,7 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
onReorder={handleReorderLayer}
onAddSubLayer={handleAddSubLayer}
onUpdateElement={handleUpdateElement}
token={token}
/>
</div>
<CommandLine