diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json index d0b10b478..5d0ad075e 100644 --- a/frontend/public/locales/en-GB/translation.json +++ b/frontend/public/locales/en-GB/translation.json @@ -145,6 +145,7 @@ "noFileSelected": "No file selected. Please upload one.", "legal": { "privacy": "Privacy Policy", + "iAgreeToThe": "I agree to all of the", "terms": "Terms and Conditions", "accessibility": "Accessibility", "cookie": "Cookie Policy", @@ -2475,6 +2476,8 @@ "title": "Sign in", "header": "Sign in", "signin": "Sign in", + "signInWith": "Sign in with", + "signInAnonymously": "Sign Up as a Guest", "rememberme": "Remember me", "invalid": "Invalid username or password.", "locked": "Your account has been locked.", @@ -2493,7 +2496,54 @@ "alreadyLoggedIn": "You are already logged in to", "alreadyLoggedIn2": "devices. Please log out of the devices and try again.", "toManySessions": "You have too many active sessions", - "logoutMessage": "You have been logged out." + "logoutMessage": "You have been logged out.", + "youAreLoggedIn": "You are logged in!", + "email": "Email", + "password": "Password", + "enterEmail": "Enter your email", + "enterPassword": "Enter your password", + "loggingIn": "Logging In...", + "signingIn": "Signing in...", + "login": "Login", + "or": "Or", + "useMagicLink": "Use magic link instead", + "enterEmailForMagicLink": "Enter your email for magic link", + "sending": "Sending…", + "sendMagicLink": "Send Magic Link", + "cancel": "Cancel", + "dontHaveAccount": "Don't have an account? Sign up", + "home": "Home", + "debug": "Debug", + "signOut": "Sign Out", + "pleaseEnterBoth": "Please enter both email and password", + "pleaseEnterEmail": "Please enter your email address", + "magicLinkSent": "Magic link sent to {{email}}! Check your email and click the link to sign in.", + "passwordResetSent": "Password reset link sent to {{email}}! Check your email and follow the instructions.", + "failedToSignIn": "Failed to sign in with {{provider}}: {{message}}", + "unexpectedError": "Unexpected error: {{message}}" + }, + "signup": { + "title": "Create an account", + "subtitle": "Join Stirling PDF to get started", + "name": "Name", + "email": "Email", + "password": "Password", + "confirmPassword": "Confirm password", + "enterName": "Enter your name", + "enterEmail": "Enter your email", + "enterPassword": "Enter your password", + "confirmPasswordPlaceholder": "Confirm password", + "or": "or", + "creatingAccount": "Creating Account...", + "signUp": "Sign Up", + "alreadyHaveAccount": "Already have an account? Sign in", + "pleaseFillAllFields": "Please fill in all fields", + "passwordsDoNotMatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters long", + "invalidEmail": "Please enter a valid email address", + "checkEmailConfirmation": "Check your email for a confirmation link to complete your registration.", + "accountCreatedSuccessfully": "Account created successfully! You can now sign in.", + "unexpectedError": "Unexpected error: {{message}}" }, "pdfToSinglePage": { "title": "PDF To Single Page", @@ -2551,6 +2601,28 @@ "grayscale": { "label": "Apply Grayscale for Compression" }, + "tooltip": { + "header": { + "title": "Compress Settings Overview" + }, + "description": { + "title": "Description", + "text": "Compression is an easy way to reduce your file size. Pick File Size to enter a target size and have us adjust quality for you. Pick Quality to set compression strength manually." + }, + "qualityAdjustment": { + "title": "Quality Adjustment", + "text": "Drag the slider to adjust the compression strength. Lower values (1-3) preserve quality but result in larger files. Higher values (7-9) shrink the file more but reduce image clarity.", + "bullet1": "Lower values preserve quality", + "bullet2": "Higher values reduce file size" + }, + "grayscale": { + "title": "Grayscale", + "text": "Select this option to convert all images to black and white, which can significantly reduce file size especially for scanned PDFs or image-heavy documents." + } + }, + "error": { + "failed": "An error occurred while compressing the PDF." + }, "selectText": { "1": { "_value": "Compression Settings", @@ -2799,6 +2871,16 @@ "rotateRight": "Rotate Right", "toggleSidebar": "Toggle Sidebar" }, + "search": { + "title": "Search PDF", + "placeholder": "Enter search term..." + }, + "guestBanner": { + "title": "You're using Stirling PDF as a guest!", + "message": "Create a free account to save your work, access more features, and support the project.", + "dismiss": "Dismiss banner", + "signUp": "Sign Up Free" + }, "toolPicker": { "searchPlaceholder": "Search tools...", "noToolsFound": "No tools found", @@ -3163,5 +3245,63 @@ "processImages": "Process Images", "processImagesDesc": "Converts multiple image files into a single PDF document, then applies OCR technology to extract searchable text from the images." } - } + }, + "common": { + "copy": "Copy", + "copied": "Copied!", + "refresh": "Refresh", + "retry": "Retry", + "remaining": "remaining", + "used": "used", + "available": "available", + "cancel": "Cancel" + }, + "config": { + "account": { + "overview": { + "title": "Account Settings", + "manageAccountPreferences": "Manage your account preferences", + "guestDescription": "You are signed in as a guest. Consider upgrading your account above." + }, + "upgrade": { + "title": "Upgrade Guest Account", + "description": "Link your account to preserve your history and access more features!", + "socialLogin": "Upgrade with Social Account", + "linkWith": "Link with", + "emailPassword": "or enter your email & password", + "email": "Email", + "emailPlaceholder": "Enter your email", + "password": "Password (optional)", + "passwordPlaceholder": "Set a password", + "passwordNote": "Leave empty to use email verification only", + "upgradeButton": "Upgrade Account" + } + }, + "apiKeys": { + "description": "Your API key for accessing Stirling's suite of PDF tools. Copy it to your project or refresh to generate a new one.", + "publicKeyAriaLabel": "Public API key", + "copyKeyAriaLabel": "Copy API key", + "refreshAriaLabel": "Refresh API key", + "includedCredits": "Included credits", + "purchasedCredits": "Purchased credits", + "totalCredits": "Total Credits", + "chartAriaLabel": "Credits usage: included {{includedUsed}} of {{includedTotal}}, purchased {{purchasedUsed}} of {{purchasedTotal}}", + "nextReset": "Next Reset", + "lastApiUse": "Last API Use", + "overlayMessage": "Generate a key to see credits and available credits", + "label": "API Key", + "guestInfo": "Guest users do not receive API keys. Create an account to get an API key you can use in your applications.", + "goToAccount": "Go to Account", + "refreshModal": { + "title": "Refresh API Keys", + "warning": "⚠️ Warning: This action will generate new API keys and make your previous keys invalid.", + "impact": "Any applications or services currently using these keys will stop working until you update them with the new keys.", + "confirmPrompt": "Are you sure you want to continue?", + "confirmCta": "Refresh Keys" + }, + "generateError": "We couldn't generate your API key." + } + }, + "termsAndConditions": "Terms & Conditions", + "logOut": "Log out" } \ No newline at end of file diff --git a/frontend/src/components/layout/Workbench.tsx b/frontend/src/components/layout/Workbench.tsx index f75072b42..5a8f36747 100644 --- a/frontend/src/components/layout/Workbench.tsx +++ b/frontend/src/components/layout/Workbench.tsx @@ -4,7 +4,6 @@ import { useToolWorkflow } from '../../contexts/ToolWorkflowContext'; import { useFileHandler } from '../../hooks/useFileHandler'; import { useFileState } from '../../contexts/FileContext'; import { useNavigationState, useNavigationActions } from '../../contexts/NavigationContext'; -import { useToolManagement } from '../../hooks/useToolManagement'; import './Workbench.css'; import TopControls from '../shared/TopControls'; @@ -39,8 +38,8 @@ export default function Workbench() { // Get navigation state - this is the source of truth const { selectedTool: selectedToolId } = useNavigationState(); - // Get tool registry to look up selected tool - const { toolRegistry } = useToolManagement(); + // Get tool registry from context (instead of direct hook call) + const { toolRegistry } = useToolWorkflow(); const selectedTool = selectedToolId ? toolRegistry[selectedToolId] : null; const { addFiles } = useFileHandler(); diff --git a/frontend/src/components/tools/ToolRenderer.tsx b/frontend/src/components/tools/ToolRenderer.tsx index 4a3146613..67c2b5dd9 100644 --- a/frontend/src/components/tools/ToolRenderer.tsx +++ b/frontend/src/components/tools/ToolRenderer.tsx @@ -1,5 +1,5 @@ import React, { Suspense } from "react"; -import { useToolManagement } from "../../hooks/useToolManagement"; +import { useToolWorkflow } from "../../contexts/ToolWorkflowContext"; import { BaseToolProps } from "../../types/tool"; import ToolLoadingFallback from "./ToolLoadingFallback"; @@ -14,8 +14,8 @@ const ToolRenderer = ({ onComplete, onError, }: ToolRendererProps) => { - // Get the tool from registry - const { toolRegistry } = useToolManagement(); + // Get the tool from context (instead of direct hook call) + const { toolRegistry } = useToolWorkflow(); const selectedTool = toolRegistry[selectedToolKey]; if (!selectedTool || !selectedTool.component) { diff --git a/frontend/src/contexts/ToolWorkflowContext.tsx b/frontend/src/contexts/ToolWorkflowContext.tsx index 4473dc020..fc70c570c 100644 --- a/frontend/src/contexts/ToolWorkflowContext.tsx +++ b/frontend/src/contexts/ToolWorkflowContext.tsx @@ -75,6 +75,7 @@ interface ToolWorkflowContextValue extends ToolWorkflowState { selectedToolKey: string | null; selectedTool: ToolRegistryEntry | null; toolRegistry: any; // From useToolManagement + getSelectedTool: (toolId: string | null) => ToolRegistryEntry | null; // UI Actions setSidebarsVisible: (visible: boolean) => void; @@ -247,6 +248,7 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) { selectedToolKey: navigationState.selectedTool, selectedTool, toolRegistry, + getSelectedTool, // Actions setSidebarsVisible, @@ -276,6 +278,7 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) { navigationState.selectedTool, selectedTool, toolRegistry, + getSelectedTool, setSidebarsVisible, setLeftPanelView, setReaderMode, diff --git a/frontend/src/hooks/useSidebarNavigation.ts b/frontend/src/hooks/useSidebarNavigation.ts index fb60e2502..a65ea96de 100644 --- a/frontend/src/hooks/useSidebarNavigation.ts +++ b/frontend/src/hooks/useSidebarNavigation.ts @@ -1,6 +1,6 @@ import { useCallback } from 'react'; import { useToolNavigation } from './useToolNavigation'; -import { useToolManagement } from './useToolManagement'; +import { useToolWorkflow } from '../contexts/ToolWorkflowContext'; import { handleUnlessSpecialClick } from '../utils/clickHandlers'; export interface SidebarNavigationProps { @@ -19,7 +19,7 @@ export function useSidebarNavigation(): { getToolNavigation: (toolId: string) => SidebarNavigationProps | null; } { const { getToolNavigation: getToolNavProps } = useToolNavigation(); - const { getSelectedTool } = useToolManagement(); + const { getSelectedTool } = useToolWorkflow(); const defaultNavClick = useCallback((e: React.MouseEvent) => { handleUnlessSpecialClick(e, () => { diff --git a/frontend/src/hooks/useSuggestedTools.ts b/frontend/src/hooks/useSuggestedTools.ts index 377cf1245..3cf6179bf 100644 --- a/frontend/src/hooks/useSuggestedTools.ts +++ b/frontend/src/hooks/useSuggestedTools.ts @@ -1,7 +1,7 @@ import { useMemo } from 'react'; import { useNavigationState } from '../contexts/NavigationContext'; import { useToolNavigation } from './useToolNavigation'; -import { useToolManagement } from './useToolManagement'; +import { useToolWorkflow } from '../contexts/ToolWorkflowContext'; import { ToolId } from '../types/toolId'; // Material UI Icons @@ -50,7 +50,7 @@ const ALL_SUGGESTED_TOOLS: Omit[] = [ export function useSuggestedTools(): SuggestedTool[] { const { selectedTool } = useNavigationState(); const { getToolNavigation } = useToolNavigation(); - const { getSelectedTool } = useToolManagement(); + const { getSelectedTool } = useToolWorkflow(); return useMemo(() => { // Filter out the current tool diff --git a/frontend/src/types/toolId.ts b/frontend/src/types/toolId.ts index 1a5fb1d4f..49bdb6523 100644 --- a/frontend/src/types/toolId.ts +++ b/frontend/src/types/toolId.ts @@ -1,5 +1,5 @@ // Define all possible tool IDs as source of truth -const TOOL_IDS = [ +export const TOOL_IDS = [ 'certSign', 'sign', 'addPassword', diff --git a/frontend/src/utils/urlMapping.ts b/frontend/src/utils/urlMapping.ts index 0c271952c..016591ab3 100644 --- a/frontend/src/utils/urlMapping.ts +++ b/frontend/src/utils/urlMapping.ts @@ -2,10 +2,20 @@ import { ToolId } from '../types/toolId'; // Map URL paths to tool keys (multiple URLs can map to same tool) export const URL_TO_TOOL_MAP: Record = { - '/split-pdfs': 'split', + // Basic tools - standard patterns '/split': 'split', + '/split-pdfs': 'split', + '/merge': 'merge', '/merge-pdfs': 'merge', + '/compress': 'compress', '/compress-pdf': 'compress', + '/rotate': 'rotate', + '/rotate-pdf': 'rotate', + '/repair': 'repair', + '/flatten': 'flatten', + '/crop': 'crop', + + // Convert tool and all its variants '/convert': 'convert', '/convert-pdf': 'convert', '/file-to-pdf': 'convert', @@ -18,20 +28,101 @@ export const URL_TO_TOOL_MAP: Record = { '/pdf-to-pdfa': 'convert', '/pdf-to-word': 'convert', '/pdf-to-xml': 'convert', + + // Security tools '/add-password': 'addPassword', + '/remove-password': 'removePassword', '/change-permissions': 'changePermissions', + '/cert-sign': 'certSign', + '/manage-signatures': 'certSign', + '/remove-certificate-sign': 'removeCertSign', + '/remove-cert-sign': 'removeCertSign', + '/unlock-pdf-forms': 'unlockPDFForms', + '/validate-signature': 'validateSignature', + '/manage-certificates': 'manageCertificates', + + // Content manipulation + '/sanitize': 'sanitize', '/sanitize-pdf': 'sanitize', '/ocr': 'ocr', '/ocr-pdf': 'ocr', + '/watermark': 'watermark', '/add-watermark': 'watermark', - '/remove-password': 'removePassword', + '/add-image': 'addImage', + '/add-stamp': 'addStamp', + '/add-page-numbers': 'addPageNumbers', + '/redact': 'redact', + + // Page manipulation + '/remove-pages': 'removePages', + '/remove-blanks': 'removeBlanks', + '/extract-pages': 'extractPages', + '/reorganize-pages': 'reorganizePages', '/single-large-page': 'pdfToSinglePage', - '/repair': 'repair', - '/rotate-pdf': 'rotate', - '/unlock-pdf-forms': 'unlockPDFForms', - '/remove-certificate-sign': 'removeCertSign', - '/remove-cert-sign': 'removeCertSign', - '/cert-sign': 'certSign', - '/manage-signatures': 'certSign', + '/page-layout': 'pageLayout', + '/scale-pages': 'scalePages', '/booklet-imposition': 'bookletImposition', + + // Splitting tools + '/auto-split-pdf': 'autoSplitPDF', + '/auto-size-split-pdf': 'autoSizeSplitPDF', + '/scanner-image-split': 'scannerImageSplit', + + // Annotation and content removal + '/remove-annotations': 'removeAnnotations', + '/remove-image': 'removeImage', + + // Image and visual tools + '/extract-images': 'extractImages', + '/adjust-contrast': 'adjustContrast', + '/fake-scan': 'fakeScan', + '/replace-color-pdf': 'replaceColorPdf', + + // Metadata and info + '/change-metadata': 'changeMetadata', + '/get-pdf-info': 'getPdfInfo', + '/add-attachments': 'addAttachments', + + // Advanced tools + '/overlay-pdfs': 'overlayPdfs', + '/edit-table-of-contents': 'editTableOfContents', + '/auto-rename': 'autoRename', + '/compare': 'compare', + '/multi-tool': 'multiTool', + '/show-js': 'showJS', + + // Special/utility tools + '/read': 'read', + '/automate': 'automate', + '/sign': 'sign', + + // Developer tools + '/dev-api': 'devApi', + '/dev-folder-scanning': 'devFolderScanning', + '/dev-sso-guide': 'devSsoGuide', + '/dev-airgapped': 'devAirgapped', + + // Legacy URL mappings from sitemap + '/pdf-organizer': 'reorganizePages', + '/multi-page-layout': 'pageLayout', + '/extract-page': 'extractPages', + '/pdf-to-single-page': 'pdfToSinglePage', + '/img-to-pdf': 'convert', + '/pdf-to-presentation': 'convert', + '/pdf-to-text': 'convert', + '/pdf-to-html': 'convert', + '/auto-redact': 'redact', + '/stamp': 'addStamp', + '/view-pdf': 'read', + '/get-info-on-pdf': 'getPdfInfo', + '/remove-image-pdf': 'removeImage', + '/replace-and-invert-color-pdf': 'replaceColorPdf', + '/pipeline': 'automate', + '/extract-image-scans': 'scannerImageSplit', + '/show-javascript': 'showJS', + '/scanner-effect': 'fakeScan', + '/split-by-size-or-count': 'autoSizeSplitPDF', + '/overlay-pdf': 'overlayPdfs', + '/split-pdf-by-sections': 'autoSplitPDF', + '/split-pdf-by-chapters': 'autoSplitPDF', };