From 47a16e71cf044274c27668b83ec838b1191c7a5e Mon Sep 17 00:00:00 2001 From: Connor Yoh Date: Fri, 5 Sep 2025 18:00:00 +0100 Subject: [PATCH] Working as well as it was --- frontend/src/contexts/file/fileActions.ts | 15 +++++- .../hooks/tools/shared/useToolOperation.ts | 27 +++++----- frontend/src/utils/fileHistoryUtils.ts | 50 ++++++++++++++----- 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/frontend/src/contexts/file/fileActions.ts b/frontend/src/contexts/file/fileActions.ts index 8477d2786..1d7a909af 100644 --- a/frontend/src/contexts/file/fileActions.ts +++ b/frontend/src/contexts/file/fileActions.ts @@ -466,8 +466,21 @@ export async function consumeFiles( // Process output files with thumbnails and metadata const outputStirlingFileStubs = await processFilesIntoRecords(outputFiles, filesRef); - // Mark input files as processed in IndexedDB (no longer leaf nodes) + // Mark input files as processed in IndexedDB (no longer leaf nodes) and save output files if (indexedDB) { + // Mark input files as processed (isLeaf = false) + await Promise.all( + inputFileIds.map(async (fileId) => { + try { + await indexedDB.markFileAsProcessed(fileId); + if (DEBUG) console.log(`📄 Marked file ${fileId} as processed (no longer leaf)`); + } catch (error) { + if (DEBUG) console.warn(`📄 Failed to mark file ${fileId} as processed:`, error); + } + }) + ); + + // Save output files to IndexedDB await persistFilesToIndexedDB(outputStirlingFileStubs, indexedDB); } diff --git a/frontend/src/hooks/tools/shared/useToolOperation.ts b/frontend/src/hooks/tools/shared/useToolOperation.ts index 7bcfecace..20ca92ef8 100644 --- a/frontend/src/hooks/tools/shared/useToolOperation.ts +++ b/frontend/src/hooks/tools/shared/useToolOperation.ts @@ -8,7 +8,7 @@ import { useToolResources } from './useToolResources'; import { extractErrorMessage } from '../../../utils/toolErrorHandler'; import { StirlingFile, extractFiles, FileId, StirlingFileStub } from '../../../types/fileContext'; import { ResponseHandler } from '../../../utils/toolResponseProcessor'; -import { prepareFilesWithHistory, verifyToolMetadataPreservation } from '../../../utils/fileHistoryUtils'; +import { prepareStirlingFilesWithHistory, verifyToolMetadataPreservation } from '../../../utils/fileHistoryUtils'; // Re-export for backwards compatibility export type { ProcessingProgress, ResponseHandler }; @@ -159,7 +159,6 @@ export const useToolOperation = ( return; } - // Reset state actions.setLoading(true); actions.setError(null); @@ -168,14 +167,13 @@ export const useToolOperation = ( // Prepare files with history metadata injection (for PDFs) actions.setStatus('Preparing files...'); - const getFileStub = (file: File) => { - const fileId = findFileId(file); - return fileId ? selectors.getStirlingFileStub(fileId) : undefined; + const getFileStubById = (fileId: FileId) => { + return selectors.getStirlingFileStub(fileId); }; - const filesWithHistory = await prepareFilesWithHistory( + const filesWithHistory = await prepareStirlingFilesWithHistory( validFiles, - getFileStub, + getFileStubById, config.operationType, params as Record ); @@ -183,8 +181,9 @@ export const useToolOperation = ( try { let processedFiles: File[]; - // Convert StirlingFile to regular File objects for API processing - const validRegularFiles = extractFiles(validFiles); + // Convert StirlingFiles with history to regular Files for API processing + // The history is already injected into the File data, we just need to extract the File objects + const filesForAPI = extractFiles(filesWithHistory); switch (config.toolType) { case ToolType.singleFile: { @@ -197,7 +196,7 @@ export const useToolOperation = ( }; processedFiles = await processFiles( params, - validRegularFiles, + filesForAPI, apiCallsConfig, actions.setProgress, actions.setStatus @@ -207,7 +206,7 @@ export const useToolOperation = ( case ToolType.multiFile: { // Multi-file processing - single API call with all files actions.setStatus('Processing files...'); - const formData = config.buildFormData(params, validRegularFiles); + const formData = config.buildFormData(params, filesForAPI); const endpoint = typeof config.endpoint === 'function' ? config.endpoint(params) : config.endpoint; const response = await axios.post(endpoint, formData, { responseType: 'blob' }); @@ -215,11 +214,11 @@ export const useToolOperation = ( // Multi-file responses are typically ZIP files that need extraction, but some may return single PDFs if (config.responseHandler) { // Use custom responseHandler for multi-file (handles ZIP extraction) - processedFiles = await config.responseHandler(response.data, validRegularFiles); + processedFiles = await config.responseHandler(response.data, filesForAPI); } else if (response.data.type === 'application/pdf' || (response.headers && response.headers['content-type'] === 'application/pdf')) { // Single PDF response (e.g. split with merge option) - use original filename - const originalFileName = validRegularFiles[0]?.name || 'document.pdf'; + const originalFileName = filesForAPI[0]?.name || 'document.pdf'; const singleFile = new File([response.data], originalFileName, { type: 'application/pdf' }); processedFiles = [singleFile]; } else { @@ -236,7 +235,7 @@ export const useToolOperation = ( case ToolType.custom: actions.setStatus('Processing files...'); - processedFiles = await config.customProcessor(params, validRegularFiles); + processedFiles = await config.customProcessor(params, filesForAPI); break; } diff --git a/frontend/src/utils/fileHistoryUtils.ts b/frontend/src/utils/fileHistoryUtils.ts index 5a8a71d33..fbea24bfb 100644 --- a/frontend/src/utils/fileHistoryUtils.ts +++ b/frontend/src/utils/fileHistoryUtils.ts @@ -123,25 +123,51 @@ export async function injectHistoryForTool( } /** - * Prepare FormData with history-injected PDFs for tool operations + * Prepare StirlingFiles with history-injected PDFs for tool operations + * Preserves fileId and all StirlingFile metadata while injecting history */ -export async function prepareFilesWithHistory( - files: File[], - getStirlingFileStub: (file: File) => StirlingFileStub | undefined, +export async function prepareStirlingFilesWithHistory( + stirlingFiles: import('../types/fileContext').StirlingFile[], + getStirlingFileStub: (fileId: import('../types/file').FileId) => StirlingFileStub | undefined, toolName: string, parameters?: Record -): Promise { - const processedFiles: File[] = []; +): Promise { + const processedFiles: import('../types/fileContext').StirlingFile[] = []; - for (const file of files) { - const record = getStirlingFileStub(file); - if (!record) { - processedFiles.push(file); + for (const stirlingFile of stirlingFiles) { + const fileStub = getStirlingFileStub(stirlingFile.fileId); + + if (!fileStub) { + // If no stub found, keep original file + processedFiles.push(stirlingFile); continue; } - const fileWithHistory = await injectHistoryForTool(file, record, toolName, parameters); - processedFiles.push(fileWithHistory); + // Inject history into the file data + const fileWithHistory = await injectHistoryForTool(stirlingFile, fileStub, toolName, parameters); + + // Create new StirlingFile with the updated file data but preserve fileId and quickKey + const updatedStirlingFile = new File([fileWithHistory], fileWithHistory.name, { + type: fileWithHistory.type, + lastModified: fileWithHistory.lastModified + }) as import('../types/fileContext').StirlingFile; + + // Preserve the original fileId and quickKey + Object.defineProperty(updatedStirlingFile, 'fileId', { + value: stirlingFile.fileId, + writable: false, + enumerable: true, + configurable: false + }); + + Object.defineProperty(updatedStirlingFile, 'quickKey', { + value: stirlingFile.quickKey, + writable: false, + enumerable: true, + configurable: false + }); + + processedFiles.push(updatedStirlingFile); } return processedFiles;