diff --git a/frontend/src/desktop/components/DesktopWorkspace.tsx b/frontend/src/desktop/components/DesktopWorkspace.tsx index 9dc4994..3b6ef9a 100644 --- a/frontend/src/desktop/components/DesktopWorkspace.tsx +++ b/frontend/src/desktop/components/DesktopWorkspace.tsx @@ -110,22 +110,71 @@ const DesktopWorkspaceContent: React.FC = ({ } }, [layoutStore, tenantId, viewId]); - // Only set layout ready when both container has size AND layout is loaded - useEffect(() => { - setIsLayoutReady(hasContainerSize && isLayoutLoaded); - }, [hasContainerSize, isLayoutLoaded]); + const metadataMap = usePreviewMetadata(items, getDocumentAsset, ensureAssetUrl); - // Sync LayoutStore items with current entries to remove stale items + const ensureDocumentSize = useCallback((doc: Document): DocumentSizeInfo => { + if (doc.id) { + const meta = metadataMap.get(String(doc.id)); + if (meta && meta.width && meta.height) + return { width: meta.width, height: meta.height, source: 'metadata' }; + } + + return computeFallbackCardSize(doc, defaultCardSize); + }, [metadataMap, defaultCardSize]); + + // Synchronize LayoutStore with current items (Initialization & Cleanup) useEffect(() => { + if (!hasContainerSize || !isLayoutLoaded) return; + + // Cleanup Stale Items const currentIds = new Set(items.map((doc, index) => doc.id ? String(doc.id) : `temp-${index}`)); - - // Identify and remove items that are no longer present for (const id of layoutStore.items.keys()) { if (!currentIds.has(id)) { layoutStore.unregister(id); } } - }, [items, layoutStore]); + + // Initialize / Update Items (Saved first, then others) + const itemsWithSavedLayout: Document[] = []; + const itemsWithoutSavedLayout: Document[] = []; + + items.forEach(doc => { + // If already initialized in store, we don't strictly need to prioritize it for collision, + // but keeping the order ensures consistent behavior on re-runs. + // However, usually we only care about *new* items for collision logic. + if (doc.id && layoutStore.hasSavedLayout(String(doc.id))) { + itemsWithSavedLayout.push(doc); + } else { + itemsWithoutSavedLayout.push(doc); + } + }); + + const initializeDoc = (doc: Document) => { + const size = ensureDocumentSize(doc); + const metadata = doc.current_version?.metadata as { page_count?: number } | undefined; + const pageCount = metadata?.page_count ?? 1; + + layoutStore.initialize(doc.id, null, { + width: size.width, + height: size.height, + pageCount, + maxSize: defaultCardSize + }); + }; + + itemsWithSavedLayout.forEach(initializeDoc); + itemsWithoutSavedLayout.forEach(initializeDoc); + + // Save newly placed items + if (itemsWithoutSavedLayout.length > 0) { + void layoutStore.saveLayout(); + } + + // Enforce constraints + layoutStore.relayout(); + + setIsLayoutReady(true); + }, [hasContainerSize, isLayoutLoaded, items, layoutStore, ensureDocumentSize, defaultCardSize]); // Selection Context const { @@ -155,18 +204,6 @@ const DesktopWorkspaceContent: React.FC = ({ clearSelection ? clearSelection() : handleSelectionChange([]); }, [clearSelection, handleSelectionChange]); - const metadataMap = usePreviewMetadata(items, getDocumentAsset, ensureAssetUrl); - - const ensureDocumentSize = useCallback((doc: Document): DocumentSizeInfo => { - if (doc.id) { - const meta = metadataMap.get(String(doc.id)); - if (meta && meta.width && meta.height) - return { width: meta.width, height: meta.height, source: 'metadata' }; - } - - return computeFallbackCardSize(doc, defaultCardSize); - }, [metadataMap, defaultCardSize]); - // Sync LayoutStore to layoutRef useEffect(() => { const sync = () => { @@ -351,18 +388,12 @@ const DesktopWorkspaceContent: React.FC = ({ {isLayoutReady && items.map((doc, index) => { const docId = doc.id ? String(doc.id) : `temp-${index}`; const isSelected = selectedDocumentIds.includes(docId); - const size = ensureDocumentSize(doc); - // Extract page count - const metadata = doc.current_version?.metadata as { page_count?: number } | undefined; - const pageCount = metadata?.page_count ?? 1; + const layoutCard = layoutStore.items.get(docId); - const layoutCard = layoutStore.initialize(docId, null, { - width: size.width, - height: size.height, - pageCount, - maxSize: defaultCardSize - }); + if (!layoutCard) { + return null; + } return (