diff --git a/frontend/public/locales/en-US/translation.json b/frontend/public/locales/en-US/translation.json
index 26c2e5b15..c15def8cc 100644
--- a/frontend/public/locales/en-US/translation.json
+++ b/frontend/public/locales/en-US/translation.json
@@ -2106,5 +2106,20 @@
"results": {
"title": "Decrypted PDFs"
}
+ },
+ "automation": {
+ "suggested": {
+ "securePdfIngestion": "Secure PDF Ingestion",
+ "securePdfIngestionDesc": "Sanitise → OCR/Cleanup → PDF/A → Compress",
+ "emailPreparation": "Email Preparation",
+ "emailPreparationDesc": "Compress → Split by Size 20MB → Sanitize metadata",
+ "secureWorkflow": "Security Workflow",
+ "secureWorkflowDesc": "Sanitize PDFs and add password protection",
+ "optimizationWorkflow": "Optimization Workflow",
+ "optimizationWorkflowDesc": "Repair and compress PDFs for better performance"
+ }
+ },
+ "automate": {
+ "copyToSaved": "Copy to Saved"
}
}
diff --git a/frontend/src/components/tools/automate/AutomationEntry.tsx b/frontend/src/components/tools/automate/AutomationEntry.tsx
index 3314831be..54295c0c6 100644
--- a/frontend/src/components/tools/automate/AutomationEntry.tsx
+++ b/frontend/src/components/tools/automate/AutomationEntry.tsx
@@ -4,6 +4,7 @@ import { Button, Group, Text, ActionIcon, Menu, Box } from '@mantine/core';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
+import ContentCopyIcon from '@mui/icons-material/ContentCopy';
interface AutomationEntryProps {
/** Optional title for the automation (usually for custom ones) */
@@ -22,6 +23,8 @@ interface AutomationEntryProps {
onEdit?: () => void;
/** Delete handler */
onDelete?: () => void;
+ /** Copy handler (for suggested automations) */
+ onCopy?: () => void;
}
export default function AutomationEntry({
@@ -32,7 +35,8 @@ export default function AutomationEntry({
keepIconColor = false,
showMenu = false,
onEdit,
- onDelete
+ onDelete,
+ onCopy
}: AutomationEntryProps) {
const { t } = useTranslation();
const [isHovered, setIsHovered] = useState(false);
@@ -132,6 +136,17 @@ export default function AutomationEntry({
+ {onCopy && (
+ }
+ onClick={(e) => {
+ e.stopPropagation();
+ onCopy();
+ }}
+ >
+ {t('automate.copyToSaved', 'Copy to Saved')}
+
+ )}
{onEdit && (
}
diff --git a/frontend/src/components/tools/automate/AutomationSelection.tsx b/frontend/src/components/tools/automate/AutomationSelection.tsx
index f55cf4c5d..326ac3fe1 100644
--- a/frontend/src/components/tools/automate/AutomationSelection.tsx
+++ b/frontend/src/components/tools/automate/AutomationSelection.tsx
@@ -5,7 +5,7 @@ import AddCircleOutline from "@mui/icons-material/AddCircleOutline";
import SettingsIcon from "@mui/icons-material/Settings";
import AutomationEntry from "./AutomationEntry";
import { useSuggestedAutomations } from "../../../hooks/tools/automate/useSuggestedAutomations";
-import { AutomationConfig } from "../../../types/automation";
+import { AutomationConfig, SuggestedAutomation } from "../../../types/automation";
interface AutomationSelectionProps {
savedAutomations: AutomationConfig[];
@@ -13,6 +13,7 @@ interface AutomationSelectionProps {
onRun: (automation: AutomationConfig) => void;
onEdit: (automation: AutomationConfig) => void;
onDelete: (automation: AutomationConfig) => void;
+ onCopyFromSuggested: (automation: SuggestedAutomation) => void;
}
export default function AutomationSelection({
@@ -20,7 +21,8 @@ export default function AutomationSelection({
onCreateNew,
onRun,
onEdit,
- onDelete
+ onDelete,
+ onCopyFromSuggested
}: AutomationSelectionProps) {
const { t } = useTranslation();
const suggestedAutomations = useSuggestedAutomations();
@@ -63,9 +65,12 @@ export default function AutomationSelection({
{suggestedAutomations.map((automation) => (
op.operation)}
onClick={() => onRun(automation)}
+ showMenu={true}
+ onCopy={() => onCopyFromSuggested(automation)}
/>
))}
diff --git a/frontend/src/hooks/tools/automate/useSavedAutomations.ts b/frontend/src/hooks/tools/automate/useSavedAutomations.ts
index c52e4c784..1f210b432 100644
--- a/frontend/src/hooks/tools/automate/useSavedAutomations.ts
+++ b/frontend/src/hooks/tools/automate/useSavedAutomations.ts
@@ -1,5 +1,6 @@
import { useState, useEffect, useCallback } from 'react';
import { AutomationConfig } from '../../../services/automationStorage';
+import { SuggestedAutomation } from '../../../types/automation';
export interface SavedAutomation extends AutomationConfig {}
@@ -40,6 +41,26 @@ export function useSavedAutomations() {
}
}, [refreshAutomations]);
+ const copyFromSuggested = useCallback(async (suggestedAutomation: SuggestedAutomation) => {
+ try {
+ const { automationStorage } = await import('../../../services/automationStorage');
+
+ // Convert suggested automation to saved automation format
+ const savedAutomation = {
+ name: suggestedAutomation.name,
+ description: suggestedAutomation.description,
+ operations: suggestedAutomation.operations
+ };
+
+ await automationStorage.saveAutomation(savedAutomation);
+ // Refresh the list after saving
+ refreshAutomations();
+ } catch (err) {
+ console.error('Error copying suggested automation:', err);
+ throw err;
+ }
+ }, [refreshAutomations]);
+
// Load automations on mount
useEffect(() => {
loadSavedAutomations();
@@ -50,6 +71,7 @@ export function useSavedAutomations() {
loading,
error,
refreshAutomations,
- deleteAutomation
+ deleteAutomation,
+ copyFromSuggested
};
}
\ No newline at end of file
diff --git a/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts b/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts
index 006c9f179..fd9b7eaec 100644
--- a/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts
+++ b/frontend/src/hooks/tools/automate/useSuggestedAutomations.ts
@@ -17,9 +17,60 @@ export function useSuggestedAutomations(): SuggestedAutomation[] {
const now = new Date().toISOString();
return [
{
- id: "compress-and-split",
- name: t("automation.suggested.compressAndSplit", "Compress & Split"),
- description: t("automation.suggested.compressAndSplitDesc", "Compress PDFs and split them by pages"),
+ id: "secure-pdf-ingestion",
+ name: t("automation.suggested.securePdfIngestion", "Secure PDF Ingestion"),
+ description: t("automation.suggested.securePdfIngestionDesc", "Sanitise → OCR/Cleanup → PDF/A → Compress"),
+ operations: [
+ {
+ operation: "sanitize",
+ parameters: {
+ removeJavaScript: true,
+ removeEmbeddedFiles: true,
+ removeXMPMetadata: true,
+ removeMetadata: true,
+ removeLinks: false,
+ removeFonts: false,
+ }
+ },
+ {
+ operation: "ocr",
+ parameters: {
+ languages: ['eng'],
+ ocrType: 'skip-text',
+ ocrRenderType: 'hocr',
+ additionalOptions: ['clean', 'cleanFinal'],
+ }
+ },
+ {
+ operation: "convert",
+ parameters: {
+ fromExtension: 'pdf',
+ toExtension: 'pdfa',
+ pdfaOptions: {
+ outputFormat: 'pdfa-1',
+ }
+ }
+ },
+ {
+ operation: "compress",
+ parameters: {
+ compressionLevel: 5,
+ grayscale: false,
+ expectedSize: '',
+ compressionMethod: 'quality',
+ fileSizeValue: '',
+ fileSizeUnit: 'MB',
+ }
+ }
+ ],
+ createdAt: now,
+ updatedAt: now,
+ icon: SecurityIcon,
+ },
+ {
+ id: "email-preparation",
+ name: t("automation.suggested.emailPreparation", "Email Preparation"),
+ description: t("automation.suggested.emailPreparationDesc", "Compress → Split by Size 20MB → Sanitize metadata"),
operations: [
{
operation: "compress",
@@ -36,41 +87,33 @@ export function useSuggestedAutomations(): SuggestedAutomation[] {
operation: "splitPdf",
parameters: {
mode: 'bySizeOrCount',
- pages: '1',
- hDiv: '2',
- vDiv: '2',
+ pages: '',
+ hDiv: '1',
+ vDiv: '1',
merge: false,
- splitType: 'pages',
- splitValue: '1',
+ splitType: 'size',
+ splitValue: '20MB',
bookmarkLevel: '1',
includeMetadata: false,
allowDuplicates: false,
}
+ },
+ {
+ operation: "sanitize",
+ parameters: {
+ removeJavaScript: false,
+ removeEmbeddedFiles: false,
+ removeXMPMetadata: true,
+ removeMetadata: true,
+ removeLinks: false,
+ removeFonts: false,
+ }
}
],
createdAt: now,
updatedAt: now,
icon: CompressIcon,
},
- {
- id: "ocr-workflow",
- name: t("automation.suggested.ocrWorkflow", "OCR Processing"),
- description: t("automation.suggested.ocrWorkflowDesc", "Extract text from PDFs using OCR technology"),
- operations: [
- {
- operation: "ocr",
- parameters: {
- languages: ['eng'],
- ocrType: 'skip-text',
- ocrRenderType: 'hocr',
- additionalOptions: [],
- }
- }
- ],
- createdAt: now,
- updatedAt: now,
- icon: TextFieldsIcon,
- },
{
id: "secure-workflow",
name: t("automation.suggested.secureWorkflow", "Security Workflow"),
diff --git a/frontend/src/tools/Automate.tsx b/frontend/src/tools/Automate.tsx
index 444cdfce9..e31d1abe3 100644
--- a/frontend/src/tools/Automate.tsx
+++ b/frontend/src/tools/Automate.tsx
@@ -28,7 +28,7 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
const automateOperation = useAutomateOperation();
const toolRegistry = useFlatToolRegistry();
const hasResults = automateOperation.files.length > 0 || automateOperation.downloadUrl !== null;
- const { savedAutomations, deleteAutomation, refreshAutomations } = useSavedAutomations();
+ const { savedAutomations, deleteAutomation, refreshAutomations, copyFromSuggested } = useSavedAutomations();
const handleStepChange = (data: AutomationStepData) => {
// If navigating away from run step, reset automation results
@@ -79,6 +79,14 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
onError?.(`Failed to delete automation: ${automation.name}`);
}
}}
+ onCopyFromSuggested={async (suggestedAutomation) => {
+ try {
+ await copyFromSuggested(suggestedAutomation);
+ } catch (error) {
+ console.error('Failed to copy suggested automation:', error);
+ onError?.(`Failed to copy automation: ${suggestedAutomation.name}`);
+ }
+ }}
/>
);