This commit is contained in:
Reece 2025-06-19 19:47:44 +01:00
parent d981968e0f
commit 868969192b
13 changed files with 345 additions and 194 deletions

View File

@ -2,7 +2,7 @@ import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Button, Stack, Text, Group } from '@mantine/core'; import { Button, Stack, Text, Group } from '@mantine/core';
const DeepLinks: React.FC = () => { const DeepLinks = () => {
const commonLinks = [ const commonLinks = [
{ {
name: "Split PDF Pages 1-5", name: "Split PDF Pages 1-5",

View File

@ -14,7 +14,7 @@ interface FileCardProps {
onDoubleClick?: () => void; onDoubleClick?: () => void;
} }
const FileCard: React.FC<FileCardProps> = ({ file, onRemove, onDoubleClick }) => { const FileCard = ({ file, onRemove, onDoubleClick }: FileCardProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { thumbnail: thumb, isGenerating } = useIndexedDBThumbnail(file); const { thumbnail: thumb, isGenerating } = useIndexedDBThumbnail(file);

View File

@ -23,13 +23,13 @@ interface FileManagerProps {
setCurrentView?: (view: string) => void; setCurrentView?: (view: string) => void;
} }
const FileManager: React.FC<FileManagerProps> = ({ const FileManager = ({
files = [], files = [],
setFiles, setFiles,
allowMultiple = true, allowMultiple = true,
setPdfFile, setPdfFile,
setCurrentView, setCurrentView,
}) => { }: FileManagerProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [storageStats, setStorageStats] = useState<StorageStats | null>(null); const [storageStats, setStorageStats] = useState<StorageStats | null>(null);

View File

@ -5,7 +5,7 @@ import { supportedLanguages } from '../i18n';
import LanguageIcon from '@mui/icons-material/Language'; import LanguageIcon from '@mui/icons-material/Language';
import styles from './LanguageSelector.module.css'; import styles from './LanguageSelector.module.css';
const LanguageSelector: React.FC = () => { const LanguageSelector = () => {
const { i18n } = useTranslation(); const { i18n } = useTranslation();
const theme = useMantineTheme(); const theme = useMantineTheme();
const { colorScheme } = useMantineColorScheme(); const { colorScheme } = useMantineColorScheme();

View File

@ -42,14 +42,31 @@ export interface PageEditorProps {
setFile?: (file: { file: File; url: string } | null) => void; setFile?: (file: { file: File; url: string } | null) => void;
downloadUrl?: string | null; downloadUrl?: string | null;
setDownloadUrl?: (url: string | null) => void; setDownloadUrl?: (url: string | null) => void;
// Optional callbacks to expose internal functions
onFunctionsReady?: (functions: {
handleUndo: () => void;
handleRedo: () => void;
canUndo: boolean;
canRedo: boolean;
handleRotate: (direction: 'left' | 'right') => void;
handleDelete: () => void;
handleSplit: () => void;
showExportPreview: (selectedOnly: boolean) => void;
exportLoading: boolean;
selectionMode: boolean;
selectedPages: string[];
closePdf: () => void;
}) => void;
} }
const PageEditor: React.FC<PageEditorProps> = ({ const PageEditor = ({
file, file,
setFile, setFile,
downloadUrl, downloadUrl,
setDownloadUrl, setDownloadUrl,
}) => { onFunctionsReady,
}: PageEditorProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { processPDFFile, loading: pdfLoading } = usePDFProcessor(); const { processPDFFile, loading: pdfLoading } = usePDFProcessor();
@ -207,7 +224,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
const handleDragStart = useCallback((pageId: string) => { const handleDragStart = useCallback((pageId: string) => {
setDraggedPage(pageId); setDraggedPage(pageId);
// Check if this is a multi-page drag in selection mode // Check if this is a multi-page drag in selection mode
if (selectionMode && selectedPages.includes(pageId) && selectedPages.length > 1) { if (selectionMode && selectedPages.includes(pageId) && selectedPages.length > 1) {
setMultiPageDrag({ setMultiPageDrag({
@ -231,7 +248,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
e.preventDefault(); e.preventDefault();
if (!draggedPage) return; if (!draggedPage) return;
// Update drag position for multi-page indicator // Update drag position for multi-page indicator
if (multiPageDrag) { if (multiPageDrag) {
setDragPosition({ x: e.clientX, y: e.clientY }); setDragPosition({ x: e.clientX, y: e.clientY });
@ -274,12 +291,12 @@ const PageEditor: React.FC<PageEditorProps> = ({
const animateReorder = useCallback((pageId: string, targetIndex: number) => { const animateReorder = useCallback((pageId: string, targetIndex: number) => {
if (!pdfDocument || isAnimating) return; if (!pdfDocument || isAnimating) return;
// In selection mode, if the dragged page is selected, move all selected pages // In selection mode, if the dragged page is selected, move all selected pages
const pagesToMove = selectionMode && selectedPages.includes(pageId) const pagesToMove = selectionMode && selectedPages.includes(pageId)
? selectedPages ? selectedPages
: [pageId]; : [pageId];
const originalIndex = pdfDocument.pages.findIndex(p => p.id === pageId); const originalIndex = pdfDocument.pages.findIndex(p => p.id === pageId);
if (originalIndex === -1 || originalIndex === targetIndex) return; if (originalIndex === -1 || originalIndex === targetIndex) return;
@ -310,11 +327,11 @@ const PageEditor: React.FC<PageEditorProps> = ({
requestAnimationFrame(() => { requestAnimationFrame(() => {
requestAnimationFrame(() => { requestAnimationFrame(() => {
const newPositions = new Map<string, { x: number; y: number }>(); const newPositions = new Map<string, { x: number; y: number }>();
// Get the updated document from the state after command execution // Get the updated document from the state after command execution
// The command has already updated the document, so we need to get the new order // The command has already updated the document, so we need to get the new order
const currentDoc = pdfDocument; // This should be the updated version after command const currentDoc = pdfDocument; // This should be the updated version after command
currentDoc.pages.forEach((page) => { currentDoc.pages.forEach((page) => {
const element = pageRefs.current.get(page.id); const element = pageRefs.current.get(page.id);
if (element) { if (element) {
@ -328,18 +345,18 @@ const PageEditor: React.FC<PageEditorProps> = ({
const element = pageRefs.current.get(page.id); const element = pageRefs.current.get(page.id);
const currentPos = currentPositions.get(page.id); const currentPos = currentPositions.get(page.id);
const newPos = newPositions.get(page.id); const newPos = newPositions.get(page.id);
if (element && currentPos && newPos) { if (element && currentPos && newPos) {
const deltaX = currentPos.x - newPos.x; const deltaX = currentPos.x - newPos.x;
const deltaY = currentPos.y - newPos.y; const deltaY = currentPos.y - newPos.y;
// Apply initial transform (from new position back to old position) // Apply initial transform (from new position back to old position)
element.style.transform = `translate(${deltaX}px, ${deltaY}px)`; element.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
element.style.transition = 'none'; element.style.transition = 'none';
// Force reflow // Force reflow
element.offsetHeight; element.offsetHeight;
// Animate to final position // Animate to final position
element.style.transition = 'transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94)'; element.style.transition = 'transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94)';
element.style.transform = 'translate(0px, 0px)'; element.style.transform = 'translate(0px, 0px)';
@ -374,12 +391,12 @@ const PageEditor: React.FC<PageEditorProps> = ({
} }
animateReorder(draggedPage, targetIndex); animateReorder(draggedPage, targetIndex);
setDraggedPage(null); setDraggedPage(null);
setDropTarget(null); setDropTarget(null);
setMultiPageDrag(null); setMultiPageDrag(null);
setDragPosition(null); setDragPosition(null);
const moveCount = multiPageDrag ? multiPageDrag.count : 1; const moveCount = multiPageDrag ? multiPageDrag.count : 1;
setStatus(`${moveCount > 1 ? `${moveCount} pages` : 'Page'} reordered`); setStatus(`${moveCount > 1 ? `${moveCount} pages` : 'Page'} reordered`);
}, [draggedPage, pdfDocument, animateReorder, multiPageDrag]); }, [draggedPage, pdfDocument, animateReorder, multiPageDrag]);
@ -394,8 +411,8 @@ const PageEditor: React.FC<PageEditorProps> = ({
if (!pdfDocument) return; if (!pdfDocument) return;
const rotation = direction === 'left' ? -90 : 90; const rotation = direction === 'left' ? -90 : 90;
const pagesToRotate = selectionMode const pagesToRotate = selectionMode
? selectedPages ? selectedPages
: pdfDocument.pages.map(p => p.id); : pdfDocument.pages.map(p => p.id);
if (selectionMode && selectedPages.length === 0) return; if (selectionMode && selectedPages.length === 0) return;
@ -415,8 +432,8 @@ const PageEditor: React.FC<PageEditorProps> = ({
const handleDelete = useCallback(() => { const handleDelete = useCallback(() => {
if (!pdfDocument) return; if (!pdfDocument) return;
const pagesToDelete = selectionMode const pagesToDelete = selectionMode
? selectedPages ? selectedPages
: pdfDocument.pages.map(p => p.id); : pdfDocument.pages.map(p => p.id);
if (selectionMode && selectedPages.length === 0) return; if (selectionMode && selectedPages.length === 0) return;
@ -438,8 +455,8 @@ const PageEditor: React.FC<PageEditorProps> = ({
const handleSplit = useCallback(() => { const handleSplit = useCallback(() => {
if (!pdfDocument) return; if (!pdfDocument) return;
const pagesToSplit = selectionMode const pagesToSplit = selectionMode
? selectedPages ? selectedPages
: pdfDocument.pages.map(p => p.id); : pdfDocument.pages.map(p => p.id);
if (selectionMode && selectedPages.length === 0) return; if (selectionMode && selectedPages.length === 0) return;
@ -521,6 +538,45 @@ const PageEditor: React.FC<PageEditorProps> = ({
} }
}, [redo]); }, [redo]);
const closePdf = useCallback(() => {
setPdfDocument(null);
setFile && setFile(null);
}, [setFile]);
// Expose functions to parent component
useEffect(() => {
if (onFunctionsReady) {
onFunctionsReady({
handleUndo,
handleRedo,
canUndo,
canRedo,
handleRotate,
handleDelete,
handleSplit,
showExportPreview,
exportLoading,
selectionMode,
selectedPages,
closePdf,
});
}
}, [
onFunctionsReady,
handleUndo,
handleRedo,
canUndo,
canRedo,
handleRotate,
handleDelete,
handleSplit,
showExportPreview,
exportLoading,
selectionMode,
selectedPages,
closePdf
]);
if (!pdfDocument) { if (!pdfDocument) {
return ( return (
<Box pos="relative" h="100vh" style={{ overflow: 'auto' }}> <Box pos="relative" h="100vh" style={{ overflow: 'auto' }}>
@ -580,7 +636,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
transform: scale(1.05); transform: scale(1.05);
box-shadow: 0 10px 30px rgba(0,0,0,0.3); box-shadow: 0 10px 30px rgba(0,0,0,0.3);
} }
.multi-drag-indicator { .multi-drag-indicator {
position: fixed; position: fixed;
background: rgba(59, 130, 246, 0.9); background: rgba(59, 130, 246, 0.9);
@ -595,7 +651,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
backdrop-filter: blur(4px); backdrop-filter: blur(4px);
} }
@keyframes pulse { @keyframes pulse {
0%, 100% { 0%, 100% {
opacity: 1; opacity: 1;
@ -616,7 +672,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
placeholder="Enter filename" placeholder="Enter filename"
style={{ minWidth: 200 }} style={{ minWidth: 200 }}
/> />
<Button <Button
onClick={toggleSelectionMode} onClick={toggleSelectionMode}
variant={selectionMode ? "filled" : "outline"} variant={selectionMode ? "filled" : "outline"}
color={selectionMode ? "blue" : "gray"} color={selectionMode ? "blue" : "gray"}
@ -708,7 +764,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
hover:shadow-md hover:shadow-md
transition-all transition-all
relative relative
${selectionMode ${selectionMode
? 'bg-white hover:bg-gray-50' ? 'bg-white hover:bg-gray-50'
: 'bg-white hover:bg-gray-50'} : 'bg-white hover:bg-gray-50'}
${draggedPage === page.id ? 'opacity-50 scale-95' : ''} ${draggedPage === page.id ? 'opacity-50 scale-95' : ''}
@ -768,20 +824,35 @@ const PageEditor: React.FC<PageEditorProps> = ({
/> />
</div> </div>
)} )}
<div className="page-container w-[90%] h-[90%]"> <div className="page-container w-[90%] h-[90%]">
<img {/* Image wrapper with simulated border */}
src={page.thumbnail} <div
alt={`Page ${page.pageNumber}`}
style={{ style={{
width: '100%', width: '100%',
height: '100%', height: '100%',
objectFit: 'contain', backgroundColor: 'var(--mantine-color-gray-1)',
borderRadius: 4, borderRadius: 6,
transform: `rotate(${page.rotation}deg)`, border: '1px solid var(--mantine-color-gray-3)',
transition: 'transform 0.3s ease-in-out' padding: 4,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}} }}
/> >
<img
src={page.thumbnail}
alt={`Page ${page.pageNumber}`}
style={{
maxWidth: '100%',
maxHeight: '100%',
objectFit: 'contain',
borderRadius: 2,
transform: `rotate(${page.rotation}deg)`,
transition: 'transform 0.3s ease-in-out'
}}
/>
</div>
{/* Page number overlay - shows on hover */} {/* Page number overlay - shows on hover */}
<Text <Text
@ -985,141 +1056,6 @@ const PageEditor: React.FC<PageEditorProps> = ({
</div> </div>
</div> </div>
{/* Floating control bar */}
<div
style={{
position: 'fixed',
left: '50%',
bottom: '20px',
transform: 'translateX(-50%)',
zIndex: 50,
display: 'flex',
justifyContent: 'center',
pointerEvents: 'none',
background: 'transparent',
}}
>
<Paper
radius="xl"
shadow="lg"
p={16}
style={{
display: 'flex',
alignItems: 'center',
gap: 12,
borderRadius: 32,
boxShadow: '0 8px 32px rgba(0,0,0,0.12)',
pointerEvents: 'auto',
minWidth: 400,
justifyContent: 'center'
}}
>
{/* Close PDF */}
<Tooltip label="Close PDF">
<ActionIcon
onClick={() => {
setPdfDocument(null);
setFile && setFile(null);
}}
color="red"
variant="light"
size="lg"
>
<CloseIcon />
</ActionIcon>
</Tooltip>
<div style={{ width: 1, height: 28, backgroundColor: 'var(--mantine-color-gray-3)', margin: '0 8px' }} />
{/* Undo/Redo */}
<Tooltip label="Undo">
<ActionIcon onClick={handleUndo} disabled={!canUndo} size="lg">
<UndoIcon />
</ActionIcon>
</Tooltip>
<Tooltip label="Redo">
<ActionIcon onClick={handleRedo} disabled={!canRedo} size="lg">
<RedoIcon />
</ActionIcon>
</Tooltip>
<div style={{ width: 1, height: 28, backgroundColor: 'var(--mantine-color-gray-3)', margin: '0 8px' }} />
{/* Page Operations */}
<Tooltip label={selectionMode ? "Rotate Selected Left" : "Rotate All Left"}>
<ActionIcon
onClick={() => handleRotate('left')}
disabled={selectionMode && selectedPages.length === 0}
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
color={selectionMode && selectedPages.length > 0 ? "blue" : undefined}
size="lg"
>
<RotateLeftIcon />
</ActionIcon>
</Tooltip>
<Tooltip label={selectionMode ? "Rotate Selected Right" : "Rotate All Right"}>
<ActionIcon
onClick={() => handleRotate('right')}
disabled={selectionMode && selectedPages.length === 0}
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
color={selectionMode && selectedPages.length > 0 ? "blue" : undefined}
size="lg"
>
<RotateRightIcon />
</ActionIcon>
</Tooltip>
<Tooltip label={selectionMode ? "Delete Selected" : "Delete All"}>
<ActionIcon
onClick={handleDelete}
disabled={selectionMode && selectedPages.length === 0}
color="red"
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
size="lg"
>
<DeleteIcon />
</ActionIcon>
</Tooltip>
<Tooltip label={selectionMode ? "Split Selected" : "Split All"}>
<ActionIcon
onClick={handleSplit}
disabled={selectionMode && selectedPages.length === 0}
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
color={selectionMode && selectedPages.length > 0 ? "blue" : undefined}
size="lg"
>
<ContentCutIcon />
</ActionIcon>
</Tooltip>
<div style={{ width: 1, height: 28, backgroundColor: 'var(--mantine-color-gray-3)', margin: '0 8px' }} />
{/* Export Controls */}
{selectionMode && selectedPages.length > 0 && (
<Tooltip label="Export Selected">
<ActionIcon
onClick={() => showExportPreview(true)}
disabled={exportLoading}
color="blue"
variant="light"
size="lg"
>
<DownloadIcon />
</ActionIcon>
</Tooltip>
)}
<Tooltip label="Export All">
<ActionIcon
onClick={() => showExportPreview(false)}
disabled={exportLoading}
color="green"
variant="light"
size="lg"
>
<DownloadIcon />
</ActionIcon>
</Tooltip>
</Paper>
</div>
</Box> </Box>

View File

@ -0,0 +1,191 @@
import React from "react";
import {
Tooltip,
ActionIcon,
Paper
} from "@mantine/core";
import UndoIcon from "@mui/icons-material/Undo";
import RedoIcon from "@mui/icons-material/Redo";
import ContentCutIcon from "@mui/icons-material/ContentCut";
import DownloadIcon from "@mui/icons-material/Download";
import RotateLeftIcon from "@mui/icons-material/RotateLeft";
import RotateRightIcon from "@mui/icons-material/RotateRight";
import DeleteIcon from "@mui/icons-material/Delete";
import CloseIcon from "@mui/icons-material/Close";
interface PageEditorControlsProps {
// Close/Reset functions
onClosePdf: () => void;
// Undo/Redo
onUndo: () => void;
onRedo: () => void;
canUndo: boolean;
canRedo: boolean;
// Page operations
onRotate: (direction: 'left' | 'right') => void;
onDelete: () => void;
onSplit: () => void;
// Export functions
onExportSelected: () => void;
onExportAll: () => void;
exportLoading: boolean;
// Selection state
selectionMode: boolean;
selectedPages: string[];
}
const PageEditorControls = ({
onClosePdf,
onUndo,
onRedo,
canUndo,
canRedo,
onRotate,
onDelete,
onSplit,
onExportSelected,
onExportAll,
exportLoading,
selectionMode,
selectedPages
}: PageEditorControlsProps) => {
return (
<div
style={{
position: 'fixed',
left: '50%',
bottom: '20px',
transform: 'translateX(-50%)',
zIndex: 50,
display: 'flex',
justifyContent: 'center',
pointerEvents: 'none',
background: 'transparent',
}}
>
<Paper
radius="xl"
shadow="lg"
p={16}
style={{
display: 'flex',
alignItems: 'center',
gap: 12,
borderRadius: 32,
boxShadow: '0 8px 32px rgba(0,0,0,0.12)',
pointerEvents: 'auto',
minWidth: 400,
justifyContent: 'center'
}}
>
{/* Close PDF */}
<Tooltip label="Close PDF">
<ActionIcon
onClick={onClosePdf}
color="red"
variant="light"
size="lg"
>
<CloseIcon />
</ActionIcon>
</Tooltip>
<div style={{ width: 1, height: 28, backgroundColor: 'var(--mantine-color-gray-3)', margin: '0 8px' }} />
{/* Undo/Redo */}
<Tooltip label="Undo">
<ActionIcon onClick={onUndo} disabled={!canUndo} size="lg">
<UndoIcon />
</ActionIcon>
</Tooltip>
<Tooltip label="Redo">
<ActionIcon onClick={onRedo} disabled={!canRedo} size="lg">
<RedoIcon />
</ActionIcon>
</Tooltip>
<div style={{ width: 1, height: 28, backgroundColor: 'var(--mantine-color-gray-3)', margin: '0 8px' }} />
{/* Page Operations */}
<Tooltip label={selectionMode ? "Rotate Selected Left" : "Rotate All Left"}>
<ActionIcon
onClick={() => onRotate('left')}
disabled={selectionMode && selectedPages.length === 0}
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
color={selectionMode && selectedPages.length > 0 ? "blue" : undefined}
size="lg"
>
<RotateLeftIcon />
</ActionIcon>
</Tooltip>
<Tooltip label={selectionMode ? "Rotate Selected Right" : "Rotate All Right"}>
<ActionIcon
onClick={() => onRotate('right')}
disabled={selectionMode && selectedPages.length === 0}
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
color={selectionMode && selectedPages.length > 0 ? "blue" : undefined}
size="lg"
>
<RotateRightIcon />
</ActionIcon>
</Tooltip>
<Tooltip label={selectionMode ? "Delete Selected" : "Delete All"}>
<ActionIcon
onClick={onDelete}
disabled={selectionMode && selectedPages.length === 0}
color="red"
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
size="lg"
>
<DeleteIcon />
</ActionIcon>
</Tooltip>
<Tooltip label={selectionMode ? "Split Selected" : "Split All"}>
<ActionIcon
onClick={onSplit}
disabled={selectionMode && selectedPages.length === 0}
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
color={selectionMode && selectedPages.length > 0 ? "blue" : undefined}
size="lg"
>
<ContentCutIcon />
</ActionIcon>
</Tooltip>
<div style={{ width: 1, height: 28, backgroundColor: 'var(--mantine-color-gray-3)', margin: '0 8px' }} />
{/* Export Controls */}
{selectionMode && selectedPages.length > 0 && (
<Tooltip label="Export Selected">
<ActionIcon
onClick={onExportSelected}
disabled={exportLoading}
color="blue"
variant="light"
size="lg"
>
<DownloadIcon />
</ActionIcon>
</Tooltip>
)}
<Tooltip label="Export All">
<ActionIcon
onClick={onExportAll}
disabled={exportLoading}
color="green"
variant="light"
size="lg"
>
<DownloadIcon />
</ActionIcon>
</Tooltip>
</Paper>
</div>
);
};
export default PageEditorControls;

View File

@ -17,14 +17,14 @@ interface QuickAccessBarProps {
readerMode: boolean; readerMode: boolean;
} }
const QuickAccessBar: React.FC<QuickAccessBarProps> = ({ const QuickAccessBar = ({
onToolsClick, onToolsClick,
onReaderToggle, onReaderToggle,
selectedToolKey, selectedToolKey,
toolRegistry, toolRegistry,
leftPanelView, leftPanelView,
readerMode, readerMode,
}) => { }: QuickAccessBarProps) => {
const { isRainbowMode } = useRainbowThemeContext(); const { isRainbowMode } = useRainbowThemeContext();
return ( return (

View File

@ -14,12 +14,12 @@ interface StorageStatsCardProps {
onReloadFiles: () => void; onReloadFiles: () => void;
} }
const StorageStatsCard: React.FC<StorageStatsCardProps> = ({ const StorageStatsCard = ({
storageStats, storageStats,
filesCount, filesCount,
onClearAll, onClearAll,
onReloadFiles, onReloadFiles,
}) => { }: StorageStatsCardProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
if (!storageStats) return null; if (!storageStats) return null;

View File

@ -17,7 +17,7 @@ interface ToolPickerProps {
toolRegistry: ToolRegistry; toolRegistry: ToolRegistry;
} }
const ToolPicker: React.FC<ToolPickerProps> = ({ selectedToolKey, onSelect, toolRegistry }) => { const ToolPicker = ({ selectedToolKey, onSelect, toolRegistry }: ToolPickerProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");

View File

@ -12,7 +12,7 @@ interface ToolRendererProps {
updateParams: (params: any) => void; updateParams: (params: any) => void;
} }
const ToolRenderer: React.FC<ToolRendererProps> = ({ const ToolRenderer = ({
selectedToolKey, selectedToolKey,
selectedTool, selectedTool,
pdfFile, pdfFile,
@ -21,7 +21,7 @@ const ToolRenderer: React.FC<ToolRendererProps> = ({
setDownloadUrl, setDownloadUrl,
toolParams, toolParams,
updateParams, updateParams,
}) => { }: ToolRendererProps) => {
if (!selectedTool || !selectedTool.component) { if (!selectedTool || !selectedTool.component) {
return <div>Tool not found</div>; return <div>Tool not found</div>;
} }

View File

@ -43,10 +43,10 @@ interface TopControlsProps {
setCurrentView: (view: string) => void; setCurrentView: (view: string) => void;
} }
const TopControls: React.FC<TopControlsProps> = ({ const TopControls = ({
currentView, currentView,
setCurrentView, setCurrentView,
}) => { }: TopControlsProps) => {
const { themeMode, isRainbowMode, isToggleDisabled, toggleTheme } = useRainbowThemeContext(); const { themeMode, isRainbowMode, isToggleDisabled, toggleTheme } = useRainbowThemeContext();
const getThemeIcon = () => { const getThemeIcon = () => {

View File

@ -25,9 +25,9 @@ interface LazyPageImageProps {
setPageRef: (index: number, ref: HTMLImageElement | null) => void; setPageRef: (index: number, ref: HTMLImageElement | null) => void;
} }
const LazyPageImage: React.FC<LazyPageImageProps> = ({ const LazyPageImage = ({
pageIndex, zoom, theme, isFirst, renderPage, pageImages, setPageRef pageIndex, zoom, theme, isFirst, renderPage, pageImages, setPageRef
}) => { }: LazyPageImageProps) => {
const [isVisible, setIsVisible] = useState(false); const [isVisible, setIsVisible] = useState(false);
const [imageUrl, setImageUrl] = useState<string | null>(pageImages[pageIndex]); const [imageUrl, setImageUrl] = useState<string | null>(pageImages[pageIndex]);
const imgRef = useRef<HTMLImageElement>(null); const imgRef = useRef<HTMLImageElement>(null);
@ -129,12 +129,12 @@ export interface ViewerProps {
setSidebarsVisible: (v: boolean) => void; setSidebarsVisible: (v: boolean) => void;
} }
const Viewer: React.FC<ViewerProps> = ({ const Viewer = ({
pdfFile, pdfFile,
setPdfFile, setPdfFile,
sidebarsVisible, sidebarsVisible,
setSidebarsVisible, setSidebarsVisible,
}) => { }: ViewerProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const theme = useMantineTheme(); const theme = useMantineTheme();
const [numPages, setNumPages] = useState<number>(0); const [numPages, setNumPages] = useState<number>(0);

View File

@ -15,6 +15,7 @@ import SplitPdfPanel from "../tools/Split";
import CompressPdfPanel from "../tools/Compress"; import CompressPdfPanel from "../tools/Compress";
import MergePdfPanel from "../tools/Merge"; import MergePdfPanel from "../tools/Merge";
import PageEditor from "../components/PageEditor"; import PageEditor from "../components/PageEditor";
import PageEditorControls from "../components/PageEditorControls";
import Viewer from "../components/Viewer"; import Viewer from "../components/Viewer";
import TopControls from "../components/TopControls"; import TopControls from "../components/TopControls";
import ToolRenderer from "../components/ToolRenderer"; import ToolRenderer from "../components/ToolRenderer";
@ -55,6 +56,9 @@ export default function HomePage() {
const [sidebarsVisible, setSidebarsVisible] = useState(true); const [sidebarsVisible, setSidebarsVisible] = useState(true);
const [leftPanelView, setLeftPanelView] = useState<'toolPicker' | 'toolContent'>('toolPicker'); const [leftPanelView, setLeftPanelView] = useState<'toolPicker' | 'toolContent'>('toolPicker');
const [readerMode, setReaderMode] = useState(false); const [readerMode, setReaderMode] = useState(false);
// Page editor functions
const [pageEditorFunctions, setPageEditorFunctions] = useState<any>(null);
// URL parameter management // URL parameter management
const { toolParams, updateParams } = useToolParams(selectedToolKey, currentView); const { toolParams, updateParams } = useToolParams(selectedToolKey, currentView);
@ -204,12 +208,32 @@ export default function HomePage() {
setSidebarsVisible={setSidebarsVisible} setSidebarsVisible={setSidebarsVisible}
/> />
) : currentView === "pageEditor" ? ( ) : currentView === "pageEditor" ? (
<PageEditor <>
file={pdfFile} <PageEditor
setFile={setPdfFile} file={pdfFile}
downloadUrl={downloadUrl} setFile={setPdfFile}
setDownloadUrl={setDownloadUrl} downloadUrl={downloadUrl}
/> setDownloadUrl={setDownloadUrl}
onFunctionsReady={setPageEditorFunctions}
/>
{pdfFile && pageEditorFunctions && (
<PageEditorControls
onClosePdf={pageEditorFunctions.closePdf}
onUndo={pageEditorFunctions.handleUndo}
onRedo={pageEditorFunctions.handleRedo}
canUndo={pageEditorFunctions.canUndo}
canRedo={pageEditorFunctions.canRedo}
onRotate={pageEditorFunctions.handleRotate}
onDelete={pageEditorFunctions.handleDelete}
onSplit={pageEditorFunctions.handleSplit}
onExportSelected={() => pageEditorFunctions.showExportPreview(true)}
onExportAll={() => pageEditorFunctions.showExportPreview(false)}
exportLoading={pageEditorFunctions.exportLoading}
selectionMode={pageEditorFunctions.selectionMode}
selectedPages={pageEditorFunctions.selectedPages}
/>
)}
</>
) : ( ) : (
<FileManager <FileManager
files={files} files={files}