diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs index d823a6af9..c0f9fd678 100644 --- a/frontend/eslint.config.mjs +++ b/frontend/eslint.config.mjs @@ -25,7 +25,18 @@ export default defineConfig( ], "@typescript-eslint/no-explicit-any": "off", // Temporarily disabled until codebase conformant "@typescript-eslint/no-require-imports": "off", // Temporarily disabled until codebase conformant - "@typescript-eslint/no-unused-vars": "off", // Temporarily disabled until codebase conformant + "@typescript-eslint/no-unused-vars": [ + "error", + { + "args": "all", // All function args must be used (or explicitly ignored) + "argsIgnorePattern": "^_", // Allow unused variables beginning with an underscore + "caughtErrors": "all", // Caught errors must be used (or explicitly ignored) + "caughtErrorsIgnorePattern": "^_", // Allow unused variables beginning with an underscore + "destructuredArrayIgnorePattern": "^_", // Allow unused variables beginning with an underscore + "varsIgnorePattern": "^_", // Allow unused variables beginning with an underscore + "ignoreRestSiblings": true, // Allow unused variables when removing attributes from objects (otherwise this requires explicit renaming like `({ x: _x, ...y }) => y`, which is clunky) + }, + ], }, } ); diff --git a/frontend/scripts/generate-icons.js b/frontend/scripts/generate-icons.js index 0fd42a4df..d99414b66 100644 --- a/frontend/scripts/generate-icons.js +++ b/frontend/scripts/generate-icons.js @@ -107,7 +107,7 @@ async function main() { needsRegeneration = false; info(`✅ Icon set already up-to-date (${usedIcons.length} icons, ${Math.round(fs.statSync(outputPath).size / 1024)}KB)`); } - } catch (error) { + } catch { // If we can't parse existing file, regenerate needsRegeneration = true; } diff --git a/frontend/scripts/generate-licenses.js b/frontend/scripts/generate-licenses.js index 7ab425e19..3daf04d8d 100644 --- a/frontend/scripts/generate-licenses.js +++ b/frontend/scripts/generate-licenses.js @@ -24,7 +24,7 @@ try { // Install license-checker if not present try { require.resolve('license-checker'); - } catch (e) { + } catch { console.log('đŸ“Ļ Installing license-checker...'); execSync('npm install --save-dev license-checker', { stdio: 'inherit' }); } diff --git a/frontend/src/components/FileManager.tsx b/frontend/src/components/FileManager.tsx index dc0001b4e..63ca5c5ec 100644 --- a/frontend/src/components/FileManager.tsx +++ b/frontend/src/components/FileManager.tsx @@ -4,7 +4,6 @@ import { Dropzone } from '@mantine/dropzone'; import { FileMetadata } from '../types/file'; import { useFileManager } from '../hooks/useFileManager'; import { useFilesModalContext } from '../contexts/FilesModalContext'; -import { createFileId } from '../types/fileContext'; import { Tool } from '../types/tool'; import MobileLayout from './fileManager/MobileLayout'; import DesktopLayout from './fileManager/DesktopLayout'; @@ -21,13 +20,7 @@ const FileManager: React.FC = ({ selectedTool }) => { const [isDragging, setIsDragging] = useState(false); const [isMobile, setIsMobile] = useState(false); - const { loadRecentFiles, handleRemoveFile, storeFile, convertToFile } = useFileManager(); - - // Wrapper for storeFile that generates UUID - const storeStirlingFile = useCallback(async (file: File) => { - const fileId = createFileId(); // Generate UUID for storage - return await storeFile(file, fileId); - }, [storeFile]); + const { loadRecentFiles, handleRemoveFile, convertToFile } = useFileManager(); // File management handlers const isFileSupported = useCallback((fileName: string) => { diff --git a/frontend/src/components/fileEditor/FileEditor.tsx b/frontend/src/components/fileEditor/FileEditor.tsx index 901eb20ca..8f71f59b0 100644 --- a/frontend/src/components/fileEditor/FileEditor.tsx +++ b/frontend/src/components/fileEditor/FileEditor.tsx @@ -1,42 +1,28 @@ -import React, { useState, useCallback, useRef, useEffect, useMemo } from 'react'; +import React, { useState, useCallback, useRef, useMemo } from 'react'; import { Text, Center, Box, Notification, LoadingOverlay, Stack, Group, Portal } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; -import { useTranslation } from 'react-i18next'; -import UploadFileIcon from '@mui/icons-material/UploadFile'; -import { useFileSelection, useFileState, useFileManagement, useFileActions } from '../../contexts/FileContext'; +import { useFileSelection, useFileState, useFileManagement } from '../../contexts/FileContext'; import { useNavigationActions } from '../../contexts/NavigationContext'; -import { FileOperation } from '../../types/fileContext'; -import { fileStorage } from '../../services/fileStorage'; -import { generateThumbnailForFile } from '../../utils/thumbnailUtils'; import { zipFileService } from '../../services/zipFileService'; import { detectFileExtension } from '../../utils/fileUtils'; -import styles from './FileEditor.module.css'; import FileEditorThumbnail from './FileEditorThumbnail'; import FilePickerModal from '../shared/FilePickerModal'; import SkeletonLoader from '../shared/SkeletonLoader'; import { FileId, StirlingFile } from '../../types/fileContext'; - interface FileEditorProps { - onOpenPageEditor?: (file: StirlingFile) => void; + onOpenPageEditor?: () => void; onMergeFiles?: (files: StirlingFile[]) => void; toolMode?: boolean; - showUpload?: boolean; - showBulkActions?: boolean; supportedExtensions?: string[]; } const FileEditor = ({ - onOpenPageEditor, - onMergeFiles, toolMode = false, - showUpload = true, - showBulkActions = true, supportedExtensions = ["pdf"] }: FileEditorProps) => { - const { t } = useTranslation(); // Utility function to check if a file extension is supported const isFileSupported = useCallback((fileName: string): boolean => { @@ -49,13 +35,10 @@ const FileEditor = ({ const { addFiles, removeFiles, reorderFiles } = useFileManagement(); // Extract needed values from state (memoized to prevent infinite loops) - const activeFiles = useMemo(() => selectors.getFiles(), [selectors.getFilesSignature()]); const activeStirlingFileStubs = useMemo(() => selectors.getStirlingFileStubs(), [selectors.getFilesSignature()]); const selectedFileIds = state.ui.selectedFileIds; - const isProcessing = state.ui.isProcessing; - // Get the real context actions - const { actions } = useFileActions(); + // Get navigation actions const { actions: navActions } = useNavigationActions(); // Get file selection context @@ -161,29 +144,9 @@ const FileEditor = ({ if (extractionResult.success) { allExtractedFiles.push(...extractionResult.extractedFiles); - // Record ZIP extraction operation - const operationId = `zip-extract-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; - const operation: FileOperation = { - id: operationId, - type: 'convert', - timestamp: Date.now(), - fileIds: extractionResult.extractedFiles.map(f => f.name) as FileId[] /* FIX ME: This doesn't seem right */, - status: 'pending', - metadata: { - originalFileName: file.name, - outputFileNames: extractionResult.extractedFiles.map(f => f.name), - fileSize: file.size, - parameters: { - extractionType: 'zip', - extractedCount: extractionResult.extractedCount, - totalFiles: extractionResult.totalFiles - } + if (extractionResult.errors.length > 0) { + errors.push(...extractionResult.errors); } - }; - - if (extractionResult.errors.length > 0) { - errors.push(...extractionResult.errors); - } } else { errors.push(`Failed to extract ZIP file "${file.name}": ${extractionResult.errors.join(', ')}`); } @@ -213,25 +176,6 @@ const FileEditor = ({ // Process all extracted files if (allExtractedFiles.length > 0) { - // Record upload operations for PDF files - for (const file of allExtractedFiles) { - const operationId = `upload-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; - const operation: FileOperation = { - id: operationId, - type: 'upload', - timestamp: Date.now(), - fileIds: [file.name as FileId /* This doesn't seem right */], - status: 'pending', - metadata: { - originalFileName: file.name, - fileSize: file.size, - parameters: { - uploadMethod: 'drag-drop' - } - } - }; - } - // Add files to context (they will be processed automatically) await addFiles(allExtractedFiles); setStatus(`Added ${allExtractedFiles.length} files`); @@ -252,23 +196,6 @@ const FileEditor = ({ } }, [addFiles]); - const selectAll = useCallback(() => { - setSelectedFiles(activeStirlingFileStubs.map(r => r.id)); // Use StirlingFileStub IDs directly - }, [activeStirlingFileStubs, setSelectedFiles]); - - const deselectAll = useCallback(() => setSelectedFiles([]), [setSelectedFiles]); - - const closeAllFiles = useCallback(() => { - if (activeStirlingFileStubs.length === 0) return; - - // Remove all files from context but keep in storage - const allFileIds = activeStirlingFileStubs.map(record => record.id); - removeFiles(allFileIds, false); // false = keep in storage - - // Clear selections - setSelectedFiles([]); - }, [activeStirlingFileStubs, removeFiles, setSelectedFiles]); - const toggleFile = useCallback((fileId: FileId) => { const currentSelectedIds = contextSelectedIdsRef.current; @@ -304,15 +231,6 @@ const FileEditor = ({ setSelectedFiles(newSelection); }, [setSelectedFiles, toolMode, setStatus, activeStirlingFileStubs]); - const toggleSelectionMode = useCallback(() => { - setSelectionMode(prev => { - const newMode = !prev; - if (!newMode) { - setSelectedFiles([]); - } - return newMode; - }); - }, [setSelectedFiles]); // File reordering handler for drag and drop const handleReorderFiles = useCallback((sourceFileId: FileId, targetFileId: FileId, selectedFileIds: FileId[]) => { @@ -378,27 +296,8 @@ const FileEditor = ({ const file = record ? selectors.getFile(record.id) : null; if (record && file) { - // Record close operation - const fileName = file.name; - const contextFileId = record.id; - const operationId = `close-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; - const operation: FileOperation = { - id: operationId, - type: 'remove', - timestamp: Date.now(), - fileIds: [fileName as FileId /* FIX ME: This doesn't seem right */], - status: 'pending', - metadata: { - originalFileName: fileName, - fileSize: record.size, - parameters: { - action: 'close', - reason: 'user_request' - } - } - }; - // Remove file from context but keep in storage (close, don't delete) + const contextFileId = record.id; removeFiles([contextFileId], false); // Remove from context selections @@ -416,24 +315,6 @@ const FileEditor = ({ } }, [activeStirlingFileStubs, setSelectedFiles, navActions.setWorkbench]); - const handleMergeFromHere = useCallback((fileId: FileId) => { - const startIndex = activeStirlingFileStubs.findIndex(r => r.id === fileId); - if (startIndex === -1) return; - - const recordsToMerge = activeStirlingFileStubs.slice(startIndex); - const filesToMerge = recordsToMerge.map(r => selectors.getFile(r.id)).filter(Boolean) as StirlingFile[]; - if (onMergeFiles) { - onMergeFiles(filesToMerge); - } - }, [activeStirlingFileStubs, selectors, onMergeFiles]); - - const handleSplitFile = useCallback((fileId: FileId) => { - const file = selectors.getFile(fileId); - if (file && onOpenPageEditor) { - onOpenPageEditor(file); - } - }, [selectors, onOpenPageEditor]); - const handleLoadFromStorage = useCallback(async (selectedFiles: File[]) => { if (selectedFiles.length === 0) return; diff --git a/frontend/src/components/fileEditor/FileEditorThumbnail.tsx b/frontend/src/components/fileEditor/FileEditorThumbnail.tsx index bfeb404c5..7e7370785 100644 --- a/frontend/src/components/fileEditor/FileEditorThumbnail.tsx +++ b/frontend/src/components/fileEditor/FileEditorThumbnail.tsx @@ -44,7 +44,6 @@ const FileEditorThumbnail = ({ selectedFiles, onToggleFile, onDeleteFile, - onViewFile, onSetStatus, onReorderFiles, onDownloadFile, diff --git a/frontend/src/components/fileManager/FileDetails.tsx b/frontend/src/components/fileManager/FileDetails.tsx index dcd460644..b1e7e93e3 100644 --- a/frontend/src/components/fileManager/FileDetails.tsx +++ b/frontend/src/components/fileManager/FileDetails.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState } from 'react'; import { Stack, Button, Box } from '@mantine/core'; import { useTranslation } from 'react-i18next'; import { useIndexedDBThumbnail } from '../../hooks/useIndexedDBThumbnail'; @@ -11,27 +11,26 @@ interface FileDetailsProps { compact?: boolean; } -const FileDetails: React.FC = ({ +const FileDetails: React.FC = ({ compact = false }) => { const { selectedFiles, onOpenFiles, modalHeight } = useFileManagerContext(); const { t } = useTranslation(); const [currentFileIndex, setCurrentFileIndex] = useState(0); const [isAnimating, setIsAnimating] = useState(false); - + // Get the currently displayed file const currentFile = selectedFiles.length > 0 ? selectedFiles[currentFileIndex] : null; const hasSelection = selectedFiles.length > 0; - const hasMultipleFiles = selectedFiles.length > 1; // Use IndexedDB hook for the current file const { thumbnail: currentThumbnail } = useIndexedDBThumbnail(currentFile); - + // Get thumbnail for current file const getCurrentThumbnail = () => { return currentThumbnail; }; - + const handlePrevious = () => { if (isAnimating) return; setIsAnimating(true); @@ -40,7 +39,7 @@ const FileDetails: React.FC = ({ setIsAnimating(false); }, 150); }; - + const handleNext = () => { if (isAnimating) return; setIsAnimating(true); @@ -49,14 +48,14 @@ const FileDetails: React.FC = ({ setIsAnimating(false); }, 150); }; - + // Reset index when selection changes React.useEffect(() => { if (currentFileIndex >= selectedFiles.length) { setCurrentFileIndex(0); } }, [selectedFiles.length, currentFileIndex]); - + if (compact) { return ( = ({ onNext={handleNext} /> - + {/* Section 2: File Details */} - - diff --git a/frontend/src/components/tools/changePermissions/ChangePermissionsSettings.tsx b/frontend/src/components/tools/changePermissions/ChangePermissionsSettings.tsx index 1090bd11f..071e27cfd 100644 --- a/frontend/src/components/tools/changePermissions/ChangePermissionsSettings.tsx +++ b/frontend/src/components/tools/changePermissions/ChangePermissionsSettings.tsx @@ -1,4 +1,4 @@ -import { Stack, Text, Checkbox } from "@mantine/core"; +import { Stack, Checkbox } from "@mantine/core"; import { useTranslation } from "react-i18next"; import { ChangePermissionsParameters } from "../../../hooks/tools/changePermissions/useChangePermissionsParameters"; diff --git a/frontend/src/components/tools/ocr/OCRSettings.tsx b/frontend/src/components/tools/ocr/OCRSettings.tsx index b4d9aa766..6009888b9 100644 --- a/frontend/src/components/tools/ocr/OCRSettings.tsx +++ b/frontend/src/components/tools/ocr/OCRSettings.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Stack, Select, Text, Divider } from '@mantine/core'; +import { Stack, Select, Divider } from '@mantine/core'; import { useTranslation } from 'react-i18next'; import LanguagePicker from './LanguagePicker'; import { OCRParameters } from '../../../hooks/tools/ocr/useOCRParameters'; diff --git a/frontend/src/components/tools/removeCertificateSign/RemoveCertificateSignSettings.tsx b/frontend/src/components/tools/removeCertificateSign/RemoveCertificateSignSettings.tsx index f34e3f2e6..cb1f480d3 100644 --- a/frontend/src/components/tools/removeCertificateSign/RemoveCertificateSignSettings.tsx +++ b/frontend/src/components/tools/removeCertificateSign/RemoveCertificateSignSettings.tsx @@ -8,11 +8,7 @@ interface RemoveCertificateSignSettingsProps { disabled?: boolean; } -const RemoveCertificateSignSettings: React.FC = ({ - parameters, - onParameterChange, // Unused - kept for interface consistency and future extensibility - disabled = false -}) => { +const RemoveCertificateSignSettings: React.FC = (_) => { const { t } = useTranslation(); return ( @@ -24,4 +20,4 @@ const RemoveCertificateSignSettings: React.FC = ({ - parameters, - onParameterChange, - disabled = false -}) => { +const RepairSettings: React.FC = (_) => { const { t } = useTranslation(); return ( @@ -24,4 +20,4 @@ const RepairSettings: React.FC = ({ ); }; -export default RepairSettings; \ No newline at end of file +export default RepairSettings; diff --git a/frontend/src/components/tools/shared/FileStatusIndicator.tsx b/frontend/src/components/tools/shared/FileStatusIndicator.tsx index a083d65ef..354613ecb 100644 --- a/frontend/src/components/tools/shared/FileStatusIndicator.tsx +++ b/frontend/src/components/tools/shared/FileStatusIndicator.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; import { Text, Anchor } from "@mantine/core"; import { useTranslation } from "react-i18next"; import FolderIcon from '@mui/icons-material/Folder'; @@ -28,7 +28,7 @@ const FileStatusIndicator = ({ try { const recentFiles = await loadRecentFiles(); setHasRecentFiles(recentFiles.length > 0); - } catch (error) { + } catch { setHasRecentFiles(false); } }; diff --git a/frontend/src/components/tools/shared/ReviewToolStep.tsx b/frontend/src/components/tools/shared/ReviewToolStep.tsx index 364077e4f..6dcb6fc6c 100644 --- a/frontend/src/components/tools/shared/ReviewToolStep.tsx +++ b/frontend/src/components/tools/shared/ReviewToolStep.tsx @@ -1,5 +1,5 @@ -import React, { useEffect, useRef, useState } from "react"; -import { Button, Group, Stack } from "@mantine/core"; +import React, { useEffect, useRef } from "react"; +import { Button, Stack } from "@mantine/core"; import { useTranslation } from "react-i18next"; import DownloadIcon from "@mui/icons-material/Download"; import UndoIcon from "@mui/icons-material/Undo"; diff --git a/frontend/src/components/tools/shared/ToolStep.tsx b/frontend/src/components/tools/shared/ToolStep.tsx index 4201fd82d..2fa8eb2da 100644 --- a/frontend/src/components/tools/shared/ToolStep.tsx +++ b/frontend/src/components/tools/shared/ToolStep.tsx @@ -1,5 +1,5 @@ -import React, { createContext, useContext, useMemo, useRef } from 'react'; -import { Text, Stack, Box, Flex, Divider } from '@mantine/core'; +import React, { createContext, useContext, useMemo } from 'react'; +import { Text, Stack, Flex, Divider } from '@mantine/core'; import LocalIcon from '../../shared/LocalIcon'; import { Tooltip } from '../../shared/Tooltip'; import { TooltipTip } from '../../../types/tips'; diff --git a/frontend/src/components/tools/shared/createToolFlow.tsx b/frontend/src/components/tools/shared/createToolFlow.tsx index 83057c13a..84051f426 100644 --- a/frontend/src/components/tools/shared/createToolFlow.tsx +++ b/frontend/src/components/tools/shared/createToolFlow.tsx @@ -81,7 +81,7 @@ export function createToolFlow(config: ToolFlowConfig) { })} {/* Middle Steps */} - {config.steps.map((stepConfig, index) => + {config.steps.map((stepConfig) => steps.create(stepConfig.title, { isVisible: stepConfig.isVisible, isCollapsed: stepConfig.isCollapsed, diff --git a/frontend/src/components/tools/shared/renderToolButtons.tsx b/frontend/src/components/tools/shared/renderToolButtons.tsx index a7535e0e0..4d92d4798 100644 --- a/frontend/src/components/tools/shared/renderToolButtons.tsx +++ b/frontend/src/components/tools/shared/renderToolButtons.tsx @@ -1,5 +1,4 @@ -import React from 'react'; -import { Box, Stack } from '@mantine/core'; +import { Box } from '@mantine/core'; import ToolButton from '../toolPicker/ToolButton'; import SubcategoryHeader from './SubcategoryHeader'; diff --git a/frontend/src/components/tools/singleLargePage/SingleLargePageSettings.tsx b/frontend/src/components/tools/singleLargePage/SingleLargePageSettings.tsx index 87dfef926..11bf7009f 100644 --- a/frontend/src/components/tools/singleLargePage/SingleLargePageSettings.tsx +++ b/frontend/src/components/tools/singleLargePage/SingleLargePageSettings.tsx @@ -8,11 +8,7 @@ interface SingleLargePageSettingsProps { disabled?: boolean; } -const SingleLargePageSettings: React.FC = ({ - parameters, - onParameterChange, - disabled = false -}) => { +const SingleLargePageSettings: React.FC = (_) => { const { t } = useTranslation(); return ( @@ -24,4 +20,4 @@ const SingleLargePageSettings: React.FC = ({ ); }; -export default SingleLargePageSettings; \ No newline at end of file +export default SingleLargePageSettings; diff --git a/frontend/src/components/tools/unlockPdfForms/UnlockPdfFormsSettings.tsx b/frontend/src/components/tools/unlockPdfForms/UnlockPdfFormsSettings.tsx index cc8697d7a..d7ca66f25 100644 --- a/frontend/src/components/tools/unlockPdfForms/UnlockPdfFormsSettings.tsx +++ b/frontend/src/components/tools/unlockPdfForms/UnlockPdfFormsSettings.tsx @@ -8,11 +8,7 @@ interface UnlockPdfFormsSettingsProps { disabled?: boolean; } -const UnlockPdfFormsSettings: React.FC = ({ - parameters, - onParameterChange, // Unused - kept for interface consistency and future extensibility - disabled = false -}) => { +const UnlockPdfFormsSettings: React.FC = (_) => { const { t } = useTranslation(); return ( @@ -24,4 +20,4 @@ const UnlockPdfFormsSettings: React.FC = ({ ); }; -export default UnlockPdfFormsSettings; \ No newline at end of file +export default UnlockPdfFormsSettings; diff --git a/frontend/src/components/viewer/Viewer.tsx b/frontend/src/components/viewer/Viewer.tsx index 53d9e98e2..dfcd5dc7d 100644 --- a/frontend/src/components/viewer/Viewer.tsx +++ b/frontend/src/components/viewer/Viewer.tsx @@ -1,19 +1,17 @@ import React, { useEffect, useState, useRef, useCallback } from "react"; -import { Paper, Stack, Text, ScrollArea, Loader, Center, Button, Group, NumberInput, useMantineTheme, ActionIcon, Box, Tabs } from "@mantine/core"; +import { Paper, Stack, Text, ScrollArea, Center, Button, Group, NumberInput, useMantineTheme, ActionIcon, Box, Tabs } from "@mantine/core"; import { useTranslation } from "react-i18next"; import { pdfWorkerManager } from "../../services/pdfWorkerManager"; import ArrowBackIosNewIcon from "@mui/icons-material/ArrowBackIosNew"; import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos"; import FirstPageIcon from "@mui/icons-material/FirstPage"; import LastPageIcon from "@mui/icons-material/LastPage"; -import ViewSidebarIcon from "@mui/icons-material/ViewSidebar"; import ViewWeekIcon from "@mui/icons-material/ViewWeek"; // for dual page (book) import DescriptionIcon from "@mui/icons-material/Description"; // for single page import CloseIcon from "@mui/icons-material/Close"; -import { useLocalStorage } from "@mantine/hooks"; import { fileStorage } from "../../services/fileStorage"; import SkeletonLoader from '../shared/SkeletonLoader'; -import { useFileState, useFileActions, useCurrentFile } from "../../contexts/FileContext"; +import { useFileState } from "../../contexts/FileContext"; import { useFileWithUrl } from "../../hooks/useFileWithUrl"; import { isFileObject } from "../../types/fileContext"; import { FileId } from "../../types/file"; @@ -142,8 +140,6 @@ export interface ViewerProps { } const Viewer = ({ - sidebarsVisible, - setSidebarsVisible, onClose, previewFile, }: ViewerProps) => { @@ -152,13 +148,7 @@ const Viewer = ({ // Get current file from FileContext const { selectors } = useFileState(); - const { actions } = useFileActions(); - const currentFile = useCurrentFile(); - const getCurrentFile = () => currentFile.file; - const getCurrentProcessedFile = () => currentFile.record?.processedFile || undefined; - const clearAllFiles = actions.clearAllFiles; - const addFiles = actions.addFiles; const activeFiles = selectors.getFiles(); // Tab management for multiple files @@ -406,7 +396,7 @@ const Viewer = ({ // Start progressive preloading after a short delay setTimeout(() => startProgressivePreload(), 100); } - } catch (error) { + } catch { if (!cancelled) { setPageImages([]); setNumPages(0); diff --git a/frontend/src/contexts/FileContext.tsx b/frontend/src/contexts/FileContext.tsx index 80735de58..3c75b2080 100644 --- a/frontend/src/contexts/FileContext.tsx +++ b/frontend/src/contexts/FileContext.tsx @@ -39,7 +39,6 @@ const DEBUG = process.env.NODE_ENV === 'development'; // Inner provider component that has access to IndexedDB function FileContextInner({ children, - enableUrlSync = true, enablePersistence = true }: FileContextProviderProps) { const [state, dispatch] = useReducer(fileContextReducer, initialFileContextState); @@ -128,20 +127,9 @@ function FileContextInner({ }, [indexedDB]); const undoConsumeFilesWrapper = useCallback(async (inputFiles: File[], inputStirlingFileStubs: StirlingFileStub[], outputFileIds: FileId[]): Promise => { - return undoConsumeFiles(inputFiles, inputStirlingFileStubs, outputFileIds, stateRef, filesRef, dispatch, indexedDB); + return undoConsumeFiles(inputFiles, inputStirlingFileStubs, outputFileIds, filesRef, dispatch, indexedDB); }, [indexedDB]); - // Helper to find FileId from File object - const findFileId = useCallback((file: File): FileId | undefined => { - return (Object.keys(stateRef.current.files.byId) as FileId[]).find(id => { - const storedFile = filesRef.current.get(id); - return storedFile && - storedFile.name === file.name && - storedFile.size === file.size && - storedFile.lastModified === file.lastModified; - }); - }, []); - // File pinning functions - use StirlingFile directly const pinFileWrapper = useCallback((file: StirlingFile) => { baseActions.pinFile(file.fileId); diff --git a/frontend/src/contexts/FileManagerContext.tsx b/frontend/src/contexts/FileManagerContext.tsx index abbdfcfbd..5a609e63e 100644 --- a/frontend/src/contexts/FileManagerContext.tsx +++ b/frontend/src/contexts/FileManagerContext.tsx @@ -1,6 +1,6 @@ import React, { createContext, useContext, useState, useRef, useCallback, useEffect, useMemo } from 'react'; import { FileMetadata } from '../types/file'; -import { StoredFile, fileStorage } from '../services/fileStorage'; +import { fileStorage } from '../services/fileStorage'; import { downloadFiles } from '../utils/downloadUtils'; import { FileId } from '../types/file'; diff --git a/frontend/src/contexts/IndexedDBContext.tsx b/frontend/src/contexts/IndexedDBContext.tsx index dfd2ac5f2..b6a0b6797 100644 --- a/frontend/src/contexts/IndexedDBContext.tsx +++ b/frontend/src/contexts/IndexedDBContext.tsx @@ -6,7 +6,7 @@ import React, { createContext, useContext, useCallback, useRef } from 'react'; const DEBUG = process.env.NODE_ENV === 'development'; -import { fileStorage, StoredFile } from '../services/fileStorage'; +import { fileStorage } from '../services/fileStorage'; import { FileId } from '../types/file'; import { FileMetadata } from '../types/file'; import { generateThumbnailForFile } from '../utils/thumbnailUtils'; @@ -61,7 +61,7 @@ export function IndexedDBProvider({ children }: IndexedDBProviderProps) { const thumbnail = existingThumbnail || await generateThumbnailForFile(file); // Store in IndexedDB - const storedFile = await fileStorage.storeFile(file, fileId, thumbnail); + await fileStorage.storeFile(file, fileId, thumbnail); // Cache the file object for immediate reuse fileCache.current.set(fileId, { file, lastAccessed: Date.now() }); diff --git a/frontend/src/contexts/NavigationContext.tsx b/frontend/src/contexts/NavigationContext.tsx index 3b65f01d1..f9e8ba9a5 100644 --- a/frontend/src/contexts/NavigationContext.tsx +++ b/frontend/src/contexts/NavigationContext.tsx @@ -103,7 +103,7 @@ const NavigationActionsContext = createContext = ({ children, enableUrlSync = true }) => { +}> = ({ children }) => { const [state, dispatch] = useReducer(navigationReducer, initialState); const toolRegistry = useFlatToolRegistry(); diff --git a/frontend/src/contexts/ToolWorkflowContext.tsx b/frontend/src/contexts/ToolWorkflowContext.tsx index b16261bc9..4473dc020 100644 --- a/frontend/src/contexts/ToolWorkflowContext.tsx +++ b/frontend/src/contexts/ToolWorkflowContext.tsx @@ -89,6 +89,7 @@ interface ToolWorkflowContextValue extends ToolWorkflowState { clearToolSelection: () => void; // Tool Reset Actions + toolResetFunctions: Record void>; registerToolReset: (toolId: string, resetFunction: () => void) => void; resetTool: (toolId: string) => void; @@ -258,6 +259,7 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) { clearToolSelection: () => actions.setSelectedTool(null), // Tool Reset Actions + toolResetFunctions, registerToolReset, resetTool, diff --git a/frontend/src/contexts/file/fileActions.ts b/frontend/src/contexts/file/fileActions.ts index 3901fabee..552ce0b1f 100644 --- a/frontend/src/contexts/file/fileActions.ts +++ b/frontend/src/contexts/file/fileActions.ts @@ -13,8 +13,7 @@ import { import { FileId, FileMetadata } from '../../types/file'; import { generateThumbnailWithMetadata } from '../../utils/thumbnailUtils'; import { FileLifecycleManager } from './lifecycle'; -import { fileProcessingService } from '../../services/fileProcessingService'; -import { buildQuickKeySet, buildQuickKeySetFromMetadata } from './fileSelectors'; +import { buildQuickKeySet } from './fileSelectors'; const DEBUG = process.env.NODE_ENV === 'development'; @@ -407,7 +406,6 @@ export async function consumeFiles( }); if (DEBUG) console.log(`📄 consumeFiles: Successfully consumed files - removed ${inputFileIds.length} inputs, added ${outputStirlingFileStubs.length} outputs`); - // Return the output file IDs for undo tracking return outputStirlingFileStubs.map(({ fileId }) => fileId); } @@ -467,7 +465,6 @@ export async function undoConsumeFiles( inputFiles: File[], inputStirlingFileStubs: StirlingFileStub[], outputFileIds: FileId[], - stateRef: React.MutableRefObject, filesRef: React.MutableRefObject>, dispatch: React.Dispatch, indexedDB?: { saveFile: (file: File, fileId: FileId, existingThumbnail?: string) => Promise; deleteFile: (fileId: FileId) => Promise } | null @@ -507,7 +504,6 @@ export async function undoConsumeFiles( }); if (DEBUG) console.log(`📄 undoConsumeFiles: Successfully undone consume operation - restored ${inputStirlingFileStubs.length} inputs, removed ${outputFileIds.length} outputs`); - } catch (error) { // Rollback filesRef to previous state if (DEBUG) console.error('📄 undoConsumeFiles: Error during undo, rolling back filesRef', error); diff --git a/frontend/src/contexts/file/fileHooks.ts b/frontend/src/contexts/file/fileHooks.ts index 1907c8fb8..7d7f9b23e 100644 --- a/frontend/src/contexts/file/fileHooks.ts +++ b/frontend/src/contexts/file/fileHooks.ts @@ -166,9 +166,9 @@ export function useFileContext() { addFiles: actions.addFiles, consumeFiles: actions.consumeFiles, undoConsumeFiles: actions.undoConsumeFiles, - recordOperation: (fileId: FileId, operation: any) => {}, // Operation tracking not implemented - markOperationApplied: (fileId: FileId, operationId: string) => {}, // Operation tracking not implemented - markOperationFailed: (fileId: FileId, operationId: string, error: string) => {}, // Operation tracking not implemented + recordOperation: (_fileId: FileId, _operation: any) => {}, // Operation tracking not implemented + markOperationApplied: (_fileId: FileId, _operationId: string) => {}, // Operation tracking not implemented + markOperationFailed: (_fileId: FileId, _operationId: string, _error: string) => {}, // Operation tracking not implemented // File ID lookup findFileId: (file: File) => { diff --git a/frontend/src/contexts/file/lifecycle.ts b/frontend/src/contexts/file/lifecycle.ts index 901a16943..c65fec127 100644 --- a/frontend/src/contexts/file/lifecycle.ts +++ b/frontend/src/contexts/file/lifecycle.ts @@ -50,7 +50,7 @@ export class FileLifecycleManager { this.blobUrls.forEach(url => { try { URL.revokeObjectURL(url); - } catch (error) { + } catch { // Ignore revocation errors } }); @@ -134,7 +134,7 @@ export class FileLifecycleManager { if (record.thumbnailUrl && record.thumbnailUrl.startsWith('blob:')) { try { URL.revokeObjectURL(record.thumbnailUrl); - } catch (error) { + } catch { // Ignore revocation errors } } @@ -142,18 +142,18 @@ export class FileLifecycleManager { if (record.blobUrl && record.blobUrl.startsWith('blob:')) { try { URL.revokeObjectURL(record.blobUrl); - } catch (error) { + } catch { // Ignore revocation errors } } // Clean up processed file thumbnails if (record.processedFile?.pages) { - record.processedFile.pages.forEach((page: ProcessedFilePage, index: number) => { + record.processedFile.pages.forEach((page: ProcessedFilePage) => { if (page.thumbnail && page.thumbnail.startsWith('blob:')) { try { URL.revokeObjectURL(page.thumbnail); - } catch (error) { + } catch { // Ignore revocation errors } } diff --git a/frontend/src/hooks/tools/addPassword/useAddPasswordOperation.test.ts b/frontend/src/hooks/tools/addPassword/useAddPasswordOperation.test.ts index 93b55bc26..53f6f7854 100644 --- a/frontend/src/hooks/tools/addPassword/useAddPasswordOperation.test.ts +++ b/frontend/src/hooks/tools/addPassword/useAddPasswordOperation.test.ts @@ -1,7 +1,7 @@ -import { describe, expect, test, vi, beforeEach, MockedFunction } from 'vitest'; +import { describe, expect, test, vi, beforeEach } from 'vitest'; import { renderHook } from '@testing-library/react'; import { useAddPasswordOperation } from './useAddPasswordOperation'; -import type { AddPasswordFullParameters, AddPasswordParameters } from './useAddPasswordParameters'; +import type { AddPasswordFullParameters } from './useAddPasswordParameters'; // Mock the useToolOperation hook vi.mock('../shared/useToolOperation', async () => { diff --git a/frontend/src/hooks/tools/automate/useAutomateOperation.ts b/frontend/src/hooks/tools/automate/useAutomateOperation.ts index 112bafbd2..3e51a615f 100644 --- a/frontend/src/hooks/tools/automate/useAutomateOperation.ts +++ b/frontend/src/hooks/tools/automate/useAutomateOperation.ts @@ -3,7 +3,6 @@ import { useCallback } from 'react'; import { executeAutomationSequence } from '../../../utils/automationExecutor'; import { useFlatToolRegistry } from '../../../data/useTranslatedToolRegistry'; import { AutomateParameters } from '../../../types/automation'; -import { AUTOMATION_CONSTANTS } from '../../../constants/automation'; export function useAutomateOperation() { const toolRegistry = useFlatToolRegistry(); diff --git a/frontend/src/hooks/tools/automate/useSavedAutomations.ts b/frontend/src/hooks/tools/automate/useSavedAutomations.ts index 410ad3217..8ad7a80b6 100644 --- a/frontend/src/hooks/tools/automate/useSavedAutomations.ts +++ b/frontend/src/hooks/tools/automate/useSavedAutomations.ts @@ -44,9 +44,9 @@ export function useSavedAutomations() { const copyFromSuggested = useCallback(async (suggestedAutomation: SuggestedAutomation) => { try { const { automationStorage } = await import('../../../services/automationStorage'); - + // Map suggested automation icons to MUI icon keys - const getIconKey = (suggestedIcon: {id: string}): string => { + const getIconKey = (_suggestedIcon: {id: string}): string => { // Check the automation ID or name to determine the appropriate icon switch (suggestedAutomation.id) { case 'secure-pdf-ingestion': @@ -60,7 +60,7 @@ export function useSavedAutomations() { return 'SettingsIcon'; // Default fallback } }; - + // Convert suggested automation to saved automation format const savedAutomation = { name: suggestedAutomation.name, @@ -68,7 +68,7 @@ export function useSavedAutomations() { icon: getIconKey(suggestedAutomation.icon), operations: suggestedAutomation.operations }; - + await automationStorage.saveAutomation(savedAutomation); // Refresh the list after saving refreshAutomations(); @@ -91,4 +91,4 @@ export function useSavedAutomations() { deleteAutomation, copyFromSuggested }; -} \ No newline at end of file +} diff --git a/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts b/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts index 7250726f1..970c14375 100644 --- a/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts +++ b/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts @@ -6,7 +6,6 @@ import { SuggestedAutomation } from '../../../types/automation'; // Create icon components const CompressIcon = () => React.createElement(LocalIcon, { icon: 'compress', width: '1.5rem', height: '1.5rem' }); -const TextFieldsIcon = () => React.createElement(LocalIcon, { icon: 'text-fields', width: '1.5rem', height: '1.5rem' }); const SecurityIcon = () => React.createElement(LocalIcon, { icon: 'security', width: '1.5rem', height: '1.5rem' }); const StarIcon = () => React.createElement(LocalIcon, { icon: 'star', width: '1.5rem', height: '1.5rem' }); diff --git a/frontend/src/hooks/tools/compress/useCompressOperation.ts b/frontend/src/hooks/tools/compress/useCompressOperation.ts index 8327dd698..c7080048f 100644 --- a/frontend/src/hooks/tools/compress/useCompressOperation.ts +++ b/frontend/src/hooks/tools/compress/useCompressOperation.ts @@ -1,5 +1,5 @@ import { useTranslation } from 'react-i18next'; -import { useToolOperation, ToolOperationConfig, ToolType } from '../shared/useToolOperation'; +import { useToolOperation, ToolType } from '../shared/useToolOperation'; import { createStandardErrorHandler } from '../../../utils/toolErrorHandler'; import { CompressParameters, defaultParameters } from './useCompressParameters'; diff --git a/frontend/src/hooks/tools/convert/useConvertOperation.ts b/frontend/src/hooks/tools/convert/useConvertOperation.ts index 6de20282f..56dcbb204 100644 --- a/frontend/src/hooks/tools/convert/useConvertOperation.ts +++ b/frontend/src/hooks/tools/convert/useConvertOperation.ts @@ -2,9 +2,8 @@ import { useCallback } from 'react'; import axios from 'axios'; import { useTranslation } from 'react-i18next'; import { ConvertParameters, defaultParameters } from './useConvertParameters'; -import { detectFileExtension } from '../../../utils/fileUtils'; import { createFileFromApiResponse } from '../../../utils/fileResponseUtils'; -import { useToolOperation, ToolOperationConfig, ToolType } from '../shared/useToolOperation'; +import { useToolOperation, ToolType } from '../shared/useToolOperation'; import { getEndpointUrl, isImageFormat, isWebFormat } from '../../../utils/convertUtils'; // Static function that can be used by both the hook and automation executor diff --git a/frontend/src/hooks/tools/convert/useConvertParameters.ts b/frontend/src/hooks/tools/convert/useConvertParameters.ts index 2a7b7d523..74a1bd3a1 100644 --- a/frontend/src/hooks/tools/convert/useConvertParameters.ts +++ b/frontend/src/hooks/tools/convert/useConvertParameters.ts @@ -2,7 +2,6 @@ import { COLOR_TYPES, OUTPUT_OPTIONS, FIT_OPTIONS, - TO_FORMAT_OPTIONS, CONVERSION_MATRIX, type ColorType, type OutputOption, @@ -127,7 +126,7 @@ export const useConvertParameters = (): ConvertParametersHook => { endpointName: getEndpointName, validateFn: validateParameters, }), []); - + const baseHook = useBaseParameters(config); const getEndpoint = () => { @@ -166,7 +165,7 @@ export const useConvertParameters = (): ConvertParametersHook => { if (prev.isSmartDetection === false && prev.smartDetectionType === 'none') { return prev; // No change needed } - + return { ...prev, isSmartDetection: false, @@ -290,13 +289,13 @@ export const useConvertParameters = (): ConvertParametersHook => { // All files are images - use image-to-pdf conversion baseHook.setParameters(prev => { // Only update if something actually changed - if (prev.isSmartDetection === true && - prev.smartDetectionType === 'images' && - prev.fromExtension === 'image' && + if (prev.isSmartDetection === true && + prev.smartDetectionType === 'images' && + prev.fromExtension === 'image' && prev.toExtension === 'pdf') { return prev; // No change needed } - + return { ...prev, isSmartDetection: true, @@ -309,13 +308,13 @@ export const useConvertParameters = (): ConvertParametersHook => { // All files are web files - use html-to-pdf conversion baseHook.setParameters(prev => { // Only update if something actually changed - if (prev.isSmartDetection === true && - prev.smartDetectionType === 'web' && - prev.fromExtension === 'html' && + if (prev.isSmartDetection === true && + prev.smartDetectionType === 'web' && + prev.fromExtension === 'html' && prev.toExtension === 'pdf') { return prev; // No change needed } - + return { ...prev, isSmartDetection: true, @@ -328,13 +327,13 @@ export const useConvertParameters = (): ConvertParametersHook => { // Mixed non-image types - use file-to-pdf conversion baseHook.setParameters(prev => { // Only update if something actually changed - if (prev.isSmartDetection === true && - prev.smartDetectionType === 'mixed' && - prev.fromExtension === 'any' && + if (prev.isSmartDetection === true && + prev.smartDetectionType === 'mixed' && + prev.fromExtension === 'any' && prev.toExtension === 'pdf') { return prev; // No change needed } - + return { ...prev, isSmartDetection: true, diff --git a/frontend/src/hooks/tools/convert/useConvertParametersAutoDetection.test.ts b/frontend/src/hooks/tools/convert/useConvertParametersAutoDetection.test.ts index 798fdc5b4..e208d4479 100644 --- a/frontend/src/hooks/tools/convert/useConvertParametersAutoDetection.test.ts +++ b/frontend/src/hooks/tools/convert/useConvertParametersAutoDetection.test.ts @@ -4,7 +4,7 @@ */ import { describe, test, expect } from 'vitest'; -import { renderHook, act, waitFor } from '@testing-library/react'; +import { renderHook, act } from '@testing-library/react'; import { useConvertParameters } from './useConvertParameters'; describe('useConvertParameters - Auto Detection & Smart Conversion', () => { diff --git a/frontend/src/hooks/tools/removeCertificateSign/useRemoveCertificateSignOperation.ts b/frontend/src/hooks/tools/removeCertificateSign/useRemoveCertificateSignOperation.ts index 106150281..91eed974a 100644 --- a/frontend/src/hooks/tools/removeCertificateSign/useRemoveCertificateSignOperation.ts +++ b/frontend/src/hooks/tools/removeCertificateSign/useRemoveCertificateSignOperation.ts @@ -4,7 +4,7 @@ import { createStandardErrorHandler } from '../../../utils/toolErrorHandler'; import { RemoveCertificateSignParameters, defaultParameters } from './useRemoveCertificateSignParameters'; // Static function that can be used by both the hook and automation executor -export const buildRemoveCertificateSignFormData = (parameters: RemoveCertificateSignParameters, file: File): FormData => { +export const buildRemoveCertificateSignFormData = (_parameters: RemoveCertificateSignParameters, file: File): FormData => { const formData = new FormData(); formData.append("fileInput", file); return formData; diff --git a/frontend/src/hooks/tools/repair/useRepairOperation.ts b/frontend/src/hooks/tools/repair/useRepairOperation.ts index 44fcc9b70..d195ee881 100644 --- a/frontend/src/hooks/tools/repair/useRepairOperation.ts +++ b/frontend/src/hooks/tools/repair/useRepairOperation.ts @@ -4,7 +4,7 @@ import { createStandardErrorHandler } from '../../../utils/toolErrorHandler'; import { RepairParameters, defaultParameters } from './useRepairParameters'; // Static function that can be used by both the hook and automation executor -export const buildRepairFormData = (parameters: RepairParameters, file: File): FormData => { +export const buildRepairFormData = (_parameters: RepairParameters, file: File): FormData => { const formData = new FormData(); formData.append("fileInput", file); return formData; diff --git a/frontend/src/hooks/tools/shared/useToolOperation.ts b/frontend/src/hooks/tools/shared/useToolOperation.ts index 7bfcc3d32..263217e42 100644 --- a/frontend/src/hooks/tools/shared/useToolOperation.ts +++ b/frontend/src/hooks/tools/shared/useToolOperation.ts @@ -128,7 +128,7 @@ export const useToolOperation = ( config: ToolOperationConfig ): ToolOperationHook => { const { t } = useTranslation(); - const { addFiles, consumeFiles, undoConsumeFiles, actions: fileActions, selectors } = useFileContext(); + const { addFiles, consumeFiles, undoConsumeFiles, selectors } = useFileContext(); // Composed hooks const { state, actions } = useToolState(); @@ -243,7 +243,7 @@ export const useToolOperation = ( // Replace input files with processed files (consumeFiles handles pinning) const inputFileIds: FileId[] = []; const inputStirlingFileStubs: StirlingFileStub[] = []; - + // Build parallel arrays of IDs and records for undo tracking for (const file of validFiles) { const fileId = file.fileId; @@ -320,7 +320,7 @@ export const useToolOperation = ( try { // Undo the consume operation await undoConsumeFiles(inputFiles, inputStirlingFileStubs, outputFileIds); - + // Clear results and operation tracking resetResults(); diff --git a/frontend/src/hooks/tools/singleLargePage/useSingleLargePageOperation.ts b/frontend/src/hooks/tools/singleLargePage/useSingleLargePageOperation.ts index ef304fa09..35eaec079 100644 --- a/frontend/src/hooks/tools/singleLargePage/useSingleLargePageOperation.ts +++ b/frontend/src/hooks/tools/singleLargePage/useSingleLargePageOperation.ts @@ -4,7 +4,7 @@ import { createStandardErrorHandler } from '../../../utils/toolErrorHandler'; import { SingleLargePageParameters, defaultParameters } from './useSingleLargePageParameters'; // Static function that can be used by both the hook and automation executor -export const buildSingleLargePageFormData = (parameters: SingleLargePageParameters, file: File): FormData => { +export const buildSingleLargePageFormData = (_parameters: SingleLargePageParameters, file: File): FormData => { const formData = new FormData(); formData.append("fileInput", file); return formData; diff --git a/frontend/src/hooks/tools/split/useSplitOperation.ts b/frontend/src/hooks/tools/split/useSplitOperation.ts index 394fb694d..b18b7c1f5 100644 --- a/frontend/src/hooks/tools/split/useSplitOperation.ts +++ b/frontend/src/hooks/tools/split/useSplitOperation.ts @@ -71,7 +71,7 @@ export const useSplitOperation = () => { // Custom response handler that extracts ZIP files // Can't add to exported config because it requires access to the zip code so must be part of the hook - const responseHandler = useCallback(async (blob: Blob, originalFiles: File[]): Promise => { + const responseHandler = useCallback(async (blob: Blob, _originalFiles: File[]): Promise => { // Split operations return ZIP files with multiple PDF pages return await extractZipFiles(blob); }, [extractZipFiles]); diff --git a/frontend/src/hooks/tools/unlockPdfForms/useUnlockPdfFormsOperation.ts b/frontend/src/hooks/tools/unlockPdfForms/useUnlockPdfFormsOperation.ts index d47800b34..faaeae428 100644 --- a/frontend/src/hooks/tools/unlockPdfForms/useUnlockPdfFormsOperation.ts +++ b/frontend/src/hooks/tools/unlockPdfForms/useUnlockPdfFormsOperation.ts @@ -4,7 +4,7 @@ import { createStandardErrorHandler } from '../../../utils/toolErrorHandler'; import { UnlockPdfFormsParameters, defaultParameters } from './useUnlockPdfFormsParameters'; // Static function that can be used by both the hook and automation executor -export const buildUnlockPdfFormsFormData = (parameters: UnlockPdfFormsParameters, file: File): FormData => { +export const buildUnlockPdfFormsFormData = (_parameters: UnlockPdfFormsParameters, file: File): FormData => { const formData = new FormData(); formData.append("fileInput", file); return formData; diff --git a/frontend/src/hooks/useCookieConsent.ts b/frontend/src/hooks/useCookieConsent.ts index 3eacaa7bd..dd00f4396 100644 --- a/frontend/src/hooks/useCookieConsent.ts +++ b/frontend/src/hooks/useCookieConsent.ts @@ -184,11 +184,6 @@ export const useCookieConsent = ({ analyticsEnabled = false }: CookieConsentConf // Force show after initialization setTimeout(() => { window.CookieConsent.show(); - - // Debug: Check if modal elements exist - const ccMain = document.getElementById('cc-main'); - const consentModal = document.querySelector('.cm-wrapper'); - }, 200); } catch (error) { diff --git a/frontend/src/hooks/useEndpointConfig.ts b/frontend/src/hooks/useEndpointConfig.ts index 5419f3506..7516826ed 100644 --- a/frontend/src/hooks/useEndpointConfig.ts +++ b/frontend/src/hooks/useEndpointConfig.ts @@ -19,17 +19,17 @@ export function useEndpointEnabled(endpoint: string): { setLoading(false); return; } - + try { setLoading(true); setError(null); - + const response = await fetch(`/api/v1/config/endpoint-enabled?endpoint=${encodeURIComponent(endpoint)}`); - + if (!response.ok) { throw new Error(`Failed to check endpoint: ${response.status} ${response.statusText}`); } - + const isEnabled: boolean = await response.json(); setEnabled(isEnabled); } catch (err) { @@ -72,27 +72,27 @@ export function useMultipleEndpointsEnabled(endpoints: string[]): { setLoading(false); return; } - + try { setLoading(true); setError(null); - + // Use batch API for efficiency const endpointsParam = endpoints.join(','); - + const response = await fetch(`/api/v1/config/endpoints-enabled?endpoints=${encodeURIComponent(endpointsParam)}`); - + if (!response.ok) { throw new Error(`Failed to check endpoints: ${response.status} ${response.statusText}`); } - + const statusMap: Record = await response.json(); setEndpointStatus(statusMap); } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred'; setError(errorMessage); console.error('Failed to check multiple endpoints:', err); - + // Fallback: assume all endpoints are disabled on error const fallbackStatus = endpoints.reduce((acc, endpoint) => { acc[endpoint] = false; @@ -105,7 +105,6 @@ export function useMultipleEndpointsEnabled(endpoints: string[]): { }; useEffect(() => { - const endpointsKey = endpoints.join(','); fetchAllEndpointStatuses(); }, [endpoints.join(',')]); // Re-run when endpoints array changes @@ -115,4 +114,4 @@ export function useMultipleEndpointsEnabled(endpoints: string[]): { error, refetch: fetchAllEndpointStatuses, }; -} \ No newline at end of file +} diff --git a/frontend/src/hooks/useEnhancedProcessedFiles.ts b/frontend/src/hooks/useEnhancedProcessedFiles.ts index ebdff4bf5..2f0ab923d 100644 --- a/frontend/src/hooks/useEnhancedProcessedFiles.ts +++ b/frontend/src/hooks/useEnhancedProcessedFiles.ts @@ -49,7 +49,7 @@ export function useEnhancedProcessedFiles( // Process files when activeFiles changes useEffect(() => { console.log('useEnhancedProcessedFiles: activeFiles changed', activeFiles.length, 'files'); - + if (activeFiles.length === 0) { console.log('useEnhancedProcessedFiles: No active files, clearing processed cache'); setProcessedFiles(new Map()); @@ -60,15 +60,15 @@ export function useEnhancedProcessedFiles( const processFiles = async () => { const newProcessedFiles = new Map(); - + for (const file of activeFiles) { // Generate hash for this file const fileHash = await FileHasher.generateHybridHash(file); fileHashMapRef.current.set(file, fileHash); - + // First, check if we have this exact File object cached let existing = processedFiles.get(file); - + // If not found by File object, try to find by hash in case File was recreated if (!existing) { for (const [cachedFile, processed] of processedFiles.entries()) { @@ -79,7 +79,7 @@ export function useEnhancedProcessedFiles( } } } - + if (existing) { newProcessedFiles.set(file, existing); continue; @@ -94,11 +94,11 @@ export function useEnhancedProcessedFiles( console.error(`Failed to start processing for ${file.name}:`, error); } } - + // Only update if the content actually changed const hasChanged = newProcessedFiles.size !== processedFiles.size || Array.from(newProcessedFiles.keys()).some(file => !processedFiles.has(file)); - + if (hasChanged) { setProcessedFiles(newProcessedFiles); } @@ -112,20 +112,20 @@ export function useEnhancedProcessedFiles( const checkForCompletedFiles = async () => { let hasNewFiles = false; const updatedFiles = new Map(processedFiles); - + // Generate file keys for all files first const fileKeyPromises = activeFiles.map(async (file) => ({ file, key: await FileHasher.generateHybridHash(file) })); - + const fileKeyPairs = await Promise.all(fileKeyPromises); - + for (const { file, key } of fileKeyPairs) { // Only check files that don't have processed results yet if (!updatedFiles.has(file)) { const processingState = processingStates.get(key); - + // Check for both processing and recently completed files // This ensures we catch completed files before they're cleaned up if (processingState?.status === 'processing' || processingState?.status === 'completed') { @@ -135,13 +135,13 @@ export function useEnhancedProcessedFiles( updatedFiles.set(file, processed); hasNewFiles = true; } - } catch (error) { + } catch { // Ignore errors in completion check } } } } - + if (hasNewFiles) { setProcessedFiles(updatedFiles); } @@ -158,11 +158,11 @@ export function useEnhancedProcessedFiles( const currentFiles = new Set(activeFiles); const previousFiles = Array.from(processedFiles.keys()); const removedFiles = previousFiles.filter(file => !currentFiles.has(file)); - + if (removedFiles.length > 0) { // Clean up processing service cache enhancedPDFProcessingService.cleanup(removedFiles); - + // Update local state setProcessedFiles(prev => { const updated = new Map(); @@ -179,10 +179,10 @@ export function useEnhancedProcessedFiles( // Calculate derived state const isProcessing = processingStates.size > 0; const hasProcessingErrors = Array.from(processingStates.values()).some(state => state.status === 'error'); - + // Calculate overall progress const processingProgress = calculateProcessingProgress(processingStates); - + // Get cache stats and metrics const cacheStats = enhancedPDFProcessingService.getCacheStats(); const metrics = enhancedPDFProcessingService.getMetrics(); @@ -192,7 +192,7 @@ export function useEnhancedProcessedFiles( cancelProcessing: (fileKey: string) => { enhancedPDFProcessingService.cancelProcessing(fileKey); }, - + retryProcessing: async (file: File) => { try { await enhancedPDFProcessingService.processFile(file, config); @@ -200,7 +200,7 @@ export function useEnhancedProcessedFiles( console.error(`Failed to retry processing for ${file.name}:`, error); } }, - + clearCache: () => { enhancedPDFProcessingService.clearAll(); } @@ -279,7 +279,7 @@ export function useEnhancedProcessedFile( }; } { const result = useEnhancedProcessedFiles(file ? [file] : [], config); - + const processedFile = file ? result.processedFiles.get(file) || null : null; // Note: This is async but we can't await in hook return - consider refactoring if needed const fileKey = file ? '' : ''; @@ -309,4 +309,4 @@ export function useEnhancedProcessedFile( canRetry, actions }; -} \ No newline at end of file +} diff --git a/frontend/src/hooks/useFileManager.ts b/frontend/src/hooks/useFileManager.ts index 8df1e2754..f3dedf5e4 100644 --- a/frontend/src/hooks/useFileManager.ts +++ b/frontend/src/hooks/useFileManager.ts @@ -1,7 +1,6 @@ import { useState, useCallback } from 'react'; import { useIndexedDB } from '../contexts/IndexedDBContext'; import { FileMetadata } from '../types/file'; -import { generateThumbnailForFile } from '../utils/thumbnailUtils'; import { FileId } from '../types/fileContext'; export const useFileManager = () => { diff --git a/frontend/src/hooks/useIndexedDBThumbnail.ts b/frontend/src/hooks/useIndexedDBThumbnail.ts index a6251db3c..cd497561b 100644 --- a/frontend/src/hooks/useIndexedDBThumbnail.ts +++ b/frontend/src/hooks/useIndexedDBThumbnail.ts @@ -4,20 +4,6 @@ import { useIndexedDB } from "../contexts/IndexedDBContext"; import { generateThumbnailForFile } from "../utils/thumbnailUtils"; import { FileId } from "../types/fileContext"; -/** - * Calculate optimal scale for thumbnail generation - * Ensures high quality while preventing oversized renders - */ -function calculateThumbnailScale(pageViewport: { width: number; height: number }): number { - const maxWidth = 400; // Max thumbnail width - const maxHeight = 600; // Max thumbnail height - - const scaleX = maxWidth / pageViewport.width; - const scaleY = maxHeight / pageViewport.height; - - // Don't upscale, only downscale if needed - return Math.min(scaleX, scaleY, 1.0); -} /** * Hook for IndexedDB-aware thumbnail loading @@ -67,7 +53,7 @@ export function useIndexedDBThumbnail(file: FileMetadata | undefined | null): { const thumbnail = await generateThumbnailForFile(fileObject); if (!cancelled) { setThumb(thumbnail); - + // Save thumbnail to IndexedDB for persistence if (file.id && indexedDB && thumbnail) { try { diff --git a/frontend/src/hooks/usePdfSignatureDetection.ts b/frontend/src/hooks/usePdfSignatureDetection.ts index b14c1a637..77b7f79ac 100644 --- a/frontend/src/hooks/usePdfSignatureDetection.ts +++ b/frontend/src/hooks/usePdfSignatureDetection.ts @@ -1,5 +1,4 @@ import { useState, useEffect } from 'react'; -import * as pdfjsLib from 'pdfjs-dist'; import { pdfWorkerManager } from '../services/pdfWorkerManager'; import { StirlingFile } from '../types/fileContext'; @@ -26,7 +25,7 @@ export const usePdfSignatureDetection = (files: StirlingFile[]): PdfSignatureDet for (const file of files) { const arrayBuffer = await file.arrayBuffer(); - + try { const pdf = await pdfWorkerManager.createDocument(arrayBuffer); @@ -42,7 +41,7 @@ export const usePdfSignatureDetection = (files: StirlingFile[]): PdfSignatureDet if (foundSignature) break; } - + // Clean up PDF document using worker manager pdfWorkerManager.destroyDocument(pdf); } catch (error) { @@ -66,4 +65,4 @@ export const usePdfSignatureDetection = (files: StirlingFile[]): PdfSignatureDet hasDigitalSignatures, isChecking }; -}; \ No newline at end of file +}; diff --git a/frontend/src/hooks/useThumbnailGeneration.ts b/frontend/src/hooks/useThumbnailGeneration.ts index 310634045..6a22fbcc9 100644 --- a/frontend/src/hooks/useThumbnailGeneration.ts +++ b/frontend/src/hooks/useThumbnailGeneration.ts @@ -1,4 +1,4 @@ -import { useCallback, useRef } from 'react'; +import { useCallback } from 'react'; import { thumbnailGenerationService } from '../services/thumbnailGenerationService'; import { createQuickKey } from '../types/fileContext'; import { FileId } from '../types/file'; diff --git a/frontend/src/hooks/useToolManagement.tsx b/frontend/src/hooks/useToolManagement.tsx index f73f458db..3239cbaaa 100644 --- a/frontend/src/hooks/useToolManagement.tsx +++ b/frontend/src/hooks/useToolManagement.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, useMemo, useEffect } from 'react'; +import { useState, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useFlatToolRegistry } from "../data/useTranslatedToolRegistry"; import { getAllEndpoints, type ToolRegistryEntry } from "../data/toolsTaxonomy"; @@ -20,15 +20,6 @@ export const useToolManagement = (): ToolManagementResult => { // Build endpoints list from registry entries with fallback to legacy mapping const baseRegistry = useFlatToolRegistry(); - const registryDerivedEndpoints = useMemo(() => { - const endpointsByTool: Record = {}; - Object.entries(baseRegistry).forEach(([key, entry]) => { - if (entry.endpoints && entry.endpoints.length > 0) { - endpointsByTool[key] = entry.endpoints; - } - }); - return endpointsByTool; - }, [baseRegistry]); const allEndpoints = useMemo(() => getAllEndpoints(baseRegistry), [baseRegistry]); const { endpointStatus, loading: endpointsLoading } = useMultipleEndpointsEnabled(allEndpoints); diff --git a/frontend/src/hooks/useToolParameters.ts b/frontend/src/hooks/useToolParameters.ts index d6eae6d8b..1afd66835 100644 --- a/frontend/src/hooks/useToolParameters.ts +++ b/frontend/src/hooks/useToolParameters.ts @@ -10,8 +10,8 @@ type ToolParameterValues = Record; * Register tool parameters and get current values */ export function useToolParameters( - toolName: string, - parameters: Record + _toolName: string, + _parameters: Record ): [ToolParameterValues, (updates: Partial) => void] { // Return empty values and noop updater @@ -30,9 +30,9 @@ export function useToolParameter( definition: any ): [T, (value: T) => void] { const [allParams, updateParams] = useToolParameters(toolName, { [paramName]: definition }); - + const value = allParams[paramName] as T; - + const setValue = useCallback((newValue: T) => { updateParams({ [paramName]: newValue }); }, [paramName, updateParams]); @@ -48,4 +48,4 @@ export function useGlobalParameters() { const updateParameters = useCallback(() => {}, []); return [currentValues, updateParameters]; -} \ No newline at end of file +} diff --git a/frontend/src/hooks/useTooltipPosition.ts b/frontend/src/hooks/useTooltipPosition.ts index 3651c1d47..51ef2e9c3 100644 --- a/frontend/src/hooks/useTooltipPosition.ts +++ b/frontend/src/hooks/useTooltipPosition.ts @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo } from 'react'; +import { useState, useEffect } from 'react'; import { clamp } from '../utils/genericUtils'; import { getSidebarInfo } from '../utils/sidebarUtils'; import { SidebarRefs, SidebarState } from '../types/sidebar'; @@ -65,10 +65,10 @@ export function useTooltipPosition({ sidebarRefs?: SidebarRefs; sidebarState?: SidebarState; }): PositionState { - const [coords, setCoords] = useState<{ top: number; left: number; arrowOffset: number | null }>({ - top: 0, - left: 0, - arrowOffset: null + const [coords, setCoords] = useState<{ top: number; left: number; arrowOffset: number | null }>({ + top: 0, + left: 0, + arrowOffset: null }); const [positionReady, setPositionReady] = useState(false); @@ -174,4 +174,4 @@ export function useTooltipPosition({ }, [open, sidebarLeft, position, gap, sidebarTooltip]); return { coords, positionReady }; -} \ No newline at end of file +} diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index 961662123..26d190dfa 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -1,4 +1,3 @@ -import React, { useEffect } from "react"; import { useTranslation } from "react-i18next"; import { useToolWorkflow } from "../contexts/ToolWorkflowContext"; import { Group } from "@mantine/core"; @@ -11,7 +10,6 @@ import Workbench from "../components/layout/Workbench"; import QuickAccessBar from "../components/shared/QuickAccessBar"; import RightRail from "../components/shared/RightRail"; import FileManager from "../components/FileManager"; -import Footer from "../components/shared/Footer"; export default function HomePage() { diff --git a/frontend/src/services/enhancedPDFProcessingService.ts b/frontend/src/services/enhancedPDFProcessingService.ts index 2b6b18c8c..bee6e200a 100644 --- a/frontend/src/services/enhancedPDFProcessingService.ts +++ b/frontend/src/services/enhancedPDFProcessingService.ts @@ -1,5 +1,4 @@ -import * as pdfjsLib from 'pdfjs-dist'; -import { ProcessedFile, ProcessingState, PDFPage, ProcessingStrategy, ProcessingConfig, ProcessingMetrics } from '../types/processing'; +import { ProcessedFile, ProcessingState, PDFPage, ProcessingConfig, ProcessingMetrics } from '../types/processing'; import { ProcessingCache } from './processingCache'; import { FileHasher } from '../utils/fileHash'; import { FileAnalyzer } from './fileAnalyzer'; @@ -355,7 +354,7 @@ export class EnhancedPDFProcessingService { */ private async processMetadataOnly( file: File, - config: ProcessingConfig, + _config: ProcessingConfig, state: ProcessingState ): Promise { const arrayBuffer = await file.arrayBuffer(); @@ -510,7 +509,7 @@ export class EnhancedPDFProcessingService { */ clearAllProcessing(): void { // Cancel all ongoing processing - this.processing.forEach((state, key) => { + this.processing.forEach((state) => { if (state.cancellationToken) { state.cancellationToken.abort(); } diff --git a/frontend/src/services/fileAnalyzer.ts b/frontend/src/services/fileAnalyzer.ts index 1705bb54e..5655902a0 100644 --- a/frontend/src/services/fileAnalyzer.ts +++ b/frontend/src/services/fileAnalyzer.ts @@ -128,7 +128,7 @@ export class FileAnalyzer { * Estimate processing time based on file characteristics and strategy */ private static estimateProcessingTime( - fileSize: number, + _fileSize: number, pageCount: number = 0, strategy: ProcessingStrategy ): number { @@ -234,7 +234,7 @@ export class FileAnalyzer { const headerString = String.fromCharCode(...headerBytes); return headerString.startsWith('%PDF-'); - } catch (error) { + } catch { return false; } } diff --git a/frontend/src/services/fileProcessingService.ts b/frontend/src/services/fileProcessingService.ts index c109cff1f..be822b846 100644 --- a/frontend/src/services/fileProcessingService.ts +++ b/frontend/src/services/fileProcessingService.ts @@ -4,7 +4,6 @@ * Called when files are added to FileContext, before any view sees them */ -import * as pdfjsLib from 'pdfjs-dist'; import { generateThumbnailForFile } from '../utils/thumbnailUtils'; import { pdfWorkerManager } from './pdfWorkerManager'; import { FileId } from '../types/file'; diff --git a/frontend/src/services/fileStorage.ts b/frontend/src/services/fileStorage.ts index e500f54b5..e6dbbb464 100644 --- a/frontend/src/services/fileStorage.ts +++ b/frontend/src/services/fileStorage.ts @@ -496,7 +496,7 @@ class FileStorageService { async updateThumbnail(id: FileId, thumbnail: string): Promise { const db = await this.getDatabase(); - return new Promise((resolve, reject) => { + return new Promise((resolve, _reject) => { try { const transaction = db.transaction([this.storeName], 'readwrite'); const store = transaction.objectStore(this.storeName); diff --git a/frontend/src/services/pdfExportService.ts b/frontend/src/services/pdfExportService.ts index c0d6929fd..aa20f4cfc 100644 --- a/frontend/src/services/pdfExportService.ts +++ b/frontend/src/services/pdfExportService.ts @@ -31,7 +31,7 @@ export class PDFExportService { const originalPDFBytes = await pdfDocument.file.arrayBuffer(); const sourceDoc = await PDFLibDocument.load(originalPDFBytes); const blob = await this.createSingleDocument(sourceDoc, pagesToExport); - const exportFilename = this.generateFilename(filename || pdfDocument.name, selectedOnly, false); + const exportFilename = this.generateFilename(filename || pdfDocument.name); return { blob, filename: exportFilename }; } catch (error) { @@ -62,7 +62,7 @@ export class PDFExportService { } const blob = await this.createMultiSourceDocument(sourceFiles, pagesToExport); - const exportFilename = this.generateFilename(filename || pdfDocument.name, selectedOnly, false); + const exportFilename = this.generateFilename(filename || pdfDocument.name); return { blob, filename: exportFilename }; } catch (error) { @@ -183,7 +183,7 @@ export class PDFExportService { /** * Generate appropriate filename for export */ - private generateFilename(originalName: string, selectedOnly: boolean, appendSuffix: boolean): string { + private generateFilename(originalName: string): string { const baseName = originalName.replace(/\.pdf$/i, ''); return `${baseName}.pdf`; } @@ -210,7 +210,7 @@ export class PDFExportService { /** * Download multiple files as a ZIP */ - async downloadAsZip(blobs: Blob[], filenames: string[], zipFilename: string): Promise { + async downloadAsZip(blobs: Blob[], filenames: string[]): Promise { blobs.forEach((blob, index) => { setTimeout(() => { this.downloadFile(blob, filenames[index]); diff --git a/frontend/src/services/pdfWorkerManager.ts b/frontend/src/services/pdfWorkerManager.ts index 57fc841e0..dda434049 100644 --- a/frontend/src/services/pdfWorkerManager.ts +++ b/frontend/src/services/pdfWorkerManager.ts @@ -93,7 +93,7 @@ class PDFWorkerManager { if (loadingTask) { try { loadingTask.destroy(); - } catch (destroyError) { + } catch { // Ignore errors } } @@ -110,7 +110,7 @@ class PDFWorkerManager { pdf.destroy(); this.activeDocuments.delete(pdf); this.workerCount = Math.max(0, this.workerCount - 1); - } catch (error) { + } catch { // Still remove from tracking even if destroy failed this.activeDocuments.delete(pdf); this.workerCount = Math.max(0, this.workerCount - 1); @@ -166,7 +166,7 @@ class PDFWorkerManager { this.activeDocuments.forEach(pdf => { try { pdf.destroy(); - } catch (error) { + } catch { // Ignore errors } }); diff --git a/frontend/src/services/zipFileService.ts b/frontend/src/services/zipFileService.ts index 0b706cd96..872157d51 100644 --- a/frontend/src/services/zipFileService.ts +++ b/frontend/src/services/zipFileService.ts @@ -277,7 +277,7 @@ export class ZipFileService { bytes[2] === 0x44 && // D bytes[3] === 0x46 && // F bytes[4] === 0x2D; // - - } catch (error) { + } catch { return false; } } @@ -324,7 +324,7 @@ export class ZipFileService { await zip.loadAsync(file); // Check if any files are encrypted - for (const [filename, zipEntry] of Object.entries(zip.files)) { + for (const [_filename, zipEntry] of Object.entries(zip.files)) { if (zipEntry.options?.compression === 'STORE' && getData(zipEntry)?.compressedSize === 0) { // This might indicate encryption, but JSZip doesn't provide direct encryption detection // We'll handle this in the extraction phase diff --git a/frontend/src/setupTests.ts b/frontend/src/setupTests.ts index 3b406a91e..b640be3b6 100644 --- a/frontend/src/setupTests.ts +++ b/frontend/src/setupTests.ts @@ -63,7 +63,7 @@ for (let i = 0; i < 32; i++) { Object.defineProperty(globalThis, 'crypto', { value: { subtle: { - digest: vi.fn().mockImplementation(async (algorithm: string, data: any) => { + digest: vi.fn().mockImplementation(async (_algorithm: string, _data: any) => { // Always return the mock hash buffer regardless of input return mockHashBuffer.slice(); }), diff --git a/frontend/src/tests/convert/ConvertE2E.spec.ts b/frontend/src/tests/convert/ConvertE2E.spec.ts index 60e2c4849..5e250030e 100644 --- a/frontend/src/tests/convert/ConvertE2E.spec.ts +++ b/frontend/src/tests/convert/ConvertE2E.spec.ts @@ -17,7 +17,6 @@ import * as fs from 'fs'; // Test configuration const BASE_URL = process.env.BASE_URL || 'http://localhost:5173'; -const BACKEND_URL = process.env.BACKEND_URL || 'http://localhost:8080'; /** * Resolves test fixture paths dynamically based on current working directory. @@ -266,7 +265,6 @@ async function testConversion(page: Page, conversion: ConversionEndpoint) { } // Discover conversions at module level before tests are defined -let allConversions: ConversionEndpoint[] = []; let availableConversions: ConversionEndpoint[] = []; let unavailableConversions: ConversionEndpoint[] = []; @@ -275,7 +273,6 @@ let unavailableConversions: ConversionEndpoint[] = []; try { availableConversions = await conversionDiscovery.getAvailableConversions(); unavailableConversions = await conversionDiscovery.getUnavailableConversions(); - allConversions = [...availableConversions, ...unavailableConversions]; } catch (error) { console.error('Failed to discover conversions during module load:', error); } diff --git a/frontend/src/tests/convert/ConvertIntegration.test.tsx b/frontend/src/tests/convert/ConvertIntegration.test.tsx index 4efb41d7e..bf2c46662 100644 --- a/frontend/src/tests/convert/ConvertIntegration.test.tsx +++ b/frontend/src/tests/convert/ConvertIntegration.test.tsx @@ -11,7 +11,7 @@ import React from 'react'; import { describe, test, expect, vi, beforeEach, afterEach, Mock } from 'vitest'; -import { renderHook, act, waitFor } from '@testing-library/react'; +import { renderHook, act } from '@testing-library/react'; import { useConvertOperation } from '../../hooks/tools/convert/useConvertOperation'; import { ConvertParameters } from '../../hooks/tools/convert/useConvertParameters'; import { FileContextProvider } from '../../contexts/FileContext'; @@ -53,10 +53,6 @@ vi.mock('../../services/thumbnailGenerationService', () => ({ })); // Create realistic test files -const createTestFile = (name: string, content: string, type: string): File => { - return new File([content], name, { type }); -}; - const createPDFFile = (): StirlingFile => { const pdfContent = '%PDF-1.4\n1 0 obj\n<<\n/Type /Catalog\n/Pages 2 0 R\n>>\nendobj\ntrailer\n<<\n/Size 2\n/Root 1 0 R\n>>\nstartxref\n0\n%%EOF'; return createTestStirlingFile('test.pdf', pdfContent, 'application/pdf'); diff --git a/frontend/src/tests/convert/ConvertSmartDetectionIntegration.test.tsx b/frontend/src/tests/convert/ConvertSmartDetectionIntegration.test.tsx index 2904135e0..52826ce3f 100644 --- a/frontend/src/tests/convert/ConvertSmartDetectionIntegration.test.tsx +++ b/frontend/src/tests/convert/ConvertSmartDetectionIntegration.test.tsx @@ -15,7 +15,6 @@ import axios from 'axios'; import { detectFileExtension } from '../../utils/fileUtils'; import { FIT_OPTIONS } from '../../constants/convertConstants'; import { createTestStirlingFile, createTestFilesWithId } from '../utils/testFileHelpers'; -import { StirlingFile } from '../../types/fileContext'; // Mock axios vi.mock('axios'); @@ -507,7 +506,7 @@ describe('Convert Tool - Smart Detection Integration Tests', () => { describe('Real File Extension Detection', () => { test('should correctly detect various file extensions', async () => { - const { result } = renderHook(() => useConvertParameters(), { + renderHook(() => useConvertParameters(), { wrapper: TestWrapper }); diff --git a/frontend/src/theme/mantineTheme.ts b/frontend/src/theme/mantineTheme.ts index 2cd70d645..b7cd70a18 100644 --- a/frontend/src/theme/mantineTheme.ts +++ b/frontend/src/theme/mantineTheme.ts @@ -75,7 +75,7 @@ export const mantineTheme = createTheme({ }, variants: { // Custom button variant for PDF tools - pdfTool: (theme: any) => ({ + pdfTool: (_theme: any) => ({ root: { backgroundColor: 'var(--bg-surface)', border: '1px solid var(--border-default)', @@ -108,7 +108,7 @@ export const mantineTheme = createTheme({ }, }, Textarea: { - styles: (theme: any) => ({ + styles: (_theme: any) => ({ input: { backgroundColor: 'var(--bg-surface)', borderColor: 'var(--border-default)', @@ -126,7 +126,7 @@ export const mantineTheme = createTheme({ }, TextInput: { - styles: (theme: any) => ({ + styles: (_theme: any) => ({ input: { backgroundColor: 'var(--bg-surface)', borderColor: 'var(--border-default)', @@ -144,7 +144,7 @@ export const mantineTheme = createTheme({ }, PasswordInput: { - styles: (theme: any) => ({ + styles: (_theme: any) => ({ input: { backgroundColor: 'var(--bg-surface)', borderColor: 'var(--border-default)', diff --git a/frontend/src/tools/AddPassword.tsx b/frontend/src/tools/AddPassword.tsx index adbc25b5a..c29491437 100644 --- a/frontend/src/tools/AddPassword.tsx +++ b/frontend/src/tools/AddPassword.tsx @@ -1,15 +1,14 @@ -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useEndpointEnabled } from "../hooks/useEndpointConfig"; import { useFileSelection } from "../contexts/FileContext"; -import { useNavigationActions } from "../contexts/NavigationContext"; import { createToolFlow } from "../components/tools/shared/createToolFlow"; import AddPasswordSettings from "../components/tools/addPassword/AddPasswordSettings"; import ChangePermissionsSettings from "../components/tools/changePermissions/ChangePermissionsSettings"; -import { useAddPasswordParameters, defaultParameters } from "../hooks/tools/addPassword/useAddPasswordParameters"; +import { useAddPasswordParameters } from "../hooks/tools/addPassword/useAddPasswordParameters"; import { useAddPasswordOperation } from "../hooks/tools/addPassword/useAddPasswordOperation"; import { useAddPasswordTips } from "../components/tooltips/useAddPasswordTips"; import { useAddPasswordPermissionsTips } from "../components/tooltips/useAddPasswordPermissionsTips"; @@ -17,7 +16,6 @@ import { BaseToolProps, ToolComponent } from "../types/tool"; const AddPassword = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { const { t } = useTranslation(); - const { actions } = useNavigationActions(); const { selectedFiles } = useFileSelection(); const [collapsedPermissions, setCollapsedPermissions] = useState(true); diff --git a/frontend/src/tools/AddWatermark.tsx b/frontend/src/tools/AddWatermark.tsx index 7065b2a5b..3bafbd329 100644 --- a/frontend/src/tools/AddWatermark.tsx +++ b/frontend/src/tools/AddWatermark.tsx @@ -1,8 +1,7 @@ -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useEndpointEnabled } from "../hooks/useEndpointConfig"; import { useFileSelection } from "../contexts/FileContext"; -import { useNavigationActions } from "../contexts/NavigationContext"; import { createToolFlow } from "../components/tools/shared/createToolFlow"; @@ -25,7 +24,6 @@ import { BaseToolProps, ToolComponent } from "../types/tool"; const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { const { t } = useTranslation(); - const { actions } = useNavigationActions(); const { selectedFiles } = useFileSelection(); const [collapsedType, setCollapsedType] = useState(false); diff --git a/frontend/src/tools/Automate.tsx b/frontend/src/tools/Automate.tsx index 589c38678..db89db162 100644 --- a/frontend/src/tools/Automate.tsx +++ b/frontend/src/tools/Automate.tsx @@ -1,6 +1,5 @@ -import React, { useState, useMemo, useEffect } from "react"; +import React, { useState, useMemo } from "react"; import { useTranslation } from "react-i18next"; -import { useFileContext } from "../contexts/FileContext"; import { useFileSelection } from "../contexts/FileContext"; import { useNavigationActions } from "../contexts/NavigationContext"; import { useToolWorkflow } from "../contexts/ToolWorkflowContext"; diff --git a/frontend/src/tools/Convert.tsx b/frontend/src/tools/Convert.tsx index 5ced39670..05fd87531 100644 --- a/frontend/src/tools/Convert.tsx +++ b/frontend/src/tools/Convert.tsx @@ -1,8 +1,7 @@ -import React, { useEffect, useRef } from "react"; +import { useEffect, useRef } from "react"; import { useTranslation } from "react-i18next"; import { useEndpointEnabled } from "../hooks/useEndpointConfig"; import { useFileState, useFileSelection } from "../contexts/FileContext"; -import { useNavigationActions } from "../contexts/NavigationContext"; import { createToolFlow } from "../components/tools/shared/createToolFlow"; @@ -15,7 +14,6 @@ import { BaseToolProps, ToolComponent } from "../types/tool"; const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { const { t } = useTranslation(); const { selectors } = useFileState(); - const { actions } = useNavigationActions(); const activeFiles = selectors.getFiles(); const { selectedFiles } = useFileSelection(); const scrollContainerRef = useRef(null); diff --git a/frontend/src/tools/OCR.tsx b/frontend/src/tools/OCR.tsx index fcfb96841..c8a30fea4 100644 --- a/frontend/src/tools/OCR.tsx +++ b/frontend/src/tools/OCR.tsx @@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useEndpointEnabled } from "../hooks/useEndpointConfig"; import { useFileSelection } from "../contexts/FileContext"; -import { useNavigationActions } from "../contexts/NavigationContext"; import { createToolFlow } from "../components/tools/shared/createToolFlow"; diff --git a/frontend/src/tools/Split.tsx b/frontend/src/tools/Split.tsx index 6a0cef697..f22ee9159 100644 --- a/frontend/src/tools/Split.tsx +++ b/frontend/src/tools/Split.tsx @@ -1,4 +1,3 @@ -import { useEffect } from "react"; import { useTranslation } from "react-i18next"; import { createToolFlow } from "../components/tools/shared/createToolFlow"; import SplitSettings from "../components/tools/split/SplitSettings"; diff --git a/frontend/src/types/fileIdSafety.d.ts b/frontend/src/types/fileIdSafety.d.ts index 08888d4c0..13aac82dd 100644 --- a/frontend/src/types/fileIdSafety.d.ts +++ b/frontend/src/types/fileIdSafety.d.ts @@ -2,19 +2,19 @@ * Type safety declarations to prevent file.name/UUID confusion */ -import { FileId, StirlingFile, OperationType, FileOperation } from './fileContext'; +import { FileId, StirlingFile } from './fileContext'; declare global { namespace FileIdSafety { // Mark functions that should never accept file.name as parameters - type SafeFileIdFunction any> = T extends (...args: infer P) => infer R + type SafeFileIdFunction any> = T extends (...args: infer P) => infer _R ? P extends readonly [string, ...any[]] ? never // Reject string parameters in first position for FileId functions : T : T; // Mark functions that should only accept StirlingFile, not regular File - type StirlingFileOnlyFunction any> = T extends (...args: infer P) => infer R + type StirlingFileOnlyFunction any> = T extends (...args: infer P) => infer _R ? P extends readonly [File, ...any[]] ? never // Reject File parameters in first position for StirlingFile functions : T @@ -38,7 +38,7 @@ declare module '../contexts/FileContext' { addFiles: (files: File[], options?: { insertAfterPageId?: string }) => Promise; // Returns StirlingFile consumeFiles: (inputFileIds: FileId[], outputFiles: File[]) => Promise; // Returns StirlingFile } - + export interface StrictFileContextSelectors { getFile: (id: FileId) => StirlingFile | undefined; // Returns StirlingFile getFiles: (ids?: FileId[]) => StirlingFile[]; // Returns StirlingFile[] @@ -46,4 +46,4 @@ declare module '../contexts/FileContext' { } } -export {}; \ No newline at end of file +export {}; diff --git a/frontend/src/utils/automationExecutor.ts b/frontend/src/utils/automationExecutor.ts index 124f065a7..3feb9b412 100644 --- a/frontend/src/utils/automationExecutor.ts +++ b/frontend/src/utils/automationExecutor.ts @@ -1,6 +1,5 @@ import axios from 'axios'; import { ToolRegistry } from '../data/toolsTaxonomy'; -import { AutomationConfig, AutomationExecutionCallbacks } from '../types/automation'; import { AUTOMATION_CONSTANTS } from '../constants/automation'; import { AutomationFileProcessor } from './automationFileProcessor'; import { ResourceManager } from './resourceManager'; diff --git a/frontend/src/utils/automationFileProcessor.ts b/frontend/src/utils/automationFileProcessor.ts index 45abbaafc..d81dd3a1b 100644 --- a/frontend/src/utils/automationFileProcessor.ts +++ b/frontend/src/utils/automationFileProcessor.ts @@ -2,7 +2,7 @@ * File processing utilities specifically for automation workflows */ -import axios, { AxiosResponse } from 'axios'; +import axios from 'axios'; import { zipFileService } from '../services/zipFileService'; import { ResourceManager } from './resourceManager'; import { AUTOMATION_CONSTANTS } from '../constants/automation'; diff --git a/frontend/src/utils/fileResponseUtils.ts b/frontend/src/utils/fileResponseUtils.ts index 472cccb05..659c2948d 100644 --- a/frontend/src/utils/fileResponseUtils.ts +++ b/frontend/src/utils/fileResponseUtils.ts @@ -11,11 +11,11 @@ export const getFilenameFromHeaders = (contentDisposition: string = ''): string const match = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/); if (match && match[1]) { const filename = match[1].replace(/['"]/g, ''); - + // Decode URL-encoded characters (e.g., %20 -> space) try { return decodeURIComponent(filename); - } catch (error) { + } catch { // If decoding fails, return the original filename return filename; } @@ -37,9 +37,9 @@ export const createFileFromApiResponse = ( ): File => { const contentType = headers?.['content-type'] || 'application/octet-stream'; const contentDisposition = headers?.['content-disposition'] || ''; - + const filename = getFilenameFromHeaders(contentDisposition) || fallbackFilename; const blob = new Blob([responseData], { type: contentType }); - + return new File([blob], filename, { type: contentType }); -}; \ No newline at end of file +}; diff --git a/frontend/src/utils/thumbnailUtils.ts b/frontend/src/utils/thumbnailUtils.ts index e4a48f9fd..5f4cac3e6 100644 --- a/frontend/src/utils/thumbnailUtils.ts +++ b/frontend/src/utils/thumbnailUtils.ts @@ -346,12 +346,12 @@ export async function generateThumbnailForFile(file: File): Promise { // Handle PDF files if (file.type.startsWith('application/pdf')) { const scale = calculateScaleFromFileSize(file.size); - + // Only read first 2MB for thumbnail generation to save memory const chunkSize = 2 * 1024 * 1024; // 2MB const chunk = file.slice(0, Math.min(chunkSize, file.size)); const arrayBuffer = await chunk.arrayBuffer(); - + try { return await generatePDFThumbnail(arrayBuffer, file, scale); } catch (error) { @@ -361,7 +361,7 @@ export async function generateThumbnailForFile(file: File): Promise { // Try with full file instead of chunk const fullArrayBuffer = await file.arrayBuffer(); return await generatePDFThumbnail(fullArrayBuffer, file, scale); - } catch (fullFileError) { + } catch { console.warn(`Full file PDF processing also failed for ${file.name} - using placeholder`); return generatePlaceholderThumbnail(file); } @@ -392,11 +392,11 @@ export async function generateThumbnailWithMetadata(file: File): Promise