Merge branch 'bug/page-editor-fixes' of https://github.com/Stirling-Tools/Stirling-PDF into bug/page-editor-fixes
This commit is contained in:
@@ -479,6 +479,21 @@ const DragDropGrid = <T extends DragDropItem>({
|
||||
onVisibleItemsChange(visibleItemsForCallback);
|
||||
}, [virtualRows, visibleItems, itemsPerRow, onVisibleItemsChange]);
|
||||
|
||||
const virtualRows = rowVirtualizer.getVirtualItems();
|
||||
|
||||
useEffect(() => {
|
||||
if (!onVisibleItemsChange) return;
|
||||
|
||||
const visibleItemsForCallback: T[] = [];
|
||||
virtualRows.forEach((row) => {
|
||||
const startIndex = row.index * itemsPerRow;
|
||||
const endIndex = Math.min(startIndex + itemsPerRow, visibleItems.length);
|
||||
visibleItemsForCallback.push(...visibleItems.slice(startIndex, endIndex));
|
||||
});
|
||||
|
||||
onVisibleItemsChange(visibleItemsForCallback);
|
||||
}, [virtualRows, visibleItems, itemsPerRow, onVisibleItemsChange]);
|
||||
|
||||
// Re-measure virtualizer when zoom or items per row changes
|
||||
// Also remeasure when items change (not just length) to handle item additions/removals
|
||||
const visibleItemsSignature = useMemo(() => visibleItems.map(item => item.id).join(','), [visibleItems]);
|
||||
@@ -774,6 +789,7 @@ const DragDropGrid = <T extends DragDropItem>({
|
||||
margin: '0 auto',
|
||||
}}
|
||||
>
|
||||
{virtualRows.map((virtualRow) => {
|
||||
{virtualRows.map((virtualRow) => {
|
||||
const startIndex = virtualRow.index * itemsPerRow;
|
||||
const endIndex = Math.min(startIndex + itemsPerRow, visibleItems.length);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useFileState, useFileActions } from "@app/contexts/FileContext";
|
||||
import { useNavigationGuard, useNavigationState } from "@app/contexts/NavigationContext";
|
||||
import { usePageEditor } from "@app/contexts/PageEditorContext";
|
||||
import { PageEditorFunctions, PDFPage } from "@app/types/pageEditor";
|
||||
import { PageEditorFunctions, PDFPage } from "@app/types/pageEditor";
|
||||
// Thumbnail generation is now handled by individual PageThumbnail components
|
||||
import '@app/components/pageEditor/PageEditor.module.css';
|
||||
import PageThumbnail from '@app/components/pageEditor/PageThumbnail';
|
||||
@@ -24,6 +25,7 @@ import { usePageSelectionManager } from "@app/components/pageEditor/hooks/usePag
|
||||
import { usePageEditorCommands } from "@app/components/pageEditor/hooks/useEditorCommands";
|
||||
import { usePageEditorExport } from "@app/components/pageEditor/hooks/usePageEditorExport";
|
||||
import { useThumbnailGeneration } from "@app/hooks/useThumbnailGeneration";
|
||||
import { useThumbnailGeneration } from "@app/hooks/useThumbnailGeneration";
|
||||
|
||||
export interface PageEditorProps {
|
||||
onFunctionsReady?: (functions: PageEditorFunctions) => void;
|
||||
@@ -51,6 +53,27 @@ const PageEditor = ({
|
||||
savePersistedDocument,
|
||||
} = usePageEditor();
|
||||
|
||||
const [visiblePageIds, setVisiblePageIds] = useState<string[]>([]);
|
||||
const thumbnailRequestsRef = useRef<Set<string>>(new Set());
|
||||
const { requestThumbnail, getThumbnailFromCache } = useThumbnailGeneration();
|
||||
const handleVisibleItemsChange = useCallback((items: PDFPage[]) => {
|
||||
setVisiblePageIds(prev => {
|
||||
const ids = items.map(item => item.id);
|
||||
if (prev.length === ids.length && prev.every((id, index) => id === ids[index])) {
|
||||
return prev;
|
||||
}
|
||||
return ids;
|
||||
});
|
||||
}, []);
|
||||
const {
|
||||
updateFileOrderFromPages,
|
||||
fileOrder,
|
||||
reorderedPages,
|
||||
clearReorderedPages,
|
||||
updateCurrentPages,
|
||||
savePersistedDocument,
|
||||
} = usePageEditor();
|
||||
|
||||
const [visiblePageIds, setVisiblePageIds] = useState<string[]>([]);
|
||||
const thumbnailRequestsRef = useRef<Set<string>>(new Set());
|
||||
const { requestThumbnail, getThumbnailFromCache } = useThumbnailGeneration();
|
||||
@@ -557,6 +580,7 @@ const PageEditor = ({
|
||||
]);
|
||||
|
||||
return (
|
||||
<div
|
||||
<div
|
||||
ref={containerRef}
|
||||
data-scrolling-container="true"
|
||||
@@ -568,6 +592,12 @@ const PageEditor = ({
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
}}
|
||||
style={{
|
||||
height: '100%',
|
||||
overflow: 'auto',
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<LoadingOverlay visible={globalProcessing && !initialDocument} />
|
||||
|
||||
@@ -589,6 +619,7 @@ const PageEditor = ({
|
||||
)}
|
||||
|
||||
{displayDocument && (
|
||||
<Box ref={gridContainerRef} p={0} pt="2rem" pb="4rem" style={{ position: 'relative' }}>
|
||||
<Box ref={gridContainerRef} p={0} pt="2rem" pb="4rem" style={{ position: 'relative' }}>
|
||||
|
||||
{/* Split Lines Overlay */}
|
||||
@@ -693,6 +724,7 @@ const PageEditor = ({
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { usePageEditor } from '@app/contexts/PageEditorContext';
|
||||
import { PDFDocument, PDFPage } from '@app/types/pageEditor';
|
||||
import { FileId } from '@app/types/file';
|
||||
import { FileAnalyzer } from '@app/services/fileAnalyzer';
|
||||
import { FileAnalyzer } from '@app/services/fileAnalyzer';
|
||||
|
||||
export interface PageDocumentHook {
|
||||
document: PDFDocument | null;
|
||||
@@ -18,6 +19,7 @@ export interface PageDocumentHook {
|
||||
export function usePageDocument(): PageDocumentHook {
|
||||
const { state, selectors } = useFileState();
|
||||
const { fileOrder, currentPages, persistedDocument, persistedDocumentSignature } = usePageEditor();
|
||||
const { fileOrder, currentPages, persistedDocument, persistedDocumentSignature } = usePageEditor();
|
||||
|
||||
// Use PageEditorContext's fileOrder instead of FileContext's global order
|
||||
// This ensures the page editor respects its own workspace ordering
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { createContext, useContext, useState, useCallback, ReactNode, use
|
||||
import { FileId } from '@app/types/file';
|
||||
import { useFileActions, useFileState } from '@app/contexts/FileContext';
|
||||
import { PDFDocument, PDFPage } from '@app/types/pageEditor';
|
||||
import { PDFDocument, PDFPage } from '@app/types/pageEditor';
|
||||
import { MAX_PAGE_EDITOR_FILES } from '@app/components/pageEditor/fileColors';
|
||||
import { useNavigationState } from '@app/contexts/NavigationContext';
|
||||
|
||||
@@ -134,6 +135,10 @@ interface PageEditorContextValue {
|
||||
persistedDocumentSignature: string | null;
|
||||
savePersistedDocument: (document: PDFDocument, signature: string) => void;
|
||||
clearPersistedDocument: () => void;
|
||||
persistedDocument: PDFDocument | null;
|
||||
persistedDocumentSignature: string | null;
|
||||
savePersistedDocument: (document: PDFDocument, signature: string) => void;
|
||||
clearPersistedDocument: () => void;
|
||||
}
|
||||
|
||||
const PageEditorContext = createContext<PageEditorContextValue | undefined>(undefined);
|
||||
@@ -315,6 +320,7 @@ export function PageEditorProvider({ children }: PageEditorProviderProps) {
|
||||
});
|
||||
}, 100);
|
||||
}, [state.files.ids, state.files.byId, fileActions]);
|
||||
}, [state.files.ids, state.files.byId, fileActions]);
|
||||
|
||||
const updateCurrentPages = useCallback((pages: PDFPage[] | null) => {
|
||||
setCurrentPages(pages);
|
||||
@@ -444,6 +450,10 @@ export function PageEditorProvider({ children }: PageEditorProviderProps) {
|
||||
persistedDocumentSignature,
|
||||
savePersistedDocument,
|
||||
clearPersistedDocument,
|
||||
persistedDocument,
|
||||
persistedDocumentSignature,
|
||||
savePersistedDocument,
|
||||
clearPersistedDocument,
|
||||
}), [
|
||||
currentPages,
|
||||
updateCurrentPages,
|
||||
@@ -460,6 +470,10 @@ export function PageEditorProvider({ children }: PageEditorProviderProps) {
|
||||
persistedDocumentSignature,
|
||||
savePersistedDocument,
|
||||
clearPersistedDocument,
|
||||
persistedDocument,
|
||||
persistedDocumentSignature,
|
||||
savePersistedDocument,
|
||||
clearPersistedDocument,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -23,10 +23,16 @@ const activeRequests = new Map<string, Promise<string | null>>();
|
||||
// Cache ArrayBuffers to avoid reading the same file multiple times
|
||||
const fileArrayBufferCache = new Map<File, ArrayBuffer>();
|
||||
|
||||
// Cache ArrayBuffers to avoid reading the same file multiple times
|
||||
const fileArrayBufferCache = new Map<File, ArrayBuffer>();
|
||||
|
||||
// Batch processing configuration
|
||||
const BATCH_SIZE = 10; // Process thumbnails in batches of 10 for faster initial load
|
||||
const BATCH_DELAY = 50; // Wait 50ms to collect requests before processing
|
||||
const PRIORITY_BATCH_DELAY = 10; // Very fast processing for the first batch (visible pages)
|
||||
const BATCH_SIZE = 10; // Process thumbnails in batches of 10 for faster initial load
|
||||
const BATCH_DELAY = 50; // Wait 50ms to collect requests before processing
|
||||
const PRIORITY_BATCH_DELAY = 10; // Very fast processing for the first batch (visible pages)
|
||||
|
||||
// Process the queue in batches for better performance
|
||||
async function processRequestQueue() {
|
||||
@@ -128,6 +134,10 @@ async function processRequestQueue() {
|
||||
if (requestQueue.length === 0) {
|
||||
fileArrayBufferCache.clear();
|
||||
}
|
||||
// Clean up ArrayBuffer cache when queue is empty
|
||||
if (requestQueue.length === 0) {
|
||||
fileArrayBufferCache.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +198,9 @@ export function useThumbnailGeneration() {
|
||||
// Clear ArrayBuffer cache
|
||||
fileArrayBufferCache.clear();
|
||||
|
||||
// Clear ArrayBuffer cache
|
||||
fileArrayBufferCache.clear();
|
||||
|
||||
thumbnailGenerationService.destroy();
|
||||
}, []);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user