mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-12-18 20:04:17 +01:00
linting and frontend validation and change requests to remove any types and clean up functinos
This commit is contained in:
parent
fbb077d2fd
commit
a99dd444c7
@ -165,7 +165,7 @@ const CompareDocumentPane = ({
|
||||
offsetPixels: OFFSET_PIXELS,
|
||||
});
|
||||
|
||||
const { highlightOffset, baseWidth, baseHeight, containerWidth, containerHeight, innerScale } = metrics;
|
||||
const { highlightOffset, containerWidth, containerHeight, innerScale } = metrics;
|
||||
|
||||
// Compute clamped pan for current zoom so content always touches edges when in bounds
|
||||
const storedPan = pagePanRef.current.get(page.pageNumber) || { x: 0, y: 0 };
|
||||
|
||||
@ -45,8 +45,8 @@ const CompareWorkbenchView = ({ data }: CompareWorkbenchViewProps) => {
|
||||
|
||||
const baseFile = getFileFromSelection(data?.baseLocalFile, baseFileId, selectors);
|
||||
const comparisonFile = getFileFromSelection(data?.comparisonLocalFile, comparisonFileId, selectors);
|
||||
const baseStub = getStubFromSelection(baseFileId, selectors) as any;
|
||||
const comparisonStub = getStubFromSelection(comparisonFileId, selectors) as any;
|
||||
const baseStub = getStubFromSelection(baseFileId, selectors);
|
||||
const comparisonStub = getStubFromSelection(comparisonFileId, selectors);
|
||||
|
||||
const processedAt = result?.totals.processedAt ?? null;
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { TokenBoundingBox, WordHighlightEntry } from '@app/types/compare';
|
||||
import type { FileId } from '@app/types/file';
|
||||
import type { StirlingFile } from '@app/types/fileContext';
|
||||
import type { StirlingFile, StirlingFileStub } from '@app/types/fileContext';
|
||||
import type { PagePreview } from '@app/types/compare';
|
||||
|
||||
/** Convert hex color (#rrggbb) to rgba() string with alpha; falls back to input if invalid. */
|
||||
@ -175,8 +175,8 @@ export const getFileFromSelection = (
|
||||
|
||||
export const getStubFromSelection = (
|
||||
fileId: FileId | null,
|
||||
selectors: { getStirlingFileStub: (id: FileId) => unknown }
|
||||
): unknown | null => {
|
||||
selectors: { getStirlingFileStub: (id: FileId) => StirlingFileStub | undefined }
|
||||
): StirlingFileStub | null => {
|
||||
if (!fileId) return null;
|
||||
const stub = selectors.getStirlingFileStub(fileId);
|
||||
return stub ?? null;
|
||||
|
||||
@ -160,7 +160,7 @@ export const useComparePagePreviews = ({
|
||||
};
|
||||
}
|
||||
|
||||
const key = `${(file as any).name || 'file'}:${(file as any).size || 0}:${cacheKey ?? 'none'}`;
|
||||
const key = `${file.name || 'file'}:${file.size || 0}:${cacheKey ?? 'none'}`;
|
||||
const refreshVersion = Symbol(key);
|
||||
latestVersionMap.set(key, refreshVersion);
|
||||
const entry = getOrCreateEntry(key);
|
||||
|
||||
@ -440,6 +440,63 @@ export const useComparePanZoom = ({
|
||||
return () => { cleanupBase(); cleanupComp(); };
|
||||
}, []);
|
||||
|
||||
// Helpers for clearer pan-edge overscroll behavior
|
||||
const getVerticalOverflow = useCallback((rawY: number, maxY: number): number => {
|
||||
if (rawY < 0) return rawY; // negative -> scroll up
|
||||
if (rawY > maxY) return rawY - maxY; // positive -> scroll down
|
||||
return 0;
|
||||
}, []);
|
||||
|
||||
const normalizeApplyCandidate = useCallback((overflowY: number): number => {
|
||||
const DEADZONE = 32; // pixels
|
||||
if (overflowY < -DEADZONE) return overflowY + DEADZONE;
|
||||
if (overflowY > DEADZONE) return overflowY - DEADZONE;
|
||||
return 0;
|
||||
}, []);
|
||||
|
||||
const applyIncrementalScroll = useCallback((container: HTMLDivElement, isBase: boolean, applyCandidate: number) => {
|
||||
const STEP = 48; // pixels per incremental scroll
|
||||
const key = isBase ? 'base' : 'comparison';
|
||||
const deltaSinceLast = applyCandidate - edgeOverscrollRef.current[key];
|
||||
const magnitude = Math.abs(deltaSinceLast);
|
||||
if (magnitude < STEP) return;
|
||||
|
||||
const stepDelta = Math.sign(deltaSinceLast) * Math.floor(magnitude / STEP) * STEP;
|
||||
edgeOverscrollRef.current[key] += stepDelta;
|
||||
|
||||
const prevTop = container.scrollTop;
|
||||
const nextTop = Math.max(0, Math.min(container.scrollHeight - container.clientHeight, prevTop + stepDelta));
|
||||
if (nextTop === prevTop) return;
|
||||
|
||||
container.scrollTop = nextTop;
|
||||
if (isScrollLinked) {
|
||||
const sourceIsBase = isBase;
|
||||
const target = isBase ? comparisonScrollRef.current : baseScrollRef.current;
|
||||
if (target) {
|
||||
const targetVerticalRange = Math.max(1, target.scrollHeight - target.clientHeight);
|
||||
const mappedTop = mapScrollTopBetweenPanes(nextTop, sourceIsBase);
|
||||
const deltaPx = sourceIsBase
|
||||
? scrollLinkAnchorsRef.current.deltaPixelsBaseToComp
|
||||
: scrollLinkAnchorsRef.current.deltaPixelsCompToBase;
|
||||
const desiredTop = Math.max(0, Math.min(targetVerticalRange, mappedTop + deltaPx));
|
||||
target.scrollTop = desiredTop;
|
||||
}
|
||||
}
|
||||
}, [isScrollLinked, mapScrollTopBetweenPanes]);
|
||||
|
||||
const handlePanEdgeOverscroll = useCallback((rawY: number, boundsMaxY: number, isBase: boolean) => {
|
||||
const container = isBase ? baseScrollRef.current : comparisonScrollRef.current;
|
||||
if (!container) return;
|
||||
const overflowY = getVerticalOverflow(rawY, boundsMaxY);
|
||||
const applyCandidate = normalizeApplyCandidate(overflowY);
|
||||
if (applyCandidate !== 0) {
|
||||
applyIncrementalScroll(container, isBase, applyCandidate);
|
||||
} else {
|
||||
// Reset accumulator when back within deadzone
|
||||
edgeOverscrollRef.current[isBase ? 'base' : 'comparison'] = 0;
|
||||
}
|
||||
}, [applyIncrementalScroll, getVerticalOverflow, normalizeApplyCandidate]);
|
||||
|
||||
const beginPan = useCallback(
|
||||
(pane: Pane, event: ReactMouseEvent<HTMLDivElement>) => {
|
||||
if (!isPanMode) return;
|
||||
@ -489,47 +546,7 @@ export const useComparePanZoom = ({
|
||||
};
|
||||
|
||||
// On vertical overscroll beyond pan bounds, scroll the page (with deadzone + incremental steps)
|
||||
const container = isBase ? baseScrollRef.current : comparisonScrollRef.current;
|
||||
if (container) {
|
||||
const DEADZONE = 32; // pixels
|
||||
const STEP = 48; // pixels per incremental scroll
|
||||
let overflowY = 0;
|
||||
if (rawY < 0) overflowY = rawY; // negative -> scroll up
|
||||
else if (rawY > bounds.maxY) overflowY = rawY - bounds.maxY; // positive -> scroll down
|
||||
let applyCandidate = 0;
|
||||
if (overflowY < -DEADZONE) applyCandidate = overflowY + DEADZONE;
|
||||
else if (overflowY > DEADZONE) applyCandidate = overflowY - DEADZONE;
|
||||
if (applyCandidate !== 0) {
|
||||
const key = isBase ? 'base' : 'comparison';
|
||||
const deltaSinceLast = applyCandidate - edgeOverscrollRef.current[key];
|
||||
const magnitude = Math.abs(deltaSinceLast);
|
||||
if (magnitude >= STEP) {
|
||||
const stepDelta = Math.sign(deltaSinceLast) * Math.floor(magnitude / STEP) * STEP;
|
||||
edgeOverscrollRef.current[key] += stepDelta;
|
||||
const prevTop = container.scrollTop;
|
||||
const nextTop = Math.max(0, Math.min(container.scrollHeight - container.clientHeight, prevTop + stepDelta));
|
||||
if (nextTop !== prevTop) {
|
||||
container.scrollTop = nextTop;
|
||||
if (isScrollLinked) {
|
||||
const sourceIsBase = isBase;
|
||||
const target = isBase ? comparisonScrollRef.current : baseScrollRef.current;
|
||||
if (target) {
|
||||
const targetVerticalRange = Math.max(1, target.scrollHeight - target.clientHeight);
|
||||
const mappedTop = mapScrollTopBetweenPanes(nextTop, sourceIsBase);
|
||||
const deltaPx = sourceIsBase
|
||||
? scrollLinkAnchorsRef.current.deltaPixelsBaseToComp
|
||||
: scrollLinkAnchorsRef.current.deltaPixelsCompToBase;
|
||||
const desiredTop = Math.max(0, Math.min(targetVerticalRange, mappedTop + deltaPx));
|
||||
target.scrollTop = desiredTop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Reset accumulator when back within deadzone
|
||||
edgeOverscrollRef.current[isBase ? 'base' : 'comparison'] = 0;
|
||||
}
|
||||
}
|
||||
handlePanEdgeOverscroll(rawY, bounds.maxY, isBase);
|
||||
|
||||
if (isBase) {
|
||||
setBasePan(desired);
|
||||
@ -729,45 +746,7 @@ export const useComparePanZoom = ({
|
||||
y: Math.max(0, Math.min(bounds.maxY, rawY)),
|
||||
};
|
||||
|
||||
const container = isBase ? baseScrollRef.current : comparisonScrollRef.current;
|
||||
if (container) {
|
||||
const DEADZONE = 32;
|
||||
const STEP = 48;
|
||||
let overflowY = 0;
|
||||
if (rawY < 0) overflowY = rawY; else if (rawY > bounds.maxY) overflowY = rawY - bounds.maxY;
|
||||
let applyCandidate = 0;
|
||||
if (overflowY < -DEADZONE) applyCandidate = overflowY + DEADZONE;
|
||||
else if (overflowY > DEADZONE) applyCandidate = overflowY - DEADZONE;
|
||||
if (applyCandidate !== 0) {
|
||||
const key = isBase ? 'base' : 'comparison';
|
||||
const deltaSinceLast = applyCandidate - edgeOverscrollRef.current[key];
|
||||
const magnitude = Math.abs(deltaSinceLast);
|
||||
if (magnitude >= STEP) {
|
||||
const stepDelta = Math.sign(deltaSinceLast) * Math.floor(magnitude / STEP) * STEP;
|
||||
edgeOverscrollRef.current[key] += stepDelta;
|
||||
const prevTop = container.scrollTop;
|
||||
const nextTop = Math.max(0, Math.min(container.scrollHeight - container.clientHeight, prevTop + stepDelta));
|
||||
if (nextTop !== prevTop) {
|
||||
container.scrollTop = nextTop;
|
||||
if (isScrollLinked) {
|
||||
const sourceIsBase = isBase;
|
||||
const target = isBase ? comparisonScrollRef.current : baseScrollRef.current;
|
||||
if (target) {
|
||||
const targetVerticalRange = Math.max(1, target.scrollHeight - target.clientHeight);
|
||||
const mappedTop = mapScrollTopBetweenPanes(nextTop, sourceIsBase);
|
||||
const deltaPx = sourceIsBase
|
||||
? scrollLinkAnchorsRef.current.deltaPixelsBaseToComp
|
||||
: scrollLinkAnchorsRef.current.deltaPixelsCompToBase;
|
||||
const desiredTop = Math.max(0, Math.min(targetVerticalRange, mappedTop + deltaPx));
|
||||
target.scrollTop = desiredTop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
edgeOverscrollRef.current[isBase ? 'base' : 'comparison'] = 0;
|
||||
}
|
||||
}
|
||||
handlePanEdgeOverscroll(rawY, bounds.maxY, isBase);
|
||||
if (isBase) {
|
||||
setBasePan(desired);
|
||||
} else {
|
||||
|
||||
@ -36,7 +36,7 @@ export interface CompareOperationHook extends ToolOperationHook<CompareParameter
|
||||
|
||||
export const useCompareOperation = (): CompareOperationHook => {
|
||||
const { t } = useTranslation();
|
||||
const { selectors, actions: fileActions } = useFileContext();
|
||||
const { selectors } = useFileContext();
|
||||
const workerRef = useRef<Worker | null>(null);
|
||||
const previousUrl = useRef<string | null>(null);
|
||||
const activeRunIdRef = useRef(0);
|
||||
@ -59,7 +59,7 @@ export const useCompareOperation = (): CompareOperationHook => {
|
||||
const ensureWorker = useCallback(() => {
|
||||
if (!workerRef.current) {
|
||||
workerRef.current = new Worker(
|
||||
new URL('../../../../workers/compareWorker.ts', import.meta.url),
|
||||
new URL('/@app/workers/compareWorker.ts', import.meta.url),
|
||||
{ type: 'module' }
|
||||
);
|
||||
}
|
||||
@ -297,8 +297,12 @@ export const useCompareOperation = (): CompareOperationHook => {
|
||||
expandable: false,
|
||||
buttonText: t('compare.earlyDissimilarity.stopButton', 'Stop comparison'),
|
||||
buttonCallback: () => {
|
||||
try { cancelOperation(); } catch {}
|
||||
try { window.dispatchEvent(new CustomEvent('compare:clear-selected')); } catch {}
|
||||
try { cancelOperation(); } catch {
|
||||
console.error('Failed to cancel operation');
|
||||
}
|
||||
try { window.dispatchEvent(new CustomEvent('compare:clear-selected')); } catch {
|
||||
console.error('Failed to dispatch clear selected event');
|
||||
}
|
||||
if (dissimilarityToastIdRef.current) {
|
||||
dismissToast(dissimilarityToastIdRef.current);
|
||||
dissimilarityToastIdRef.current = null;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { pdfWorkerManager } from '@app/services/pdfWorkerManager';
|
||||
import type { PDFDocumentProxy } from 'pdfjs-dist/legacy/build/pdf.mjs';
|
||||
import { PagePreview } from '@app/types/compare';
|
||||
|
||||
const DISPLAY_SCALE = 1;
|
||||
@ -36,11 +37,11 @@ export const useProgressivePagePreviews = ({
|
||||
loadingPages: new Set(),
|
||||
});
|
||||
|
||||
const pdfRef = useRef<any>(null);
|
||||
const pdfRef = useRef<PDFDocumentProxy | null>(null);
|
||||
const abortControllerRef = useRef<AbortController | null>(null);
|
||||
|
||||
const renderPageBatch = useCallback(async (
|
||||
pdf: any,
|
||||
pdf: PDFDocumentProxy,
|
||||
pageNumbers: number[],
|
||||
signal: AbortSignal
|
||||
): Promise<PagePreview[]> => {
|
||||
@ -114,7 +115,9 @@ export const useProgressivePagePreviews = ({
|
||||
}));
|
||||
|
||||
try {
|
||||
const previews = await renderPageBatch(pdfRef.current, pagesToLoad, signal);
|
||||
const pdfDoc = pdfRef.current;
|
||||
if (!pdfDoc) return;
|
||||
const previews = await renderPageBatch(pdfDoc, pagesToLoad, signal);
|
||||
|
||||
if (!signal.aborted) {
|
||||
setState(prev => {
|
||||
|
||||
@ -80,7 +80,7 @@ const Compare = (props: BaseToolProps) => {
|
||||
// Use a static label at registration time to avoid re-registering on i18n changes
|
||||
label: 'Compare view',
|
||||
icon: compareIcon,
|
||||
component: CompareWorkbenchView as any,
|
||||
component: CompareWorkbenchView,
|
||||
});
|
||||
|
||||
return () => {
|
||||
|
||||
@ -47,7 +47,7 @@ const ValidateSignature = (props: BaseToolProps) => {
|
||||
workbenchId: REPORT_WORKBENCH_ID,
|
||||
label: t('validateSignature.report.shortTitle', 'Signature Report'),
|
||||
icon: reportIcon,
|
||||
component: ValidateSignatureReportView as any,
|
||||
component: ValidateSignatureReportView,
|
||||
});
|
||||
|
||||
return () => {
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
// Shared text diff and normalization utilities for compare tool
|
||||
|
||||
export const PARAGRAPH_SENTINEL = '\uE000¶';
|
||||
|
||||
export const shouldConcatWithoutSpace = (word: string) => {
|
||||
return /^[.,!?;:)\]}]/.test(word) || word.startsWith("'") || word === "'s";
|
||||
};
|
||||
@ -11,17 +9,6 @@ export const appendWord = (existing: string, word: string) => {
|
||||
if (shouldConcatWithoutSpace(word)) return `${existing}${word}`;
|
||||
return `${existing} ${word}`;
|
||||
};
|
||||
|
||||
export const normalizeToken = (s: string) =>
|
||||
s
|
||||
.normalize('NFKC')
|
||||
.replace(/[\u00AD\u200B-\u200F\u202A-\u202E]/g, '') // soft hyphen + zero width controls
|
||||
.replace(/[“”]/g, '"')
|
||||
.replace(/[‘’]/g, "'")
|
||||
.replace(/[–—]/g, '-')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
|
||||
export const tokenize = (text: string): string[] => text.split(/\s+/).filter(Boolean);
|
||||
|
||||
type TokenType = 'unchanged' | 'removed' | 'added';
|
||||
|
||||
@ -264,8 +264,7 @@ const chunkedDiff = (
|
||||
if (unchangedRatio < runtimeStop.minUnchangedRatio) {
|
||||
// Signal early termination for extreme dissimilarity
|
||||
const err = new Error('EARLY_STOP_TOO_DISSIMILAR');
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(err as any).__earlyStop = true;
|
||||
(err as Error & { __earlyStop?: boolean }).__earlyStop = true;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@ -423,10 +422,9 @@ self.onmessage = (event: MessageEvent<CompareWorkerRequest>) => {
|
||||
},
|
||||
{ maxProcessedTokens: runtimeMaxProcessedTokens, minUnchangedRatio: runtimeMinUnchangedRatio }
|
||||
);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const anyErr = err as any;
|
||||
if (anyErr && (anyErr.__earlyStop || anyErr?.message === 'EARLY_STOP_TOO_DISSIMILAR')) {
|
||||
} catch (err) {
|
||||
const error = err as Error & { __earlyStop?: boolean };
|
||||
if (error && (error.__earlyStop || error.message === 'EARLY_STOP_TOO_DISSIMILAR')) {
|
||||
const response: CompareWorkerResponse = {
|
||||
type: 'error',
|
||||
message:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user