Files
Stirling-PDF/frontend/src/core/utils/automationConverter.ts
T
b8ce4e47c1 Preserve local paths for desktop saves (#5543)
# Summary

- Adds desktop file tracking: local paths are preserved and save buttons
now work as expcted (doing Save/Save As as appropriate)
- Adds logic to track whether files are 'dirty' (they've been modified
by some tool, and not saved to disk yet).
- Improves file state UX (dirty vs saved) and close warnings
- Web behaviour should be unaffected by these changes

## Indicators
Files now have indicators in desktop mode to tell you their state.

### File up-to-date with disk

<img width="318" height="393" alt="image"
src="https://github.com/user-attachments/assets/06325f9a-afd7-4c2f-8a5b-6d11e3093115"
/>

### File modified by a tool but not saved to disk yet

<img width="357" height="385" alt="image"
src="https://github.com/user-attachments/assets/1a7716d9-c6f7-4d13-be0d-c1de6493954b"
/>

### File not tracked on disk

<img width="312" height="379" alt="image"
src="https://github.com/user-attachments/assets/9cffe300-bd9a-4e19-97c7-9b98bebefacc"
/>

# Limitations
- It's a bit weird that we still have files stored in indexeddb in the
app, which are still loadable. We might want to change this behaviour in
the future
- Viewer's Save doesn't persist to disk. I've left that out here because
it'd need a lot of testing to make sure the logic's right with making
sure you can leave the Viewer with applying the changes to the PDF
_without_ saving to disk
- There's no current way to do Save As on a file that has already been
persisted to disk - it's only ever Save. Similarly, there's no way to
duplicate a file.

---------

Co-authored-by: James Brunton <[email protected]>
Co-authored-by: James Brunton <[email protected]>
2026-02-13 23:15:28 +00:00

101 lines
3.1 KiB
TypeScript

/**
* Utility functions for converting between automation formats
*/
import { AutomationConfig } from '@app/types/automation';
import { ToolRegistry } from '@app/data/toolsTaxonomy';
import { downloadFile } from '@app/services/downloadService';
import { ToolId } from '@app/types/toolId';
/**
* Pipeline configuration format used by folder scanning
*/
interface FolderScanningPipeline {
name: string;
pipeline: Array<{
operation: string;
parameters: Record<string, any>;
}>;
_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
* @param toolRegistry The tool registry to map operation types to endpoints
* @returns Folder scanning pipeline configuration
*/
export function convertToFolderScanningConfig(
automation: AutomationConfig,
toolRegistry: Partial<ToolRegistry>
): FolderScanningPipeline {
return {
name: automation.name,
pipeline: automation.operations.map(op => {
// Map operationType to full API endpoint path
const toolId = op.operation as ToolId;
const toolEntry = toolRegistry[toolId];
const endpointConfig = toolEntry?.operationConfig?.endpoint;
let endpoint: string | undefined;
if (typeof endpointConfig === 'string') {
endpoint = endpointConfig;
} else if (typeof endpointConfig === 'function') {
// For dynamic endpoints, call with the saved parameters
try {
endpoint = endpointConfig(op.parameters);
} catch (error) {
console.warn(
`Failed to resolve dynamic endpoint for operation "${op.operation}". ` +
`This may happen if the tool requires specific parameters. ` +
`Error: ${error}`
);
}
}
if (!endpoint) {
console.warn(
`No endpoint found for operation "${op.operation}". ` +
`This operation may fail in folder scanning. ` +
`Using operation type as fallback.`
);
}
return {
operation: endpoint || 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
* @param toolRegistry The tool registry to map operation types to endpoints
*/
export function downloadFolderScanningConfig(
automation: AutomationConfig,
toolRegistry: Partial<ToolRegistry>
): void {
const config = convertToFolderScanningConfig(automation, toolRegistry);
const json = JSON.stringify(config, null, 2);
const blob = new Blob([json], { type: 'application/json' });
void downloadFile({ data: blob, filename: `${automation.name}.json` });
}