fix: SQLite boolean binding, CSS brace, race condition, 0 npm vulns, code-splitting

This commit is contained in:
Leopoldadmin
2026-07-04 16:08:51 +02:00
parent a4371ca3ce
commit 0606dbb501
20 changed files with 1070 additions and 53 deletions
+127 -7
View File
@@ -18,6 +18,7 @@ import StatusBar from './components/StatusBar';
import MobileDrawers from './components/MobileDrawers';
import BackgroundImport from './components/BackgroundImport';
import HistoryPanel from './components/HistoryPanel';
import SettingsModal from './components/SettingsModal';
import { BackgroundService, type BackgroundConfig } from './services/backgroundService';
import { HistoryManager, type CADStateSnapshot, type HistoryEntry } from './history';
import { getCommandRegistry } from './services/commandRegistry';
@@ -146,6 +147,10 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
// Mobile drawers
const [mobileLeftOpen, setMobileLeftOpen] = useState(false);
const [mobileRightOpen, setMobileRightOpen] = useState(false);
// Desktop sidebar collapse
const [leftSidebarCollapsed, setLeftSidebarCollapsed] = useState(false);
const [rightSidebarCollapsed, setRightSidebarCollapsed] = useState(false);
const [settingsOpen, setSettingsOpen] = useState(false);
const [activeDrawerTab, setActiveDrawerTab] = useState<DrawerTab>('tool');
// Background
@@ -263,6 +268,8 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
collab.loadFromState({ elements: data.elements, layers: data.layers, blocks: data.blocks });
}
} catch (err) {
if (cancelled) return;
if (err instanceof Error && err.message.includes('Superseded')) return;
console.error('Failed to load project:', err);
setSavedStatus('Fehler beim Laden');
}
@@ -297,13 +304,19 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
setElements(merged);
}
const sameLayers = collab.layers.length === layers.length &&
collab.layers.every((l, i) => l.id === layers[i]?.id);
if (!sameLayers) setLayers(collab.layers);
// Never overwrite local layers with empty remote layers (prevents losing initialLayers)
if (collab.layers.length > 0) {
const sameLayers = collab.layers.length === layers.length &&
collab.layers.every((l, i) => l.id === layers[i]?.id);
if (!sameLayers) setLayers(collab.layers);
}
const sameBlocks = collab.blocks.length === blocks.length &&
collab.blocks.every((b, i) => b.id === blocks[i]?.id);
if (!sameBlocks) setBlocks(collab.blocks);
// Never overwrite local blocks with empty remote blocks (prevents losing defaultBlocks)
if (collab.blocks.length > 0) {
const sameBlocks = collab.blocks.length === blocks.length &&
collab.blocks.every((b, i) => b.id === blocks[i]?.id);
if (!sameBlocks) setBlocks(collab.blocks);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [collab.elements, collab.layers, collab.blocks, collab.status]);
@@ -787,6 +800,89 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Zoom: an Ansicht angepasst', type: 'info' }]);
}, []);
// Layer reorder (drag & drop)
const handleReorderLayer = useCallback((draggedId: string, targetId: string, position: 'before' | 'after' | 'inside') => {
setLayers((prev) => {
const dragged = prev.find((l) => l.id === draggedId);
if (!dragged) return prev;
if (position === 'inside') {
// Make dragged a child of target
const updated = prev.map((l) =>
l.id === draggedId ? { ...l, parentId: targetId } : l
);
return updated;
}
// before/after: reorder at same level
const target = prev.find((l) => l.id === targetId);
if (!target) return prev;
const parentId = target.parentId;
const siblings = prev
.filter((l) => l.parentId === parentId && l.id !== draggedId)
.sort((a, b) => a.sortOrder - b.sortOrder);
const targetIdx = siblings.findIndex((l) => l.id === targetId);
const insertIdx = position === 'before' ? targetIdx : targetIdx + 1;
siblings.splice(insertIdx, 0, { ...dragged, parentId });
// Reassign sortOrder
const reordered = siblings.map((l, i) => ({ ...l, sortOrder: i }));
// Merge back: non-siblings keep original, siblings get reordered
const nonSiblings = prev.filter((l) => l.parentId !== parentId || l.id === draggedId);
const result = [...nonSiblings.filter((l) => l.id !== draggedId), ...reordered];
return result;
});
setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Ebene verschoben', type: 'info' }]);
}, []);
// Add sub-layer
const handleAddSubLayer = useCallback((parentId: string) => {
setLayers((prev) => {
const parent = prev.find((l) => l.id === parentId);
if (!parent) return prev;
const newId = `layer-${Date.now()}`;
const siblings = prev.filter((l) => l.parentId === parentId);
const newLayer: CADLayer = {
id: newId, name: `Teilebene ${siblings.length + 1}`, visible: true, locked: false,
color: parent.color, lineType: 'solid', transparency: 0,
sortOrder: siblings.length, parentId,
};
if (drawingId && token) {
createLayerTyped(token, drawingId, newLayer).catch((err) => {
console.error('Failed to save sub-layer:', err);
});
}
return [...prev, newLayer];
});
}, [drawingId, token]);
// Update single element (from properties panel)
const handleUpdateElement = useCallback((updated: CADElement) => {
setElements((prev) => {
const newElements = prev.map((e) => (e.id === updated.id ? updated : e));
historyManagerRef.current.pushSnapshot({
elements: newElements, layers, blocks, groups, bgConfig,
}, 'Element geändert');
return newElements;
});
syncHistory();
collab.setElement(updated);
if (token) {
setSavedStatus('Speichert…');
updateElement(token, updated.id, updated).then(() => {
setSavedStatus('gespeichert');
}).catch((err) => {
console.error('Failed to update element:', err);
setSavedStatus('Fehler beim Speichern');
});
}
setSelectedElement(updated);
}, [layers, blocks, groups, bgConfig, syncHistory, token, collab]);
const handleBgApply = useCallback((config: BackgroundConfig, _image: HTMLImageElement | null) => {
setBgConfig(config);
setBgImportOpen(false);
@@ -936,18 +1032,34 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
onRedo={handleRedo}
onThemeToggle={handleThemeToggle}
theme={theme}
onOpenSettings={() => setSettingsOpen(true)}
onOpenLeftDrawer={() => setMobileLeftOpen(true)}
onOpenRightDrawer={() => setMobileRightOpen(true)}
/>
<RibbonBar
activeTab={activeRibbonTab}
onTabChange={setActiveRibbonTab}
onAction={handleRibbonAction}
/>
<div className="app-body">
<div className={`app-body${leftSidebarCollapsed ? ' left-collapsed' : ''}${rightSidebarCollapsed ? ' right-collapsed' : ''}`}>
<LeftSidebar
activeTool={activeTool}
onToolChange={handleToolChange}
selectedTemplate={selectedTemplate}
onTemplateSelect={handleTemplateSelect}
className={mobileLeftOpen ? 'open' : ''}
onCollapse={() => setMobileLeftOpen(false)}
collapsed={leftSidebarCollapsed}
onToggleCollapse={() => setLeftSidebarCollapsed((p) => !p)}
blocks={blocks}
onDragBlock={handleDragBlock}
onBlockCategoryChange={handleBlockCategoryChange}
onBlockSearch={handleBlockSearch}
onRenameBlock={handleRenameBlock}
onDuplicateBlock={handleDuplicateBlock}
onDeleteBlock={handleDeleteBlock}
onSvgImport={handleSvgImport}
onSaveGroupAsBlock={handleSaveGroupAsBlock}
/>
<CanvasArea
cursorPos={cursorPos}
@@ -1010,6 +1122,13 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
onKISend={handleKISend}
onKISuggestionClick={handleSuggestionClick}
kiLoading={kiLoading}
className={mobileRightOpen ? 'open' : ''}
onCollapse={() => setMobileRightOpen(false)}
collapsed={rightSidebarCollapsed}
onToggleCollapse={() => setRightSidebarCollapsed((p) => !p)}
onReorder={handleReorderLayer}
onAddSubLayer={handleAddSubLayer}
onUpdateElement={handleUpdateElement}
/>
</div>
<CommandLine
@@ -1040,6 +1159,7 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
onCloseRight={() => setMobileRightOpen(false)}
onRightTabChange={setActiveDrawerTab}
/>
<SettingsModal open={settingsOpen} onClose={() => setSettingsOpen(false)} />
<BackgroundImport
open={bgImportOpen}
onClose={() => setBgImportOpen(false)}