mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-11-16 01:21:16 +01:00
tweaks
This commit is contained in:
parent
c8bf43ea6b
commit
c4f8c42a5a
@ -2256,34 +2256,7 @@
|
|||||||
},
|
},
|
||||||
"clear": "Clear",
|
"clear": "Clear",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
"saved": {
|
"saved": "Saved Signatures",
|
||||||
"heading": "Saved signatures",
|
|
||||||
"description": "Reuse saved signatures at any time.",
|
|
||||||
"emptyTitle": "No saved signatures yet",
|
|
||||||
"emptyDescription": "Draw, upload, or type a signature above, then use \"Save to library\" to keep up to {{max}} favourites ready to use.",
|
|
||||||
"type": {
|
|
||||||
"canvas": "Drawing",
|
|
||||||
"image": "Upload",
|
|
||||||
"text": "Text"
|
|
||||||
},
|
|
||||||
"limitTitle": "Limit reached",
|
|
||||||
"limitDescription": "Remove a saved signature before adding new ones (max {{max}}).",
|
|
||||||
"carouselPosition": "{{current}} of {{total}}",
|
|
||||||
"prev": "Previous",
|
|
||||||
"next": "Next",
|
|
||||||
"delete": "Remove",
|
|
||||||
"label": "Label",
|
|
||||||
"defaultLabel": "Signature",
|
|
||||||
"defaultCanvasLabel": "Drawing signature",
|
|
||||||
"defaultImageLabel": "Uploaded signature",
|
|
||||||
"defaultTextLabel": "Typed signature",
|
|
||||||
"saveButton": "Save signature",
|
|
||||||
"saveUnavailable": "Create a signature first to save it.",
|
|
||||||
"noChanges": "Current signature is already saved.",
|
|
||||||
"status": {
|
|
||||||
"saved": "Saved"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"save": "Save Signature",
|
"save": "Save Signature",
|
||||||
"applySignatures": "Apply Signatures",
|
"applySignatures": "Apply Signatures",
|
||||||
"personalSigs": "Personal Signatures",
|
"personalSigs": "Personal Signatures",
|
||||||
@ -2318,7 +2291,6 @@
|
|||||||
"title": "How to add signature",
|
"title": "How to add signature",
|
||||||
"canvas": "After drawing your signature in the canvas, close the modal then click anywhere on the PDF to place it.",
|
"canvas": "After drawing your signature in the canvas, close the modal then click anywhere on the PDF to place it.",
|
||||||
"image": "After uploading your signature image above, click anywhere on the PDF to place it.",
|
"image": "After uploading your signature image above, click anywhere on the PDF to place it.",
|
||||||
"saved": "Select a saved signature above, then click anywhere on the PDF to place it.",
|
|
||||||
"text": "After entering your name above, click anywhere on the PDF to place your signature."
|
"text": "After entering your name above, click anywhere on the PDF to place your signature."
|
||||||
},
|
},
|
||||||
"mode": {
|
"mode": {
|
||||||
|
|||||||
@ -283,7 +283,7 @@ export const DrawingCanvas: React.FC<DrawingCanvasProps> = ({
|
|||||||
touchAction: 'none',
|
touchAction: 'none',
|
||||||
backgroundColor: 'white',
|
backgroundColor: 'white',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
maxWidth: '800px',
|
maxWidth: '50rem',
|
||||||
height: '25rem',
|
height: '25rem',
|
||||||
cursor: 'crosshair',
|
cursor: 'crosshair',
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -31,7 +31,6 @@ export const SavedSignaturesSection = ({
|
|||||||
const [labelDrafts, setLabelDrafts] = useState<Record<string, string>>({});
|
const [labelDrafts, setLabelDrafts] = useState<Record<string, string>>({});
|
||||||
const [activeIndex, setActiveIndex] = useState(0);
|
const [activeIndex, setActiveIndex] = useState(0);
|
||||||
const activeSignature = signatures[activeIndex];
|
const activeSignature = signatures[activeIndex];
|
||||||
const activeSignatureRef = useRef<SavedSignature | null>(activeSignature ?? null);
|
|
||||||
const appliedSignatureIdRef = useRef<string | null>(null);
|
const appliedSignatureIdRef = useRef<string | null>(null);
|
||||||
const onUseSignatureRef = useRef(onUseSignature);
|
const onUseSignatureRef = useRef(onUseSignature);
|
||||||
|
|
||||||
@ -39,10 +38,6 @@ export const SavedSignaturesSection = ({
|
|||||||
onUseSignatureRef.current = onUseSignature;
|
onUseSignatureRef.current = onUseSignature;
|
||||||
}, [onUseSignature]);
|
}, [onUseSignature]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
activeSignatureRef.current = activeSignature ?? null;
|
|
||||||
}, [activeSignature]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLabelDrafts(prev => {
|
setLabelDrafts(prev => {
|
||||||
const nextDrafts: Record<string, string> = {};
|
const nextDrafts: Record<string, string> = {};
|
||||||
@ -187,19 +182,18 @@ export const SavedSignaturesSection = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const signature = activeSignatureRef.current;
|
if (!activeSignature || disabled) {
|
||||||
if (!signature || disabled) {
|
|
||||||
appliedSignatureIdRef.current = null;
|
appliedSignatureIdRef.current = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appliedSignatureIdRef.current === signature.id) {
|
if (appliedSignatureIdRef.current === activeSignature.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
appliedSignatureIdRef.current = signature.id;
|
appliedSignatureIdRef.current = activeSignature.id;
|
||||||
onUseSignatureRef.current(signature);
|
onUseSignatureRef.current(activeSignature);
|
||||||
}, [activeSignature?.id, disabled]);
|
}, [activeSignature, disabled]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
|
|||||||
@ -588,7 +588,9 @@ const SignSettings = ({
|
|||||||
const timer = window.setTimeout(() => {
|
const timer = window.setTimeout(() => {
|
||||||
onActivateSignaturePlacement?.();
|
onActivateSignaturePlacement?.();
|
||||||
}, PLACEMENT_ACTIVATION_DELAY);
|
}, PLACEMENT_ACTIVATION_DELAY);
|
||||||
return () => window.clearTimeout(timer);
|
return () => {
|
||||||
|
window.clearTimeout(timer);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onActivateSignaturePlacement?.();
|
onActivateSignaturePlacement?.();
|
||||||
@ -625,7 +627,9 @@ const SignSettings = ({
|
|||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
const timer = window.setTimeout(trigger, PLACEMENT_ACTIVATION_DELAY);
|
const timer = window.setTimeout(trigger, PLACEMENT_ACTIVATION_DELAY);
|
||||||
return () => window.clearTimeout(timer);
|
return () => {
|
||||||
|
window.clearTimeout(timer);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
trigger();
|
trigger();
|
||||||
@ -648,7 +652,9 @@ const SignSettings = ({
|
|||||||
const timer = window.setTimeout(() => {
|
const timer = window.setTimeout(() => {
|
||||||
onActivateSignaturePlacement?.();
|
onActivateSignaturePlacement?.();
|
||||||
}, FILE_SWITCH_ACTIVATION_DELAY);
|
}, FILE_SWITCH_ACTIVATION_DELAY);
|
||||||
return () => window.clearTimeout(timer);
|
return () => {
|
||||||
|
window.clearTimeout(timer);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onActivateSignaturePlacement?.();
|
onActivateSignaturePlacement?.();
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { useAnnotationCapability } from '@embedpdf/plugin-annotation/react';
|
|||||||
import { useSignature } from '@app/contexts/SignatureContext';
|
import { useSignature } from '@app/contexts/SignatureContext';
|
||||||
import { uuidV4 } from '@embedpdf/models';
|
import { uuidV4 } from '@embedpdf/models';
|
||||||
import type { HistoryAPI } from '@app/components/viewer/viewerTypes';
|
import type { HistoryAPI } from '@app/components/viewer/viewerTypes';
|
||||||
|
import { ANNOTATION_RECREATION_DELAY_MS, ANNOTATION_VERIFICATION_DELAY_MS } from '@app/core/constants/app';
|
||||||
|
|
||||||
export const HistoryAPIBridge = forwardRef<HistoryAPI>(function HistoryAPIBridge(_, ref) {
|
export const HistoryAPIBridge = forwardRef<HistoryAPI>(function HistoryAPIBridge(_, ref) {
|
||||||
const { provides: historyApi } = useHistoryCapability();
|
const { provides: historyApi } = useHistoryCapability();
|
||||||
@ -59,7 +60,7 @@ export const HistoryAPIBridge = forwardRef<HistoryAPI>(function HistoryAPIBridge
|
|||||||
data: storedImageData,
|
data: storedImageData,
|
||||||
appearance: storedImageData,
|
appearance: storedImageData,
|
||||||
});
|
});
|
||||||
}, 50);
|
}, ANNOTATION_RECREATION_DELAY_MS);
|
||||||
} catch (restoreError) {
|
} catch (restoreError) {
|
||||||
console.error('HistoryAPI: Failed to restore cropped signature:', restoreError);
|
console.error('HistoryAPI: Failed to restore cropped signature:', restoreError);
|
||||||
}
|
}
|
||||||
@ -103,12 +104,12 @@ export const HistoryAPIBridge = forwardRef<HistoryAPI>(function HistoryAPIBridge
|
|||||||
// Small delay to ensure deletion completes
|
// Small delay to ensure deletion completes
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
annotationApi.createAnnotation(event.pageIndex, restoredData);
|
annotationApi.createAnnotation(event.pageIndex, restoredData);
|
||||||
}, 50);
|
}, ANNOTATION_RECREATION_DELAY_MS);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('HistoryAPI: Failed to restore annotation:', error);
|
console.error('HistoryAPI: Failed to restore annotation:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 100);
|
}, ANNOTATION_VERIFICATION_DELAY_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,6 +14,14 @@ const MIN_SIGNATURE_DIMENSION = 12;
|
|||||||
// This provides a good balance between visual fidelity and performance/memory usage.
|
// This provides a good balance between visual fidelity and performance/memory usage.
|
||||||
const TEXT_OVERSAMPLE_FACTOR = 2;
|
const TEXT_OVERSAMPLE_FACTOR = 2;
|
||||||
|
|
||||||
|
type TextStampImageResult = {
|
||||||
|
dataUrl: string;
|
||||||
|
pixelWidth: number;
|
||||||
|
pixelHeight: number;
|
||||||
|
displayWidth: number;
|
||||||
|
displayHeight: number;
|
||||||
|
};
|
||||||
|
|
||||||
const extractDataUrl = (value: unknown, depth = 0, visited: Set<unknown> = new Set()): string | undefined => {
|
const extractDataUrl = (value: unknown, depth = 0, visited: Set<unknown> = new Set()): string | undefined => {
|
||||||
if (!value || depth > 6) return undefined;
|
if (!value || depth > 6) return undefined;
|
||||||
|
|
||||||
@ -48,7 +56,7 @@ const extractDataUrl = (value: unknown, depth = 0, visited: Set<unknown> = new S
|
|||||||
const createTextStampImage = (
|
const createTextStampImage = (
|
||||||
config: SignParameters,
|
config: SignParameters,
|
||||||
displaySize?: { width: number; height: number } | null
|
displaySize?: { width: number; height: number } | null
|
||||||
): { dataUrl: string; pixelWidth: number; pixelHeight: number; displayWidth: number; displayHeight: number } | null => {
|
): TextStampImageResult | null => {
|
||||||
const text = (config.signerName ?? '').trim();
|
const text = (config.signerName ?? '').trim();
|
||||||
if (!text) {
|
if (!text) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -3,6 +3,11 @@
|
|||||||
// When no subpath, use empty string instead of '.' to avoid relative path issues
|
// When no subpath, use empty string instead of '.' to avoid relative path issues
|
||||||
export const BASE_PATH = (import.meta.env.BASE_URL || '/').replace(/\/$/, '').replace(/^\.$/, '');
|
export const BASE_PATH = (import.meta.env.BASE_URL || '/').replace(/\/$/, '').replace(/^\.$/, '');
|
||||||
|
|
||||||
|
// EmbedPDF needs time to remove annotations internally before a recreation runs.
|
||||||
|
// Without the buffer we occasionally end up with duplicate annotations or stale image data.
|
||||||
|
export const ANNOTATION_RECREATION_DELAY_MS = 50;
|
||||||
|
export const ANNOTATION_VERIFICATION_DELAY_MS = 100;
|
||||||
|
|
||||||
/** For in-app navigations when you must touch window.location (rare). */
|
/** For in-app navigations when you must touch window.location (rare). */
|
||||||
export const withBasePath = (path: string): string => {
|
export const withBasePath = (path: string): string => {
|
||||||
const clean = path.startsWith('/') ? path : `/${path}`;
|
const clean = path.startsWith('/') ? path : `/${path}`;
|
||||||
|
|||||||
@ -97,12 +97,7 @@ const writeToStorage = (entries: SavedSignature[]) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateId = () => {
|
const generateId = () => crypto.randomUUID();
|
||||||
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
||||||
return crypto.randomUUID();
|
|
||||||
}
|
|
||||||
return `sig_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useSavedSignatures = () => {
|
export const useSavedSignatures = () => {
|
||||||
const [savedSignatures, setSavedSignatures] = useState<SavedSignature[]>(() => readFromStorage());
|
const [savedSignatures, setSavedSignatures] = useState<SavedSignature[]>(() => readFromStorage());
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user