mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-08 17:51:20 +02:00
Homepage refactor, tool management refactor
This commit is contained in:
parent
5d7c572929
commit
cb5c35249f
@ -1,13 +1,10 @@
|
||||
import React from "react";
|
||||
import { FileWithUrl } from "../../types/file";
|
||||
import { useToolManagement } from "../../hooks/useToolManagement";
|
||||
|
||||
interface ToolRendererProps {
|
||||
selectedToolKey: string;
|
||||
selectedTool: any;
|
||||
pdfFile: any;
|
||||
files: FileWithUrl[];
|
||||
downloadUrl: string | null;
|
||||
setDownloadUrl: (url: string | null) => void;
|
||||
toolParams: any;
|
||||
updateParams: (params: any) => void;
|
||||
toolSelectedFiles?: File[];
|
||||
@ -16,18 +13,18 @@ interface ToolRendererProps {
|
||||
|
||||
const ToolRenderer = ({
|
||||
selectedToolKey,
|
||||
selectedTool,
|
||||
pdfFile,
|
||||
files,
|
||||
downloadUrl,
|
||||
setDownloadUrl,
|
||||
files,
|
||||
toolParams,
|
||||
updateParams,
|
||||
toolSelectedFiles = [],
|
||||
onPreviewFile,
|
||||
}: ToolRendererProps) => {
|
||||
// Get the tool from registry
|
||||
const { toolRegistry } = useToolManagement();
|
||||
const selectedTool = toolRegistry[selectedToolKey];
|
||||
|
||||
if (!selectedTool || !selectedTool.component) {
|
||||
return <div>Tool not found</div>;
|
||||
return <div>Tool not found: {selectedToolKey}</div>;
|
||||
}
|
||||
|
||||
const ToolComponent = selectedTool.component;
|
||||
@ -37,8 +34,6 @@ const ToolRenderer = ({
|
||||
case "split":
|
||||
return (
|
||||
<ToolComponent
|
||||
params={toolParams}
|
||||
updateParams={updateParams}
|
||||
selectedFiles={toolSelectedFiles}
|
||||
onPreviewFile={onPreviewFile}
|
||||
/>
|
||||
@ -47,7 +42,6 @@ const ToolRenderer = ({
|
||||
return (
|
||||
<ToolComponent
|
||||
files={files}
|
||||
setDownloadUrl={setDownloadUrl}
|
||||
setLoading={(loading: boolean) => {}}
|
||||
params={toolParams}
|
||||
updateParams={updateParams}
|
||||
@ -57,7 +51,6 @@ const ToolRenderer = ({
|
||||
return (
|
||||
<ToolComponent
|
||||
files={files}
|
||||
setDownloadUrl={setDownloadUrl}
|
||||
params={toolParams}
|
||||
updateParams={updateParams}
|
||||
/>
|
||||
@ -66,7 +59,6 @@ const ToolRenderer = ({
|
||||
return (
|
||||
<ToolComponent
|
||||
files={files}
|
||||
setDownloadUrl={setDownloadUrl}
|
||||
params={toolParams}
|
||||
updateParams={updateParams}
|
||||
/>
|
||||
|
@ -13,6 +13,8 @@ import CloseIcon from "@mui/icons-material/Close";
|
||||
import { useLocalStorage } from "@mantine/hooks";
|
||||
import { fileStorage } from "../../services/fileStorage";
|
||||
import SkeletonLoader from '../shared/SkeletonLoader';
|
||||
import { useFileContext } from "../../contexts/FileContext";
|
||||
import { useFileWithUrl } from "../../hooks/useFileWithUrl";
|
||||
|
||||
GlobalWorkerOptions.workerSrc = "/pdf.worker.js";
|
||||
|
||||
@ -132,8 +134,6 @@ const LazyPageImage = ({
|
||||
};
|
||||
|
||||
export interface ViewerProps {
|
||||
pdfFile: { file: File; url: string } | null; // First file in the array
|
||||
setPdfFile: (file: { file: File; url: string } | null) => void;
|
||||
sidebarsVisible: boolean;
|
||||
setSidebarsVisible: (v: boolean) => void;
|
||||
onClose?: () => void;
|
||||
@ -141,8 +141,6 @@ export interface ViewerProps {
|
||||
}
|
||||
|
||||
const Viewer = ({
|
||||
pdfFile,
|
||||
setPdfFile,
|
||||
sidebarsVisible,
|
||||
setSidebarsVisible,
|
||||
onClose,
|
||||
@ -150,6 +148,14 @@ const Viewer = ({
|
||||
}: ViewerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useMantineTheme();
|
||||
|
||||
// Get current file from FileContext
|
||||
const { getCurrentFile, getCurrentProcessedFile, clearAllFiles, addFiles } = useFileContext();
|
||||
const currentFile = getCurrentFile();
|
||||
const processedFile = getCurrentProcessedFile();
|
||||
|
||||
// Convert File to FileWithUrl format for viewer
|
||||
const pdfFile = useFileWithUrl(currentFile);
|
||||
const [numPages, setNumPages] = useState<number>(0);
|
||||
const [pageImages, setPageImages] = useState<string[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
@ -448,8 +454,7 @@ const Viewer = ({
|
||||
onChange={(e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file && file.type === "application/pdf") {
|
||||
const fileUrl = URL.createObjectURL(file);
|
||||
setPdfFile({ file, url: fileUrl });
|
||||
addFiles([file]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
129
frontend/src/hooks/useToolManagement.tsx
Normal file
129
frontend/src/hooks/useToolManagement.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import AddToPhotosIcon from "@mui/icons-material/AddToPhotos";
|
||||
import ContentCutIcon from "@mui/icons-material/ContentCut";
|
||||
import ZoomInMapIcon from "@mui/icons-material/ZoomInMap";
|
||||
import SplitPdfPanel from "../tools/Split";
|
||||
import CompressPdfPanel from "../tools/Compress";
|
||||
import MergePdfPanel from "../tools/Merge";
|
||||
|
||||
type ToolRegistryEntry = {
|
||||
icon: React.ReactNode;
|
||||
name: string;
|
||||
component: React.ComponentType<any>;
|
||||
view: string;
|
||||
};
|
||||
|
||||
type ToolRegistry = {
|
||||
[key: string]: ToolRegistryEntry;
|
||||
};
|
||||
|
||||
// Base tool registry without translations
|
||||
const baseToolRegistry = {
|
||||
split: { icon: <ContentCutIcon />, component: SplitPdfPanel, view: "split" },
|
||||
compress: { icon: <ZoomInMapIcon />, component: CompressPdfPanel, view: "viewer" },
|
||||
merge: { icon: <AddToPhotosIcon />, component: MergePdfPanel, view: "pageEditor" },
|
||||
};
|
||||
|
||||
// Tool parameter defaults
|
||||
const getToolDefaults = (toolKey: string) => {
|
||||
switch (toolKey) {
|
||||
case 'split':
|
||||
return {
|
||||
mode: '',
|
||||
pages: '',
|
||||
hDiv: '2',
|
||||
vDiv: '2',
|
||||
merge: false,
|
||||
splitType: 'size',
|
||||
splitValue: '',
|
||||
bookmarkLevel: '1',
|
||||
includeMetadata: false,
|
||||
allowDuplicates: false,
|
||||
};
|
||||
case 'compress':
|
||||
return {
|
||||
quality: 80,
|
||||
imageCompression: true,
|
||||
removeMetadata: false
|
||||
};
|
||||
case 'merge':
|
||||
return {
|
||||
sortOrder: 'name',
|
||||
includeMetadata: true
|
||||
};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
export const useToolManagement = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [selectedToolKey, setSelectedToolKey] = useState<string | null>(null);
|
||||
const [toolSelectedFileIds, setToolSelectedFileIds] = useState<string[]>([]);
|
||||
const [toolParams, setToolParams] = useState<Record<string, any>>({});
|
||||
|
||||
// Tool registry with translations
|
||||
const toolRegistry: ToolRegistry = useMemo(() => ({
|
||||
split: { ...baseToolRegistry.split, name: t("home.split.title", "Split PDF") },
|
||||
compress: { ...baseToolRegistry.compress, name: t("home.compressPdfs.title", "Compress PDF") },
|
||||
merge: { ...baseToolRegistry.merge, name: t("home.merge.title", "Merge PDFs") },
|
||||
}), [t]);
|
||||
|
||||
// Get tool parameters with defaults
|
||||
const getToolParams = useCallback((toolKey: string | null) => {
|
||||
if (!toolKey) return {};
|
||||
|
||||
const storedParams = toolParams[toolKey] || {};
|
||||
const defaultParams = getToolDefaults(toolKey);
|
||||
|
||||
return { ...defaultParams, ...storedParams };
|
||||
}, [toolParams]);
|
||||
|
||||
// Update tool parameters
|
||||
const updateToolParams = useCallback((toolKey: string, newParams: any) => {
|
||||
setToolParams(prev => ({
|
||||
...prev,
|
||||
[toolKey]: {
|
||||
...prev[toolKey],
|
||||
...newParams
|
||||
}
|
||||
}));
|
||||
}, []);
|
||||
|
||||
// Select tool
|
||||
const selectTool = useCallback((toolKey: string) => {
|
||||
setSelectedToolKey(toolKey);
|
||||
}, []);
|
||||
|
||||
// Clear tool selection
|
||||
const clearToolSelection = useCallback(() => {
|
||||
setSelectedToolKey(null);
|
||||
}, []);
|
||||
|
||||
// Get currently selected tool
|
||||
const selectedTool = selectedToolKey ? toolRegistry[selectedToolKey] : null;
|
||||
|
||||
return {
|
||||
// State
|
||||
selectedToolKey,
|
||||
selectedTool,
|
||||
toolSelectedFileIds,
|
||||
toolParams: getToolParams(selectedToolKey),
|
||||
toolRegistry,
|
||||
|
||||
// Actions
|
||||
selectTool,
|
||||
clearToolSelection,
|
||||
updateToolParams: (newParams: any) => {
|
||||
if (selectedToolKey) {
|
||||
updateToolParams(selectedToolKey, newParams);
|
||||
}
|
||||
},
|
||||
setToolSelectedFileIds,
|
||||
|
||||
// Utilities
|
||||
getToolParams,
|
||||
};
|
||||
};
|
@ -1,12 +1,8 @@
|
||||
import React, { useState, useCallback, useEffect } from "react";
|
||||
import React, { useState, useCallback} from "react";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useFileWithUrl } from "../hooks/useFileWithUrl";
|
||||
import { useFileContext } from "../contexts/FileContext";
|
||||
import { fileStorage } from "../services/fileStorage";
|
||||
import AddToPhotosIcon from "@mui/icons-material/AddToPhotos";
|
||||
import ContentCutIcon from "@mui/icons-material/ContentCut";
|
||||
import ZoomInMapIcon from "@mui/icons-material/ZoomInMap";
|
||||
import { Group, Paper, Box, Button, useMantineTheme, Container } from "@mantine/core";
|
||||
import { useToolManagement } from "../hooks/useToolManagement";
|
||||
import { Group, Box, Button, Container } from "@mantine/core";
|
||||
import { useRainbowThemeContext } from "../components/shared/RainbowThemeProvider";
|
||||
import rainbowStyles from '../styles/rainbow.module.css';
|
||||
|
||||
@ -17,163 +13,50 @@ import PageEditor from "../components/pageEditor/PageEditor";
|
||||
import PageEditorControls from "../components/pageEditor/PageEditorControls";
|
||||
import Viewer from "../components/viewer/Viewer";
|
||||
import FileUploadSelector from "../components/shared/FileUploadSelector";
|
||||
import SplitPdfPanel from "../tools/Split";
|
||||
import CompressPdfPanel from "../tools/Compress";
|
||||
import MergePdfPanel from "../tools/Merge";
|
||||
import ToolRenderer from "../components/tools/ToolRenderer";
|
||||
import QuickAccessBar from "../components/shared/QuickAccessBar";
|
||||
|
||||
type ToolRegistryEntry = {
|
||||
icon: React.ReactNode;
|
||||
name: string;
|
||||
component: React.ComponentType<any>;
|
||||
view: string;
|
||||
};
|
||||
|
||||
type ToolRegistry = {
|
||||
[key: string]: ToolRegistryEntry;
|
||||
};
|
||||
|
||||
// Base tool registry without translations
|
||||
const baseToolRegistry = {
|
||||
split: { icon: <ContentCutIcon />, component: SplitPdfPanel, view: "split" },
|
||||
compress: { icon: <ZoomInMapIcon />, component: CompressPdfPanel, view: "viewer" },
|
||||
merge: { icon: <AddToPhotosIcon />, component: MergePdfPanel, view: "pageEditor" },
|
||||
};
|
||||
|
||||
export default function HomePage() {
|
||||
const { t } = useTranslation();
|
||||
const theme = useMantineTheme();
|
||||
const { isRainbowMode } = useRainbowThemeContext();
|
||||
|
||||
// Get file context
|
||||
const fileContext = useFileContext();
|
||||
const { activeFiles, currentView, currentMode, setCurrentView, addFiles } = fileContext;
|
||||
|
||||
// Core app state
|
||||
const [selectedToolKey, setSelectedToolKey] = useState<string | null>(null);
|
||||
const {
|
||||
selectedToolKey,
|
||||
selectedTool,
|
||||
toolParams,
|
||||
toolRegistry,
|
||||
selectTool,
|
||||
clearToolSelection,
|
||||
updateToolParams,
|
||||
} = useToolManagement();
|
||||
|
||||
const [storedFiles, setStoredFiles] = useState<any[]>([]);
|
||||
const [preSelectedFiles, setPreSelectedFiles] = useState([]);
|
||||
const [downloadUrl, setDownloadUrl] = useState<string | null>(null);
|
||||
const [toolSelectedFiles, setToolSelectedFiles] = useState<File[]>([]);
|
||||
const [sidebarsVisible, setSidebarsVisible] = useState(true);
|
||||
const [leftPanelView, setLeftPanelView] = useState<'toolPicker' | 'toolContent'>('toolPicker');
|
||||
const [readerMode, setReaderMode] = useState(false);
|
||||
const [pageEditorFunctions, setPageEditorFunctions] = useState<any>(null);
|
||||
const [toolSelectedFiles, setToolSelectedFiles] = useState<File[]>([]);
|
||||
const [toolParams, setToolParams] = useState<Record<string, any>>({});
|
||||
const [previewFile, setPreviewFile] = useState<File | null>(null);
|
||||
|
||||
// Tool registry
|
||||
const toolRegistry: ToolRegistry = {
|
||||
split: { ...baseToolRegistry.split, name: t("home.split.title", "Split PDF") },
|
||||
compress: { ...baseToolRegistry.compress, name: t("home.compressPdfs.title", "Compress PDF") },
|
||||
merge: { ...baseToolRegistry.merge, name: t("home.merge.title", "Merge PDFs") },
|
||||
};
|
||||
|
||||
// Tool parameters with state management
|
||||
const getToolParams = (toolKey: string | null) => {
|
||||
if (!toolKey) return {};
|
||||
|
||||
// Get stored params for this tool, or use defaults
|
||||
const storedParams = toolParams[toolKey] || {};
|
||||
|
||||
const defaultParams = (() => {
|
||||
switch (toolKey) {
|
||||
case 'split':
|
||||
return {
|
||||
mode: '',
|
||||
pages: '',
|
||||
hDiv: '2',
|
||||
vDiv: '2',
|
||||
merge: false,
|
||||
splitType: 'size',
|
||||
splitValue: '',
|
||||
bookmarkLevel: '1',
|
||||
includeMetadata: false,
|
||||
allowDuplicates: false,
|
||||
};
|
||||
case 'compress':
|
||||
return {
|
||||
quality: 80,
|
||||
imageCompression: true,
|
||||
removeMetadata: false
|
||||
};
|
||||
case 'merge':
|
||||
return {
|
||||
sortOrder: 'name',
|
||||
includeMetadata: true
|
||||
};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
})();
|
||||
|
||||
return { ...defaultParams, ...storedParams };
|
||||
};
|
||||
|
||||
const updateToolParams = useCallback((toolKey: string, newParams: any) => {
|
||||
setToolParams(prev => ({
|
||||
...prev,
|
||||
[toolKey]: {
|
||||
...prev[toolKey],
|
||||
...newParams
|
||||
}
|
||||
}));
|
||||
}, []);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const activeFileData = activeFiles.map(file => ({
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
lastModified: file.lastModified
|
||||
}));
|
||||
localStorage.setItem('activeFiles', JSON.stringify(activeFileData));
|
||||
}, [activeFiles]);
|
||||
|
||||
useEffect(() => {
|
||||
const loadStoredFiles = async () => {
|
||||
try {
|
||||
const files = await fileStorage.getAllFiles();
|
||||
setStoredFiles(files);
|
||||
} catch (error) {
|
||||
console.warn('Failed to load stored files:', error);
|
||||
}
|
||||
};
|
||||
loadStoredFiles();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const restoreActiveFiles = async () => {
|
||||
try {
|
||||
const savedFileData = JSON.parse(localStorage.getItem('activeFiles') || '[]');
|
||||
if (savedFileData.length > 0) {
|
||||
// File restoration handled by FileContext
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to restore active files:', error);
|
||||
}
|
||||
};
|
||||
restoreActiveFiles();
|
||||
}, []);
|
||||
|
||||
const handleToolSelect = useCallback(
|
||||
(id: string) => {
|
||||
setSelectedToolKey(id);
|
||||
selectTool(id);
|
||||
if (toolRegistry[id]?.view) setCurrentView(toolRegistry[id].view);
|
||||
setLeftPanelView('toolContent');
|
||||
setReaderMode(false);
|
||||
},
|
||||
[toolRegistry, setCurrentView]
|
||||
[selectTool, toolRegistry, setCurrentView]
|
||||
);
|
||||
|
||||
const handleQuickAccessTools = useCallback(() => {
|
||||
setLeftPanelView('toolPicker');
|
||||
setReaderMode(false);
|
||||
setSelectedToolKey(null);
|
||||
}, []);
|
||||
clearToolSelection();
|
||||
}, [clearToolSelection]);
|
||||
|
||||
const handleReaderToggle = useCallback(() => {
|
||||
setReaderMode(!readerMode);
|
||||
@ -190,123 +73,7 @@ export default function HomePage() {
|
||||
}
|
||||
}, [activeFiles, addFiles]);
|
||||
|
||||
const removeFromActiveFiles = useCallback((file: File) => {
|
||||
fileContext.removeFiles([file.name]);
|
||||
}, [fileContext]);
|
||||
|
||||
const setCurrentActiveFile = useCallback(async (file: File) => {
|
||||
const filtered = activeFiles.filter(f => !(f.name === file.name && f.size === file.size));
|
||||
await addFiles([file, ...filtered]);
|
||||
}, [activeFiles, addFiles]);
|
||||
|
||||
const handleFileSelect = useCallback((file: File) => {
|
||||
addToActiveFiles(file);
|
||||
}, [addToActiveFiles]);
|
||||
|
||||
// Handle opening file editor with selected files
|
||||
const handleOpenFileEditor = useCallback(async (selectedFiles) => {
|
||||
if (!selectedFiles || selectedFiles.length === 0) {
|
||||
setPreSelectedFiles([]);
|
||||
handleViewChange("fileEditor");
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert FileWithUrl[] to File[] and add to activeFiles
|
||||
try {
|
||||
const convertedFiles = await Promise.all(
|
||||
selectedFiles.map(async (fileItem) => {
|
||||
// If it's already a File, return as is
|
||||
if (fileItem instanceof File) {
|
||||
return fileItem;
|
||||
}
|
||||
|
||||
// If it has a file property, use that
|
||||
if (fileItem.file && fileItem.file instanceof File) {
|
||||
return fileItem.file;
|
||||
}
|
||||
|
||||
// If it's from IndexedDB storage, reconstruct the File
|
||||
if (fileItem.arrayBuffer && typeof fileItem.arrayBuffer === 'function') {
|
||||
const arrayBuffer = await fileItem.arrayBuffer();
|
||||
const blob = new Blob([arrayBuffer], { type: fileItem.type || 'application/pdf' });
|
||||
const file = new File([blob], fileItem.name, {
|
||||
type: fileItem.type || 'application/pdf',
|
||||
lastModified: fileItem.lastModified || Date.now()
|
||||
});
|
||||
// Mark as from storage to avoid re-storing
|
||||
(file as any).storedInIndexedDB = true;
|
||||
return file;
|
||||
}
|
||||
|
||||
console.warn('Could not convert file item:', fileItem);
|
||||
return null;
|
||||
})
|
||||
);
|
||||
|
||||
// Filter out nulls and add to activeFiles
|
||||
const validFiles = convertedFiles.filter((f): f is File => f !== null);
|
||||
await addFiles(validFiles);
|
||||
setPreSelectedFiles([]); // Clear preselected since we're using activeFiles now
|
||||
handleViewChange("fileEditor");
|
||||
} catch (error) {
|
||||
console.error('Error converting selected files:', error);
|
||||
}
|
||||
}, [handleViewChange, addFiles]);
|
||||
|
||||
// Handle opening page editor with selected files
|
||||
const handleOpenPageEditor = useCallback(async (selectedFiles) => {
|
||||
if (!selectedFiles || selectedFiles.length === 0) {
|
||||
handleViewChange("pageEditor");
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert FileWithUrl[] to File[] and add to activeFiles
|
||||
try {
|
||||
const convertedFiles = await Promise.all(
|
||||
selectedFiles.map(async (fileItem) => {
|
||||
// If it's already a File, return as is
|
||||
if (fileItem instanceof File) {
|
||||
return fileItem;
|
||||
}
|
||||
|
||||
// If it has a file property, use that
|
||||
if (fileItem.file && fileItem.file instanceof File) {
|
||||
return fileItem.file;
|
||||
}
|
||||
|
||||
// If it's from IndexedDB storage, reconstruct the File
|
||||
if (fileItem.arrayBuffer && typeof fileItem.arrayBuffer === 'function') {
|
||||
const arrayBuffer = await fileItem.arrayBuffer();
|
||||
const blob = new Blob([arrayBuffer], { type: fileItem.type || 'application/pdf' });
|
||||
const file = new File([blob], fileItem.name, {
|
||||
type: fileItem.type || 'application/pdf',
|
||||
lastModified: fileItem.lastModified || Date.now()
|
||||
});
|
||||
// Mark as from storage to avoid re-storing
|
||||
(file as any).storedInIndexedDB = true;
|
||||
return file;
|
||||
}
|
||||
|
||||
console.warn('Could not convert file item:', fileItem);
|
||||
return null;
|
||||
})
|
||||
);
|
||||
|
||||
// Filter out nulls and add to activeFiles
|
||||
const validFiles = convertedFiles.filter((f): f is File => f !== null);
|
||||
await addFiles(validFiles);
|
||||
handleViewChange("pageEditor");
|
||||
} catch (error) {
|
||||
console.error('Error converting selected files for page editor:', error);
|
||||
}
|
||||
}, [handleViewChange, addFiles]);
|
||||
|
||||
const selectedTool = toolRegistry[selectedToolKey];
|
||||
|
||||
// For Viewer - convert first active file to expected format (only when needed)
|
||||
const currentFileWithUrl = useFileWithUrl(
|
||||
(currentView === "viewer" && activeFiles[0]) ? activeFiles[0] : null
|
||||
);
|
||||
|
||||
return (
|
||||
<Group
|
||||
@ -324,17 +91,12 @@ export default function HomePage() {
|
||||
readerMode={readerMode}
|
||||
/>
|
||||
|
||||
{/* Left: Tool Picker OR Selected Tool Panel */}
|
||||
{/* Left: Tool Picker or Selected Tool Panel */}
|
||||
<div
|
||||
className={`h-screen z-sticky flex flex-col ${isRainbowMode ? rainbowStyles.rainbowPaper : ''} overflow-hidden`}
|
||||
className={`h-screen flex flex-col overflow-hidden bg-[var(--bg-surface)] border-r border-[var(--border-subtle)] transition-all duration-300 ease-out ${isRainbowMode ? rainbowStyles.rainbowPaper : ''}`}
|
||||
style={{
|
||||
backgroundColor: 'var(--bg-surface)',
|
||||
borderRight: '1px solid var(--border-subtle)',
|
||||
width: sidebarsVisible && !readerMode ? '25vw' : '0px',
|
||||
minWidth: sidebarsVisible && !readerMode ? '300px' : '0px',
|
||||
maxWidth: sidebarsVisible && !readerMode ? '450px' : '0px',
|
||||
transition: 'width 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), min-width 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), max-width 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
padding: sidebarsVisible && !readerMode ? '1rem' : '0rem'
|
||||
width: sidebarsVisible && !readerMode ? '14vw' : '0',
|
||||
padding: sidebarsVisible && !readerMode ? '1rem' : '0'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
@ -379,13 +141,6 @@ export default function HomePage() {
|
||||
<div className="flex-1 min-h-0">
|
||||
<ToolRenderer
|
||||
selectedToolKey={selectedToolKey}
|
||||
selectedTool={selectedTool}
|
||||
pdfFile={activeFiles[0] || null}
|
||||
files={activeFiles}
|
||||
downloadUrl={downloadUrl}
|
||||
setDownloadUrl={setDownloadUrl}
|
||||
toolParams={getToolParams(selectedToolKey)}
|
||||
updateParams={(newParams) => updateToolParams(selectedToolKey, newParams)}
|
||||
toolSelectedFiles={toolSelectedFiles}
|
||||
onPreviewFile={setPreviewFile}
|
||||
/>
|
||||
@ -423,7 +178,6 @@ export default function HomePage() {
|
||||
: t("fileUpload.selectPdfToEdit", "Select a PDF to edit")
|
||||
}
|
||||
subtitle={t("fileUpload.chooseFromStorage", "Choose a file from storage or upload a new PDF")}
|
||||
sharedFiles={storedFiles}
|
||||
onFileSelect={(file) => {
|
||||
addToActiveFiles(file);
|
||||
}}
|
||||
@ -439,7 +193,6 @@ export default function HomePage() {
|
||||
) : currentView === "fileEditor" ? (
|
||||
<FileEditor
|
||||
onOpenPageEditor={(file) => {
|
||||
setCurrentActiveFile(file);
|
||||
handleViewChange("pageEditor");
|
||||
}}
|
||||
onMergeFiles={(filesToMerge) => {
|
||||
@ -450,14 +203,6 @@ export default function HomePage() {
|
||||
/>
|
||||
) : currentView === "viewer" ? (
|
||||
<Viewer
|
||||
pdfFile={currentFileWithUrl}
|
||||
setPdfFile={(fileObj) => {
|
||||
if (fileObj) {
|
||||
setCurrentActiveFile(fileObj.file);
|
||||
} else {
|
||||
fileContext.clearAllFiles();
|
||||
}
|
||||
}}
|
||||
sidebarsVisible={sidebarsVisible}
|
||||
setSidebarsVisible={setSidebarsVisible}
|
||||
previewFile={previewFile}
|
||||
@ -466,7 +211,7 @@ export default function HomePage() {
|
||||
setPreviewFile(null); // Clear preview file
|
||||
const previousMode = sessionStorage.getItem('previousMode');
|
||||
if (previousMode === 'split') {
|
||||
setSelectedToolKey('split');
|
||||
selectTool('split');
|
||||
setCurrentView('split');
|
||||
setLeftPanelView('toolContent');
|
||||
sessionStorage.removeItem('previousMode');
|
||||
@ -512,20 +257,12 @@ export default function HomePage() {
|
||||
) : selectedToolKey && selectedTool ? (
|
||||
<ToolRenderer
|
||||
selectedToolKey={selectedToolKey}
|
||||
selectedTool={selectedTool}
|
||||
pdfFile={activeFiles[0] || null}
|
||||
files={activeFiles}
|
||||
downloadUrl={downloadUrl}
|
||||
setDownloadUrl={setDownloadUrl}
|
||||
toolParams={getToolParams(selectedToolKey)}
|
||||
updateParams={() => {}}
|
||||
/>
|
||||
) : (
|
||||
<Container size="lg" p="xl" h="100%" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<FileUploadSelector
|
||||
title="File Management"
|
||||
subtitle="Choose files from storage or upload new PDFs"
|
||||
sharedFiles={storedFiles}
|
||||
onFileSelect={(file) => {
|
||||
addToActiveFiles(file);
|
||||
}}
|
||||
|
@ -26,26 +26,11 @@ import { generateThumbnailForFile } from "../utils/thumbnailUtils";
|
||||
import FileEditor from "../components/fileEditor/FileEditor";
|
||||
|
||||
export interface SplitPdfPanelProps {
|
||||
params: {
|
||||
mode: string;
|
||||
pages: string;
|
||||
hDiv: string;
|
||||
vDiv: string;
|
||||
merge: boolean;
|
||||
splitType: string;
|
||||
splitValue: string;
|
||||
bookmarkLevel: string;
|
||||
includeMetadata: boolean;
|
||||
allowDuplicates: boolean;
|
||||
};
|
||||
updateParams: (newParams: Partial<SplitPdfPanelProps["params"]>) => void;
|
||||
selectedFiles?: File[];
|
||||
onPreviewFile?: (file: File | null) => void;
|
||||
}
|
||||
|
||||
const SplitPdfPanel: React.FC<SplitPdfPanelProps> = ({
|
||||
params,
|
||||
updateParams,
|
||||
selectedFiles = [],
|
||||
onPreviewFile,
|
||||
}) => {
|
||||
@ -53,6 +38,18 @@ const SplitPdfPanel: React.FC<SplitPdfPanelProps> = ({
|
||||
const fileContext = useFileContext();
|
||||
const { activeFiles, selectedFileIds, updateProcessedFile, recordOperation, markOperationApplied, markOperationFailed, setCurrentMode } = fileContext;
|
||||
|
||||
// Internal split parameter state
|
||||
const [mode, setMode] = useState('');
|
||||
const [pages, setPages] = useState('');
|
||||
const [hDiv, setHDiv] = useState('2');
|
||||
const [vDiv, setVDiv] = useState('2');
|
||||
const [merge, setMerge] = useState(false);
|
||||
const [splitType, setSplitType] = useState('size');
|
||||
const [splitValue, setSplitValue] = useState('');
|
||||
const [bookmarkLevel, setBookmarkLevel] = useState('1');
|
||||
const [includeMetadata, setIncludeMetadata] = useState(false);
|
||||
const [allowDuplicates, setAllowDuplicates] = useState(false);
|
||||
|
||||
const [status, setStatus] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
@ -67,19 +64,6 @@ const SplitPdfPanel: React.FC<SplitPdfPanelProps> = ({
|
||||
isGeneratingThumbnails: false
|
||||
});
|
||||
|
||||
const {
|
||||
mode,
|
||||
pages,
|
||||
hDiv,
|
||||
vDiv,
|
||||
merge,
|
||||
splitType,
|
||||
splitValue,
|
||||
bookmarkLevel,
|
||||
includeMetadata,
|
||||
allowDuplicates,
|
||||
} = params;
|
||||
|
||||
// Clear download when parameters or files change
|
||||
React.useEffect(() => {
|
||||
if (downloadUrl) {
|
||||
@ -339,7 +323,7 @@ const SplitPdfPanel: React.FC<SplitPdfPanelProps> = ({
|
||||
label="Choose split method"
|
||||
placeholder="Select how to split the PDF"
|
||||
value={mode}
|
||||
onChange={(v) => v && updateParams({ mode: v })}
|
||||
onChange={(v) => v && setMode(v)}
|
||||
data={[
|
||||
{ value: "byPages", label: t("split.header", "Split by Pages") + " (e.g. 1,3,5-10)" },
|
||||
{ value: "bySections", label: t("split-by-sections.title", "Split by Grid Sections") },
|
||||
@ -354,7 +338,7 @@ const SplitPdfPanel: React.FC<SplitPdfPanelProps> = ({
|
||||
label={t("split.splitPages", "Pages")}
|
||||
placeholder={t("pageSelectionPrompt", "e.g. 1,3,5-10")}
|
||||
value={pages}
|
||||
onChange={(e) => updateParams({ pages: e.target.value })}
|
||||
onChange={(e) => setPages(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -366,7 +350,7 @@ const SplitPdfPanel: React.FC<SplitPdfPanelProps> = ({
|
||||
min="0"
|
||||
max="300"
|
||||
value={hDiv}
|
||||
onChange={(e) => updateParams({ hDiv: e.target.value })}
|
||||
onChange={(e) => setHDiv(e.target.value)}
|
||||
placeholder={t("split-by-sections.horizontal.placeholder", "Enter number of horizontal divisions")}
|
||||
/>
|
||||
<TextInput
|
||||
@ -375,13 +359,13 @@ const SplitPdfPanel: React.FC<SplitPdfPanelProps> = ({
|
||||
min="0"
|
||||
max="300"
|
||||
value={vDiv}
|
||||
onChange={(e) => updateParams({ vDiv: e.target.value })}
|
||||
onChange={(e) => setVDiv(e.target.value)}
|
||||
placeholder={t("split-by-sections.vertical.placeholder", "Enter number of vertical divisions")}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t("split-by-sections.merge", "Merge sections into one PDF")}
|
||||
checked={merge}
|
||||
onChange={(e) => updateParams({ merge: e.currentTarget.checked })}
|
||||
onChange={(e) => setMerge(e.currentTarget.checked)}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
@ -391,7 +375,7 @@ const SplitPdfPanel: React.FC<SplitPdfPanelProps> = ({
|
||||
<Select
|
||||
label={t("split-by-size-or-count.type.label", "Split Type")}
|
||||
value={splitType}
|
||||
onChange={(v) => v && updateParams({ splitType: v })}
|
||||
onChange={(v) => v && setSplitType(v)}
|
||||
data={[
|
||||
{ value: "size", label: t("split-by-size-or-count.type.size", "By Size") },
|
||||
{ value: "pages", label: t("split-by-size-or-count.type.pageCount", "By Page Count") },
|
||||
@ -402,7 +386,7 @@ const SplitPdfPanel: React.FC<SplitPdfPanelProps> = ({
|
||||
label={t("split-by-size-or-count.value.label", "Split Value")}
|
||||
placeholder={t("split-by-size-or-count.value.placeholder", "e.g. 10MB or 5 pages")}
|
||||
value={splitValue}
|
||||
onChange={(e) => updateParams({ splitValue: e.target.value })}
|
||||
onChange={(e) => setSplitValue(e.target.value)}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
@ -413,17 +397,17 @@ const SplitPdfPanel: React.FC<SplitPdfPanelProps> = ({
|
||||
label={t("splitByChapters.bookmarkLevel", "Bookmark Level")}
|
||||
type="number"
|
||||
value={bookmarkLevel}
|
||||
onChange={(e) => updateParams({ bookmarkLevel: e.target.value })}
|
||||
onChange={(e) => setBookmarkLevel(e.target.value)}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t("splitByChapters.includeMetadata", "Include Metadata")}
|
||||
checked={includeMetadata}
|
||||
onChange={(e) => updateParams({ includeMetadata: e.currentTarget.checked })}
|
||||
onChange={(e) => setIncludeMetadata(e.currentTarget.checked)}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t("splitByChapters.allowDuplicates", "Allow Duplicate Bookmarks")}
|
||||
checked={allowDuplicates}
|
||||
onChange={(e) => updateParams({ allowDuplicates: e.currentTarget.checked })}
|
||||
onChange={(e) => setAllowDuplicates(e.currentTarget.checked)}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
|
Loading…
Reference in New Issue
Block a user