diff --git a/frontend/src/core/components/shared/AllToolsNavButton.tsx b/frontend/src/core/components/shared/AllToolsNavButton.tsx index 1608d5bd3..3ba3bb70b 100644 --- a/frontend/src/core/components/shared/AllToolsNavButton.tsx +++ b/frontend/src/core/components/shared/AllToolsNavButton.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { Tooltip } from '@app/components/shared/Tooltip'; import AppsIcon from '@mui/icons-material/AppsRounded'; import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext'; import { useNavigationState, useNavigationActions } from '@app/contexts/NavigationContext'; @@ -11,13 +10,11 @@ import QuickAccessButton from '@app/components/shared/quickAccessBar/QuickAccess interface AllToolsNavButtonProps { activeButton: string; setActiveButton: (id: string) => void; - tooltipPosition?: 'left' | 'right' | 'top' | 'bottom'; } const AllToolsNavButton: React.FC = ({ activeButton, setActiveButton, - tooltipPosition = 'right' }) => { const { t } = useTranslation(); const { handleReaderToggle, handleBackToTools, selectedToolKey, leftPanelView } = useToolWorkflow(); @@ -55,26 +52,18 @@ const AllToolsNavButton: React.FC = ({ }; return ( - -
- } - label={t("quickAccess.allTools", "Tools")} - isActive={isActive} - onClick={handleNavClick} - href={navProps.href} - ariaLabel={t("quickAccess.allTools", "Tools")} - textClassName="all-tools-text" - component="a" - /> -
-
+
+ } + label={t("quickAccess.allTools", "Tools")} + isActive={isActive} + onClick={handleNavClick} + href={navProps.href} + ariaLabel={t("quickAccess.allTools", "Tools")} + textClassName="all-tools-text" + component="a" + /> +
); }; diff --git a/frontend/src/core/components/shared/rightRail/ViewerAnnotationControls.tsx b/frontend/src/core/components/shared/rightRail/ViewerAnnotationControls.tsx index cd95b90b9..0c9cfc3dd 100644 --- a/frontend/src/core/components/shared/rightRail/ViewerAnnotationControls.tsx +++ b/frontend/src/core/components/shared/rightRail/ViewerAnnotationControls.tsx @@ -1,15 +1,9 @@ -import React, { useState, useEffect } from 'react'; -import { ActionIcon, Popover } from '@mantine/core'; +import React from 'react'; +import { ActionIcon } from '@mantine/core'; import { useTranslation } from 'react-i18next'; import LocalIcon from '@app/components/shared/LocalIcon'; import { Tooltip } from '@app/components/shared/Tooltip'; import { ViewerContext } from '@app/contexts/ViewerContext'; -import { useSignature } from '@app/contexts/SignatureContext'; -import { ColorSwatchButton, ColorPicker } from '@app/components/annotation/shared/ColorPicker'; -import { useFileState, useFileContext } from '@app/contexts/FileContext'; -import { generateThumbnailWithMetadata } from '@app/utils/thumbnailUtils'; -import { createProcessedFile } from '@app/contexts/file/fileActions'; -import { createStirlingFile, createNewStirlingFileStub } from '@app/types/fileContext'; import { useNavigationState } from '@app/contexts/NavigationContext'; import { useSidebarContext } from '@app/contexts/SidebarContext'; import { useRightRailTooltipSide } from '@app/hooks/useRightRailTooltipSide'; @@ -23,32 +17,14 @@ export default function ViewerAnnotationControls({ currentView, disabled = false const { t } = useTranslation(); const { sidebarRefs } = useSidebarContext(); const { position: tooltipPosition, offset: tooltipOffset } = useRightRailTooltipSide(sidebarRefs); - const [selectedColor, setSelectedColor] = useState('#000000'); - const [isColorPickerOpen, setIsColorPickerOpen] = useState(false); - const [isHoverColorPickerOpen, setIsHoverColorPickerOpen] = useState(false); // Viewer context for PDF controls - safely handle when not available const viewerContext = React.useContext(ViewerContext); - // Signature context for accessing drawing API - const { signatureApiRef, isPlacementMode } = useSignature(); - - // File state for save functionality - const { state, selectors } = useFileState(); - const { actions: fileActions } = useFileContext(); - const activeFiles = selectors.getFiles(); - // Check if we're in sign mode const { selectedTool } = useNavigationState(); const isSignMode = selectedTool === 'sign'; - // Turn off annotation mode when switching away from viewer - useEffect(() => { - if (currentView !== 'viewer' && viewerContext?.isAnnotationMode) { - viewerContext.setAnnotationMode(false); - } - }, [currentView, viewerContext]); - // Don't show any annotation controls in sign mode if (isSignMode) { return null; @@ -65,7 +41,7 @@ export default function ViewerAnnotationControls({ currentView, disabled = false onClick={() => { viewerContext?.toggleAnnotationsVisibility(); }} - disabled={disabled || currentView !== 'viewer' || viewerContext?.isAnnotationMode || isPlacementMode} + disabled={disabled || currentView !== 'viewer'} > - - {/* Annotation Mode Toggle with Drawing Controls */} - {viewerContext?.isAnnotationMode ? ( - // When active: Show color picker on hover -
setIsHoverColorPickerOpen(true)} - onMouseLeave={() => setIsHoverColorPickerOpen(false)} - style={{ display: 'inline-flex' }} - > - setIsHoverColorPickerOpen(false)} - position="left" - withArrow - shadow="md" - offset={8} - > - - { - viewerContext?.toggleAnnotationMode(); - setIsHoverColorPickerOpen(false); // Close hover color picker when toggling off - // Deactivate drawing tool when exiting annotation mode - if (signatureApiRef?.current) { - try { - signatureApiRef.current.deactivateTools(); - } catch (error) { - console.log('Signature API not ready:', error); - } - } - }} - disabled={disabled} - aria-label="Drawing mode active" - > - - - - -
-
-
Drawing Color
- { - setIsHoverColorPickerOpen(false); // Close hover picker - setIsColorPickerOpen(true); // Open main color picker modal - }} - /> -
-
-
-
-
- ) : ( - // When inactive: Show "Draw" tooltip - - { - viewerContext?.toggleAnnotationMode(); - // Activate ink drawing tool when entering annotation mode - if (signatureApiRef?.current && currentView === 'viewer') { - try { - signatureApiRef.current.activateDrawMode(); - signatureApiRef.current.updateDrawSettings(selectedColor, 2); - } catch (error) { - console.log('Signature API not ready:', error); - } - } - }} - disabled={disabled} - aria-label={typeof t === 'function' ? t('rightRail.draw', 'Draw') : 'Draw'} - > - - - - )} - - {/* Save PDF with Annotations */} - - { - if (viewerContext?.exportActions?.saveAsCopy && currentView === 'viewer') { - try { - const pdfArrayBuffer = await viewerContext.exportActions.saveAsCopy(); - if (pdfArrayBuffer) { - // Create new File object with flattened annotations - const blob = new Blob([pdfArrayBuffer], { type: 'application/pdf' }); - - // Get the original file name or use a default - const originalFileName = activeFiles.length > 0 ? activeFiles[0].name : 'document.pdf'; - const newFile = new File([blob], originalFileName, { type: 'application/pdf' }); - - // Replace the current file in context with the saved version (exact same logic as Sign tool) - if (activeFiles.length > 0) { - // Generate thumbnail and metadata for the saved file - const thumbnailResult = await generateThumbnailWithMetadata(newFile); - const processedFileMetadata = createProcessedFile(thumbnailResult.pageCount, thumbnailResult.thumbnail); - - // Get current file info - const currentFileIds = state.files.ids; - if (currentFileIds.length > 0) { - const currentFileId = currentFileIds[0]; - const currentRecord = selectors.getStirlingFileStub(currentFileId); - - if (!currentRecord) { - console.error('No file record found for:', currentFileId); - return; - } - - // Create output stub and file (exact same as Sign tool) - const outputStub = createNewStirlingFileStub(newFile, undefined, thumbnailResult.thumbnail, processedFileMetadata); - const outputStirlingFile = createStirlingFile(newFile, outputStub.id); - - // Replace the original file with the saved version - await fileActions.consumeFiles([currentFileId], [outputStirlingFile], [outputStub]); - } - } - } - } catch (error) { - console.error('Error saving PDF:', error); - } - } - }} - disabled={disabled} - > - - - - - {/* Color Picker Modal */} - setIsColorPickerOpen(false)} - selectedColor={selectedColor} - onColorChange={(color) => { - setSelectedColor(color); - // Update drawing tool color if annotation mode is active - if (viewerContext?.isAnnotationMode && signatureApiRef?.current && currentView === 'viewer') { - try { - signatureApiRef.current.updateDrawSettings(color, 2); - } catch (error) { - console.log('Unable to update drawing settings:', error); - } - } - }} - title="Choose Drawing Color" - /> ); } diff --git a/frontend/src/core/components/viewer/EmbedPdfViewer.tsx b/frontend/src/core/components/viewer/EmbedPdfViewer.tsx index 94bff5a35..8e66f0b5c 100644 --- a/frontend/src/core/components/viewer/EmbedPdfViewer.tsx +++ b/frontend/src/core/components/viewer/EmbedPdfViewer.tsx @@ -51,6 +51,7 @@ const EmbedPdfViewerContent = ({ getScrollState, getRotationState, isAnnotationMode, + setAnnotationMode, isAnnotationsVisible, exportActions, } = useViewer(); @@ -82,15 +83,18 @@ const EmbedPdfViewerContent = ({ // Navigation guard for unsaved changes const { setHasUnsavedChanges, registerUnsavedChangesChecker, unregisterUnsavedChangesChecker } = useNavigationGuard(); - // Check if we're in signature mode OR viewer annotation mode + // Check if we're in an annotation tool const { selectedTool } = useNavigationState(); - // Tools that use the stamp/signature placement system with hover preview - const isSignatureMode = selectedTool === 'sign' || selectedTool === 'addText' || selectedTool === 'addImage'; + // Tools that require the annotation layer (Sign, Add Text, Add Image) + const isInAnnotationTool = selectedTool === 'sign' || selectedTool === 'addText' || selectedTool === 'addImage'; + + // Sync isAnnotationMode in ViewerContext with current tool + useEffect(() => { + setAnnotationMode(isInAnnotationTool); + }, [isInAnnotationTool, setAnnotationMode]); - // Enable annotations when: in sign mode, OR annotation mode is active, OR we want to show existing annotations - const shouldEnableAnnotations = isSignatureMode || isAnnotationMode || isAnnotationsVisible; const isPlacementOverlayActive = Boolean( - isSignatureMode && shouldEnableAnnotations && isPlacementMode && signatureConfig + isInAnnotationTool && isPlacementMode && signatureConfig ); // Track which file tab is active @@ -333,7 +337,8 @@ const EmbedPdfViewerContent = ({ key={currentFile && isStirlingFile(currentFile) ? currentFile.fileId : (effectiveFile.file instanceof File ? effectiveFile.file.name : effectiveFile.url)} file={effectiveFile.file} url={effectiveFile.url} - enableAnnotations={shouldEnableAnnotations} + enableAnnotations={isAnnotationMode} + showBakedAnnotations={isAnnotationsVisible} signatureApiRef={signatureApiRef as React.RefObject} historyApiRef={historyApiRef as React.RefObject} onSignatureAdded={() => { diff --git a/frontend/src/core/components/viewer/LocalEmbedPDF.tsx b/frontend/src/core/components/viewer/LocalEmbedPDF.tsx index 0b79604fd..8f20cd96f 100644 --- a/frontend/src/core/components/viewer/LocalEmbedPDF.tsx +++ b/frontend/src/core/components/viewer/LocalEmbedPDF.tsx @@ -52,12 +52,13 @@ interface LocalEmbedPDFProps { file?: File | Blob; url?: string | null; enableAnnotations?: boolean; + showBakedAnnotations?: boolean; onSignatureAdded?: (annotation: any) => void; signatureApiRef?: React.RefObject; historyApiRef?: React.RefObject; } -export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatureAdded, signatureApiRef, historyApiRef }: LocalEmbedPDFProps) { +export function LocalEmbedPDF({ file, url, enableAnnotations = false, showBakedAnnotations = true, onSignatureAdded, signatureApiRef, historyApiRef }: LocalEmbedPDFProps) { const { t } = useTranslation(); const [pdfUrl, setPdfUrl] = useState(null); const [, setAnnotations] = useState>([]); @@ -100,7 +101,7 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur }), createPluginRegistration(RenderPluginPackage, { withForms: true, - withAnnotations: true, + withAnnotations: showBakedAnnotations && !enableAnnotations, // Show baked annotations only when: visibility is ON and annotation layer is OFF }), // Register interaction manager (required for zoom and selection features) @@ -166,7 +167,7 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur // Register print plugin for printing PDFs createPluginRegistration(PrintPluginPackage), ]; - }, [pdfUrl]); + }, [pdfUrl, enableAnnotations, showBakedAnnotations]); // Initialize the engine with the React hook - use local WASM for offline support const { engine, isLoading, error } = usePdfiumEngine({ diff --git a/frontend/src/core/contexts/ViewerContext.tsx b/frontend/src/core/contexts/ViewerContext.tsx index 9217511ef..aa2dc497b 100644 --- a/frontend/src/core/contexts/ViewerContext.tsx +++ b/frontend/src/core/contexts/ViewerContext.tsx @@ -95,7 +95,6 @@ interface ViewerContextType { // Annotation/drawing mode for viewer isAnnotationMode: boolean; setAnnotationMode: (enabled: boolean) => void; - toggleAnnotationMode: () => void; // Active file index for multi-file viewing activeFileIndex: number; @@ -230,10 +229,6 @@ export const ViewerProvider: React.FC = ({ children }) => { setIsAnnotationModeState(enabled); }; - const toggleAnnotationMode = () => { - setIsAnnotationModeState(prev => !prev); - }; - // State getters - read from bridge refs const getScrollState = (): ScrollState => { return bridgeRefs.current.scroll?.state || { currentPage: 1, totalPages: 0 }; @@ -318,7 +313,6 @@ export const ViewerProvider: React.FC = ({ children }) => { toggleAnnotationsVisibility, isAnnotationMode, setAnnotationMode, - toggleAnnotationMode, // Active file index activeFileIndex,