diff --git a/build.gradle b/build.gradle index 5f44fcd5d..404639a4f 100644 --- a/build.gradle +++ b/build.gradle @@ -67,7 +67,7 @@ springBoot { allprojects { group = 'stirling.software' - version = '2.3.1' + version = '2.4.0' configurations.configureEach { exclude group: 'commons-logging', module: 'commons-logging' diff --git a/frontend/public/locales/en-GB/translation.toml b/frontend/public/locales/en-GB/translation.toml index 3d0b4518c..9e818ca3f 100644 --- a/frontend/public/locales/en-GB/translation.toml +++ b/frontend/public/locales/en-GB/translation.toml @@ -306,6 +306,7 @@ selectOperation = "Select Operation" addOperationButton = "Add operation" pipelineHeader = "Pipeline:" saveButton = "Download" +saveForFolderScanning = "Save for Folder Scanning" validateButton = "Validate" [enterpriseEdition] @@ -5465,6 +5466,7 @@ desc = "Build multi-step workflows by chaining together PDF actions. Ideal for r invalidStep = "Invalid step" reviewTitle = "Automation Results" copyToSaved = "Copy to Saved" +exportForFolderScanning = "Export for Folder Scanning" [automate.files] placeholder = "Select files to process with this automation" @@ -5486,6 +5488,7 @@ createTitle = "Create Automation" editTitle = "Edit Automation" intro = "Automations run tools sequentially. To get started, add tools in the order you want them to run." save = "Save Automation" +exportForFolderScanning = "Export for Folder Scanning" [automate.creation.name] label = "Automation Name" diff --git a/frontend/src-tauri/tauri.conf.json b/frontend/src-tauri/tauri.conf.json index c30f8c957..f22d2d44f 100644 --- a/frontend/src-tauri/tauri.conf.json +++ b/frontend/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "Stirling-PDF", - "version": "2.3.1", + "version": "2.4.0", "identifier": "stirling.pdf.dev", "build": { "frontendDist": "../dist", diff --git a/frontend/src/core/components/tools/automate/AutomationCreation.tsx b/frontend/src/core/components/tools/automate/AutomationCreation.tsx index 836c14fed..e71eb73e4 100644 --- a/frontend/src/core/components/tools/automate/AutomationCreation.tsx +++ b/frontend/src/core/components/tools/automate/AutomationCreation.tsx @@ -12,12 +12,14 @@ import { } from '@mantine/core'; import { Z_INDEX_AUTOMATE_MODAL } from '@app/styles/zIndex'; import CheckIcon from '@mui/icons-material/Check'; +import DownloadIcon from '@mui/icons-material/Download'; import { ToolRegistry } from '@app/data/toolsTaxonomy'; import ToolConfigurationModal from '@app/components/tools/automate/ToolConfigurationModal'; import ToolList from '@app/components/tools/automate/ToolList'; import IconSelector from '@app/components/tools/automate/IconSelector'; import { AutomationConfig, AutomationMode, AutomationTool } from '@app/types/automation'; import { useAutomationForm } from '@app/hooks/tools/automate/useAutomationForm'; +import { downloadFolderScanningConfig } from '@app/utils/automationConverter'; interface AutomationCreationProps { @@ -194,15 +196,42 @@ export default function AutomationCreation({ mode, existingAutomation, onBack, o - {/* Save Button */} - + {/* Action Buttons */} + + + + + {/* Tool Configuration Modal */} diff --git a/frontend/src/core/components/tools/automate/AutomationEntry.tsx b/frontend/src/core/components/tools/automate/AutomationEntry.tsx index 7871033b9..9f40a07ae 100644 --- a/frontend/src/core/components/tools/automate/AutomationEntry.tsx +++ b/frontend/src/core/components/tools/automate/AutomationEntry.tsx @@ -5,6 +5,7 @@ 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 DownloadIcon from '@mui/icons-material/Download'; import { Tooltip } from '@app/components/shared/Tooltip'; import { ToolIcon } from '@app/components/shared/ToolIcon'; import { ToolRegistry } from '@app/data/toolsTaxonomy'; @@ -31,6 +32,8 @@ interface AutomationEntryProps { onDelete?: () => void; /** Copy handler (for suggested automations) */ onCopy?: () => void; + /** Export handler (for folder scanning) */ + onExport?: () => void; /** Tool registry to resolve operation names */ toolRegistry?: Partial; } @@ -46,6 +49,7 @@ export default function AutomationEntry({ onEdit, onDelete, onCopy, + onExport, toolRegistry }: AutomationEntryProps) { const { t } = useTranslation(); @@ -225,6 +229,17 @@ export default function AutomationEntry({ {t('edit', 'Edit')} )} + {onExport && ( + } + onClick={(e) => { + e.stopPropagation(); + onExport(); + }} + > + {t('automate.exportForFolderScanning', 'Export for Folder Scanning')} + + )} {onDelete && ( } diff --git a/frontend/src/core/components/tools/automate/AutomationSelection.tsx b/frontend/src/core/components/tools/automate/AutomationSelection.tsx index 1b53ff900..73dda0164 100644 --- a/frontend/src/core/components/tools/automate/AutomationSelection.tsx +++ b/frontend/src/core/components/tools/automate/AutomationSelection.tsx @@ -7,6 +7,7 @@ import { useSuggestedAutomations } from "@app/hooks/tools/automate/useSuggestedA import { AutomationConfig, SuggestedAutomation } from "@app/types/automation"; import { iconMap } from '@app/components/tools/automate/iconMap'; import { ToolRegistry } from '@app/data/toolsTaxonomy'; +import { downloadFolderScanningConfig } from '@app/utils/automationConverter'; interface AutomationSelectionProps { savedAutomations: AutomationConfig[]; @@ -58,6 +59,7 @@ export default function AutomationSelection({ onClick={() => onRun(automation)} showMenu={true} onEdit={() => onEdit(automation)} + onExport={() => downloadFolderScanningConfig(automation)} onDelete={() => onDelete(automation)} toolRegistry={toolRegistry} /> diff --git a/frontend/src/core/testing/serverExperienceSimulations.ts b/frontend/src/core/testing/serverExperienceSimulations.ts index 57fe7b199..121c14fd1 100644 --- a/frontend/src/core/testing/serverExperienceSimulations.ts +++ b/frontend/src/core/testing/serverExperienceSimulations.ts @@ -38,7 +38,7 @@ const FREE_LICENSE_INFO: LicenseInfo = { const BASE_NO_LOGIN_CONFIG: AppConfig = { enableAnalytics: true, - appVersion: '2.3.1', + appVersion: '2.4.0', serverCertificateEnabled: false, enableAlphaFunctionality: false, serverPort: 8080, diff --git a/frontend/src/core/utils/automationConverter.ts b/frontend/src/core/utils/automationConverter.ts new file mode 100644 index 000000000..1744f52f3 --- /dev/null +++ b/frontend/src/core/utils/automationConverter.ts @@ -0,0 +1,68 @@ +/** + * Utility functions for converting between automation formats + */ + +import { AutomationConfig } from '@app/types/automation'; + +/** + * Pipeline configuration format used by folder scanning + */ +interface FolderScanningPipeline { + name: string; + pipeline: Array<{ + operation: string; + parameters: Record; + }>; + _examples: { + outputDir: string; + outputFileName: string; + }; + outputDir: string; + outputFileName: string; +} + +/** + * Converts an AutomationConfig to a folder scanning pipeline configuration + * @param automation The automation configuration to convert + * @returns Folder scanning pipeline configuration + */ +export function convertToFolderScanningConfig(automation: AutomationConfig): FolderScanningPipeline { + return { + name: automation.name, + pipeline: automation.operations.map(op => ({ + operation: op.operation, + parameters: { + ...op.parameters, + fileInput: "automated" + } + })), + _examples: { + outputDir: "{outputFolder}/{folderName}", + outputFileName: "{filename}-{pipelineName}-{date}-{time}" + }, + outputDir: "{outputFolder}", + outputFileName: "{filename}" + }; +} + +/** + * Downloads a folder scanning configuration as a JSON file + * @param automation The automation configuration to export + */ +export function downloadFolderScanningConfig(automation: AutomationConfig): void { + const config = convertToFolderScanningConfig(automation); + const json = JSON.stringify(config, null, 2); + const blob = new Blob([json], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = `${automation.name}.json`; + a.style.display = 'none'; + + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + + URL.revokeObjectURL(url); +} diff --git a/frontend/src/proprietary/testing/serverExperienceSimulations.ts b/frontend/src/proprietary/testing/serverExperienceSimulations.ts index a4add6ab7..1c3b5a6ad 100644 --- a/frontend/src/proprietary/testing/serverExperienceSimulations.ts +++ b/frontend/src/proprietary/testing/serverExperienceSimulations.ts @@ -48,7 +48,7 @@ const FREE_LICENSE_INFO: LicenseInfo = { const BASE_NO_LOGIN_CONFIG: AppConfig = { enableAnalytics: true, - appVersion: '2.3.1', + appVersion: '2.4.0', serverCertificateEnabled: false, enableAlphaFunctionality: false, enableDesktopInstallSlide: true,