mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 03:20:46 +02:00
Switch to use ESLint 10 (#5794)
This commit is contained in:
@@ -217,7 +217,7 @@ export function usePageDocument(): PageDocumentHook {
|
||||
});
|
||||
|
||||
// Build pages by interleaving original pages with insertions
|
||||
let pages: PDFPage[] = [];
|
||||
let pages: PDFPage[];
|
||||
|
||||
// Helper function to create pages from a file (or placeholder if deselected)
|
||||
const createPagesFromFile = (fileId: FileId, startPageNumber: number, isSelected: boolean): PDFPage[] => {
|
||||
@@ -242,11 +242,10 @@ export function usePageDocument(): PageDocumentHook {
|
||||
}
|
||||
|
||||
const processedFile = stirlingFileStub.processedFile;
|
||||
let filePages: PDFPage[] = [];
|
||||
|
||||
if (processedFile?.pages && processedFile.pages.length > 0) {
|
||||
// Use fully processed pages with thumbnails
|
||||
filePages = processedFile.pages.map((page, pageIndex) => ({
|
||||
return processedFile.pages.map((page, pageIndex) => ({
|
||||
id: `${fileId}-${pageIndex + 1}`,
|
||||
pageNumber: startPageNumber + pageIndex,
|
||||
thumbnail: page.thumbnail || null,
|
||||
@@ -259,9 +258,11 @@ export function usePageDocument(): PageDocumentHook {
|
||||
originalFileId: fileId,
|
||||
isPlaceholder: false,
|
||||
}));
|
||||
} else if (processedFile?.totalPages) {
|
||||
}
|
||||
|
||||
if (processedFile?.totalPages) {
|
||||
// Fallback: create pages without thumbnails but with correct count
|
||||
filePages = Array.from({ length: processedFile.totalPages }, (_, pageIndex) => ({
|
||||
return Array.from({ length: processedFile.totalPages }, (_, pageIndex) => ({
|
||||
id: `${fileId}-${pageIndex + 1}`,
|
||||
pageNumber: startPageNumber + pageIndex,
|
||||
originalPageNumber: pageIndex + 1,
|
||||
@@ -272,23 +273,21 @@ export function usePageDocument(): PageDocumentHook {
|
||||
splitAfter: false,
|
||||
isPlaceholder: false,
|
||||
}));
|
||||
} else {
|
||||
// No processedFile yet - create a single loading placeholder
|
||||
// This will be replaced when processing completes
|
||||
filePages = [{
|
||||
id: `${fileId}-loading`,
|
||||
pageNumber: startPageNumber,
|
||||
originalPageNumber: 1,
|
||||
originalFileId: fileId,
|
||||
rotation: 0,
|
||||
thumbnail: null,
|
||||
selected: false,
|
||||
splitAfter: false,
|
||||
isPlaceholder: true,
|
||||
}];
|
||||
}
|
||||
|
||||
return filePages;
|
||||
// No processedFile yet - create a single loading placeholder
|
||||
// This will be replaced when processing completes
|
||||
return [{
|
||||
id: `${fileId}-loading`,
|
||||
pageNumber: startPageNumber,
|
||||
originalPageNumber: 1,
|
||||
originalFileId: fileId,
|
||||
rotation: 0,
|
||||
thumbnail: null,
|
||||
selected: false,
|
||||
splitAfter: false,
|
||||
isPlaceholder: true,
|
||||
}];
|
||||
};
|
||||
|
||||
// Collect all pages from original files, respecting their previous positions
|
||||
|
||||
@@ -4,13 +4,12 @@ import iconSet from '../../../assets/material-symbols-icons.json'; // eslint-dis
|
||||
|
||||
// Load icons synchronously at import time - guaranteed to be ready on first render
|
||||
let iconsLoaded = false;
|
||||
let localIconCount = 0;
|
||||
|
||||
try {
|
||||
if (iconSet) {
|
||||
addCollection(iconSet);
|
||||
iconsLoaded = true;
|
||||
localIconCount = Object.keys(iconSet.icons || {}).length;
|
||||
const localIconCount = Object.keys(iconSet.icons || {}).length;
|
||||
console.info(`✅ Local icons loaded: ${localIconCount} icons (${Math.round(JSON.stringify(iconSet).length / 1024)}KB)`);
|
||||
}
|
||||
} catch {
|
||||
|
||||
@@ -110,7 +110,7 @@ export function computeStampPreviewStyle(
|
||||
// Convert measured px width back to PDF points using horizontal scale
|
||||
widthPtsContent = measuredWidthPx / scaleX;
|
||||
|
||||
let adjustmentFactor = 1.0;
|
||||
let adjustmentFactor: number;
|
||||
switch (parameters.alphabet) {
|
||||
case 'roman':
|
||||
adjustmentFactor = 0.90;
|
||||
@@ -126,6 +126,7 @@ export function computeStampPreviewStyle(
|
||||
break;
|
||||
default:
|
||||
adjustmentFactor = 0.93;
|
||||
break;
|
||||
}
|
||||
widthPtsContent *= adjustmentFactor;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ const CompactToolItem: React.FC<CompactToolItemProps> = ({ id, tool, isSelected,
|
||||
const iconBg = getIconBackground(categoryColor, false);
|
||||
const iconClasses = 'tool-panel__fullscreen-list-icon';
|
||||
|
||||
let iconNode: React.ReactNode = null;
|
||||
let iconNode: React.ReactNode;
|
||||
if (React.isValidElement<{ style?: React.CSSProperties }>(tool.icon)) {
|
||||
const element = tool.icon as React.ReactElement<{ style?: React.CSSProperties }>;
|
||||
iconNode = React.cloneElement(element, {
|
||||
@@ -120,4 +120,3 @@ const CompactToolItem: React.FC<CompactToolItemProps> = ({ id, tool, isSelected,
|
||||
|
||||
export default CompactToolItem;
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const DetailedToolItem: React.FC<DetailedToolItemProps> = ({ id, tool, isSelecte
|
||||
const iconBg = getIconBackground(categoryColor, true);
|
||||
const iconClasses = 'tool-panel__fullscreen-icon';
|
||||
|
||||
let iconNode: React.ReactNode = null;
|
||||
let iconNode: React.ReactNode;
|
||||
if (React.isValidElement<{ style?: React.CSSProperties }>(tool.icon)) {
|
||||
const element = tool.icon as React.ReactElement<{ style?: React.CSSProperties }>;
|
||||
iconNode = React.cloneElement(element, {
|
||||
@@ -70,7 +70,7 @@ const DetailedToolItem: React.FC<DetailedToolItemProps> = ({ id, tool, isSelecte
|
||||
color="orange"
|
||||
>
|
||||
{/* we can add more translations for different badges in future, like beta, etc. */}
|
||||
{t('toolPanel.alpha', 'Alpha')}
|
||||
{t('toolPanel.alpha', 'Alpha')}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
@@ -106,4 +106,3 @@ const DetailedToolItem: React.FC<DetailedToolItemProps> = ({ id, tool, isSelecte
|
||||
|
||||
export default DetailedToolItem;
|
||||
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ const validateParameters = (params: ConvertParameters): boolean => {
|
||||
if (!fromExtension || !toExtension) return false;
|
||||
|
||||
// Handle dynamic format identifiers (file-<extension>)
|
||||
let supportedToExtensions: string[] = [];
|
||||
let supportedToExtensions: string[];
|
||||
if (fromExtension.startsWith('file-')) {
|
||||
// Dynamic format - use 'any' conversion options
|
||||
supportedToExtensions = CONVERSION_MATRIX['any'] || [];
|
||||
|
||||
@@ -71,7 +71,10 @@ const removeAnnotationsProcessor = async (_parameters: RemoveAnnotationsParamete
|
||||
processedFiles.push(processedFile);
|
||||
} catch (error) {
|
||||
console.error('Error processing file:', file.name, error);
|
||||
throw new Error(`Failed to process ${file.name}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
throw new Error(
|
||||
`Failed to process ${file.name}: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
{ cause: error }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,4 +99,4 @@ export const useRemoveAnnotationsOperation = () => {
|
||||
...removeAnnotationsOperationConfig,
|
||||
getErrorMessage: createStandardErrorHandler(t('removeAnnotations.error.failed', 'An error occurred while removing annotations from the PDF.'))
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -77,7 +77,7 @@ export const useToolApiCalls = <TParams = void>() => {
|
||||
|
||||
} catch (error) {
|
||||
if (axios.isCancel(error)) {
|
||||
throw new Error('Operation was cancelled');
|
||||
throw new Error('Operation was cancelled', { cause: error });
|
||||
}
|
||||
console.error('[processFiles] Failed', { name: file.name, error });
|
||||
failedFiles.push(file.name);
|
||||
|
||||
@@ -71,7 +71,7 @@ export const createReportPdf = async (
|
||||
cursorY -= 16;
|
||||
|
||||
if (entry.error) {
|
||||
cursorY = drawCenteredMessage({
|
||||
drawCenteredMessage({
|
||||
page,
|
||||
font,
|
||||
fontBold,
|
||||
@@ -86,7 +86,7 @@ export const createReportPdf = async (
|
||||
}
|
||||
|
||||
if (entry.signatures.length === 0) {
|
||||
cursorY = drawCenteredMessage({
|
||||
drawCenteredMessage({
|
||||
page,
|
||||
font,
|
||||
fontBold,
|
||||
|
||||
@@ -71,8 +71,7 @@ export const useCookieConsent = ({
|
||||
const hasDarkClass = document.documentElement.classList.contains('dark');
|
||||
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
let isDarkMode = false;
|
||||
|
||||
let isDarkMode: boolean;
|
||||
if (mantineScheme) {
|
||||
isDarkMode = mantineScheme === 'dark';
|
||||
} else if (hasLightClass) {
|
||||
@@ -94,9 +93,8 @@ export const useCookieConsent = ({
|
||||
return;
|
||||
}
|
||||
|
||||
let themeObserver: MutationObserver | null = null;
|
||||
if (!forceLightMode) {
|
||||
themeObserver = new MutationObserver((mutations) => {
|
||||
const themeObserver = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.type === 'attributes' &&
|
||||
(mutation.attributeName === 'data-mantine-color-scheme' ||
|
||||
@@ -241,7 +239,7 @@ export const useCookieConsent = ({
|
||||
const hasDarkClass = document.documentElement.classList.contains('dark');
|
||||
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
let isDarkMode = false;
|
||||
let isDarkMode: boolean;
|
||||
if (mantineScheme) {
|
||||
isDarkMode = mantineScheme === 'dark';
|
||||
} else if (hasLightClass) {
|
||||
|
||||
@@ -335,10 +335,10 @@ class FileStorageService {
|
||||
* Get storage statistics
|
||||
*/
|
||||
async getStorageStats(): Promise<StorageStats> {
|
||||
let used = 0;
|
||||
let used: number;
|
||||
let fileCount: number;
|
||||
let available = 0;
|
||||
let quota: number | undefined;
|
||||
let fileCount = 0;
|
||||
|
||||
try {
|
||||
// Get browser quota for context
|
||||
|
||||
@@ -37,7 +37,10 @@ export class PDFExportService {
|
||||
return { blob, filename: exportFilename };
|
||||
} catch (error) {
|
||||
console.error('PDF export error:', error);
|
||||
throw new Error(`Failed to export PDF: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
throw new Error(
|
||||
`Failed to export PDF: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
{ cause: error }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +71,10 @@ export class PDFExportService {
|
||||
return { blob, filename: exportFilename };
|
||||
} catch (error) {
|
||||
console.error('Multi-file PDF export error:', error);
|
||||
throw new Error(`Failed to export PDF: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
throw new Error(
|
||||
`Failed to export PDF: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
{ cause: error }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,10 @@ export class ZipFileService {
|
||||
|
||||
return { zipFile, size: zipFile.size };
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to create ZIP file: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
throw new Error(
|
||||
`Failed to create ZIP file: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
{ cause: error }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -687,7 +687,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
message: pollError?.message,
|
||||
});
|
||||
if (pollError?.response?.status === 404) {
|
||||
throw new Error('Job not found on server');
|
||||
throw new Error('Job not found on server', { cause: pollError });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1164,7 +1164,9 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
return;
|
||||
} catch (incrementalError) {
|
||||
if (isLazyMode && cachedJobIdRef.current) {
|
||||
throw new Error('Incremental export failed for cached document. Please reload and retry.');
|
||||
throw new Error('Incremental export failed for cached document. Please reload and retry.', {
|
||||
cause: incrementalError,
|
||||
});
|
||||
}
|
||||
console.warn(
|
||||
'[handleGeneratePdf] Incremental export failed, falling back to full export',
|
||||
@@ -1340,7 +1342,9 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
pdfBlob = response.data;
|
||||
} catch (incrementalError) {
|
||||
if (isLazyMode && cachedJobId) {
|
||||
throw new Error('Incremental export failed for cached document. Please reload and retry.');
|
||||
throw new Error('Incremental export failed for cached document. Please reload and retry.', {
|
||||
cause: incrementalError,
|
||||
});
|
||||
}
|
||||
console.warn(
|
||||
'[handleSaveToWorkbench] Incremental export failed, falling back to full export',
|
||||
@@ -1354,7 +1358,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||
|
||||
const payload = buildPayload();
|
||||
if (!payload) {
|
||||
throw new Error('Failed to build payload');
|
||||
throw new Error('Failed to build payload', { cause: incrementalError });
|
||||
}
|
||||
|
||||
const { document, filename } = payload;
|
||||
|
||||
@@ -174,7 +174,9 @@ export const executeToolOperationWithPrefix = async (
|
||||
|
||||
} catch (error: any) {
|
||||
console.error(`❌ ${operationName} failed:`, error);
|
||||
throw new Error(`${operationName} operation failed: ${error.response?.data || error.message}`);
|
||||
throw new Error(`${operationName} operation failed: ${error.response?.data || error.message}`, {
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -139,7 +139,8 @@ export async function convertImageToPdf(
|
||||
} catch (error) {
|
||||
console.error('Error converting image to PDF:', error);
|
||||
throw new Error(
|
||||
`Failed to convert image to PDF: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
`Failed to convert image to PDF: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
{ cause: error }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,10 +167,10 @@ const chunkedDiff = (
|
||||
const remaining2 = Math.max(0, words2.length - index2);
|
||||
|
||||
let windowSize = Math.max(dynamicChunkSize, buffer1.length, buffer2.length);
|
||||
let window1: string[] = [];
|
||||
let window2: string[] = [];
|
||||
let chunkTokens: CompareDiffToken[] = [];
|
||||
let reachedEnd = false;
|
||||
let window1: string[];
|
||||
let window2: string[];
|
||||
let chunkTokens: CompareDiffToken[];
|
||||
let reachedEnd: boolean;
|
||||
|
||||
while (true) {
|
||||
const take1 = Math.min(Math.max(0, windowSize - buffer1.length), remaining1);
|
||||
@@ -220,7 +220,6 @@ const chunkedDiff = (
|
||||
flushRemainder();
|
||||
return;
|
||||
}
|
||||
windowSize = Math.min(windowSize + dynamicStep, dynamicMaxWindow);
|
||||
stallIterations += 1;
|
||||
if (stallIterations >= 3) {
|
||||
increaseChunkSizes();
|
||||
@@ -422,7 +421,7 @@ self.onmessage = (event: MessageEvent<CompareWorkerRequest>) => {
|
||||
},
|
||||
{ maxProcessedTokens: runtimeMaxProcessedTokens, minUnchangedRatio: runtimeMinUnchangedRatio }
|
||||
);
|
||||
} catch (err) {
|
||||
} catch (err) {
|
||||
const error = err as Error & { __earlyStop?: boolean };
|
||||
if (error && (error.__earlyStop || error.message === 'EARLY_STOP_TOO_DISSIMILAR')) {
|
||||
const response: CompareWorkerResponse = {
|
||||
|
||||
@@ -247,9 +247,9 @@ export class AuthService {
|
||||
error.response?.data?.msg ||
|
||||
error.response?.data?.message ||
|
||||
error.message;
|
||||
throw new Error(message || 'Sign up failed');
|
||||
throw new Error(message || 'Sign up failed', { cause: error });
|
||||
}
|
||||
throw error instanceof Error ? error : new Error('Sign up failed');
|
||||
throw error instanceof Error ? error : new Error('Sign up failed', { cause: error });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ export class AuthService {
|
||||
await this.saveTokenEverywhere(token);
|
||||
} catch (error) {
|
||||
console.error('[Desktop AuthService] Failed to save token:', error);
|
||||
throw new Error('Failed to save authentication token');
|
||||
throw new Error('Failed to save authentication token', { cause: error });
|
||||
}
|
||||
|
||||
// Save user info to store
|
||||
@@ -320,37 +320,49 @@ export class AuthService {
|
||||
// Authentication errors
|
||||
if (errMsg.includes('401') || errMsg.includes('unauthorized') || errMsg.includes('invalid credentials')) {
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('Invalid username or password. Please check your credentials and try again.');
|
||||
throw new Error('Invalid username or password. Please check your credentials and try again.', {
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
// Server not found or unreachable
|
||||
else if (errMsg.includes('connection refused') || errMsg.includes('econnrefused')) {
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('Cannot connect to server. Please check the server URL and ensure the server is running.');
|
||||
throw new Error('Cannot connect to server. Please check the server URL and ensure the server is running.', {
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
// Timeout
|
||||
else if (errMsg.includes('timeout') || errMsg.includes('timed out')) {
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('Login request timed out. Please check your network connection and try again.');
|
||||
throw new Error('Login request timed out. Please check your network connection and try again.', {
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
// DNS failure
|
||||
else if (errMsg.includes('getaddrinfo') || errMsg.includes('dns') || errMsg.includes('not found') || errMsg.includes('enotfound')) {
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('Cannot resolve server address. Please check the server URL is correct.');
|
||||
throw new Error('Cannot resolve server address. Please check the server URL is correct.', { cause: error });
|
||||
}
|
||||
// SSL/TLS errors
|
||||
else if (errMsg.includes('ssl') || errMsg.includes('tls') || errMsg.includes('certificate') || errMsg.includes('cert')) {
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('SSL/TLS certificate error. Server may have an invalid or self-signed certificate.');
|
||||
throw new Error('SSL/TLS certificate error. Server may have an invalid or self-signed certificate.', {
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
// 404 - endpoint not found
|
||||
else if (errMsg.includes('404') || errMsg.includes('not found')) {
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('Login endpoint not found. Please ensure you are connecting to a valid Stirling PDF server.');
|
||||
throw new Error('Login endpoint not found. Please ensure you are connecting to a valid Stirling PDF server.', {
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
// 403 - security disabled
|
||||
else if (errMsg.includes('403') || errMsg.includes('forbidden')) {
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('Login is not enabled on this server. Please enable security mode (DOCKER_ENABLE_SECURITY=true).');
|
||||
throw new Error('Login is not enabled on this server. Please enable security mode (DOCKER_ENABLE_SECURITY=true).', {
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user