automation

This commit is contained in:
Anthony Stirling 2025-08-26 11:00:17 +01:00
parent c8b911366c
commit 1c043b60fb
5 changed files with 140 additions and 36 deletions

View File

@ -2110,13 +2110,24 @@
"automation": {
"suggested": {
"securePdfIngestion": "Secure PDF Ingestion",
"securePdfIngestionDesc": "Sanitise → OCR/Cleanup → PDF/A → Compress",
"securePdfIngestionDesc": "Comprehensive PDF processing workflow that sanitizes documents, applies OCR with cleanup, converts to PDF/A format for long-term archival, and optimizes file size.",
"emailPreparation": "Email Preparation",
"emailPreparationDesc": "Compress → Split by Size 20MB → Sanitize metadata",
"emailPreparationDesc": "Optimizes PDFs for email distribution by compressing files, splitting large documents into 20MB chunks for email compatibility, and removing metadata for privacy.",
"secureWorkflow": "Security Workflow",
"secureWorkflowDesc": "Sanitize PDFs and add password protection",
"optimizationWorkflow": "Optimization Workflow",
"optimizationWorkflowDesc": "Repair and compress PDFs for better performance"
"secureWorkflowDesc": "Secures PDF documents by removing potentially malicious content like JavaScript and embedded files, then adds password protection to prevent unauthorized access.",
"processImages": "Process Images",
"processImagesDesc": "Converts multiple image files into a single PDF document, then applies OCR technology to extract searchable text from the images."
},
"operation": {
"sanitize": "Sanitize",
"ocrCleanup": "OCR & Cleanup",
"pdfaConversion": "PDF/A Conversion",
"compress": "Compress",
"splitBySize": "Split by Size (20MB)",
"sanitizeMetadata": "Remove Metadata",
"addPassword": "Add Password Protection",
"imageToPdf": "Image to PDF",
"ocr": "OCR Text Extraction"
}
},
"automate": {

View File

@ -5,14 +5,17 @@ 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';
import { Tooltip } from '../../shared/Tooltip';
interface AutomationEntryProps {
/** Optional title for the automation (usually for custom ones) */
title?: string;
/** Optional description for tooltip */
description?: string;
/** MUI Icon component for the badge */
badgeIcon?: React.ComponentType<any>;
/** Array of tool operation names in the workflow */
operations: string[];
/** Array of tool operation names in the workflow OR full operation objects with display names */
operations: string[] | Array<{operation: string; displayName?: string}>;
/** Click handler */
onClick: () => void;
/** Whether to keep the icon at normal color (for special cases like "Add New") */
@ -29,6 +32,7 @@ interface AutomationEntryProps {
export default function AutomationEntry({
title,
description,
badgeIcon: BadgeIcon,
operations,
onClick,
@ -45,6 +49,53 @@ export default function AutomationEntry({
// Keep item in hovered state if menu is open
const shouldShowHovered = isHovered || isMenuOpen;
// Create tooltip content with description and tool chain
const createTooltipContent = () => {
if (!description) return null;
const toolChain = operations.map((op, index) => {
// Handle both string[] and operation object arrays
const operationName = typeof op === 'string' ? op : op.operation;
const displayName = typeof op === 'object' && op.displayName ? op.displayName : t(`${operationName}.title`, operationName);
return (
<React.Fragment key={`${operationName}-${index}`}>
<Text
component="span"
size="sm"
fw={600}
style={{
color: 'var(--mantine-primary-color-filled)',
background: 'var(--mantine-primary-color-light)',
padding: '2px 6px',
borderRadius: '4px',
fontSize: '0.75rem',
whiteSpace: 'nowrap'
}}
>
{displayName}
</Text>
{index < operations.length - 1 && (
<Text component="span" size="sm" mx={4}>
</Text>
)}
</React.Fragment>
);
});
return (
<div style={{ minWidth: '400px', width: 'auto' }}>
<Text size="sm" mb={8} style={{ whiteSpace: 'normal', wordWrap: 'break-word' }}>
{description}
</Text>
<div style={{ display: 'flex', alignItems: 'center', gap: '4px', whiteSpace: 'nowrap' }}>
{toolChain}
</div>
</div>
);
};
const renderContent = () => {
if (title) {
// Custom automation with title
@ -74,26 +125,32 @@ export default function AutomationEntry({
/>
)}
<Group gap="xs" justify="flex-start" style={{ flex: 1 }}>
{operations.map((op, index) => (
<React.Fragment key={`${op}-${index}`}>
<Text size="xs" style={{ color: 'var(--mantine-color-text)' }}>
{t(`${op}.title`, op)}
</Text>
{index < operations.length - 1 && (
<Text size="xs" c="dimmed" style={{ color: 'var(--mantine-color-text)' }}>
{operations.map((op, index) => {
// Handle both string[] and operation object arrays
const operationName = typeof op === 'string' ? op : op.operation;
const displayName = typeof op === 'object' && op.displayName ? op.displayName : t(`${operationName}.title`, operationName);
return (
<React.Fragment key={`${operationName}-${index}`}>
<Text size="xs" style={{ color: 'var(--mantine-color-text)' }}>
{displayName}
</Text>
)}
</React.Fragment>
))}
{index < operations.length - 1 && (
<Text size="xs" c="dimmed" style={{ color: 'var(--mantine-color-text)' }}>
</Text>
)}
</React.Fragment>
);
})}
</Group>
</Group>
);
}
};
return (
const boxContent = (
<Box
style={{
backgroundColor: shouldShowHovered ? 'var(--mantine-color-gray-1)' : 'transparent',
@ -175,4 +232,18 @@ export default function AutomationEntry({
</Group>
</Box>
);
// Only show tooltip if description exists, otherwise return plain content
return description ? (
<Tooltip
content={createTooltipContent()}
position="right"
arrow={true}
delay={500}
>
{boxContent}
</Tooltip>
) : (
boxContent
);
}

View File

@ -66,8 +66,9 @@ export default function AutomationSelection({
<AutomationEntry
key={automation.id}
title={automation.name}
description={automation.description}
badgeIcon={automation.icon}
operations={automation.operations.map(op => op.operation)}
operations={automation.operations}
onClick={() => onRun(automation)}
showMenu={true}
onCopy={() => onCopyFromSuggested(automation)}

View File

@ -19,10 +19,11 @@ export function useSuggestedAutomations(): SuggestedAutomation[] {
{
id: "secure-pdf-ingestion",
name: t("automation.suggested.securePdfIngestion", "Secure PDF Ingestion"),
description: t("automation.suggested.securePdfIngestionDesc", "Sanitise → OCR/Cleanup → PDF/A → Compress"),
description: t("automation.suggested.securePdfIngestionDesc", "Comprehensive PDF processing workflow that sanitizes documents, applies OCR with cleanup, converts to PDF/A format for long-term archival, and optimizes file size."),
operations: [
{
operation: "sanitize",
displayName: t("automation.operation.sanitize", "Sanitize"),
parameters: {
removeJavaScript: true,
removeEmbeddedFiles: true,
@ -34,6 +35,7 @@ export function useSuggestedAutomations(): SuggestedAutomation[] {
},
{
operation: "ocr",
displayName: t("automation.operation.ocrCleanup", "OCR & Cleanup"),
parameters: {
languages: ['eng'],
ocrType: 'skip-text',
@ -43,6 +45,7 @@ export function useSuggestedAutomations(): SuggestedAutomation[] {
},
{
operation: "convert",
displayName: t("automation.operation.pdfaConversion", "PDF/A Conversion"),
parameters: {
fromExtension: 'pdf',
toExtension: 'pdfa',
@ -53,6 +56,7 @@ export function useSuggestedAutomations(): SuggestedAutomation[] {
},
{
operation: "compress",
displayName: t("automation.operation.compress", "Compress"),
parameters: {
compressionLevel: 5,
grayscale: false,
@ -70,10 +74,11 @@ export function useSuggestedAutomations(): SuggestedAutomation[] {
{
id: "email-preparation",
name: t("automation.suggested.emailPreparation", "Email Preparation"),
description: t("automation.suggested.emailPreparationDesc", "Compress → Split by Size 20MB → Sanitize metadata"),
description: t("automation.suggested.emailPreparationDesc", "Optimizes PDFs for email distribution by compressing files, splitting large documents into 20MB chunks for email compatibility, and removing metadata for privacy."),
operations: [
{
operation: "compress",
displayName: t("automation.operation.compress", "Compress"),
parameters: {
compressionLevel: 5,
grayscale: false,
@ -85,6 +90,7 @@ export function useSuggestedAutomations(): SuggestedAutomation[] {
},
{
operation: "splitPdf",
displayName: t("automation.operation.splitBySize", "Split by Size (20MB)"),
parameters: {
mode: 'bySizeOrCount',
pages: '',
@ -100,6 +106,7 @@ export function useSuggestedAutomations(): SuggestedAutomation[] {
},
{
operation: "sanitize",
displayName: t("automation.operation.sanitizeMetadata", "Remove Metadata"),
parameters: {
removeJavaScript: false,
removeEmbeddedFiles: false,
@ -117,10 +124,11 @@ export function useSuggestedAutomations(): SuggestedAutomation[] {
{
id: "secure-workflow",
name: t("automation.suggested.secureWorkflow", "Security Workflow"),
description: t("automation.suggested.secureWorkflowDesc", "Sanitize PDFs and add password protection"),
description: t("automation.suggested.secureWorkflowDesc", "Secures PDF documents by removing potentially malicious content like JavaScript and embedded files, then adds password protection to prevent unauthorized access."),
operations: [
{
operation: "sanitize",
displayName: t("automation.operation.sanitize", "Sanitize"),
parameters: {
removeJavaScript: true,
removeEmbeddedFiles: true,
@ -132,6 +140,7 @@ export function useSuggestedAutomations(): SuggestedAutomation[] {
},
{
operation: "addPassword",
displayName: t("automation.operation.addPassword", "Add Password Protection"),
parameters: {
password: 'password',
ownerPassword: '',
@ -154,23 +163,34 @@ export function useSuggestedAutomations(): SuggestedAutomation[] {
icon: SecurityIcon,
},
{
id: "optimization-workflow",
name: t("automation.suggested.optimizationWorkflow", "Optimization Workflow"),
description: t("automation.suggested.optimizationWorkflowDesc", "Repair and compress PDFs for better performance"),
id: "process-images",
name: t("automation.suggested.processImages", "Process Images"),
description: t("automation.suggested.processImagesDesc", "Converts multiple image files into a single PDF document, then applies OCR technology to extract searchable text from the images."),
operations: [
{
operation: "repair",
parameters: {}
operation: "convert",
displayName: t("automation.operation.imageToPdf", "Image to PDF"),
parameters: {
fromExtension: 'image',
toExtension: 'pdf',
imageOptions: {
colorType: 'color',
dpi: 300,
singleOrMultiple: 'multiple',
fitOption: 'maintainAspectRatio',
autoRotate: true,
combineImages: true,
}
}
},
{
operation: "compress",
operation: "ocr",
displayName: t("automation.operation.ocr", "OCR Text Extraction"),
parameters: {
compressionLevel: 7,
grayscale: false,
expectedSize: '',
compressionMethod: 'quality',
fileSizeValue: '',
fileSizeUnit: 'MB',
languages: ['eng'],
ocrType: 'skip-text',
ocrRenderType: 'hocr',
additionalOptions: [],
}
}
],

View File

@ -5,6 +5,7 @@
export interface AutomationOperation {
operation: string;
parameters: Record<string, any>;
displayName?: string; // Custom display name for tooltip
}
export interface AutomationConfig {