From d8f34769aa0e199d756f59da083b172fda5c8200 Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Mon, 25 Aug 2025 18:44:34 +0100 Subject: [PATCH] Fix split display on zoom --- .../service/KeyPairCleanupService.java | 8 --- .../service/KeyPersistenceService.java | 35 ----------- .../components/pageEditor/DragDropGrid.tsx | 18 ++++-- .../src/components/pageEditor/PageEditor.tsx | 59 +++++++++++++++---- .../src/components/pageEditor/constants.ts | 8 +++ 5 files changed, 66 insertions(+), 62 deletions(-) create mode 100644 frontend/src/components/pageEditor/constants.ts diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPairCleanupService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPairCleanupService.java index 0bc329596..af7c5f7e2 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPairCleanupService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPairCleanupService.java @@ -8,10 +8,6 @@ import java.time.LocalDateTime; import java.util.List; import java.util.concurrent.TimeUnit; -<<<<<<< HEAD -import org.springframework.beans.factory.annotation.Autowired; -======= ->>>>>>> origin import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -33,10 +29,6 @@ public class KeyPairCleanupService { private final KeyPersistenceService keyPersistenceService; private final ApplicationProperties.Security.Jwt jwtProperties; -<<<<<<< HEAD - @Autowired -======= ->>>>>>> origin public KeyPairCleanupService( KeyPersistenceService keyPersistenceService, ApplicationProperties applicationProperties) { diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java index 6e5160e56..eb02b6368 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java @@ -1,17 +1,11 @@ package stirling.software.proprietary.security.service; import java.io.IOException; -<<<<<<< HEAD -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -======= import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; ->>>>>>> origin import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -28,10 +22,6 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -<<<<<<< HEAD -import org.springframework.beans.factory.annotation.Autowired; -======= ->>>>>>> origin import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheEvict; @@ -54,24 +44,10 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface { public static final String KEY_SUFFIX = ".key"; private final ApplicationProperties.Security.Jwt jwtProperties; -<<<<<<< HEAD - private final CacheManager cacheManager; -======= ->>>>>>> origin private final Cache verifyingKeyCache; private volatile JwtVerificationKey activeKey; -<<<<<<< HEAD - @Autowired - public KeyPersistenceService( - ApplicationProperties applicationProperties, CacheManager cacheManager) { - this.jwtProperties = applicationProperties.getSecurity().getJwt(); - this.cacheManager = cacheManager; - this.verifyingKeyCache = cacheManager.getCache("verifyingKeys"); - } - -======= public KeyPersistenceService( ApplicationProperties applicationProperties, CacheManager cacheManager) { this.jwtProperties = applicationProperties.getSecurity().getJwt(); @@ -106,7 +82,6 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface { } } ->>>>>>> origin @PostConstruct public void initializeKeystore() { if (!isKeystoreEnabled()) { @@ -114,10 +89,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface { } try { -<<<<<<< HEAD -======= moveKeysToBackup(); ->>>>>>> origin ensurePrivateKeyDirectoryExists(); loadKeyPair(); } catch (Exception e) { @@ -214,11 +186,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface { nativeCache.asMap().size()); return nativeCache.asMap().values().stream() -<<<<<<< HEAD - .filter(value -> value instanceof JwtVerificationKey) -======= .filter(JwtVerificationKey.class::isInstance) ->>>>>>> origin .map(value -> (JwtVerificationKey) value) .filter( key -> { @@ -292,10 +260,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface { return Base64.getEncoder().encodeToString(publicKey.getEncoded()); } -<<<<<<< HEAD -======= @Override ->>>>>>> origin public PublicKey decodePublicKey(String encodedKey) throws NoSuchAlgorithmException, InvalidKeySpecException { byte[] keyBytes = Base64.getDecoder().decode(encodedKey); diff --git a/frontend/src/components/pageEditor/DragDropGrid.tsx b/frontend/src/components/pageEditor/DragDropGrid.tsx index 995778129..8e0ab0010 100644 --- a/frontend/src/components/pageEditor/DragDropGrid.tsx +++ b/frontend/src/components/pageEditor/DragDropGrid.tsx @@ -3,6 +3,7 @@ import { Box } from '@mantine/core'; import { useVirtualizer } from '@tanstack/react-virtual'; import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'; import styles from './PageEditor.module.css'; +import { GRID_CONSTANTS } from './constants'; interface DragDropItem { id: string; @@ -33,10 +34,7 @@ const DragDropGrid = ({ // Responsive grid configuration const [itemsPerRow, setItemsPerRow] = useState(4); - const ITEM_WIDTH = 320; // 20rem (page width) - const ITEM_GAP = 24; // 1.5rem gap between items - const ITEM_HEIGHT = 340; // 20rem + gap - const OVERSCAN = items.length > 1000 ? 8 : 4; // More overscan for large documents + const OVERSCAN = items.length > 1000 ? GRID_CONSTANTS.OVERSCAN_LARGE : GRID_CONSTANTS.OVERSCAN_SMALL; // Calculate items per row based on container width const calculateItemsPerRow = useCallback(() => { @@ -45,6 +43,11 @@ const DragDropGrid = ({ const containerWidth = containerRef.current.offsetWidth; if (containerWidth === 0) return 4; // Container not measured yet + // Convert rem to pixels for calculation + const remToPx = parseFloat(getComputedStyle(document.documentElement).fontSize); + const ITEM_WIDTH = parseFloat(GRID_CONSTANTS.ITEM_WIDTH) * remToPx; + const ITEM_GAP = parseFloat(GRID_CONSTANTS.ITEM_GAP) * remToPx; + // Calculate how many items fit: (width - gap) / (itemWidth + gap) const availableWidth = containerWidth - ITEM_GAP; // Account for first gap const itemWithGap = ITEM_WIDTH + ITEM_GAP; @@ -82,7 +85,10 @@ const DragDropGrid = ({ const rowVirtualizer = useVirtualizer({ count: Math.ceil(items.length / itemsPerRow), getScrollElement: () => containerRef.current?.closest('[data-scrolling-container]') as Element, - estimateSize: () => ITEM_HEIGHT, + estimateSize: () => { + const remToPx = parseFloat(getComputedStyle(document.documentElement).fontSize); + return parseFloat(GRID_CONSTANTS.ITEM_HEIGHT) * remToPx; + }, overscan: OVERSCAN, }); @@ -124,7 +130,7 @@ const DragDropGrid = ({
(null); + + // State to trigger re-renders when container size changes + const [containerDimensions, setContainerDimensions] = useState({ width: 0, height: 0 }); // Undo/Redo state const [canUndo, setCanUndo] = useState(false); @@ -121,6 +125,28 @@ const PageEditor = ({ updateUndoRedoState(); }, [updateUndoRedoState]); + // Watch for container size changes to update split line positions + useEffect(() => { + const container = gridContainerRef.current; + if (!container) return; + + const resizeObserver = new ResizeObserver((entries) => { + const entry = entries[0]; + if (entry) { + setContainerDimensions({ + width: entry.contentRect.width, + height: entry.contentRect.height + }); + } + }); + + resizeObserver.observe(container); + + return () => { + resizeObserver.disconnect(); + }; + }, []); + // Interface functions for parent component const displayDocument = editedDocument || mergedPdfDocument; @@ -576,18 +602,25 @@ const PageEditor = ({ }} > {Array.from(splitPositions).map((position) => { - // Calculate the split line position based on grid layout - const ITEM_WIDTH = 320; // 20rem - const ITEM_HEIGHT = 340; // 20rem + gap - const ITEM_GAP = 24; // 1.5rem - const ITEMS_PER_ROW = 4; // Default, could be dynamic - - const row = Math.floor(position / ITEMS_PER_ROW); - const col = position % ITEMS_PER_ROW; - - // Position after the current item - const leftPosition = (col + 1) * (ITEM_WIDTH + ITEM_GAP) - ITEM_GAP / 2; - const topPosition = row * ITEM_HEIGHT + 100; // Offset for header controls + // Match DragDropGrid's layout calculations exactly + const containerWidth = containerDimensions.width; + const remToPx = parseFloat(getComputedStyle(document.documentElement).fontSize); + const ITEM_WIDTH = parseFloat(GRID_CONSTANTS.ITEM_WIDTH) * remToPx; + const ITEM_HEIGHT = parseFloat(GRID_CONSTANTS.ITEM_HEIGHT) * remToPx; + const ITEM_GAP = parseFloat(GRID_CONSTANTS.ITEM_GAP) * remToPx; + + // Calculate items per row using DragDropGrid's logic + const availableWidth = containerWidth - ITEM_GAP; // Account for first gap + const itemWithGap = ITEM_WIDTH + ITEM_GAP; + const itemsPerRow = Math.max(1, Math.floor(availableWidth / itemWithGap)); + + // Calculate position within the grid (same as DragDropGrid) + const row = Math.floor(position / itemsPerRow); + const col = position % itemsPerRow; + + // Position split line between pages (after the current page) + const leftPosition = col * itemWithGap + ITEM_WIDTH + (ITEM_GAP / 2); + const topPosition = row * ITEM_HEIGHT; return (
diff --git a/frontend/src/components/pageEditor/constants.ts b/frontend/src/components/pageEditor/constants.ts new file mode 100644 index 000000000..13239d722 --- /dev/null +++ b/frontend/src/components/pageEditor/constants.ts @@ -0,0 +1,8 @@ +// Shared constants for PageEditor grid layout +export const GRID_CONSTANTS = { + ITEM_WIDTH: '20rem', // page width + ITEM_HEIGHT: '21.5rem', // 20rem + 1.5rem gap + ITEM_GAP: '1.5rem', // gap between items + OVERSCAN_SMALL: 4, // Overscan for normal documents + OVERSCAN_LARGE: 8, // Overscan for large documents (>1000 pages) +} as const; \ No newline at end of file