Feature/v2/page editor selection persistance (#4306)

Fixed page editor selection persistance
Fixed delete sleected
Fixed display issue on some tools on reset settings call

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
This commit is contained in:
Reece Browne 2025-08-26 17:26:30 +01:00 committed by GitHub
parent 47ccb6a6ed
commit 68d59fd377
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 192 additions and 171 deletions

View File

@ -117,7 +117,8 @@ export default function Workbench() {
onExportAll={pageEditorFunctions.onExportAll} onExportAll={pageEditorFunctions.onExportAll}
exportLoading={pageEditorFunctions.exportLoading} exportLoading={pageEditorFunctions.exportLoading}
selectionMode={pageEditorFunctions.selectionMode} selectionMode={pageEditorFunctions.selectionMode}
selectedPages={pageEditorFunctions.selectedPages} selectedPageIds={pageEditorFunctions.selectedPageIds}
displayDocument={pageEditorFunctions.displayDocument}
splitPositions={pageEditorFunctions.splitPositions} splitPositions={pageEditorFunctions.splitPositions}
totalPages={pageEditorFunctions.totalPages} totalPages={pageEditorFunctions.totalPages}
/> />

View File

@ -4,14 +4,16 @@ import { Group, TextInput, Button, Text } from '@mantine/core';
interface BulkSelectionPanelProps { interface BulkSelectionPanelProps {
csvInput: string; csvInput: string;
setCsvInput: (value: string) => void; setCsvInput: (value: string) => void;
selectedPages: number[]; selectedPageIds: string[];
displayDocument?: { pages: { id: string; pageNumber: number }[] };
onUpdatePagesFromCSV: () => void; onUpdatePagesFromCSV: () => void;
} }
const BulkSelectionPanel = ({ const BulkSelectionPanel = ({
csvInput, csvInput,
setCsvInput, setCsvInput,
selectedPages, selectedPageIds,
displayDocument,
onUpdatePagesFromCSV, onUpdatePagesFromCSV,
}: BulkSelectionPanelProps) => { }: BulkSelectionPanelProps) => {
return ( return (
@ -30,9 +32,12 @@ const BulkSelectionPanel = ({
Apply Apply
</Button> </Button>
</Group> </Group>
{selectedPages.length > 0 && ( {selectedPageIds.length > 0 && (
<Text size="sm" c="dimmed" mt="sm"> <Text size="sm" c="dimmed" mt="sm">
Selected: {selectedPages.length} pages Selected: {selectedPageIds.length} pages ({displayDocument ? selectedPageIds.map(id => {
const page = displayDocument.pages.find(p => p.id === id);
return page?.pageNumber || 0;
}).filter(n => n > 0).join(', ') : ''})
</Text> </Text>
)} )}
</> </>

View File

@ -12,10 +12,10 @@ interface DragDropItem {
interface DragDropGridProps<T extends DragDropItem> { interface DragDropGridProps<T extends DragDropItem> {
items: T[]; items: T[];
selectedItems: number[]; selectedItems: string[];
selectionMode: boolean; selectionMode: boolean;
isAnimating: boolean; isAnimating: boolean;
onReorderPages: (sourcePageNumber: number, targetIndex: number, selectedPages?: number[]) => void; onReorderPages: (sourcePageNumber: number, targetIndex: number, selectedPageIds?: string[]) => void;
renderItem: (item: T, index: number, refs: React.MutableRefObject<Map<string, HTMLDivElement>>) => React.ReactNode; renderItem: (item: T, index: number, refs: React.MutableRefObject<Map<string, HTMLDivElement>>) => React.ReactNode;
renderSplitMarker?: (item: T, index: number) => React.ReactNode; renderSplitMarker?: (item: T, index: number) => React.ReactNode;
} }

View File

@ -7,7 +7,7 @@ import {
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useFileState, useFileActions, useCurrentFile, useFileSelection } from "../../contexts/FileContext"; import { useFileState, useFileActions, useCurrentFile, useFileSelection } from "../../contexts/FileContext";
import { ModeType } from "../../contexts/NavigationContext"; import { ModeType } from "../../contexts/NavigationContext";
import { PDFDocument, PDFPage } from "../../types/pageEditor"; import { PDFDocument, PDFPage, PageEditorFunctions } from "../../types/pageEditor";
import { ProcessedFile as EnhancedProcessedFile } from "../../types/processing"; import { ProcessedFile as EnhancedProcessedFile } from "../../types/processing";
import { pdfExportService } from "../../services/pdfExportService"; import { pdfExportService } from "../../services/pdfExportService";
import { documentManipulationService } from "../../services/documentManipulationService"; import { documentManipulationService } from "../../services/documentManipulationService";
@ -36,31 +36,7 @@ import { usePageDocument } from './hooks/usePageDocument';
import { usePageEditorState } from './hooks/usePageEditorState'; import { usePageEditorState } from './hooks/usePageEditorState';
export interface PageEditorProps { export interface PageEditorProps {
onFunctionsReady?: (functions: { onFunctionsReady?: (functions: PageEditorFunctions) => void;
handleUndo: () => void;
handleRedo: () => void;
canUndo: boolean;
canRedo: boolean;
handleRotate: (direction: 'left' | 'right') => void;
handleDelete: () => void;
handleSplit: () => void;
handleSplitAll: () => void;
handlePageBreak: () => void;
handlePageBreakAll: () => void;
handleSelectAll: () => void;
handleDeselectAll: () => void;
handleSetSelectedPages: (pageNumbers: number[]) => void;
showExportPreview: (selectedOnly: boolean) => void;
onExportSelected: () => void;
onExportAll: () => void;
applyChanges: () => void;
exportLoading: boolean;
selectionMode: boolean;
selectedPages: number[];
splitPositions: Set<number>;
totalPages: number;
closePdf: () => void;
}) => void;
} }
const PageEditor = ({ const PageEditor = ({
@ -100,8 +76,8 @@ const PageEditor = ({
// UI state management // UI state management
const { const {
selectionMode, selectedPageNumbers, movingPage, isAnimating, splitPositions, exportLoading, selectionMode, selectedPageIds, movingPage, isAnimating, splitPositions, exportLoading,
setSelectionMode, setSelectedPageNumbers, setMovingPage, setIsAnimating, setSplitPositions, setExportLoading, setSelectionMode, setSelectedPageIds, setMovingPage, setIsAnimating, setSplitPositions, setExportLoading,
togglePage, toggleSelectAll, animateReorder togglePage, toggleSelectAll, animateReorder
} = usePageEditorState(); } = usePageEditorState();
@ -152,17 +128,40 @@ const PageEditor = ({
// Interface functions for parent component // Interface functions for parent component
const displayDocument = editedDocument || mergedPdfDocument; const displayDocument = editedDocument || mergedPdfDocument;
// Utility functions to convert between page IDs and page numbers
const getPageNumbersFromIds = useCallback((pageIds: string[]): number[] => {
if (!displayDocument) return [];
return pageIds.map(id => {
const page = displayDocument.pages.find(p => p.id === id);
return page?.pageNumber || 0;
}).filter(num => num > 0);
}, [displayDocument]);
const getPageIdsFromNumbers = useCallback((pageNumbers: number[]): string[] => {
if (!displayDocument) return [];
return pageNumbers.map(num => {
const page = displayDocument.pages.find(p => p.pageNumber === num);
return page?.id || '';
}).filter(id => id !== '');
}, [displayDocument]);
// Convert selectedPageIds to numbers for components that still need numbers
const selectedPageNumbers = useMemo(() =>
getPageNumbersFromIds(selectedPageIds),
[selectedPageIds, getPageNumbersFromIds]
);
// Select all pages by default when document initially loads // Select all pages by default when document initially loads
const hasInitializedSelection = useRef(false); const hasInitializedSelection = useRef(false);
useEffect(() => { useEffect(() => {
if (displayDocument && displayDocument.pages.length > 0 && !hasInitializedSelection.current) { if (displayDocument && displayDocument.pages.length > 0 && !hasInitializedSelection.current) {
const allPageNumbers = Array.from({ length: displayDocument.pages.length }, (_, i) => i + 1); const allPageIds = displayDocument.pages.map(p => p.id);
setSelectedPageNumbers(allPageNumbers); setSelectedPageIds(allPageIds);
setSelectionMode(true); setSelectionMode(true);
hasInitializedSelection.current = true; hasInitializedSelection.current = true;
} }
}, [displayDocument, setSelectedPageNumbers, setSelectionMode]); }, [displayDocument, setSelectedPageIds, setSelectionMode]);
// DOM-first command handlers // DOM-first command handlers
const handleRotatePages = useCallback((pageIds: string[], rotation: number) => { const handleRotatePages = useCallback((pageIds: string[], rotation: number) => {
@ -192,15 +191,18 @@ const PageEditor = ({
pagesToDelete, pagesToDelete,
() => displayDocument, () => displayDocument,
setEditedDocument, setEditedDocument,
setSelectedPageNumbers, (pageNumbers: number[]) => {
const pageIds = getPageIdsFromNumbers(pageNumbers);
setSelectedPageIds(pageIds);
},
() => splitPositions, () => splitPositions,
setSplitPositions, setSplitPositions,
() => selectedPageNumbers () => getPageNumbersFromIds(selectedPageIds)
); );
undoManagerRef.current.executeCommand(deleteCommand); undoManagerRef.current.executeCommand(deleteCommand);
} }
} }
}), [displayDocument, splitPositions, selectedPageNumbers]); }), [displayDocument, splitPositions, selectedPageIds, getPageNumbersFromIds]);
const createSplitCommand = useCallback((position: number) => ({ const createSplitCommand = useCallback((position: number) => ({
execute: () => { execute: () => {
@ -230,30 +232,32 @@ const PageEditor = ({
}, []); }, []);
const handleRotate = useCallback((direction: 'left' | 'right') => { const handleRotate = useCallback((direction: 'left' | 'right') => {
if (!displayDocument || selectedPageNumbers.length === 0) return; if (!displayDocument || selectedPageIds.length === 0) return;
const rotation = direction === 'left' ? -90 : 90; const rotation = direction === 'left' ? -90 : 90;
const pagesToRotate = selectedPageNumbers.map(pageNum => {
const page = displayDocument.pages.find(p => p.pageNumber === pageNum); handleRotatePages(selectedPageIds, rotation);
return page?.id || ''; }, [displayDocument, selectedPageIds, handleRotatePages]);
}).filter(id => id);
handleRotatePages(pagesToRotate, rotation);
}, [displayDocument, selectedPageNumbers, handleRotatePages]);
const handleDelete = useCallback(() => { const handleDelete = useCallback(() => {
if (!displayDocument || selectedPageNumbers.length === 0) return; if (!displayDocument || selectedPageIds.length === 0) return;
// Convert selected page IDs to page numbers for the command
const selectedPageNumbers = getPageNumbersFromIds(selectedPageIds);
const deleteCommand = new DeletePagesCommand( const deleteCommand = new DeletePagesCommand(
selectedPageNumbers, selectedPageNumbers,
() => displayDocument, () => displayDocument,
setEditedDocument, setEditedDocument,
setSelectedPageNumbers, (pageNumbers: number[]) => {
const pageIds = getPageIdsFromNumbers(pageNumbers);
setSelectedPageIds(pageIds);
},
() => splitPositions, () => splitPositions,
setSplitPositions, setSplitPositions,
() => selectedPageNumbers () => selectedPageNumbers
); );
undoManagerRef.current.executeCommand(deleteCommand); undoManagerRef.current.executeCommand(deleteCommand);
}, [selectedPageNumbers, displayDocument, splitPositions]); }, [selectedPageIds, displayDocument, splitPositions, getPageNumbersFromIds, getPageIdsFromNumbers]);
const handleDeletePage = useCallback((pageNumber: number) => { const handleDeletePage = useCallback((pageNumber: number) => {
if (!displayDocument) return; if (!displayDocument) return;
@ -262,18 +266,22 @@ const PageEditor = ({
[pageNumber], [pageNumber],
() => displayDocument, () => displayDocument,
setEditedDocument, setEditedDocument,
setSelectedPageNumbers, (pageNumbers: number[]) => {
const pageIds = getPageIdsFromNumbers(pageNumbers);
setSelectedPageIds(pageIds);
},
() => splitPositions, () => splitPositions,
setSplitPositions, setSplitPositions,
() => selectedPageNumbers () => getPageNumbersFromIds(selectedPageIds)
); );
undoManagerRef.current.executeCommand(deleteCommand); undoManagerRef.current.executeCommand(deleteCommand);
}, [displayDocument, splitPositions, selectedPageNumbers]); }, [displayDocument, splitPositions, selectedPageIds, getPageNumbersFromIds]);
const handleSplit = useCallback(() => { const handleSplit = useCallback(() => {
if (!displayDocument || selectedPageNumbers.length === 0) return; if (!displayDocument || selectedPageIds.length === 0) return;
// Convert selected page numbers to split positions (0-based indices) // Convert selected page IDs to page numbers, then to split positions (0-based indices)
const selectedPageNumbers = getPageNumbersFromIds(selectedPageIds);
const selectedPositions: number[] = []; const selectedPositions: number[] = [];
selectedPageNumbers.forEach(pageNum => { selectedPageNumbers.forEach(pageNum => {
const pageIndex = displayDocument.pages.findIndex(p => p.pageNumber === pageNum); const pageIndex = displayDocument.pages.findIndex(p => p.pageNumber === pageNum);
@ -314,12 +322,13 @@ const PageEditor = ({
}; };
undoManagerRef.current.executeCommand(smartSplitCommand); undoManagerRef.current.executeCommand(smartSplitCommand);
}, [selectedPageNumbers, displayDocument, splitPositions, setSplitPositions]); }, [selectedPageIds, displayDocument, splitPositions, setSplitPositions, getPageNumbersFromIds]);
const handleSplitAll = useCallback(() => { const handleSplitAll = useCallback(() => {
if (!displayDocument || selectedPageNumbers.length === 0) return; if (!displayDocument || selectedPageIds.length === 0) return;
// Convert selected page numbers to split positions (0-based indices) // Convert selected page IDs to page numbers, then to split positions (0-based indices)
const selectedPageNumbers = getPageNumbersFromIds(selectedPageIds);
const selectedPositions: number[] = []; const selectedPositions: number[] = [];
selectedPageNumbers.forEach(pageNum => { selectedPageNumbers.forEach(pageNum => {
const pageIndex = displayDocument.pages.findIndex(p => p.pageNumber === pageNum); const pageIndex = displayDocument.pages.findIndex(p => p.pageNumber === pageNum);
@ -359,31 +368,35 @@ const PageEditor = ({
}; };
undoManagerRef.current.executeCommand(smartSplitCommand); undoManagerRef.current.executeCommand(smartSplitCommand);
}, [selectedPageNumbers, displayDocument, splitPositions, setSplitPositions]); }, [selectedPageIds, displayDocument, splitPositions, setSplitPositions, getPageNumbersFromIds]);
const handlePageBreak = useCallback(() => { const handlePageBreak = useCallback(() => {
if (!displayDocument || selectedPageNumbers.length === 0) return; if (!displayDocument || selectedPageIds.length === 0) return;
// Convert selected page IDs to page numbers for the command
const selectedPageNumbers = getPageNumbersFromIds(selectedPageIds);
const pageBreakCommand = new PageBreakCommand( const pageBreakCommand = new PageBreakCommand(
selectedPageNumbers, selectedPageNumbers,
() => displayDocument, () => displayDocument,
setEditedDocument, setEditedDocument
setSelectedPageNumbers
); );
undoManagerRef.current.executeCommand(pageBreakCommand); undoManagerRef.current.executeCommand(pageBreakCommand);
}, [selectedPageNumbers, displayDocument]); }, [selectedPageIds, displayDocument, getPageNumbersFromIds]);
const handlePageBreakAll = useCallback(() => { const handlePageBreakAll = useCallback(() => {
if (!displayDocument || selectedPageNumbers.length === 0) return; if (!displayDocument || selectedPageIds.length === 0) return;
// Convert selected page IDs to page numbers for the command
const selectedPageNumbers = getPageNumbersFromIds(selectedPageIds);
const pageBreakCommand = new PageBreakCommand( const pageBreakCommand = new PageBreakCommand(
selectedPageNumbers, selectedPageNumbers,
() => displayDocument, () => displayDocument,
setEditedDocument, setEditedDocument
setSelectedPageNumbers
); );
undoManagerRef.current.executeCommand(pageBreakCommand); undoManagerRef.current.executeCommand(pageBreakCommand);
}, [selectedPageNumbers, displayDocument]); }, [selectedPageIds, displayDocument, getPageNumbersFromIds]);
const handleInsertFiles = useCallback(async (files: File[], insertAfterPage: number) => { const handleInsertFiles = useCallback(async (files: File[], insertAfterPage: number) => {
if (!displayDocument || files.length === 0) return; if (!displayDocument || files.length === 0) return;
@ -400,21 +413,25 @@ const PageEditor = ({
const handleSelectAll = useCallback(() => { const handleSelectAll = useCallback(() => {
if (!displayDocument) return; if (!displayDocument) return;
const allPageNumbers = Array.from({ length: displayDocument.pages.length }, (_, i) => i + 1); const allPageIds = displayDocument.pages.map(p => p.id);
setSelectedPageNumbers(allPageNumbers); toggleSelectAll(allPageIds);
}, [displayDocument]); }, [displayDocument, toggleSelectAll]);
const handleDeselectAll = useCallback(() => { const handleDeselectAll = useCallback(() => {
setSelectedPageNumbers([]); setSelectedPageIds([]);
}, []); }, [setSelectedPageIds]);
const handleSetSelectedPages = useCallback((pageNumbers: number[]) => { const handleSetSelectedPages = useCallback((pageNumbers: number[]) => {
setSelectedPageNumbers(pageNumbers); const pageIds = getPageIdsFromNumbers(pageNumbers);
}, []); setSelectedPageIds(pageIds);
}, [getPageIdsFromNumbers, setSelectedPageIds]);
const handleReorderPages = useCallback((sourcePageNumber: number, targetIndex: number, selectedPages?: number[]) => { const handleReorderPages = useCallback((sourcePageNumber: number, targetIndex: number, selectedPageIds?: string[]) => {
if (!displayDocument) return; if (!displayDocument) return;
// Convert selectedPageIds to page numbers for the reorder command
const selectedPages = selectedPageIds ? getPageNumbersFromIds(selectedPageIds) : undefined;
const reorderCommand = new ReorderPagesCommand( const reorderCommand = new ReorderPagesCommand(
sourcePageNumber, sourcePageNumber,
targetIndex, targetIndex,
@ -423,7 +440,7 @@ const PageEditor = ({
setEditedDocument setEditedDocument
); );
undoManagerRef.current.executeCommand(reorderCommand); undoManagerRef.current.executeCommand(reorderCommand);
}, [displayDocument]); }, [displayDocument, getPageNumbersFromIds]);
// Helper function to collect source files for multi-file export // Helper function to collect source files for multi-file export
const getSourceFiles = useCallback((): Map<string, File> | null => { const getSourceFiles = useCallback((): Map<string, File> | null => {
@ -466,7 +483,7 @@ const PageEditor = ({
}, [activeFileIds, selectors, displayDocument]); }, [activeFileIds, selectors, displayDocument]);
const onExportSelected = useCallback(async () => { const onExportSelected = useCallback(async () => {
if (!displayDocument || selectedPageNumbers.length === 0) return; if (!displayDocument || selectedPageIds.length === 0) return;
setExportLoading(true); setExportLoading(true);
try { try {
@ -480,11 +497,11 @@ const PageEditor = ({
// For selected pages export, we work with the first document (or single document) // For selected pages export, we work with the first document (or single document)
const documentWithDOMState = Array.isArray(processedDocuments) ? processedDocuments[0] : processedDocuments; const documentWithDOMState = Array.isArray(processedDocuments) ? processedDocuments[0] : processedDocuments;
// Step 2: Convert selected page numbers to page IDs from the document with DOM state // Step 2: Use the already selected page IDs
const selectedPageIds = selectedPageNumbers.map(pageNum => { // Filter to only include IDs that exist in the document with DOM state
const page = documentWithDOMState.pages.find(p => p.pageNumber === pageNum); const validSelectedPageIds = selectedPageIds.filter(pageId =>
return page?.id || ''; documentWithDOMState.pages.some(p => p.id === pageId)
}).filter(id => id); );
// Step 3: Export with pdfExportService // Step 3: Export with pdfExportService
@ -494,12 +511,12 @@ const PageEditor = ({
? await pdfExportService.exportPDFMultiFile( ? await pdfExportService.exportPDFMultiFile(
documentWithDOMState, documentWithDOMState,
sourceFiles, sourceFiles,
selectedPageIds, validSelectedPageIds,
{ selectedOnly: true, filename: exportFilename } { selectedOnly: true, filename: exportFilename }
) )
: await pdfExportService.exportPDF( : await pdfExportService.exportPDF(
documentWithDOMState, documentWithDOMState,
selectedPageIds, validSelectedPageIds,
{ selectedOnly: true, filename: exportFilename } { selectedOnly: true, filename: exportFilename }
); );
@ -511,7 +528,7 @@ const PageEditor = ({
console.error('Export failed:', error); console.error('Export failed:', error);
setExportLoading(false); setExportLoading(false);
} }
}, [displayDocument, selectedPageNumbers, mergedPdfDocument, splitPositions, getSourceFiles, getExportFilename]); }, [displayDocument, selectedPageIds, mergedPdfDocument, splitPositions, getSourceFiles, getExportFilename]);
const onExportAll = useCallback(async () => { const onExportAll = useCallback(async () => {
if (!displayDocument) return; if (!displayDocument) return;
@ -606,7 +623,7 @@ const PageEditor = ({
const closePdf = useCallback(() => { const closePdf = useCallback(() => {
actions.clearAllFiles(); actions.clearAllFiles();
undoManagerRef.current.clear(); undoManagerRef.current.clear();
setSelectedPageNumbers([]); setSelectedPageIds([]);
setSelectionMode(false); setSelectionMode(false);
}, [actions]); }, [actions]);
@ -646,7 +663,8 @@ const PageEditor = ({
applyChanges, applyChanges,
exportLoading, exportLoading,
selectionMode, selectionMode,
selectedPages: selectedPageNumbers, selectedPageIds,
displayDocument: displayDocument || undefined,
splitPositions, splitPositions,
totalPages: displayDocument?.pages.length || 0, totalPages: displayDocument?.pages.length || 0,
closePdf, closePdf,
@ -655,7 +673,7 @@ const PageEditor = ({
}, [ }, [
onFunctionsReady, handleUndo, handleRedo, canUndo, canRedo, handleRotate, handleDelete, handleSplit, handleSplitAll, onFunctionsReady, handleUndo, handleRedo, canUndo, canRedo, handleRotate, handleDelete, handleSplit, handleSplitAll,
handlePageBreak, handlePageBreakAll, handleSelectAll, handleDeselectAll, handleSetSelectedPages, handleExportPreview, onExportSelected, onExportAll, applyChanges, exportLoading, handlePageBreak, handlePageBreakAll, handleSelectAll, handleDeselectAll, handleSetSelectedPages, handleExportPreview, onExportSelected, onExportAll, applyChanges, exportLoading,
selectionMode, selectedPageNumbers, splitPositions, displayDocument?.pages.length, closePdf selectionMode, selectedPageIds, splitPositions, displayDocument?.pages.length, closePdf
]); ]);
// Display all pages - use edited or original document // Display all pages - use edited or original document
@ -745,7 +763,7 @@ const PageEditor = ({
{/* Pages Grid */} {/* Pages Grid */}
<DragDropGrid <DragDropGrid
items={displayedPages} items={displayedPages}
selectedItems={selectedPageNumbers} selectedItems={selectedPageIds}
selectionMode={selectionMode} selectionMode={selectionMode}
isAnimating={isAnimating} isAnimating={isAnimating}
onReorderPages={handleReorderPages} onReorderPages={handleReorderPages}
@ -756,7 +774,7 @@ const PageEditor = ({
index={index} index={index}
totalPages={displayDocument.pages.length} totalPages={displayDocument.pages.length}
originalFile={(page as any).originalFileId ? selectors.getFile((page as any).originalFileId) : undefined} originalFile={(page as any).originalFileId ? selectors.getFile((page as any).originalFileId) : undefined}
selectedPages={selectedPageNumbers} selectedPageIds={selectedPageIds}
selectionMode={selectionMode} selectionMode={selectionMode}
movingPage={movingPage} movingPage={movingPage}
isAnimating={isAnimating} isAnimating={isAnimating}

View File

@ -37,7 +37,8 @@ interface PageEditorControlsProps {
// Selection state // Selection state
selectionMode: boolean; selectionMode: boolean;
selectedPages: number[]; selectedPageIds: string[];
displayDocument?: { pages: { id: string; pageNumber: number }[] };
// Split state (for tooltip logic) // Split state (for tooltip logic)
splitPositions?: Set<number>; splitPositions?: Set<number>;
@ -59,18 +60,23 @@ const PageEditorControls = ({
onExportAll, onExportAll,
exportLoading, exportLoading,
selectionMode, selectionMode,
selectedPages, selectedPageIds,
displayDocument,
splitPositions, splitPositions,
totalPages totalPages
}: PageEditorControlsProps) => { }: PageEditorControlsProps) => {
// Calculate split tooltip text using smart toggle logic // Calculate split tooltip text using smart toggle logic
const getSplitTooltip = () => { const getSplitTooltip = () => {
if (!splitPositions || !totalPages || selectedPages.length === 0) { if (!splitPositions || !totalPages || selectedPageIds.length === 0) {
return "Split Selected"; return "Split Selected";
} }
// Convert selected pages to split positions (same logic as handleSplit) // Convert selected pages to split positions (same logic as handleSplit)
const selectedSplitPositions = selectedPages.map(pageNum => pageNum - 1).filter(pos => pos < totalPages - 1); const selectedPageNumbers = displayDocument ? selectedPageIds.map(id => {
const page = displayDocument.pages.find(p => p.id === id);
return page?.pageNumber || 0;
}).filter(num => num > 0) : [];
const selectedSplitPositions = selectedPageNumbers.map(pageNum => pageNum - 1).filter(pos => pos < totalPages - 1);
if (selectedSplitPositions.length === 0) { if (selectedSplitPositions.length === 0) {
return "Split Selected"; return "Split Selected";
@ -97,8 +103,8 @@ const PageEditorControls = ({
// Calculate page break tooltip text // Calculate page break tooltip text
const getPageBreakTooltip = () => { const getPageBreakTooltip = () => {
return selectedPages.length > 0 return selectedPageIds.length > 0
? `Insert ${selectedPages.length} Page Break${selectedPages.length > 1 ? 's' : ''}` ? `Insert ${selectedPageIds.length} Page Break${selectedPageIds.length > 1 ? 's' : ''}`
: "Insert Page Breaks"; : "Insert Page Breaks";
}; };
@ -157,7 +163,7 @@ const PageEditorControls = ({
<Tooltip label="Rotate Selected Left"> <Tooltip label="Rotate Selected Left">
<ActionIcon <ActionIcon
onClick={() => onRotate('left')} onClick={() => onRotate('left')}
disabled={selectedPages.length === 0} disabled={selectedPageIds.length === 0}
variant="subtle" variant="subtle"
style={{ color: 'var(--mantine-color-dimmed)' }} style={{ color: 'var(--mantine-color-dimmed)' }}
radius="md" radius="md"
@ -169,7 +175,7 @@ const PageEditorControls = ({
<Tooltip label="Rotate Selected Right"> <Tooltip label="Rotate Selected Right">
<ActionIcon <ActionIcon
onClick={() => onRotate('right')} onClick={() => onRotate('right')}
disabled={selectedPages.length === 0} disabled={selectedPageIds.length === 0}
variant="subtle" variant="subtle"
style={{ color: 'var(--mantine-color-dimmed)' }} style={{ color: 'var(--mantine-color-dimmed)' }}
radius="md" radius="md"
@ -181,7 +187,7 @@ const PageEditorControls = ({
<Tooltip label="Delete Selected"> <Tooltip label="Delete Selected">
<ActionIcon <ActionIcon
onClick={onDelete} onClick={onDelete}
disabled={selectedPages.length === 0} disabled={selectedPageIds.length === 0}
variant="subtle" variant="subtle"
style={{ color: 'var(--mantine-color-dimmed)' }} style={{ color: 'var(--mantine-color-dimmed)' }}
radius="md" radius="md"
@ -193,7 +199,7 @@ const PageEditorControls = ({
<Tooltip label={getSplitTooltip()}> <Tooltip label={getSplitTooltip()}>
<ActionIcon <ActionIcon
onClick={onSplit} onClick={onSplit}
disabled={selectedPages.length === 0} disabled={selectedPageIds.length === 0}
variant="subtle" variant="subtle"
style={{ color: 'var(--mantine-color-dimmed)' }} style={{ color: 'var(--mantine-color-dimmed)' }}
radius="md" radius="md"
@ -205,7 +211,7 @@ const PageEditorControls = ({
<Tooltip label={getPageBreakTooltip()}> <Tooltip label={getPageBreakTooltip()}>
<ActionIcon <ActionIcon
onClick={onPageBreak} onClick={onPageBreak}
disabled={selectedPages.length === 0} disabled={selectedPageIds.length === 0}
variant="subtle" variant="subtle"
style={{ color: 'var(--mantine-color-dimmed)' }} style={{ color: 'var(--mantine-color-dimmed)' }}
radius="md" radius="md"

View File

@ -19,13 +19,13 @@ interface PageThumbnailProps {
index: number; index: number;
totalPages: number; totalPages: number;
originalFile?: File; originalFile?: File;
selectedPages: number[]; selectedPageIds: string[];
selectionMode: boolean; selectionMode: boolean;
movingPage: number | null; movingPage: number | null;
isAnimating: boolean; isAnimating: boolean;
pageRefs: React.MutableRefObject<Map<string, HTMLDivElement>>; pageRefs: React.MutableRefObject<Map<string, HTMLDivElement>>;
onReorderPages: (sourcePageNumber: number, targetIndex: number, selectedPages?: number[]) => void; onReorderPages: (sourcePageNumber: number, targetIndex: number, selectedPageIds?: string[]) => void;
onTogglePage: (pageNumber: number) => void; onTogglePage: (pageId: string) => void;
onAnimateReorder: () => void; onAnimateReorder: () => void;
onExecuteCommand: (command: { execute: () => void }) => void; onExecuteCommand: (command: { execute: () => void }) => void;
onSetStatus: (status: string) => void; onSetStatus: (status: string) => void;
@ -45,7 +45,7 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
index, index,
totalPages, totalPages,
originalFile, originalFile,
selectedPages, selectedPageIds,
selectionMode, selectionMode,
movingPage, movingPage,
isAnimating, isAnimating,
@ -139,7 +139,7 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
getInitialData: () => ({ getInitialData: () => ({
pageNumber: page.pageNumber, pageNumber: page.pageNumber,
pageId: page.id, pageId: page.id,
selectedPages: [page.pageNumber] selectedPageIds: [page.id]
}), }),
onDragStart: () => { onDragStart: () => {
setIsDragging(true); setIsDragging(true);
@ -185,7 +185,7 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
(dragElementRef.current as any).__dragCleanup(); (dragElementRef.current as any).__dragCleanup();
} }
} }
}, [page.id, page.pageNumber, pageRefs, selectionMode, selectedPages, pdfDocument.pages, onReorderPages]); }, [page.id, page.pageNumber, pageRefs, selectionMode, selectedPageIds, pdfDocument.pages, onReorderPages]);
// DOM command handlers // DOM command handlers
const handleRotateLeft = useCallback((e: React.MouseEvent) => { const handleRotateLeft = useCallback((e: React.MouseEvent) => {
@ -263,12 +263,12 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
// If mouse moved less than 5 pixels, consider it a click (not a drag) // If mouse moved less than 5 pixels, consider it a click (not a drag)
if (distance < 5 && !isDragging) { if (distance < 5 && !isDragging) {
onTogglePage(page.pageNumber); onTogglePage(page.id);
} }
setIsMouseDown(false); setIsMouseDown(false);
setMouseStartPos(null); setMouseStartPos(null);
}, [isMouseDown, mouseStartPos, isDragging, page.pageNumber, onTogglePage]); }, [isMouseDown, mouseStartPos, isDragging, page.id, onTogglePage]);
const handleMouseLeave = useCallback(() => { const handleMouseLeave = useCallback(() => {
setIsMouseDown(false); setIsMouseDown(false);
@ -323,7 +323,7 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
}} }}
onMouseDown={(e) => { onMouseDown={(e) => {
e.stopPropagation(); e.stopPropagation();
onTogglePage(page.pageNumber); onTogglePage(page.id);
}} }}
onMouseUp={(e) => e.stopPropagation()} onMouseUp={(e) => e.stopPropagation()}
onDragStart={(e) => { onDragStart={(e) => {
@ -332,7 +332,7 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
}} }}
> >
<Checkbox <Checkbox
checked={Array.isArray(selectedPages) ? selectedPages.includes(page.pageNumber) : false} checked={Array.isArray(selectedPageIds) ? selectedPageIds.includes(page.id) : false}
onChange={() => { onChange={() => {
// Selection is handled by container mouseDown // Selection is handled by container mouseDown
}} }}

View File

@ -403,8 +403,7 @@ export class PageBreakCommand extends DOMCommand {
constructor( constructor(
private selectedPageNumbers: number[], private selectedPageNumbers: number[],
private getCurrentDocument: () => PDFDocument | null, private getCurrentDocument: () => PDFDocument | null,
private setDocument: (doc: PDFDocument) => void, private setDocument: (doc: PDFDocument) => void
private setSelectedPages: (pages: number[]) => void
) { ) {
super(); super();
} }
@ -455,19 +454,7 @@ export class PageBreakCommand extends DOMCommand {
this.setDocument(updatedDocument); this.setDocument(updatedDocument);
// Maintain existing selection by mapping original selected pages to their new positions // No need to maintain selection - page IDs remain stable, so selection persists automatically
const updatedSelection: number[] = [];
this.selectedPageNumbers.forEach(originalPageNum => {
// Find the original page by matching the page ID from the original document
const originalPage = this.originalDocument?.pages[originalPageNum - 1];
if (originalPage) {
const foundPage = newPages.find(page => page.id === originalPage.id && !page.isBlankPage);
if (foundPage) {
updatedSelection.push(foundPage.pageNumber);
}
}
});
this.setSelectedPages(updatedSelection);
} }
undo(): void { undo(): void {

View File

@ -3,7 +3,7 @@ import { useState, useCallback } from 'react';
export interface PageEditorState { export interface PageEditorState {
// Selection state // Selection state
selectionMode: boolean; selectionMode: boolean;
selectedPageNumbers: number[]; selectedPageIds: string[];
// Animation state // Animation state
movingPage: number | null; movingPage: number | null;
@ -17,15 +17,15 @@ export interface PageEditorState {
// Actions // Actions
setSelectionMode: (mode: boolean) => void; setSelectionMode: (mode: boolean) => void;
setSelectedPageNumbers: (pages: number[]) => void; setSelectedPageIds: (pages: string[]) => void;
setMovingPage: (pageNumber: number | null) => void; setMovingPage: (pageNumber: number | null) => void;
setIsAnimating: (animating: boolean) => void; setIsAnimating: (animating: boolean) => void;
setSplitPositions: (positions: Set<number>) => void; setSplitPositions: (positions: Set<number>) => void;
setExportLoading: (loading: boolean) => void; setExportLoading: (loading: boolean) => void;
// Helper functions // Helper functions
togglePage: (pageNumber: number) => void; togglePage: (pageId: string) => void;
toggleSelectAll: (totalPages: number) => void; toggleSelectAll: (allPageIds: string[]) => void;
animateReorder: () => void; animateReorder: () => void;
} }
@ -36,7 +36,7 @@ export interface PageEditorState {
export function usePageEditorState(): PageEditorState { export function usePageEditorState(): PageEditorState {
// Selection state // Selection state
const [selectionMode, setSelectionMode] = useState(false); const [selectionMode, setSelectionMode] = useState(false);
const [selectedPageNumbers, setSelectedPageNumbers] = useState<number[]>([]); const [selectedPageIds, setSelectedPageIds] = useState<string[]>([]);
// Animation state // Animation state
const [movingPage, setMovingPage] = useState<number | null>(null); const [movingPage, setMovingPage] = useState<number | null>(null);
@ -49,20 +49,19 @@ export function usePageEditorState(): PageEditorState {
const [exportLoading, setExportLoading] = useState(false); const [exportLoading, setExportLoading] = useState(false);
// Helper functions // Helper functions
const togglePage = useCallback((pageNumber: number) => { const togglePage = useCallback((pageId: string) => {
setSelectedPageNumbers(prev => setSelectedPageIds(prev =>
prev.includes(pageNumber) prev.includes(pageId)
? prev.filter(n => n !== pageNumber) ? prev.filter(id => id !== pageId)
: [...prev, pageNumber] : [...prev, pageId]
); );
}, []); }, []);
const toggleSelectAll = useCallback((totalPages: number) => { const toggleSelectAll = useCallback((allPageIds: string[]) => {
if (!totalPages) return; if (!allPageIds.length) return;
const allPageNumbers = Array.from({ length: totalPages }, (_, i) => i + 1); setSelectedPageIds(prev =>
setSelectedPageNumbers(prev => prev.length === allPageIds.length ? [] : allPageIds
prev.length === allPageNumbers.length ? [] : allPageNumbers
); );
}, []); }, []);
@ -74,7 +73,7 @@ export function usePageEditorState(): PageEditorState {
return { return {
// State // State
selectionMode, selectionMode,
selectedPageNumbers, selectedPageIds,
movingPage, movingPage,
isAnimating, isAnimating,
splitPositions, splitPositions,
@ -82,7 +81,7 @@ export function usePageEditorState(): PageEditorState {
// Setters // Setters
setSelectionMode, setSelectionMode,
setSelectedPageNumbers, setSelectedPageIds,
setMovingPage, setMovingPage,
setIsAnimating, setIsAnimating,
setSplitPositions, setSplitPositions,

View File

@ -47,7 +47,7 @@ export default function RightRail() {
if (currentView === 'pageEditor') { if (currentView === 'pageEditor') {
// Use PageEditor's own state // Use PageEditor's own state
const totalItems = pageEditorFunctions?.totalPages || 0; const totalItems = pageEditorFunctions?.totalPages || 0;
const selectedCount = pageEditorFunctions?.selectedPages?.length || 0; const selectedCount = pageEditorFunctions?.selectedPageIds?.length || 0;
return { totalItems, selectedCount }; return { totalItems, selectedCount };
} }
@ -147,12 +147,15 @@ export default function RightRail() {
// Sync csvInput with PageEditor's selected pages // Sync csvInput with PageEditor's selected pages
useEffect(() => { useEffect(() => {
const sortedPageNumbers = Array.isArray(pageEditorFunctions?.selectedPages) const sortedPageNumbers = Array.isArray(pageEditorFunctions?.selectedPageIds) && pageEditorFunctions.displayDocument
? [...pageEditorFunctions.selectedPages].sort((a, b) => a - b) ? pageEditorFunctions.selectedPageIds.map(id => {
const page = pageEditorFunctions.displayDocument!.pages.find(p => p.id === id);
return page?.pageNumber || 0;
}).filter(num => num > 0).sort((a, b) => a - b)
: []; : [];
const newCsvInput = sortedPageNumbers.join(', '); const newCsvInput = sortedPageNumbers.join(', ');
setCsvInput(newCsvInput); setCsvInput(newCsvInput);
}, [pageEditorFunctions?.selectedPages]); }, [pageEditorFunctions?.selectedPageIds]);
// Clear CSV input when files change (use stable signature to avoid ref churn) // Clear CSV input when files change (use stable signature to avoid ref churn)
useEffect(() => { useEffect(() => {
@ -262,7 +265,8 @@ export default function RightRail() {
<BulkSelectionPanel <BulkSelectionPanel
csvInput={csvInput} csvInput={csvInput}
setCsvInput={setCsvInput} setCsvInput={setCsvInput}
selectedPages={Array.isArray(pageEditorFunctions?.selectedPages) ? pageEditorFunctions.selectedPages : []} selectedPageIds={Array.isArray(pageEditorFunctions?.selectedPageIds) ? pageEditorFunctions.selectedPageIds : []}
displayDocument={pageEditorFunctions?.displayDocument}
onUpdatePagesFromCSV={updatePagesFromCSV} onUpdatePagesFromCSV={updatePagesFromCSV}
/> />
</div> </div>
@ -284,7 +288,7 @@ export default function RightRail() {
radius="md" radius="md"
className="right-rail-icon" className="right-rail-icon"
onClick={() => { pageEditorFunctions?.handleDelete?.(); }} onClick={() => { pageEditorFunctions?.handleDelete?.(); }}
disabled={!pageControlsVisible || (pageEditorFunctions?.selectedPages?.length || 0) === 0} disabled={!pageControlsVisible || (pageEditorFunctions?.selectedPageIds?.length || 0) === 0}
aria-label={typeof t === 'function' ? t('rightRail.deleteSelected', 'Delete Selected Pages') : 'Delete Selected Pages'} aria-label={typeof t === 'function' ? t('rightRail.deleteSelected', 'Delete Selected Pages') : 'Delete Selected Pages'}
> >
<LocalIcon icon="delete-outline-rounded" width="1.5rem" height="1.5rem" /> <LocalIcon icon="delete-outline-rounded" width="1.5rem" height="1.5rem" />
@ -305,7 +309,7 @@ export default function RightRail() {
radius="md" radius="md"
className="right-rail-icon" className="right-rail-icon"
onClick={() => { pageEditorFunctions?.onExportSelected?.(); }} onClick={() => { pageEditorFunctions?.onExportSelected?.(); }}
disabled={!pageControlsVisible || (pageEditorFunctions?.selectedPages?.length || 0) === 0 || pageEditorFunctions?.exportLoading} disabled={!pageControlsVisible || (pageEditorFunctions?.selectedPageIds?.length || 0) === 0 || pageEditorFunctions?.exportLoading}
aria-label={typeof t === 'function' ? t('rightRail.exportSelected', 'Export Selected Pages') : 'Export Selected Pages'} aria-label={typeof t === 'function' ? t('rightRail.exportSelected', 'Export Selected Pages') : 'Export Selected Pages'}
> >
<LocalIcon icon="download" width="1.5rem" height="1.5rem" /> <LocalIcon icon="download" width="1.5rem" height="1.5rem" />

View File

@ -11,8 +11,6 @@ interface RainbowThemeHook {
deactivateRainbow: () => void; deactivateRainbow: () => void;
} }
const allowRainbowMode = false; // Override to allow/disallow fun
export function useRainbowTheme(initialTheme: 'light' | 'dark' = 'light'): RainbowThemeHook { export function useRainbowTheme(initialTheme: 'light' | 'dark' = 'light'): RainbowThemeHook {
// Get theme from localStorage or use initial // Get theme from localStorage or use initial
const [themeMode, setThemeMode] = useState<ThemeMode>(() => { const [themeMode, setThemeMode] = useState<ThemeMode>(() => {
@ -37,11 +35,11 @@ export function useRainbowTheme(initialTheme: 'light' | 'dark' = 'light'): Rainb
// Save theme to localStorage whenever it changes // Save theme to localStorage whenever it changes
useEffect(() => { useEffect(() => {
localStorage.setItem('stirling-theme', themeMode); localStorage.setItem('stirling-theme', themeMode);
// Apply rainbow class to body if in rainbow mode // Apply rainbow class to body if in rainbow mode
if (themeMode === 'rainbow') { if (themeMode === 'rainbow') {
document.body.classList.add('rainbow-mode-active'); document.body.classList.add('rainbow-mode-active');
// Show easter egg notification // Show easter egg notification
showRainbowNotification(); showRainbowNotification();
} else { } else {
@ -79,7 +77,7 @@ export function useRainbowTheme(initialTheme: 'light' | 'dark' = 'light'): Rainb
pointer-events: none; pointer-events: none;
transition: opacity 0.3s ease; transition: opacity 0.3s ease;
`; `;
document.body.appendChild(notification); document.body.appendChild(notification);
// Auto-remove notification after 3 seconds // Auto-remove notification after 3 seconds
@ -123,7 +121,7 @@ export function useRainbowTheme(initialTheme: 'light' | 'dark' = 'light'): Rainb
pointer-events: none; pointer-events: none;
transition: opacity 0.3s ease; transition: opacity 0.3s ease;
`; `;
document.body.appendChild(notification); document.body.appendChild(notification);
// Auto-remove notification after 2 seconds // Auto-remove notification after 2 seconds
@ -146,7 +144,7 @@ export function useRainbowTheme(initialTheme: 'light' | 'dark' = 'light'): Rainb
} }
const currentTime = Date.now(); const currentTime = Date.now();
// Simple exit from rainbow mode with single click (after cooldown period) // Simple exit from rainbow mode with single click (after cooldown period)
if (themeMode === 'rainbow') { if (themeMode === 'rainbow') {
setThemeMode('light'); setThemeMode('light');
@ -154,7 +152,7 @@ export function useRainbowTheme(initialTheme: 'light' | 'dark' = 'light'): Rainb
showExitNotification(); showExitNotification();
return; return;
} }
// Reset counter if too much time has passed (2.5 seconds) // Reset counter if too much time has passed (2.5 seconds)
if (currentTime - lastToggleTime.current > 2500) { if (currentTime - lastToggleTime.current > 2500) {
toggleCount.current = 1; toggleCount.current = 1;
@ -164,18 +162,18 @@ export function useRainbowTheme(initialTheme: 'light' | 'dark' = 'light'): Rainb
lastToggleTime.current = currentTime; lastToggleTime.current = currentTime;
// Easter egg: Activate rainbow mode after 10 rapid toggles // Easter egg: Activate rainbow mode after 10 rapid toggles
if (allowRainbowMode && toggleCount.current >= 10) { if (toggleCount.current >= 10) {
setThemeMode('rainbow'); setThemeMode('rainbow');
console.log('🌈 RAINBOW MODE ACTIVATED! 🌈 You found the secret easter egg!'); console.log('🌈 RAINBOW MODE ACTIVATED! 🌈 You found the secret easter egg!');
console.log('🌈 Button will be disabled for 3 seconds, then click once to exit!'); console.log('🌈 Button will be disabled for 3 seconds, then click once to exit!');
// Disable toggle for 3 seconds // Disable toggle for 3 seconds
setIsToggleDisabled(true); setIsToggleDisabled(true);
setTimeout(() => { setTimeout(() => {
setIsToggleDisabled(false); setIsToggleDisabled(false);
console.log('🌈 Theme toggle re-enabled! Click once to exit rainbow mode.'); console.log('🌈 Theme toggle re-enabled! Click once to exit rainbow mode.');
}, 3000); }, 3000);
// Reset counter // Reset counter
toggleCount.current = 0; toggleCount.current = 0;
return; return;
@ -205,4 +203,4 @@ export function useRainbowTheme(initialTheme: 'light' | 'dark' = 'light'): Rainb
activateRainbow, activateRainbow,
deactivateRainbow, deactivateRainbow,
}; };
} }

View File

@ -50,9 +50,9 @@ const Compress = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
const handleSettingsReset = () => { const handleSettingsReset = () => {
compressOperation.resetResults(); compressOperation.resetResults();
onPreviewFile?.(null); onPreviewFile?.(null); };
actions.setMode("compress");
};
const hasFiles = selectedFiles.length > 0; const hasFiles = selectedFiles.length > 0;
const hasResults = compressOperation.files.length > 0 || compressOperation.downloadUrl !== null; const hasResults = compressOperation.files.length > 0 || compressOperation.downloadUrl !== null;

View File

@ -28,13 +28,14 @@ const Split = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
onPreviewFile?.(null); onPreviewFile?.(null);
}, [splitParams.parameters]); }, [splitParams.parameters]);
useEffect(() => { useEffect(() => {
// Reset results when selected files change (user selected different files) // Reset results when selected files change (user selected different files)
if (selectedFiles.length > 0) { if (selectedFiles.length > 0) {
splitOperation.resetResults(); splitOperation.resetResults();
onPreviewFile?.(null); onPreviewFile?.(null);
} }
}, [selectedFiles]); }, [selectedFiles]);
const handleSplit = async () => { const handleSplit = async () => {
try { try {
await splitOperation.executeOperation(splitParams.parameters, selectedFiles); await splitOperation.executeOperation(splitParams.parameters, selectedFiles);
@ -56,8 +57,7 @@ const Split = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
const handleSettingsReset = () => { const handleSettingsReset = () => {
splitOperation.resetResults(); splitOperation.resetResults();
onPreviewFile?.(null); onPreviewFile?.(null);
actions.setMode("split"); };
};
const hasFiles = selectedFiles.length > 0; const hasFiles = selectedFiles.length > 0;
const hasResults = splitOperation.files.length > 0 || splitOperation.downloadUrl !== null; const hasResults = splitOperation.files.length > 0 || splitOperation.downloadUrl !== null;

View File

@ -56,11 +56,14 @@ export interface PageEditorFunctions {
handleSelectAll: () => void; handleSelectAll: () => void;
handleDeselectAll: () => void; handleDeselectAll: () => void;
handleSetSelectedPages: (pageNumbers: number[]) => void; handleSetSelectedPages: (pageNumbers: number[]) => void;
showExportPreview: (selectedOnly: boolean) => void;
onExportSelected: () => void; onExportSelected: () => void;
onExportAll: () => void; onExportAll: () => void;
applyChanges: () => void;
exportLoading: boolean; exportLoading: boolean;
selectionMode: boolean; selectionMode: boolean;
selectedPages: number[]; selectedPageIds: string[];
displayDocument?: PDFDocument;
splitPositions: Set<number>; splitPositions: Set<number>;
totalPages: number; totalPages: number;
} }