Annotation fixes

This commit is contained in:
Reece 2025-12-16 17:48:34 +00:00
parent 68f8bb749f
commit 72ddd997a2
4 changed files with 58 additions and 60 deletions

View File

@ -4,7 +4,8 @@ import { PdfAnnotationSubtype, PdfAnnotationIcon } from '@embedpdf/models';
import type { AnnotationToolId, AnnotationToolOptions, AnnotationAPI } from '@app/components/viewer/viewerTypes';
export const AnnotationAPIBridge = forwardRef<AnnotationAPI>(function AnnotationAPIBridge(_props, ref) {
const annotationApi = useAnnotationCapability();
// Use the provided annotation API just like SignatureAPIBridge/HistoryAPIBridge
const { provides: annotationApi } = useAnnotationCapability();
const getIconEnum = (icon?: string): PdfAnnotationIcon => {
switch (icon) {
@ -164,13 +165,16 @@ export const AnnotationAPIBridge = forwardRef<AnnotationAPI>(function Annotation
if (!annotationApi) return;
const defaults = buildAnnotationDefaults(toolId, options);
const api = annotationApi as any;
if (defaults) {
api.setToolDefaults?.(toolId, defaults);
// Reset tool first, then activate (like SignatureAPIBridge does)
annotationApi.setActiveTool(null);
annotationApi.setActiveTool(toolId === 'select' ? null : toolId);
// Verify tool was activated before setting defaults (like SignatureAPIBridge does)
const activeTool = annotationApi.getActiveTool();
if (activeTool && activeTool.id === toolId && defaults) {
annotationApi.setToolDefaults(toolId, defaults);
}
api.setActiveTool?.(toolId === 'select' ? null : toolId);
},
[annotationApi, buildAnnotationDefaults]
);

View File

@ -85,8 +85,8 @@ const EmbedPdfViewerContent = ({
// Check if we're in an annotation tool
const { selectedTool } = useNavigationState();
// Tools that require the annotation layer (Sign, Add Text, Add Image)
const isInAnnotationTool = selectedTool === 'sign' || selectedTool === 'addText' || selectedTool === 'addImage';
// Tools that require the annotation layer (Sign, Add Text, Add Image, Annotate)
const isInAnnotationTool = selectedTool === 'sign' || selectedTool === 'addText' || selectedTool === 'addImage' || selectedTool === 'annotate';
// Sync isAnnotationMode in ViewerContext with current tool
useEffect(() => {

View File

@ -60,7 +60,7 @@ interface LocalEmbedPDFProps {
historyApiRef?: React.RefObject<HistoryAPI>;
}
export function LocalEmbedPDF({ file, url, enableAnnotations = false, showBakedAnnotations = true, onSignatureAdded, signatureApiRef, historyApiRef }: LocalEmbedPDFProps) {
export function LocalEmbedPDF({ file, url, enableAnnotations = false, showBakedAnnotations = true, onSignatureAdded, signatureApiRef, annotationApiRef, historyApiRef }: LocalEmbedPDFProps) {
const { t } = useTranslation();
const [pdfUrl, setPdfUrl] = useState<string | null>(null);
const [, setAnnotations] = useState<Array<{id: string, pageIndex: number, rect: any}>>([]);

View File

@ -1,6 +1,6 @@
import { useEffect, useMemo, useState, useContext, useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { Alert, Text, Group, ActionIcon, Stack, Divider, Slider, Box, Tooltip as MantineTooltip, Button, TextInput, Textarea, NumberInput, Tooltip } from '@mantine/core';
import { Alert, Text, Group, ActionIcon, Stack, Divider, Slider, Box, Tooltip as MantineTooltip, Button, TextInput, Textarea, NumberInput, Tooltip, Paper } from '@mantine/core';
import { createToolFlow } from '@app/components/tools/shared/createToolFlow';
import { useNavigation } from '@app/contexts/NavigationContext';
@ -12,6 +12,7 @@ import { ColorPicker, ColorSwatchButton } from '@app/components/annotation/share
import { ImageUploader } from '@app/components/annotation/shared/ImageUploader';
import LocalIcon from '@app/components/shared/LocalIcon';
import type { AnnotationToolId } from '@app/components/viewer/viewerTypes';
import { SuggestedToolsSection } from '@app/components/tools/shared/SuggestedToolsSection';
const Annotate = (_props: BaseToolProps) => {
const { t } = useTranslation();
@ -366,10 +367,11 @@ const Annotate = (_props: BaseToolProps) => {
);
const defaultStyleControls = (
<Stack gap="sm">
{activeTool === 'stamp' ? (
<>
<Text size="sm" fw={600}>{t('annotation.stampSettings', 'Stamp Settings')}</Text>
<Paper withBorder p="sm" radius="md">
<Stack gap="sm">
{activeTool === 'stamp' ? (
<>
<Text size="sm" fw={600}>{t('annotation.stampSettings', 'Stamp Settings')}</Text>
<ImageUploader
onImageChange={async (file) => {
if (file) {
@ -575,9 +577,8 @@ const Annotate = (_props: BaseToolProps) => {
</Box>
</>
)}
</>
)}
</Stack>
</Stack>
</Paper>
);
const selectedAnnotationControls = selectedAnn && (() => {
@ -586,7 +587,8 @@ const Annotate = (_props: BaseToolProps) => {
// Type 9: Highlight, Type 10: Underline, Type 11: Squiggly, Type 12: Strikeout
if ([9, 10, 11, 12].includes(type)) {
return (
<Stack gap="sm">
<Paper withBorder p="sm" radius="md">
<Stack gap="sm">
<Text size="sm" fw={600}>{t('annotation.editTextMarkup', 'Edit Text Markup')}</Text>
<Box>
<Text size="xs" c="dimmed" mb={4}>{t('annotation.color', 'Color')}</Text>
@ -614,15 +616,17 @@ const Annotate = (_props: BaseToolProps) => {
}}
/>
</Box>
</Stack>
</Stack>
</Paper>
);
}
// Type 15: Ink (pen)
if (type === 15) {
return (
<Stack gap="sm">
<Text size="sm" fw={600}>{t('annotation.editInk', 'Edit Pen')}</Text>
<Paper withBorder p="sm" radius="md">
<Stack gap="sm">
<Text size="sm" fw={600}>{t('annotation.editInk', 'Edit Pen')}</Text>
<Box>
<Text size="xs" c="dimmed" mb={4}>{t('annotation.color', 'Color')}</Text>
<ColorSwatchButton
@ -654,15 +658,17 @@ const Annotate = (_props: BaseToolProps) => {
}}
/>
</Box>
</Stack>
</Stack>
</Paper>
);
}
// Type 3: Text box
if (type === 3) {
return (
<Stack gap="sm">
<Text size="sm" fw={600}>{t('annotation.editText', 'Edit Text Box')}</Text>
<Paper withBorder p="sm" radius="md">
<Stack gap="sm">
<Text size="sm" fw={600}>{t('annotation.editText', 'Edit Text Box')}</Text>
<Box>
<Text size="xs" c="dimmed" mb={4}>{t('annotation.color', 'Color')}</Text>
<ColorSwatchButton
@ -784,15 +790,17 @@ const Annotate = (_props: BaseToolProps) => {
</ActionIcon>
</Group>
</Box>
</Stack>
</Stack>
</Paper>
);
}
// Type 4: Line
if (type === 4) {
return (
<Stack gap="sm">
<Text size="sm" fw={600}>{t('annotation.editLine', 'Edit Line')}</Text>
<Paper withBorder p="sm" radius="md">
<Stack gap="sm">
<Text size="sm" fw={600}>{t('annotation.editLine', 'Edit Line')}</Text>
<Box>
<Text size="xs" c="dimmed" mb={4}>{t('annotation.color', 'Color')}</Text>
<ColorSwatchButton
@ -839,7 +847,8 @@ const Annotate = (_props: BaseToolProps) => {
}}
/>
</Box>
</Stack>
</Stack>
</Paper>
);
}
@ -847,8 +856,9 @@ const Annotate = (_props: BaseToolProps) => {
if ([5, 6, 7].includes(type)) {
const shapeName = type === 5 ? 'Square' : type === 6 ? 'Circle' : 'Polygon';
return (
<Stack gap="sm">
<Text size="sm" fw={600}>{t(`annotation.edit${shapeName}`, `Edit ${shapeName}`)}</Text>
<Paper withBorder p="sm" radius="md">
<Stack gap="sm">
<Text size="sm" fw={600}>{t(`annotation.edit${shapeName}`, `Edit ${shapeName}`)}</Text>
<Group gap="md">
<Stack gap={4} align="center">
<Text size="xs" c="dimmed">{t('annotation.strokeColor', 'Stroke Color')}</Text>
@ -932,43 +942,24 @@ const Annotate = (_props: BaseToolProps) => {
}
</Button>
</Group>
</Stack>
</Stack>
</Paper>
);
}
// Default fallback
return (
<Stack gap="sm">
<Text size="sm" fw={600}>{t('annotation.editSelected', 'Edit Annotation')}</Text>
<Text size="xs" c="dimmed">{t('annotation.unsupportedType', 'This annotation type is not fully supported for editing.')}</Text>
</Stack>
<Paper withBorder p="sm" radius="md">
<Stack gap="sm">
<Text size="sm" fw={600}>{t('annotation.editSelected', 'Edit Annotation')}</Text>
<Text size="xs" c="dimmed">{t('annotation.unsupportedType', 'This annotation type is not fully supported for editing.')}</Text>
</Stack>
</Paper>
);
})();
const saveAndColorPicker = (
const colorPickerComponent = (
<>
<Button
fullWidth
variant="light"
leftSection={<LocalIcon icon="save" width="1rem" height="1rem" />}
onClick={async () => {
try {
const pdfArrayBuffer = await viewerContext?.exportActions?.saveAsCopy?.();
if (!pdfArrayBuffer) return;
const blob = new Blob([pdfArrayBuffer], { type: 'application/pdf' });
const fileName = selectors.getFiles()[0]?.name || 'annotated.pdf';
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = fileName;
link.click();
URL.revokeObjectURL(link.href);
} catch (error) {
console.error('Failed to save annotated PDF', error);
}
}}
>
{t('rightRail.save', 'Save')}
</Button>
<ColorPicker
isOpen={isColorPickerOpen}
onClose={() => setIsColorPickerOpen(false)}
@ -1224,8 +1215,11 @@ const Annotate = (_props: BaseToolProps) => {
{/* Edit Selected */}
{selectedAnn && selectedAnnotationControls}
{/* Save Button */}
{saveAndColorPicker}
{/* Color Picker */}
{colorPickerComponent}
{/* Suggested Tools */}
<SuggestedToolsSection currentTool="annotate" />
</Stack>
),
},