From cfaf777f2b776c207ffeab8aef8ff82981872027 Mon Sep 17 00:00:00 2001 From: James Brunton Date: Mon, 10 Aug 2026 11:40:47 +0100 Subject: [PATCH] Fix more `any` type usages in frontend code (#7334) # Description of Changes Continued effort towards removing all uses of the any type in our frontend code (last PR was #7326). This PR fixes 7 more folders and removes them from the exclude list. All of them were localised within the folder in the exclude list so again were pretty easy to fix. --- .../providers/PDFAnnotationProvider.tsx | 9 ++--- .../annotation/shared/BaseAnnotationTool.tsx | 12 +++++-- .../pageEditor/DragDropGrid.stories.tsx | 6 ++-- .../components/pageEditor/DragDropGrid.tsx | 11 +++++-- .../core/components/pageEditor/PageEditor.tsx | 17 +++++++--- .../components/pageEditor/PageThumbnail.tsx | 3 +- .../components/tools/FullscreenToolList.tsx | 8 ++--- .../certSign/CertificateFilesSettings.tsx | 5 ++- .../certSign/CertificateFormatSettings.tsx | 5 ++- .../certSign/CertificateTypeSettings.tsx | 5 ++- .../certSign/HardwareCertificateSettings.tsx | 33 ++++++++++++------- .../certSign/SignatureAppearanceSettings.tsx | 12 +++++-- .../tools/certSign/SignatureSettingsInput.tsx | 9 +++-- .../tools/pdfTextEditor/PdfTextEditorView.tsx | 22 +++++++++---- .../src/core/contexts/file/fileActions.ts | 8 ++--- .../src/core/contexts/file/fileHooks.ts | 2 +- .../src/core/contexts/file/lifecycle.ts | 11 ++++--- .../tools/convert/useConvertOperation.ts | 12 ++++--- .../core/hooks/tools/ocr/useOCROperation.ts | 10 +++--- .../hooks/tools/shared/toolOperationTypes.ts | 2 +- .../hooks/tools/shared/useToolOperation.ts | 13 ++++---- frontend/oxlint.config.ts | 7 ---- 22 files changed, 145 insertions(+), 77 deletions(-) diff --git a/frontend/editor/src/core/components/annotation/providers/PDFAnnotationProvider.tsx b/frontend/editor/src/core/components/annotation/providers/PDFAnnotationProvider.tsx index bbc6484f2e..a009e3d1a1 100644 --- a/frontend/editor/src/core/components/annotation/providers/PDFAnnotationProvider.tsx +++ b/frontend/editor/src/core/components/annotation/providers/PDFAnnotationProvider.tsx @@ -1,4 +1,5 @@ import React, { createContext, useContext, ReactNode } from "react"; +import { SignParameters } from "@app/hooks/tools/sign/useSignParameters"; interface PDFAnnotationContextValue { // Drawing mode management @@ -22,8 +23,8 @@ interface PDFAnnotationContextValue { isPlacementMode: boolean; // Signature configuration - signatureConfig: any | null; - setSignatureConfig: (config: any | null) => void; + signatureConfig: SignParameters | null; + setSignatureConfig: (config: SignParameters | null) => void; } const PDFAnnotationContext = createContext< @@ -43,8 +44,8 @@ interface PDFAnnotationProviderProps { storeImageData: (id: string, data: string) => void; getImageData: (id: string) => string | undefined; isPlacementMode: boolean; - signatureConfig: any | null; - setSignatureConfig: (config: any | null) => void; + signatureConfig: SignParameters | null; + setSignatureConfig: (config: SignParameters | null) => void; } export const PDFAnnotationProvider: React.FC = ({ diff --git a/frontend/editor/src/core/components/annotation/shared/BaseAnnotationTool.tsx b/frontend/editor/src/core/components/annotation/shared/BaseAnnotationTool.tsx index 4c0862a012..e611ba4b83 100644 --- a/frontend/editor/src/core/components/annotation/shared/BaseAnnotationTool.tsx +++ b/frontend/editor/src/core/components/annotation/shared/BaseAnnotationTool.tsx @@ -14,9 +14,17 @@ export interface AnnotationToolConfig { placeButtonText?: string; } +interface InjectedAnnotationToolProps { + selectedColor: string; + signatureData: string | null; + onSignatureDataChange: (data: string | null) => void; + onColorSwatchClick: () => void; + disabled: boolean; +} + interface BaseAnnotationToolProps { config: AnnotationToolConfig; - children: React.ReactNode; + children: React.ReactElement>; onSignatureDataChange?: (data: string | null) => void; disabled?: boolean; } @@ -90,7 +98,7 @@ export const BaseAnnotationTool: React.FC = ({ /> {/* Tool Content */} - {React.cloneElement(children as React.ReactElement, { + {React.cloneElement(children, { selectedColor, signatureData, onSignatureDataChange: handleSignatureDataChange, diff --git a/frontend/editor/src/core/components/pageEditor/DragDropGrid.stories.tsx b/frontend/editor/src/core/components/pageEditor/DragDropGrid.stories.tsx index be7616f1f9..a437402985 100644 --- a/frontend/editor/src/core/components/pageEditor/DragDropGrid.stories.tsx +++ b/frontend/editor/src/core/components/pageEditor/DragDropGrid.stories.tsx @@ -1,5 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import DragDropGrid from "@app/components/pageEditor/DragDropGrid"; +import DragDropGrid, { + type DragHandleProps, +} from "@app/components/pageEditor/DragDropGrid"; interface MockGridItem { id: string; @@ -22,7 +24,7 @@ const renderItem = ( clearBoxSelection: () => void, activeDragIds: string[], justMoved: boolean, - dragHandleProps?: any, + dragHandleProps?: DragHandleProps, zoomLevel?: number, ) => { const { ref: dndRef, ...restDragProps } = dragHandleProps ?? {}; diff --git a/frontend/editor/src/core/components/pageEditor/DragDropGrid.tsx b/frontend/editor/src/core/components/pageEditor/DragDropGrid.tsx index de1a3f59d7..03fa1dd096 100644 --- a/frontend/editor/src/core/components/pageEditor/DragDropGrid.tsx +++ b/frontend/editor/src/core/components/pageEditor/DragDropGrid.tsx @@ -20,6 +20,7 @@ import { DragEndEvent, DragStartEvent, DragOverlay, + DraggableAttributes, useSensor, useSensors, PointerSensor, @@ -28,6 +29,10 @@ import { useDroppable, } from "@dnd-kit/core"; +export type DragHandleProps = DraggableAttributes & { + ref: React.RefCallback; +} & Record; + interface DragDropItem { id: string; splitAfter?: boolean; @@ -51,7 +56,7 @@ interface DragDropGridProps { clearBoxSelection: () => void, activeDragIds: string[], justMoved: boolean, - dragHandleProps?: any, + dragHandleProps?: DragHandleProps, zoomLevel?: number, ) => React.ReactNode; getThumbnailData?: ( @@ -232,7 +237,7 @@ interface DraggableItemProps { clearBoxSelection: () => void, activeDragIds: string[], justMoved: boolean, - dragHandleProps?: any, + dragHandleProps?: DragHandleProps, zoomLevel?: number, ) => React.ReactNode; zoomLevel: number; @@ -253,7 +258,7 @@ const DraggableItemInner = ({ zoomLevel, }: DraggableItemProps) => { const isPlaceholder = Boolean(item.isPlaceholder); - const pageNumber = (item as any).pageNumber ?? index + 1; + const pageNumber = item.pageNumber ?? index + 1; const { attributes, listeners, diff --git a/frontend/editor/src/core/components/pageEditor/PageEditor.tsx b/frontend/editor/src/core/components/pageEditor/PageEditor.tsx index b635dc7dc4..08971c0c59 100644 --- a/frontend/editor/src/core/components/pageEditor/PageEditor.tsx +++ b/frontend/editor/src/core/components/pageEditor/PageEditor.tsx @@ -11,7 +11,9 @@ import { PageEditorFunctions, PDFPage } from "@app/types/pageEditor"; // Thumbnail generation is now handled by individual PageThumbnail components import "@app/components/pageEditor/PageEditor.module.css"; import PageThumbnail from "@app/components/pageEditor/PageThumbnail"; -import DragDropGrid from "@app/components/pageEditor/DragDropGrid"; +import DragDropGrid, { + type DragHandleProps, +} from "@app/components/pageEditor/DragDropGrid"; import SkeletonLoader from "@app/components/shared/SkeletonLoader"; import { FileId } from "@app/types/file"; import { GRID_CONSTANTS } from "@app/components/pageEditor/constants"; @@ -33,6 +35,13 @@ export interface PageEditorProps { onFunctionsReady?: (functions: PageEditorFunctions) => void; } +interface PageEditorFileEntry { + fileId: FileId; + name: string; + versionNumber: number | undefined; + isSelected: boolean; +} + const PageEditor = ({ onFunctionsReady }: PageEditorProps) => { const { t } = useTranslation(); // Use split contexts to prevent re-renders @@ -106,14 +115,14 @@ const PageEditor = ({ onFunctionsReady }: PageEditorProps) => { const selectedIdsKey = [...state.ui.selectedFileIds].sort().join(","); const filesSignature = selectors.getFilesSignature(); - const fileObjectsRef = useRef(new Map()); + const fileObjectsRef = useRef(new Map()); const gridItemRefsRef = useRef > | null>(null); const pageEditorFiles = useMemo(() => { const cache = fileObjectsRef.current; - const newFiles: any[] = []; + const newFiles: PageEditorFileEntry[] = []; fileOrder.forEach((fileId) => { const stub = selectors.getStirlingFileStub(fileId); @@ -605,7 +614,7 @@ const PageEditor = ({ onFunctionsReady }: PageEditorProps) => { clearBoxSelection: () => void, activeDragIds: string[], justMoved: boolean, - dragHandleProps?: any, + dragHandleProps?: DragHandleProps, zoomLevelParam?: number, ) => { gridItemRefsRef.current = refs; diff --git a/frontend/editor/src/core/components/pageEditor/PageThumbnail.tsx b/frontend/editor/src/core/components/pageEditor/PageThumbnail.tsx index da54602d81..127c982a9d 100644 --- a/frontend/editor/src/core/components/pageEditor/PageThumbnail.tsx +++ b/frontend/editor/src/core/components/pageEditor/PageThumbnail.tsx @@ -17,6 +17,7 @@ import AddIcon from "@mui/icons-material/Add"; import { PDFPage, PDFDocument } from "@app/types/pageEditor"; import { useFilesModalContext } from "@app/contexts/FilesModalContext"; import { getFileColorWithOpacity } from "@app/components/pageEditor/fileColors"; +import { type DragHandleProps } from "@app/components/pageEditor/DragDropGrid"; import styles from "@app/components/pageEditor/PageEditor.module.css"; import HoverActionMenu, { HoverAction, @@ -38,7 +39,7 @@ interface PageThumbnailProps { activeDragIds: string[]; justMoved?: boolean; pageRefs: React.MutableRefObject>; - dragHandleProps?: any; + dragHandleProps?: DragHandleProps; onReorderPages: ( sourcePageNumber: number, targetIndex: number, diff --git a/frontend/editor/src/core/components/tools/FullscreenToolList.tsx b/frontend/editor/src/core/components/tools/FullscreenToolList.tsx index ef7818ff9d..5296e21299 100644 --- a/frontend/editor/src/core/components/tools/FullscreenToolList.tsx +++ b/frontend/editor/src/core/components/tools/FullscreenToolList.tsx @@ -58,8 +58,8 @@ const FullscreenToolList = ({ ); const recommendedItems = useMemo(() => { if (!quickSection) - return [] as Array<{ id: string; tool: ToolRegistryEntry }>; - const items: Array<{ id: string; tool: ToolRegistryEntry }> = []; + return [] as Array<{ id: ToolId; tool: ToolRegistryEntry }>; + const items: Array<{ id: ToolId; tool: ToolRegistryEntry }> = []; quickSection.subcategories.forEach((sc) => sc.tools.forEach((t) => items.push(t)), ); @@ -217,13 +217,13 @@ const FullscreenToolList = ({ {showDescriptions ? (
- {recommendedItems.map((item: any) => + {recommendedItems.map((item) => renderToolItem(item.id, item.tool), )}
) : (
- {recommendedItems.map((item: any) => + {recommendedItems.map((item) => renderToolItem(item.id, item.tool), )}
diff --git a/frontend/editor/src/core/components/tools/certSign/CertificateFilesSettings.tsx b/frontend/editor/src/core/components/tools/certSign/CertificateFilesSettings.tsx index 035f57e5c4..4f97b0a83c 100644 --- a/frontend/editor/src/core/components/tools/certSign/CertificateFilesSettings.tsx +++ b/frontend/editor/src/core/components/tools/certSign/CertificateFilesSettings.tsx @@ -5,7 +5,10 @@ import FileUploadButton from "@app/components/shared/FileUploadButton"; interface CertificateFilesSettingsProps { parameters: CertSignParameters; - onParameterChange: (key: keyof CertSignParameters, value: any) => void; + onParameterChange: ( + key: K, + value: CertSignParameters[K], + ) => void; disabled?: boolean; } diff --git a/frontend/editor/src/core/components/tools/certSign/CertificateFormatSettings.tsx b/frontend/editor/src/core/components/tools/certSign/CertificateFormatSettings.tsx index cbc4b67163..a3a17f928a 100644 --- a/frontend/editor/src/core/components/tools/certSign/CertificateFormatSettings.tsx +++ b/frontend/editor/src/core/components/tools/certSign/CertificateFormatSettings.tsx @@ -4,7 +4,10 @@ import { CertSignParameters } from "@app/hooks/tools/certSign/useCertSignParamet interface CertificateFormatSettingsProps { parameters: CertSignParameters; - onParameterChange: (key: keyof CertSignParameters, value: any) => void; + onParameterChange: ( + key: K, + value: CertSignParameters[K], + ) => void; disabled?: boolean; } diff --git a/frontend/editor/src/core/components/tools/certSign/CertificateTypeSettings.tsx b/frontend/editor/src/core/components/tools/certSign/CertificateTypeSettings.tsx index aaab1cb93e..1dc3865c66 100644 --- a/frontend/editor/src/core/components/tools/certSign/CertificateTypeSettings.tsx +++ b/frontend/editor/src/core/components/tools/certSign/CertificateTypeSettings.tsx @@ -7,7 +7,10 @@ import { useAppConfig } from "@app/contexts/AppConfigContext"; interface CertificateTypeSettingsProps { parameters: CertSignParameters; - onParameterChange: (key: keyof CertSignParameters, value: any) => void; + onParameterChange: ( + key: K, + value: CertSignParameters[K], + ) => void; disabled?: boolean; } diff --git a/frontend/editor/src/core/components/tools/certSign/HardwareCertificateSettings.tsx b/frontend/editor/src/core/components/tools/certSign/HardwareCertificateSettings.tsx index b384ce8d5c..75b93210a0 100644 --- a/frontend/editor/src/core/components/tools/certSign/HardwareCertificateSettings.tsx +++ b/frontend/editor/src/core/components/tools/certSign/HardwareCertificateSettings.tsx @@ -22,7 +22,10 @@ import { interface HardwareCertificateSettingsProps { parameters: CertSignParameters; - onParameterChange: (key: keyof CertSignParameters, value: any) => void; + onParameterChange: ( + key: K, + value: CertSignParameters[K], + ) => void; disabled?: boolean; } @@ -176,16 +179,20 @@ const HardwareCertificateSettings = ({ setError(null); listWindowsCertificates() .then(applyCerts) - .catch((e: any) => + .catch((e) => { + const err = e as { + response?: { data?: { message?: string } }; + message?: string; + }; setError( - e?.response?.data?.message || - e?.message || + err?.response?.data?.message || + err?.message || t( "certSign.hardware.windowsLoadError", "Could not read the Windows certificate store", ), - ), - ) + ); + }) .finally(() => setLoading(false)); }, [applyCerts, t]); @@ -221,16 +228,20 @@ const HardwareCertificateSettings = ({ pin: parameters.password, }) .then(applyCerts) - .catch((e: any) => + .catch((e) => { + const err = e as { + response?: { data?: { message?: string } }; + message?: string; + }; setError( - e?.response?.data?.message || - e?.message || + err?.response?.data?.message || + err?.message || t( "certSign.hardware.pkcs11LoadError", "Could not read certificates from the token. Check the PIN and driver.", ), - ), - ) + ); + }) .finally(() => setLoading(false)); }, [ applyCerts, diff --git a/frontend/editor/src/core/components/tools/certSign/SignatureAppearanceSettings.tsx b/frontend/editor/src/core/components/tools/certSign/SignatureAppearanceSettings.tsx index 75998b1c88..dcbeac026e 100644 --- a/frontend/editor/src/core/components/tools/certSign/SignatureAppearanceSettings.tsx +++ b/frontend/editor/src/core/components/tools/certSign/SignatureAppearanceSettings.tsx @@ -5,7 +5,10 @@ import { CertSignParameters } from "@app/hooks/tools/certSign/useCertSignParamet interface SignatureAppearanceSettingsProps { parameters: CertSignParameters; - onParameterChange: (key: keyof CertSignParameters, value: any) => void; + onParameterChange: ( + key: K, + value: CertSignParameters[K], + ) => void; disabled?: boolean; } @@ -101,7 +104,12 @@ const SignatureAppearanceSettings = ({ onParameterChange("pageNumber", value || 1)} + onChange={(value) => + onParameterChange( + "pageNumber", + typeof value === "number" ? value : 1, + ) + } min={1} disabled={disabled} /> diff --git a/frontend/editor/src/core/components/tools/certSign/SignatureSettingsInput.tsx b/frontend/editor/src/core/components/tools/certSign/SignatureSettingsInput.tsx index d0e7a69d2f..a89d66e447 100644 --- a/frontend/editor/src/core/components/tools/certSign/SignatureSettingsInput.tsx +++ b/frontend/editor/src/core/components/tools/certSign/SignatureSettingsInput.tsx @@ -24,7 +24,10 @@ const SignatureSettingsInput = ({ }: SignatureSettingsInputProps) => { const { t } = useTranslation(); - const handleChange = (key: keyof SignatureSettings, val: any) => { + const handleChange = ( + key: K, + val: SignatureSettings[K], + ) => { onChange({ ...value, [key]: val }); }; @@ -104,7 +107,9 @@ const SignatureSettingsInput = ({ handleChange("pageNumber", val || 1)} + onChange={(val) => + handleChange("pageNumber", typeof val === "number" ? val : 1) + } min={1} disabled={disabled} size="xs" diff --git a/frontend/editor/src/core/components/tools/pdfTextEditor/PdfTextEditorView.tsx b/frontend/editor/src/core/components/tools/pdfTextEditor/PdfTextEditorView.tsx index 0cbd88e619..1d314fec14 100644 --- a/frontend/editor/src/core/components/tools/pdfTextEditor/PdfTextEditorView.tsx +++ b/frontend/editor/src/core/components/tools/pdfTextEditor/PdfTextEditorView.tsx @@ -52,6 +52,14 @@ import { const MAX_RENDER_WIDTH = 820; const MIN_BOX_SIZE = 18; +// Firefox-only fallback for document.caretRangeFromPoint (not in lib.dom.d.ts). +const docWithCaret = document as Document & { + caretPositionFromPoint?: ( + x: number, + y: number, + ) => { offsetNode: Node; offset: number } | null; +}; + const normalizeFontFormat = (format?: string | null): string => { if (!format) { return "ttf"; @@ -352,7 +360,7 @@ const PdfTextEditorView = ({ data }: PdfTextEditorViewProps) => { new Map(), ); const draggingImageRef = useRef(null); - const rndRefs = useRef>(new Map()); + const rndRefs = useRef>(new Map()); const pendingDragUpdateRef = useRef(null); const [fontFamilies, setFontFamilies] = useState>( new Map(), @@ -1378,7 +1386,7 @@ const PdfTextEditorView = ({ data }: PdfTextEditorViewProps) => { const cssTop = (pageHeight - bounds.top) * scale; // Get current position from Rnd component - const currentState = rndRef.state || {}; + const currentState = (rndRef.state as { x?: number; y?: number }) || {}; const currentX = currentState.x ?? 0; const currentY = currentState.y ?? 0; @@ -2851,11 +2859,13 @@ const PdfTextEditorView = ({ data }: PdfTextEditorViewProps) => { } } } else if ( - (document as any).caretPositionFromPoint + docWithCaret.caretPositionFromPoint ) { - const pos = ( - document as any - ).caretPositionFromPoint(clickX, clickY); + const pos = + docWithCaret.caretPositionFromPoint( + clickX, + clickY, + ); if (pos) { const range = document.createRange(); range.setStart( diff --git a/frontend/editor/src/core/contexts/file/fileActions.ts b/frontend/editor/src/core/contexts/file/fileActions.ts index db99e1c673..a95f42939d 100644 --- a/frontend/editor/src/core/contexts/file/fileActions.ts +++ b/frontend/editor/src/core/contexts/file/fileActions.ts @@ -447,10 +447,8 @@ export async function addFiles( if (file.type === "application/pdf") { try { if (await FileAnalyzer.isPDFUserPasswordProtected(file)) { - fileStub.processedFile = (fileStub.processedFile || { - pages: [], - }) as any; - fileStub.processedFile!.isEncrypted = true; + fileStub.processedFile = fileStub.processedFile || { pages: [] }; + fileStub.processedFile.isEncrypted = true; } } catch (error) { // Never block upload on analysis failure — but log so it's debuggable @@ -699,7 +697,7 @@ export async function undoConsumeFiles( file: File, fileId: FileId, existingThumbnail?: string, - ) => Promise; + ) => Promise; deleteFile: (fileId: FileId) => Promise; bumpRevision?: () => void; } | null, diff --git a/frontend/editor/src/core/contexts/file/fileHooks.ts b/frontend/editor/src/core/contexts/file/fileHooks.ts index f5c1bfc460..c6207e442e 100644 --- a/frontend/editor/src/core/contexts/file/fileHooks.ts +++ b/frontend/editor/src/core/contexts/file/fileHooks.ts @@ -398,7 +398,7 @@ export function useFileContext() { addFiles: actions.addFiles, consumeFiles: actions.consumeFiles, undoConsumeFiles: actions.undoConsumeFiles, - recordOperation: (_fileId: FileId, _operation: any) => {}, // Operation tracking not implemented + recordOperation: (_fileId: FileId, _operation: unknown) => {}, // Operation tracking not implemented markOperationApplied: (_fileId: FileId, _operationId: string) => {}, // Operation tracking not implemented markOperationFailed: ( _fileId: FileId, diff --git a/frontend/editor/src/core/contexts/file/lifecycle.ts b/frontend/editor/src/core/contexts/file/lifecycle.ts index 7e072d7db0..4b3d639ad9 100644 --- a/frontend/editor/src/core/contexts/file/lifecycle.ts +++ b/frontend/editor/src/core/contexts/file/lifecycle.ts @@ -5,6 +5,7 @@ import { FileId } from "@app/types/file"; import { FileContextAction, + FileContextState, StirlingFileStub, ProcessedFilePage, } from "@app/types/fileContext"; @@ -39,7 +40,7 @@ export class FileLifecycleManager { */ cleanupFile = ( fileId: FileId, - stateRef?: React.MutableRefObject, + stateRef?: React.MutableRefObject, ): void => { // Use comprehensive cleanup (same as removeFiles) this.cleanupAllResourcesForFile(fileId, stateRef); @@ -77,7 +78,7 @@ export class FileLifecycleManager { scheduleCleanup = ( fileId: FileId, delay: number = 30000, - stateRef?: React.MutableRefObject, + stateRef?: React.MutableRefObject, ): void => { // Cancel existing timer const existingTimer = this.cleanupTimers.get(fileId); @@ -116,7 +117,7 @@ export class FileLifecycleManager { */ removeFiles = ( fileIds: FileId[], - stateRef?: React.MutableRefObject, + stateRef?: React.MutableRefObject, ): void => { fileIds.forEach((fileId) => { // Clean up all resources for this file @@ -132,7 +133,7 @@ export class FileLifecycleManager { */ private cleanupAllResourcesForFile = ( fileId: FileId, - stateRef?: React.MutableRefObject, + stateRef?: React.MutableRefObject, ): void => { // Remove from files ref this.filesRef.current.delete(fileId); @@ -188,7 +189,7 @@ export class FileLifecycleManager { updateStirlingFileStub = ( fileId: FileId, updates: Partial, - stateRef?: React.MutableRefObject, + stateRef?: React.MutableRefObject, ): void => { // Guard against updating removed files (race condition protection) if (!this.filesRef.current.has(fileId)) { diff --git a/frontend/editor/src/core/hooks/tools/convert/useConvertOperation.ts b/frontend/editor/src/core/hooks/tools/convert/useConvertOperation.ts index d912cc4ede..d329f1feb0 100644 --- a/frontend/editor/src/core/hooks/tools/convert/useConvertOperation.ts +++ b/frontend/editor/src/core/hooks/tools/convert/useConvertOperation.ts @@ -345,11 +345,15 @@ export const useConvertOperation = (parameters?: ConvertParameters) => { ...convertOperationConfig, customProcessor: customConvertProcessor, // Use instance-specific processor for translation support getErrorMessage: (error) => { - if (error.response?.data && typeof error.response.data === "string") { - return error.response.data; + const err = error as { + response?: { data?: unknown }; + message?: string; + }; + if (err.response?.data && typeof err.response.data === "string") { + return err.response.data; } - if (error.message) { - return error.message; + if (err.message) { + return err.message; } return t( "convert.errorConversion", diff --git a/frontend/editor/src/core/hooks/tools/ocr/useOCROperation.ts b/frontend/editor/src/core/hooks/tools/ocr/useOCROperation.ts index 79b339dda9..0f8016ab67 100644 --- a/frontend/editor/src/core/hooks/tools/ocr/useOCROperation.ts +++ b/frontend/editor/src/core/hooks/tools/ocr/useOCROperation.ts @@ -179,13 +179,15 @@ export const useOCROperation = () => { const ocrConfig: ToolOperationConfig = { ...ocrOperationConfig, responseHandler, - getErrorMessage: (error) => - error.message?.includes("OCR tools") && - error.message?.includes("not installed") + getErrorMessage: (error) => { + const message = (error as { message?: string }).message; + return message?.includes("OCR tools") && + message?.includes("not installed") ? "OCR tools (OCRmyPDF or Tesseract) are not installed on the server. Use the standard or fat Docker image instead of ultra-lite, or install OCR tools manually." : createStandardErrorHandler( t("ocr.error.failed", "OCR operation failed"), - )(error), + )(error); + }, }; return useToolOperation(ocrConfig); diff --git a/frontend/editor/src/core/hooks/tools/shared/toolOperationTypes.ts b/frontend/editor/src/core/hooks/tools/shared/toolOperationTypes.ts index d4d4ccceec..60453c576f 100644 --- a/frontend/editor/src/core/hooks/tools/shared/toolOperationTypes.ts +++ b/frontend/editor/src/core/hooks/tools/shared/toolOperationTypes.ts @@ -74,7 +74,7 @@ interface BaseToolOperationConfig { responseHandler?: ResponseHandler; /** Extract user-friendly error messages from API errors */ - getErrorMessage?: (error: any) => string; + getErrorMessage?: (error: unknown) => string; /** Default parameter values for automation */ defaultParameters?: TParams; diff --git a/frontend/editor/src/core/hooks/tools/shared/useToolOperation.ts b/frontend/editor/src/core/hooks/tools/shared/useToolOperation.ts index 7ee69da142..f01c8770ab 100644 --- a/frontend/editor/src/core/hooks/tools/shared/useToolOperation.ts +++ b/frontend/editor/src/core/hooks/tools/shared/useToolOperation.ts @@ -218,7 +218,7 @@ export const useToolOperation = ( // Listen for global error file id events from HTTP interceptor during this run let externalErrorFileIds: string[] = []; const errorListener = (e: Event) => { - const detail = (e as CustomEvent)?.detail as any; + const detail = (e as CustomEvent<{ fileIds?: unknown }>)?.detail; if (detail?.fileIds) { externalErrorFileIds = Array.isArray(detail.fileIds) ? detail.fileIds @@ -588,7 +588,7 @@ export const useToolOperation = ( }; } } - } catch (error: any) { + } catch (error) { try { const handled = await handle422Error(error, (id) => fileActions.markFileError(id as FileId), @@ -691,21 +691,22 @@ export const useToolOperation = ( // Show success message actions.setStatus(t("undoSuccess", "Operation undone successfully")); - } catch (error: any) { + } catch (error) { let errorMessage = extractErrorMessage(error); // Provide more specific error messages based on error type - if (error.message?.includes("Mismatch between input files")) { + const err = error as { message?: string; name?: string }; + if (err.message?.includes("Mismatch between input files")) { errorMessage = t( "undoDataMismatch", "Cannot undo: operation data is corrupted", ); - } else if (error.message?.includes("IndexedDB")) { + } else if (err.message?.includes("IndexedDB")) { errorMessage = t( "undoStorageError", "Undo completed but some files could not be saved to storage", ); - } else if (error.name === "QuotaExceededError") { + } else if (err.name === "QuotaExceededError") { errorMessage = t( "undoQuotaError", "Cannot undo: insufficient storage space", diff --git a/frontend/oxlint.config.ts b/frontend/oxlint.config.ts index fd07f17c6d..ac29c9b2bb 100644 --- a/frontend/oxlint.config.ts +++ b/frontend/oxlint.config.ts @@ -74,21 +74,14 @@ const modernGlobals: OxlintGlobals = { // Folders not yet conformant to the stricter no-explicit-any rule const noExplicitAnyExcludes = [ - "editor/src/core/components/annotation/**/*.{js,mjs,jsx,ts,tsx}", - "editor/src/core/components/pageEditor/*.{js,mjs,jsx,ts,tsx}", "editor/src/core/components/shared/*.{js,mjs,jsx,ts,tsx}", "editor/src/core/components/shared/config/configSections/*.{js,mjs,jsx,ts,tsx}", - "editor/src/core/components/tools/*.{js,mjs,jsx,ts,tsx}", "editor/src/core/components/tools/addStamp/*.{js,mjs,jsx,ts,tsx}", "editor/src/core/components/tools/automate/*.{js,mjs,jsx,ts,tsx}", - "editor/src/core/components/tools/certSign/*.{js,mjs,jsx,ts,tsx}", - "editor/src/core/components/tools/pdfTextEditor/*.{js,mjs,jsx,ts,tsx}", "editor/src/core/components/viewer/*.{js,mjs,jsx,ts,tsx}", "editor/src/core/contexts/*.{js,mjs,jsx,ts,tsx}", - "editor/src/core/contexts/file/*.{js,mjs,jsx,ts,tsx}", "editor/src/core/contexts/viewer/*.{js,mjs,jsx,ts,tsx}", "editor/src/core/hooks/*.{js,mjs,jsx,ts,tsx}", - "editor/src/core/hooks/tools/shared/*.{js,mjs,jsx,ts,tsx}", "editor/src/core/services/*.{js,mjs,jsx,ts,tsx}", "editor/src/core/tools/annotate/useAnnotationSelection.ts", "editor/src/core/types/*.{js,mjs,jsx,ts,tsx}",