fix: SQLite boolean binding, CSS brace, race condition, 0 npm vulns, code-splitting
This commit is contained in:
+127
-7
@@ -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)}
|
||||
|
||||
Reference in New Issue
Block a user