diff --git a/frontend/src/core/hooks/tools/shared/useToolOperation.ts b/frontend/src/core/hooks/tools/shared/useToolOperation.ts index 4ef803efb5..4804032d0d 100644 --- a/frontend/src/core/hooks/tools/shared/useToolOperation.ts +++ b/frontend/src/core/hooks/tools/shared/useToolOperation.ts @@ -8,6 +8,7 @@ import { useToolResources } from '@app/hooks/tools/shared/useToolResources'; import { extractErrorMessage } from '@app/utils/toolErrorHandler'; import { StirlingFile, extractFiles, FileId, StirlingFileStub, createStirlingFile } from '@app/types/fileContext'; import { FILE_EVENTS } from '@app/services/errorUtils'; +import { getFilenameWithoutExtension } from '@app/utils/fileUtils'; import { ResponseHandler } from '@app/utils/toolResponseProcessor'; import { createChildStub, generateProcessedFileMetadata } from '@app/contexts/file/fileActions'; import { ToolOperation } from '@app/types/file'; @@ -305,12 +306,12 @@ export const useToolOperation = ( // Try to map outputs back to inputs by filename (before extension) const inputBaseNames = new Map(); for (const f of validFiles) { - const base = (f.name || '').replace(/\.[^.]+$/, '').toLowerCase(); + const base = getFilenameWithoutExtension(f.name || ''); inputBaseNames.set(base, f.fileId); } const mappedSuccess: FileId[] = []; for (const out of processedFiles) { - const base = (out.name || '').replace(/\.[^.]+$/, '').toLowerCase(); + const base = getFilenameWithoutExtension(out.name || ''); const id = inputBaseNames.get(base); if (id) mappedSuccess.push(id); } diff --git a/frontend/src/core/utils/fileUtils.ts b/frontend/src/core/utils/fileUtils.ts index 0f14714019..4061884b55 100644 --- a/frontend/src/core/utils/fileUtils.ts +++ b/frontend/src/core/utils/fileUtils.ts @@ -52,6 +52,29 @@ export function detectFileExtension(filename: string): string { return extension; } +/** + * Removes the file extension from a filename + * @param filename - The filename to process + * @param options - Options for processing + * @param options.preserveCase - If true, preserves original case. If false (default), converts to lowercase + * @returns Filename without extension + * @example + * getFilenameWithoutExtension('document.pdf') // 'document' + * getFilenameWithoutExtension('my.file.name.txt') // 'my.file.name' + * getFilenameWithoutExtension('REPORT.PDF', { preserveCase: true }) // 'REPORT' + */ +export function getFilenameWithoutExtension( + filename: string, + options: { preserveCase?: boolean } = {} +): string { + if (!filename || typeof filename !== 'string') return ''; + + const { preserveCase = false } = options; + const withoutExtension = filename.replace(/\.[^.]+$/, ''); + + return preserveCase ? withoutExtension : withoutExtension.toLowerCase(); +} + /** * Checks if a file is a PDF based on extension and MIME type * @param file - File or file-like object with name and type properties