mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-02 21:03:34 +03:00
Fix more any typing usage in the frontend (#6664)
# Description of Changes Continued effort to remove the remaining uses of the `any` type from our TS code. The vast majority of these uses that it cleans up was just catching errors as `any`, which are pretty simple to fix. I couldn't completely remove the `any` type usage from `core/tools` because there were cascading issues from a couple of the files in there (most notably Automate) but still, moving in the right direction.
This commit is contained in:
@@ -20,12 +20,32 @@ import PhotoCameraRoundedIcon from "@mui/icons-material/PhotoCameraRounded";
|
||||
import UploadRoundedIcon from "@mui/icons-material/UploadRounded";
|
||||
import AddPhotoAlternateRoundedIcon from "@mui/icons-material/AddPhotoAlternateRounded";
|
||||
import CheckCircleRoundedIcon from "@mui/icons-material/CheckCircleRounded";
|
||||
import { loadJscanify } from "@app/utils/loadJscanify";
|
||||
import {
|
||||
loadJscanify,
|
||||
type JscanifyCornerPoints,
|
||||
type JscanifyScanner,
|
||||
} from "@app/utils/loadJscanify";
|
||||
import apiClient from "@app/services/apiClient";
|
||||
|
||||
// Use the configured API base (e.g. api.stirling.com), not the page origin.
|
||||
const API_BASE = (apiClient.defaults.baseURL ?? "").replace(/\/+$/, "");
|
||||
|
||||
// Experimental camera controls (W3C Image Capture / MediaStream extensions) that
|
||||
// are not yet part of the standard DOM lib typings but are widely shipped on
|
||||
// mobile browsers and required for document scanning.
|
||||
declare global {
|
||||
interface MediaTrackCapabilities {
|
||||
focusMode?: string[];
|
||||
exposureMode?: string[];
|
||||
torch?: boolean;
|
||||
}
|
||||
interface MediaTrackConstraintSet {
|
||||
focusMode?: ConstrainDOMString;
|
||||
exposureMode?: ConstrainDOMString;
|
||||
torch?: ConstrainBoolean;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MobileScannerPage
|
||||
*
|
||||
@@ -63,7 +83,7 @@ export default function MobileScannerPage() {
|
||||
const highlightCanvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const streamRef = useRef<MediaStream | null>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const scannerRef = useRef<any>(null);
|
||||
const scannerRef = useRef<JscanifyScanner | null>(null);
|
||||
const highlightIntervalRef = useRef<number | null>(null);
|
||||
|
||||
// Detection resolution - extremely low for mobile performance
|
||||
@@ -254,15 +274,15 @@ export default function MobileScannerPage() {
|
||||
|
||||
// Configure camera capabilities for document scanning
|
||||
try {
|
||||
const capabilities = videoTrack.getCapabilities() as any; // Cast to any for experimental camera APIs
|
||||
const constraints: any = { advanced: [] };
|
||||
const capabilities = videoTrack.getCapabilities();
|
||||
const advanced: MediaTrackConstraintSet[] = [];
|
||||
|
||||
// 1. Enable continuous autofocus
|
||||
if (
|
||||
capabilities.focusMode &&
|
||||
capabilities.focusMode.includes("continuous")
|
||||
) {
|
||||
constraints.advanced.push({ focusMode: "continuous" });
|
||||
advanced.push({ focusMode: "continuous" });
|
||||
console.log("✓ Continuous autofocus enabled");
|
||||
}
|
||||
|
||||
@@ -271,7 +291,7 @@ export default function MobileScannerPage() {
|
||||
capabilities.exposureMode &&
|
||||
capabilities.exposureMode.includes("continuous")
|
||||
) {
|
||||
constraints.advanced.push({ exposureMode: "continuous" });
|
||||
advanced.push({ exposureMode: "continuous" });
|
||||
console.log("✓ Auto-exposure enabled");
|
||||
}
|
||||
|
||||
@@ -282,8 +302,8 @@ export default function MobileScannerPage() {
|
||||
}
|
||||
|
||||
// Apply all constraints
|
||||
if (constraints.advanced.length > 0) {
|
||||
await videoTrack.applyConstraints(constraints);
|
||||
if (advanced.length > 0) {
|
||||
await videoTrack.applyConstraints({ advanced });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Could not configure camera features:", err);
|
||||
@@ -444,15 +464,19 @@ export default function MobileScannerPage() {
|
||||
|
||||
// Step 2: Simple jscanify detection
|
||||
const detectionStart = performance.now();
|
||||
let corners = null;
|
||||
let corners: JscanifyCornerPoints | null = null;
|
||||
|
||||
// Run jscanify detection directly - convert canvas to Mat first
|
||||
const mat = (window as any).cv.imread(detectionCanvas);
|
||||
const contour = scannerRef.current.findPaperContour(mat);
|
||||
mat.delete();
|
||||
const cv = window.cv;
|
||||
const scanner = scannerRef.current;
|
||||
if (cv && scanner) {
|
||||
const mat = cv.imread(detectionCanvas);
|
||||
const contour = scanner.findPaperContour(mat);
|
||||
mat.delete();
|
||||
|
||||
if (contour) {
|
||||
corners = scannerRef.current.getCornerPoints(contour);
|
||||
if (contour) {
|
||||
corners = scanner.getCornerPoints(contour);
|
||||
}
|
||||
}
|
||||
|
||||
const detectionTime = performance.now() - detectionStart;
|
||||
@@ -660,7 +684,9 @@ export default function MobileScannerPage() {
|
||||
let finalDataUrl: string;
|
||||
|
||||
// Apply jscanify processing if enabled and available
|
||||
if (autoEnhance && scannerRef.current && openCvReady) {
|
||||
const cv = window.cv;
|
||||
const scanner = scannerRef.current;
|
||||
if (autoEnhance && scanner && openCvReady && cv) {
|
||||
try {
|
||||
// Create low-res canvas for detection (faster processing)
|
||||
const detectionCanvas = document.createElement("canvas");
|
||||
@@ -683,11 +709,11 @@ export default function MobileScannerPage() {
|
||||
);
|
||||
|
||||
// Run detection on low-res image
|
||||
const mat = (window as any).cv.imread(detectionCanvas);
|
||||
const contour = scannerRef.current.findPaperContour(mat);
|
||||
const mat = cv.imread(detectionCanvas);
|
||||
const contour = scanner.findPaperContour(mat);
|
||||
|
||||
if (contour) {
|
||||
const cornerPoints = scannerRef.current.getCornerPoints(contour);
|
||||
const cornerPoints = scanner.getCornerPoints(contour);
|
||||
|
||||
// Scale corner points back to full resolution
|
||||
if (cornerPoints) {
|
||||
@@ -746,7 +772,7 @@ export default function MobileScannerPage() {
|
||||
const docHeight = Math.round((leftHeight + rightHeight) / 2);
|
||||
|
||||
// Extract paper from full-resolution canvas with scaled corner points
|
||||
const resultCanvas = scannerRef.current.extractPaper(
|
||||
const resultCanvas = scanner.extractPaper(
|
||||
canvas,
|
||||
docWidth,
|
||||
docHeight,
|
||||
@@ -891,8 +917,8 @@ export default function MobileScannerPage() {
|
||||
try {
|
||||
const videoTrack = streamRef.current.getVideoTracks()[0];
|
||||
await videoTrack.applyConstraints({
|
||||
advanced: [{ torch: !torchEnabled } as any], // Cast to any for experimental torch API
|
||||
} as any);
|
||||
advanced: [{ torch: !torchEnabled }],
|
||||
});
|
||||
setTorchEnabled(!torchEnabled);
|
||||
console.log("Torch:", !torchEnabled ? "ON" : "OFF");
|
||||
} catch (err) {
|
||||
|
||||
@@ -123,7 +123,7 @@ describe("Convert Tool Integration Tests", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// Setup default apiClient mock
|
||||
mockedApiClient.post = vi.fn() as any;
|
||||
mockedApiClient.post = vi.fn() as typeof mockedApiClient.post;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -83,7 +83,10 @@ async function getIDBFolders(
|
||||
const all = tx.objectStore(storeName).getAll();
|
||||
all.onsuccess = () =>
|
||||
resolve(
|
||||
(all.result || []).map((f: any) => ({ id: f.id, name: f.name })),
|
||||
(all.result || []).map((f: { id: string; name: string }) => ({
|
||||
id: f.id,
|
||||
name: f.name,
|
||||
})),
|
||||
);
|
||||
all.onerror = () => resolve([]);
|
||||
};
|
||||
@@ -275,7 +278,7 @@ test.describe("Watched Folders — Create / Edit / Delete", () => {
|
||||
dbName: string,
|
||||
storeName: string,
|
||||
key: string,
|
||||
value: any,
|
||||
value: unknown,
|
||||
) =>
|
||||
new Promise<void>((resolve) => {
|
||||
const req = indexedDB.open(dbName);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useViewScopedFiles } from "@app/hooks/tools/shared/useViewScopedFiles";
|
||||
import { createToolFlow } from "@app/components/tools/shared/createToolFlow";
|
||||
import {
|
||||
createToolFlow,
|
||||
type MiddleStepConfig,
|
||||
} from "@app/components/tools/shared/createToolFlow";
|
||||
import { BaseToolProps, ToolComponent } from "@app/types/tool";
|
||||
import { useEndpointEnabled } from "@app/hooks/useEndpointConfig";
|
||||
import { useAddAttachmentsParameters } from "@app/hooks/tools/addAttachments/useAddAttachmentsParameters";
|
||||
@@ -36,9 +39,9 @@ const AddAttachments = ({
|
||||
if (operation.files && onComplete) {
|
||||
onComplete(operation.files);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
onError?.(
|
||||
error?.message ||
|
||||
(error instanceof Error ? error.message : undefined) ||
|
||||
t(
|
||||
"AddAttachmentsRequest.error.failed",
|
||||
"Add attachments operation failed",
|
||||
@@ -70,7 +73,7 @@ const AddAttachments = ({
|
||||
});
|
||||
|
||||
const getSteps = () => {
|
||||
const steps: any[] = [];
|
||||
const steps: MiddleStepConfig[] = [];
|
||||
|
||||
// Step 1: Attachments Selection
|
||||
steps.push({
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useViewScopedFiles } from "@app/hooks/tools/shared/useViewScopedFiles";
|
||||
import { createToolFlow } from "@app/components/tools/shared/createToolFlow";
|
||||
import {
|
||||
createToolFlow,
|
||||
type MiddleStepConfig,
|
||||
} from "@app/components/tools/shared/createToolFlow";
|
||||
import { BaseToolProps, ToolComponent } from "@app/types/tool";
|
||||
import { useEndpointEnabled } from "@app/hooks/useEndpointConfig";
|
||||
import { useAddPageNumbersParameters } from "@app/components/tools/addPageNumbers/useAddPageNumbersParameters";
|
||||
@@ -35,9 +38,9 @@ const AddPageNumbers = ({
|
||||
if (operation.files && onComplete) {
|
||||
onComplete(operation.files);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
onError?.(
|
||||
error?.message ||
|
||||
(error instanceof Error ? error.message : undefined) ||
|
||||
t("addPageNumbers.error.failed", "Add page numbers operation failed"),
|
||||
);
|
||||
}
|
||||
@@ -67,7 +70,7 @@ const AddPageNumbers = ({
|
||||
});
|
||||
|
||||
const getSteps = () => {
|
||||
const steps: any[] = [];
|
||||
const steps: MiddleStepConfig[] = [];
|
||||
|
||||
// Step 1: Position Selection & Pages/Starting Number
|
||||
steps.push({
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createToolFlow } from "@app/components/tools/shared/createToolFlow";
|
||||
import {
|
||||
createToolFlow,
|
||||
type MiddleStepConfig,
|
||||
} from "@app/components/tools/shared/createToolFlow";
|
||||
import { BaseToolProps, ToolComponent } from "@app/types/tool";
|
||||
import { useEndpointEnabled } from "@app/hooks/useEndpointConfig";
|
||||
import { useViewScopedFiles } from "@app/hooks/tools/shared/useViewScopedFiles";
|
||||
@@ -41,9 +44,9 @@ const AddStamp = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
||||
if (operation.files && onComplete) {
|
||||
onComplete(operation.files);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
onError?.(
|
||||
error?.message ||
|
||||
(error instanceof Error ? error.message : undefined) ||
|
||||
t("AddStampRequest.error.failed", "Add stamp operation failed"),
|
||||
);
|
||||
}
|
||||
@@ -73,7 +76,7 @@ const AddStamp = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
||||
});
|
||||
|
||||
const getSteps = () => {
|
||||
const steps: any[] = [];
|
||||
const steps: MiddleStepConfig[] = [];
|
||||
|
||||
// Step 1: Stamp Setup
|
||||
steps.push({
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
useNavigationState,
|
||||
} from "@app/contexts/NavigationContext";
|
||||
import { useFileSelection } from "@app/contexts/FileContext";
|
||||
import { isStirlingFile } from "@app/types/fileContext";
|
||||
|
||||
const extractBookmarks = async (file: File): Promise<BookmarkPayload[]> => {
|
||||
const formData = new FormData();
|
||||
@@ -39,7 +40,7 @@ const extractBookmarks = async (file: File): Promise<BookmarkPayload[]> => {
|
||||
return response.data as BookmarkPayload[];
|
||||
};
|
||||
|
||||
const useStableCallback = <T extends (...args: any[]) => any>(
|
||||
const useStableCallback = <T extends (...args: never[]) => unknown>(
|
||||
callback: T,
|
||||
): T => {
|
||||
const callbackRef = useRef(callback);
|
||||
@@ -112,7 +113,7 @@ const EditTableOfContents = (props: BaseToolProps) => {
|
||||
const payload = await extractBookmarks(file);
|
||||
const bookmarks = hydrateBookmarkPayload(payload);
|
||||
setBookmarks(bookmarks);
|
||||
setLastLoadedFileId((file as any)?.fileId ?? file.name);
|
||||
setLastLoadedFileId(isStirlingFile(file) ? file.fileId : file.name);
|
||||
|
||||
if (showToast) {
|
||||
alert({
|
||||
@@ -164,7 +165,7 @@ const EditTableOfContents = (props: BaseToolProps) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileId = (selectedFile as any)?.fileId ?? selectedFile.name;
|
||||
const fileId = selectedFile.fileId;
|
||||
if (fileId === lastLoadedFileId) {
|
||||
return;
|
||||
}
|
||||
@@ -466,6 +467,7 @@ const EditTableOfContents = (props: BaseToolProps) => {
|
||||
});
|
||||
};
|
||||
|
||||
(EditTableOfContents as any).tool = () => useEditTableOfContentsOperation;
|
||||
(EditTableOfContents as ToolComponent).tool = () =>
|
||||
useEditTableOfContentsOperation;
|
||||
|
||||
export default EditTableOfContents as ToolComponent;
|
||||
|
||||
@@ -34,9 +34,9 @@ const ReorganizePages = ({
|
||||
if (operation.files && onComplete) {
|
||||
onComplete(operation.files);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
onError?.(
|
||||
error?.message ||
|
||||
(error instanceof Error ? error.message : undefined) ||
|
||||
t("reorganizePages.error.failed", "Failed to reorganize pages"),
|
||||
);
|
||||
}
|
||||
@@ -107,6 +107,6 @@ const ReorganizePages = ({
|
||||
});
|
||||
};
|
||||
|
||||
(ReorganizePages as any).tool = () => useReorganizePagesOperation;
|
||||
(ReorganizePages as ToolComponent).tool = () => useReorganizePagesOperation;
|
||||
|
||||
export default ReorganizePages as ToolComponent;
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
ActionIcon,
|
||||
} from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { isAxiosError } from "axios";
|
||||
import {
|
||||
useFormFill,
|
||||
useAllFormValues,
|
||||
@@ -267,13 +268,15 @@ const FormFill = (_props: BaseToolProps) => {
|
||||
detail: { blob: filledBlob },
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
const status = isAxiosError(err) ? err.response?.status : undefined;
|
||||
const message =
|
||||
err?.response?.status === 413
|
||||
status === 413
|
||||
? "File too large. Try reducing the PDF size first."
|
||||
: err?.response?.status === 400
|
||||
: status === 400
|
||||
? "Invalid form data. Please check all fields."
|
||||
: err?.message || "Failed to save filled form";
|
||||
: (err instanceof Error ? err.message : undefined) ||
|
||||
"Failed to save filled form";
|
||||
setSaveError(message);
|
||||
console.error("[FormFill] Save failed:", err);
|
||||
} finally {
|
||||
|
||||
@@ -30,6 +30,7 @@ import React, {
|
||||
useSyncExternalStore,
|
||||
} from "react";
|
||||
import { useDebouncedCallback } from "@mantine/hooks";
|
||||
import { isAxiosError } from "axios";
|
||||
import type {
|
||||
FormField,
|
||||
FormFillState,
|
||||
@@ -361,11 +362,13 @@ export function FormFillProvider({
|
||||
forFileIdRef.current = fileId ?? null;
|
||||
setForFileId(fileId ?? null);
|
||||
dispatch({ type: "FETCH_SUCCESS", fields });
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (fetchVersionRef.current !== version) return; // stale
|
||||
const msg =
|
||||
err?.response?.data?.message ||
|
||||
err?.message ||
|
||||
(isAxiosError<{ message?: string }>(err)
|
||||
? err.response?.data?.message
|
||||
: undefined) ||
|
||||
(err instanceof Error ? err.message : undefined) ||
|
||||
"Failed to fetch form fields";
|
||||
dispatch({ type: "FETCH_ERROR", error: msg });
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo, useState, useRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { isAxiosError } from "axios";
|
||||
import DescriptionIcon from "@mui/icons-material/DescriptionOutlined";
|
||||
|
||||
import { useToolWorkflow } from "@app/contexts/ToolWorkflowContext";
|
||||
@@ -16,6 +17,7 @@ import {
|
||||
import { useViewer } from "@app/contexts/ViewerContext";
|
||||
import { createStirlingFilesAndStubs } from "@app/services/fileStubHelpers";
|
||||
import { BaseToolProps, ToolComponent } from "@app/types/tool";
|
||||
import type { FileId } from "@app/types/file";
|
||||
import { getDefaultWorkbench } from "@app/types/workbench";
|
||||
import { CONVERSION_ENDPOINTS } from "@app/constants/convertConstants";
|
||||
import apiClient from "@app/services/apiClient";
|
||||
@@ -294,7 +296,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
const imagesByPageRef = useRef<PdfJsonImageElement[][]>([]);
|
||||
const lastLoadedFileRef = useRef<File | null>(null);
|
||||
const autoLoadKeyRef = useRef<string | null>(null);
|
||||
const sourceFileIdRef = useRef<string | null>(null);
|
||||
const sourceFileIdRef = useRef<FileId | null>(null);
|
||||
const loadRequestIdRef = useRef(0);
|
||||
const latestPdfRequestIdRef = useRef<number | null>(null);
|
||||
const loadedDocumentRef = useRef<PdfJsonDocument | null>(null);
|
||||
@@ -339,8 +341,8 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const isCacheUnavailableError = useCallback((error: any): boolean => {
|
||||
const status = error?.response?.status;
|
||||
const isCacheUnavailableError = useCallback((error: unknown): boolean => {
|
||||
const status = isAxiosError(error) ? error.response?.status : undefined;
|
||||
// Treat any 410 as cache unavailable, since responseType: 'blob' makes
|
||||
// it impossible to reliably check the JSON body
|
||||
return status === 410;
|
||||
@@ -804,14 +806,20 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
} else {
|
||||
console.log("Job not complete yet, continuing to poll...");
|
||||
}
|
||||
} catch (pollError: any) {
|
||||
} catch (pollError) {
|
||||
console.error("Error polling job status:", pollError);
|
||||
const status = isAxiosError(pollError)
|
||||
? pollError.response?.status
|
||||
: undefined;
|
||||
console.error("Poll error details:", {
|
||||
status: pollError?.response?.status,
|
||||
data: pollError?.response?.data,
|
||||
message: pollError?.message,
|
||||
status,
|
||||
data: isAxiosError(pollError)
|
||||
? pollError.response?.data
|
||||
: undefined,
|
||||
message:
|
||||
pollError instanceof Error ? pollError.message : undefined,
|
||||
});
|
||||
if (pollError?.response?.status === 404) {
|
||||
if (status === 404) {
|
||||
throw new Error("Job not found on server", {
|
||||
cause: pollError,
|
||||
});
|
||||
@@ -864,12 +872,12 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
cachedJobIdRef.current = newJobId;
|
||||
setFileName(file.name);
|
||||
setErrorMessage(null);
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.error("Failed to load file", error);
|
||||
console.error("Error details:", {
|
||||
message: error?.message,
|
||||
response: error?.response?.data,
|
||||
stack: error?.stack,
|
||||
message: error instanceof Error ? error.message : undefined,
|
||||
response: isAxiosError(error) ? error.response?.data : undefined,
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
});
|
||||
|
||||
if (loadRequestIdRef.current !== requestId) {
|
||||
@@ -885,7 +893,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
|
||||
if (isPdf) {
|
||||
const errorMsg =
|
||||
error?.message ||
|
||||
(error instanceof Error ? error.message : undefined) ||
|
||||
t(
|
||||
"pdfTextEditor.conversionFailed",
|
||||
"Failed to convert PDF. Please try again.",
|
||||
@@ -1406,11 +1414,11 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
onComplete([pdfFile]);
|
||||
}
|
||||
setErrorMessage(null);
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.error("Failed to convert JSON back to PDF", error);
|
||||
const message =
|
||||
error?.response?.data ||
|
||||
error?.message ||
|
||||
(isAxiosError(error) ? error.response?.data : undefined) ||
|
||||
(error instanceof Error ? error.message : undefined) ||
|
||||
t(
|
||||
"pdfTextEditor.errors.pdfConversion",
|
||||
"Unable to convert the edited JSON back into a PDF.",
|
||||
@@ -1451,9 +1459,8 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const parentStub = selectors.getStirlingFileStub(
|
||||
sourceFileIdRef.current as any,
|
||||
);
|
||||
const sourceFileId = sourceFileIdRef.current;
|
||||
const parentStub = selectors.getStirlingFileStub(sourceFileId);
|
||||
if (!parentStub) {
|
||||
console.warn(
|
||||
"[PdfTextEditor] Could not find parent stub for save to workbench",
|
||||
@@ -1660,11 +1667,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
);
|
||||
|
||||
// Replace the original file with the edited version
|
||||
await consumeFiles(
|
||||
[sourceFileIdRef.current as any],
|
||||
stirlingFiles,
|
||||
stubs,
|
||||
);
|
||||
await consumeFiles([sourceFileId], stirlingFiles, stubs);
|
||||
|
||||
// Update the source file ID to point to the new file
|
||||
sourceFileIdRef.current = stubs[0].id;
|
||||
@@ -1676,11 +1679,11 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
|
||||
// Set flag to trigger navigation after state update is processed
|
||||
setShouldNavigateAfterSave(true);
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.error("Failed to save to workbench", error);
|
||||
const message =
|
||||
error?.response?.data ||
|
||||
error?.message ||
|
||||
(isAxiosError(error) ? error.response?.data : undefined) ||
|
||||
(error instanceof Error ? error.message : undefined) ||
|
||||
t(
|
||||
"pdfTextEditor.errors.pdfConversion",
|
||||
"Unable to save changes to workbench.",
|
||||
@@ -1955,7 +1958,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
|
||||
autoLoadKeyRef.current = fileKey;
|
||||
// Capture the source file ID for save-to-workbench functionality
|
||||
sourceFileIdRef.current = (autoLoadFile as any).fileId ?? null;
|
||||
sourceFileIdRef.current = autoLoadFile.fileId ?? null;
|
||||
void handleLoadFile(autoLoadFile);
|
||||
}, [autoLoadFile, navigationState.selectedTool, handleLoadFile]);
|
||||
|
||||
|
||||
@@ -1,9 +1,52 @@
|
||||
import { withBasePath } from "@app/constants/app";
|
||||
|
||||
/** A single point in image space, as returned by jscanify corner detection. */
|
||||
export interface JscanifyPoint {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
/** The four detected document corners returned by {@link JscanifyScanner.getCornerPoints}. */
|
||||
export interface JscanifyCornerPoints {
|
||||
topLeftCorner: JscanifyPoint;
|
||||
topRightCorner: JscanifyPoint;
|
||||
bottomLeftCorner: JscanifyPoint;
|
||||
bottomRightCorner: JscanifyPoint;
|
||||
}
|
||||
|
||||
/** Minimal subset of an OpenCV.js `Mat` that this app interacts with directly. */
|
||||
export interface OpenCVMat {
|
||||
delete(): void;
|
||||
}
|
||||
|
||||
/** Minimal subset of the OpenCV.js runtime exposed on `window.cv`. */
|
||||
export interface OpenCV {
|
||||
/** Defined only once the WASM runtime has finished initializing. */
|
||||
readonly Mat: unknown;
|
||||
imread(source: HTMLImageElement | HTMLCanvasElement | string): OpenCVMat;
|
||||
}
|
||||
|
||||
/** The jscanify scanner instance API used by the mobile scanner. */
|
||||
export interface JscanifyScanner {
|
||||
findPaperContour(image: OpenCVMat): OpenCVMat | undefined;
|
||||
getCornerPoints(contour: OpenCVMat): JscanifyCornerPoints;
|
||||
extractPaper(
|
||||
image: HTMLCanvasElement,
|
||||
resultWidth: number,
|
||||
resultHeight: number,
|
||||
cornerPoints?: JscanifyCornerPoints,
|
||||
): HTMLCanvasElement;
|
||||
}
|
||||
|
||||
/** Constructor for jscanify, exposed on `window.jscanify`. */
|
||||
export interface JscanifyConstructor {
|
||||
new (): JscanifyScanner;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
cv?: any;
|
||||
jscanify?: any;
|
||||
cv?: OpenCV;
|
||||
jscanify?: JscanifyConstructor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,10 +215,9 @@ export default defineConfig(
|
||||
"editor/src/core/contexts/**/*.{js,mjs,jsx,ts,tsx}",
|
||||
"editor/src/core/data/**/*.{js,mjs,jsx,ts,tsx}",
|
||||
"editor/src/core/hooks/**/*.{js,mjs,jsx,ts,tsx}",
|
||||
"editor/src/core/pages/**/*.{js,mjs,jsx,ts,tsx}",
|
||||
"editor/src/core/services/**/*.{js,mjs,jsx,ts,tsx}",
|
||||
"editor/src/core/tests/**/*.{js,mjs,jsx,ts,tsx}",
|
||||
"editor/src/core/tools/**/*.{js,mjs,jsx,ts,tsx}",
|
||||
"editor/src/core/tools/Automate.tsx",
|
||||
"editor/src/core/tools/annotate/useAnnotationSelection.ts",
|
||||
"editor/src/core/types/**/*.{js,mjs,jsx,ts,tsx}",
|
||||
"editor/src/core/utils/**/*.{js,mjs,jsx,ts,tsx}",
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user