mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-02 18:45:21 +00:00
fix tests, removed excess logging and warnings
This commit is contained in:
parent
f01e3081a7
commit
78e5594cc3
@ -43,7 +43,6 @@ const ConvertSettings = ({
|
|||||||
const { setSelectedFiles } = useFileSelectionActions();
|
const { setSelectedFiles } = useFileSelectionActions();
|
||||||
const { setSelectedFiles: setContextSelectedFiles } = useFileContext();
|
const { setSelectedFiles: setContextSelectedFiles } = useFileContext();
|
||||||
|
|
||||||
// Get all possible conversion endpoints to check their availability
|
|
||||||
const allEndpoints = useMemo(() => {
|
const allEndpoints = useMemo(() => {
|
||||||
const endpoints = new Set<string>();
|
const endpoints = new Set<string>();
|
||||||
Object.values(EXTENSION_TO_ENDPOINT).forEach(toEndpoints => {
|
Object.values(EXTENSION_TO_ENDPOINT).forEach(toEndpoints => {
|
||||||
@ -56,7 +55,6 @@ const ConvertSettings = ({
|
|||||||
|
|
||||||
const { endpointStatus } = useMultipleEndpointsEnabled(allEndpoints);
|
const { endpointStatus } = useMultipleEndpointsEnabled(allEndpoints);
|
||||||
|
|
||||||
// Function to check if a conversion is available based on endpoint
|
|
||||||
const isConversionAvailable = (fromExt: string, toExt: string): boolean => {
|
const isConversionAvailable = (fromExt: string, toExt: string): boolean => {
|
||||||
const endpointKey = EXTENSION_TO_ENDPOINT[fromExt]?.[toExt];
|
const endpointKey = EXTENSION_TO_ENDPOINT[fromExt]?.[toExt];
|
||||||
if (!endpointKey) return false;
|
if (!endpointKey) return false;
|
||||||
@ -100,7 +98,6 @@ const ConvertSettings = ({
|
|||||||
const autoTarget = availableToOptions.length === 1 ? availableToOptions[0].value : '';
|
const autoTarget = availableToOptions.length === 1 ? availableToOptions[0].value : '';
|
||||||
onParameterChange('toExtension', autoTarget);
|
onParameterChange('toExtension', autoTarget);
|
||||||
|
|
||||||
// Reset format-specific options
|
|
||||||
onParameterChange('imageOptions', {
|
onParameterChange('imageOptions', {
|
||||||
colorType: COLOR_TYPES.COLOR,
|
colorType: COLOR_TYPES.COLOR,
|
||||||
dpi: 300,
|
dpi: 300,
|
||||||
@ -118,30 +115,23 @@ const ConvertSettings = ({
|
|||||||
onParameterChange('pdfaOptions', {
|
onParameterChange('pdfaOptions', {
|
||||||
outputFormat: 'pdfa-1',
|
outputFormat: 'pdfa-1',
|
||||||
});
|
});
|
||||||
// Disable smart detection when manually changing source format
|
|
||||||
onParameterChange('isSmartDetection', false);
|
onParameterChange('isSmartDetection', false);
|
||||||
onParameterChange('smartDetectionType', 'none');
|
onParameterChange('smartDetectionType', 'none');
|
||||||
|
|
||||||
// Deselect files that don't match the new source format
|
|
||||||
if (selectedFiles.length > 0 && value !== 'any') {
|
if (selectedFiles.length > 0 && value !== 'any') {
|
||||||
const matchingFiles = selectedFiles.filter(file => {
|
const matchingFiles = selectedFiles.filter(file => {
|
||||||
const extension = file.name.split('.').pop()?.toLowerCase() || '';
|
const extension = file.name.split('.').pop()?.toLowerCase() || '';
|
||||||
|
|
||||||
// For 'image' source format, check if it's an image
|
|
||||||
if (value === 'image') {
|
if (value === 'image') {
|
||||||
return isImageFormat(extension);
|
return isImageFormat(extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For specific extensions, match exactly
|
|
||||||
return extension === value;
|
return extension === value;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Only update selection if files were filtered out
|
|
||||||
if (matchingFiles.length !== selectedFiles.length) {
|
if (matchingFiles.length !== selectedFiles.length) {
|
||||||
// Update both selection contexts
|
|
||||||
setSelectedFiles(matchingFiles);
|
setSelectedFiles(matchingFiles);
|
||||||
|
|
||||||
// Update File Context selection with file IDs
|
|
||||||
const matchingFileIds = matchingFiles.map(file => (file as any).id || file.name);
|
const matchingFileIds = matchingFiles.map(file => (file as any).id || file.name);
|
||||||
setContextSelectedFiles(matchingFileIds);
|
setContextSelectedFiles(matchingFileIds);
|
||||||
}
|
}
|
||||||
@ -150,7 +140,6 @@ const ConvertSettings = ({
|
|||||||
|
|
||||||
const handleToExtensionChange = (value: string) => {
|
const handleToExtensionChange = (value: string) => {
|
||||||
onParameterChange('toExtension', value);
|
onParameterChange('toExtension', value);
|
||||||
// Reset format-specific options when target extension changes
|
|
||||||
onParameterChange('imageOptions', {
|
onParameterChange('imageOptions', {
|
||||||
colorType: COLOR_TYPES.COLOR,
|
colorType: COLOR_TYPES.COLOR,
|
||||||
dpi: 300,
|
dpi: 300,
|
||||||
|
@ -32,7 +32,6 @@ const GroupedFormatDropdown = ({
|
|||||||
const theme = useMantineTheme();
|
const theme = useMantineTheme();
|
||||||
const { colorScheme } = useMantineColorScheme();
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
|
||||||
// Group options by category
|
|
||||||
const groupedOptions = useMemo(() => {
|
const groupedOptions = useMemo(() => {
|
||||||
const groups: Record<string, FormatOption[]> = {};
|
const groups: Record<string, FormatOption[]> = {};
|
||||||
|
|
||||||
@ -46,7 +45,6 @@ const GroupedFormatDropdown = ({
|
|||||||
return groups;
|
return groups;
|
||||||
}, [options]);
|
}, [options]);
|
||||||
|
|
||||||
// Get selected option label for display in format "Group (EXTENSION)"
|
|
||||||
const selectedLabel = useMemo(() => {
|
const selectedLabel = useMemo(() => {
|
||||||
if (!value) return placeholder;
|
if (!value) return placeholder;
|
||||||
const selected = options.find(opt => opt.value === value);
|
const selected = options.find(opt => opt.value === value);
|
||||||
|
@ -31,11 +31,6 @@ export interface ConvertOperationHook {
|
|||||||
clearError: () => void;
|
clearError: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility functions for better maintainability
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if multiple files should be processed separately
|
|
||||||
*/
|
|
||||||
const shouldProcessFilesSeparately = (
|
const shouldProcessFilesSeparately = (
|
||||||
selectedFiles: File[],
|
selectedFiles: File[],
|
||||||
parameters: ConvertParameters
|
parameters: ConvertParameters
|
||||||
@ -58,9 +53,6 @@ const shouldProcessFilesSeparately = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a file from API response with fallback naming
|
|
||||||
*/
|
|
||||||
const createFileFromResponse = (
|
const createFileFromResponse = (
|
||||||
responseData: any,
|
responseData: any,
|
||||||
headers: any,
|
headers: any,
|
||||||
@ -73,9 +65,6 @@ const createFileFromResponse = (
|
|||||||
return createFileFromApiResponse(responseData, headers, fallbackFilename);
|
return createFileFromApiResponse(responseData, headers, fallbackFilename);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates thumbnails for multiple files
|
|
||||||
*/
|
|
||||||
const generateThumbnailsForFiles = async (files: File[]): Promise<string[]> => {
|
const generateThumbnailsForFiles = async (files: File[]): Promise<string[]> => {
|
||||||
const thumbnails: string[] = [];
|
const thumbnails: string[] = [];
|
||||||
|
|
||||||
@ -84,7 +73,6 @@ const generateThumbnailsForFiles = async (files: File[]): Promise<string[]> => {
|
|||||||
const thumbnail = await generateThumbnailForFile(file);
|
const thumbnail = await generateThumbnailForFile(file);
|
||||||
thumbnails.push(thumbnail);
|
thumbnails.push(thumbnail);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Failed to generate thumbnail for ${file.name}:`, error);
|
|
||||||
thumbnails.push('');
|
thumbnails.push('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,16 +80,11 @@ const generateThumbnailsForFiles = async (files: File[]): Promise<string[]> => {
|
|||||||
return thumbnails;
|
return thumbnails;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates download URL and filename for single or multiple files
|
|
||||||
*/
|
|
||||||
const createDownloadInfo = async (files: File[]): Promise<{ url: string; filename: string }> => {
|
const createDownloadInfo = async (files: File[]): Promise<{ url: string; filename: string }> => {
|
||||||
if (files.length === 1) {
|
if (files.length === 1) {
|
||||||
// Single file - direct download
|
|
||||||
const url = window.URL.createObjectURL(files[0]);
|
const url = window.URL.createObjectURL(files[0]);
|
||||||
return { url, filename: files[0].name };
|
return { url, filename: files[0].name };
|
||||||
} else {
|
} else {
|
||||||
// Multiple files - create ZIP for convenient download
|
|
||||||
const JSZip = (await import('jszip')).default;
|
const JSZip = (await import('jszip')).default;
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
|
|
||||||
@ -125,7 +108,6 @@ export const useConvertOperation = (): ConvertOperationHook => {
|
|||||||
addFiles
|
addFiles
|
||||||
} = useFileContext();
|
} = useFileContext();
|
||||||
|
|
||||||
// Internal state management
|
|
||||||
const [files, setFiles] = useState<File[]>([]);
|
const [files, setFiles] = useState<File[]>([]);
|
||||||
const [thumbnails, setThumbnails] = useState<string[]>([]);
|
const [thumbnails, setThumbnails] = useState<string[]>([]);
|
||||||
const [isGeneratingThumbnails, setIsGeneratingThumbnails] = useState(false);
|
const [isGeneratingThumbnails, setIsGeneratingThumbnails] = useState(false);
|
||||||
@ -147,7 +129,6 @@ export const useConvertOperation = (): ConvertOperationHook => {
|
|||||||
|
|
||||||
const { fromExtension, toExtension, imageOptions, htmlOptions, emailOptions, pdfaOptions } = parameters;
|
const { fromExtension, toExtension, imageOptions, htmlOptions, emailOptions, pdfaOptions } = parameters;
|
||||||
|
|
||||||
// Add conversion-specific parameters
|
|
||||||
if (isImageFormat(toExtension)) {
|
if (isImageFormat(toExtension)) {
|
||||||
formData.append("imageFormat", toExtension);
|
formData.append("imageFormat", toExtension);
|
||||||
formData.append("colorType", imageOptions.colorType);
|
formData.append("colorType", imageOptions.colorType);
|
||||||
@ -164,19 +145,15 @@ export const useConvertOperation = (): ConvertOperationHook => {
|
|||||||
formData.append("colorType", imageOptions.colorType);
|
formData.append("colorType", imageOptions.colorType);
|
||||||
formData.append("autoRotate", imageOptions.autoRotate.toString());
|
formData.append("autoRotate", imageOptions.autoRotate.toString());
|
||||||
} else if ((fromExtension === 'html' || fromExtension === 'zip') && toExtension === 'pdf') {
|
} else if ((fromExtension === 'html' || fromExtension === 'zip') && toExtension === 'pdf') {
|
||||||
// HTML to PDF conversion with zoom level (includes ZIP files with HTML)
|
|
||||||
formData.append("zoom", htmlOptions.zoomLevel.toString());
|
formData.append("zoom", htmlOptions.zoomLevel.toString());
|
||||||
} else if (fromExtension === 'eml' && toExtension === 'pdf') {
|
} else if (fromExtension === 'eml' && toExtension === 'pdf') {
|
||||||
// Email to PDF conversion with email-specific options
|
|
||||||
formData.append("includeAttachments", emailOptions.includeAttachments.toString());
|
formData.append("includeAttachments", emailOptions.includeAttachments.toString());
|
||||||
formData.append("maxAttachmentSizeMB", emailOptions.maxAttachmentSizeMB.toString());
|
formData.append("maxAttachmentSizeMB", emailOptions.maxAttachmentSizeMB.toString());
|
||||||
formData.append("downloadHtml", emailOptions.downloadHtml.toString());
|
formData.append("downloadHtml", emailOptions.downloadHtml.toString());
|
||||||
formData.append("includeAllRecipients", emailOptions.includeAllRecipients.toString());
|
formData.append("includeAllRecipients", emailOptions.includeAllRecipients.toString());
|
||||||
} else if (fromExtension === 'pdf' && toExtension === 'pdfa') {
|
} else if (fromExtension === 'pdf' && toExtension === 'pdfa') {
|
||||||
// PDF to PDF/A conversion with output format
|
|
||||||
formData.append("outputFormat", pdfaOptions.outputFormat);
|
formData.append("outputFormat", pdfaOptions.outputFormat);
|
||||||
} else if (fromExtension === 'pdf' && toExtension === 'csv') {
|
} else if (fromExtension === 'pdf' && toExtension === 'csv') {
|
||||||
// CSV extraction - always process all pages for simplified workflow
|
|
||||||
formData.append("pageNumbers", "all");
|
formData.append("pageNumbers", "all");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,12 +227,9 @@ export const useConvertOperation = (): ConvertOperationHook => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use utility function to determine processing strategy
|
|
||||||
if (shouldProcessFilesSeparately(selectedFiles, parameters)) {
|
if (shouldProcessFilesSeparately(selectedFiles, parameters)) {
|
||||||
// Process each file separately with appropriate endpoint
|
|
||||||
await executeMultipleSeparateFiles(parameters, selectedFiles);
|
await executeMultipleSeparateFiles(parameters, selectedFiles);
|
||||||
} else {
|
} else {
|
||||||
// Process all files together (default behavior)
|
|
||||||
await executeSingleCombinedOperation(parameters, selectedFiles);
|
await executeSingleCombinedOperation(parameters, selectedFiles);
|
||||||
}
|
}
|
||||||
}, [t]);
|
}, [t]);
|
||||||
@ -276,14 +250,9 @@ export const useConvertOperation = (): ConvertOperationHook => {
|
|||||||
const file = selectedFiles[i];
|
const file = selectedFiles[i];
|
||||||
setStatus(t("convert.processingFile", `Processing file ${i + 1} of ${selectedFiles.length}...`));
|
setStatus(t("convert.processingFile", `Processing file ${i + 1} of ${selectedFiles.length}...`));
|
||||||
|
|
||||||
// Detect the specific file type for this file using the shared utility
|
|
||||||
const fileExtension = detectFileExtension(file.name);
|
const fileExtension = detectFileExtension(file.name);
|
||||||
|
|
||||||
// Determine the best endpoint for this specific file type
|
|
||||||
let endpoint = getEndpointUrl(fileExtension, parameters.toExtension);
|
let endpoint = getEndpointUrl(fileExtension, parameters.toExtension);
|
||||||
let fileSpecificParams = { ...parameters, fromExtension: fileExtension };
|
let fileSpecificParams = { ...parameters, fromExtension: fileExtension };
|
||||||
|
|
||||||
// Fallback to file-to-pdf if specific endpoint doesn't exist
|
|
||||||
if (!endpoint && parameters.toExtension === 'pdf') {
|
if (!endpoint && parameters.toExtension === 'pdf') {
|
||||||
endpoint = '/api/v1/convert/file/pdf';
|
endpoint = '/api/v1/convert/file/pdf';
|
||||||
console.log(`Using file-to-pdf fallback for ${fileExtension} file: ${file.name}`);
|
console.log(`Using file-to-pdf fallback for ${fileExtension} file: ${file.name}`);
|
||||||
@ -291,10 +260,9 @@ export const useConvertOperation = (): ConvertOperationHook => {
|
|||||||
|
|
||||||
if (!endpoint) {
|
if (!endpoint) {
|
||||||
console.error(`No endpoint available for ${fileExtension} to ${parameters.toExtension}`);
|
console.error(`No endpoint available for ${fileExtension} to ${parameters.toExtension}`);
|
||||||
continue; // Skip this file
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create individual operation for this file
|
|
||||||
const { operation, operationId, fileId } = createOperation(fileSpecificParams, [file]);
|
const { operation, operationId, fileId } = createOperation(fileSpecificParams, [file]);
|
||||||
const formData = buildFormData(fileSpecificParams, [file]);
|
const formData = buildFormData(fileSpecificParams, [file]);
|
||||||
|
|
||||||
@ -316,32 +284,24 @@ export const useConvertOperation = (): ConvertOperationHook => {
|
|||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(`Error converting file ${file.name}:`, error);
|
console.error(`Error converting file ${file.name}:`, error);
|
||||||
markOperationFailed(fileId, operationId);
|
markOperationFailed(fileId, operationId);
|
||||||
// Continue with other files even if one fails
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (results.length > 0) {
|
if (results.length > 0) {
|
||||||
console.log(`Multi-file conversion completed: ${results.length} files processed from ${selectedFiles.length} input files`);
|
|
||||||
console.log('Result files:', results.map(f => f.name));
|
|
||||||
|
|
||||||
// Use utility function to generate thumbnails
|
|
||||||
const generatedThumbnails = await generateThumbnailsForFiles(results);
|
const generatedThumbnails = await generateThumbnailsForFiles(results);
|
||||||
|
|
||||||
// Set results for multiple files
|
|
||||||
setFiles(results);
|
setFiles(results);
|
||||||
setThumbnails(generatedThumbnails);
|
setThumbnails(generatedThumbnails);
|
||||||
|
|
||||||
// Add all converted files to FileContext
|
|
||||||
await addFiles(results);
|
await addFiles(results);
|
||||||
|
|
||||||
// Use utility function to create download info
|
|
||||||
try {
|
try {
|
||||||
const { url, filename } = await createDownloadInfo(results);
|
const { url, filename } = await createDownloadInfo(results);
|
||||||
setDownloadUrl(url);
|
setDownloadUrl(url);
|
||||||
setDownloadFilename(filename);
|
setDownloadFilename(filename);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create download info:', error);
|
console.error('Failed to create download info:', error);
|
||||||
// Fallback to first file only
|
|
||||||
const url = window.URL.createObjectURL(results[0]);
|
const url = window.URL.createObjectURL(results[0]);
|
||||||
setDownloadUrl(url);
|
setDownloadUrl(url);
|
||||||
setDownloadFilename(results[0].name);
|
setDownloadFilename(results[0].name);
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect } from 'vitest';
|
||||||
import { renderHook, act } from '@testing-library/react';
|
import { renderHook, act, waitFor } from '@testing-library/react';
|
||||||
import { useConvertParameters } from './useConvertParameters';
|
import { useConvertParameters } from './useConvertParameters';
|
||||||
|
|
||||||
describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||||
@ -53,23 +53,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
|||||||
expect(result.current.parameters.toExtension).toBe('pdf'); // Fallback to file-to-pdf
|
expect(result.current.parameters.toExtension).toBe('pdf'); // Fallback to file-to-pdf
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should reset parameters when no files provided', () => {
|
|
||||||
const { result } = renderHook(() => useConvertParameters());
|
|
||||||
|
|
||||||
// First set some parameters
|
|
||||||
act(() => {
|
|
||||||
result.current.analyzeFileTypes([{ name: 'test.pdf' }]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Then analyze empty file list
|
|
||||||
act(() => {
|
|
||||||
result.current.analyzeFileTypes([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.current.parameters.fromExtension).toBe('');
|
|
||||||
expect(result.current.parameters.toExtension).toBe('');
|
|
||||||
expect(result.current.parameters.isSmartDetection).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Multiple Identical Files', () => {
|
describe('Multiple Identical Files', () => {
|
||||||
|
@ -30,6 +30,56 @@ vi.mock('i18next-http-backend', () => ({
|
|||||||
global.URL.createObjectURL = vi.fn(() => 'mocked-url')
|
global.URL.createObjectURL = vi.fn(() => 'mocked-url')
|
||||||
global.URL.revokeObjectURL = vi.fn()
|
global.URL.revokeObjectURL = vi.fn()
|
||||||
|
|
||||||
|
// Mock File and Blob API methods that aren't available in jsdom
|
||||||
|
if (!globalThis.File.prototype.arrayBuffer) {
|
||||||
|
globalThis.File.prototype.arrayBuffer = function() {
|
||||||
|
// Return a simple ArrayBuffer with some mock data
|
||||||
|
const buffer = new ArrayBuffer(8);
|
||||||
|
const view = new Uint8Array(buffer);
|
||||||
|
view.set([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||||
|
return Promise.resolve(buffer);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!globalThis.Blob.prototype.arrayBuffer) {
|
||||||
|
globalThis.Blob.prototype.arrayBuffer = function() {
|
||||||
|
// Return a simple ArrayBuffer with some mock data
|
||||||
|
const buffer = new ArrayBuffer(8);
|
||||||
|
const view = new Uint8Array(buffer);
|
||||||
|
view.set([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||||
|
return Promise.resolve(buffer);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock crypto.subtle for hashing in tests - force override even if exists
|
||||||
|
const mockHashBuffer = new ArrayBuffer(32);
|
||||||
|
const mockHashView = new Uint8Array(mockHashBuffer);
|
||||||
|
// Fill with predictable mock hash data
|
||||||
|
for (let i = 0; i < 32; i++) {
|
||||||
|
mockHashView[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force override crypto.subtle to avoid Node.js native implementation
|
||||||
|
Object.defineProperty(globalThis, 'crypto', {
|
||||||
|
value: {
|
||||||
|
subtle: {
|
||||||
|
digest: vi.fn().mockImplementation(async (algorithm: string, data: any) => {
|
||||||
|
// Always return the mock hash buffer regardless of input
|
||||||
|
return mockHashBuffer.slice();
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
getRandomValues: vi.fn().mockImplementation((array: any) => {
|
||||||
|
// Mock getRandomValues if needed
|
||||||
|
for (let i = 0; i < array.length; i++) {
|
||||||
|
array[i] = Math.floor(Math.random() * 256);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}),
|
||||||
|
} as Crypto,
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
|
||||||
// Mock Worker for tests (Web Workers not available in test environment)
|
// Mock Worker for tests (Web Workers not available in test environment)
|
||||||
global.Worker = vi.fn().mockImplementation(() => ({
|
global.Worker = vi.fn().mockImplementation(() => ({
|
||||||
postMessage: vi.fn(),
|
postMessage: vi.fn(),
|
||||||
|
@ -27,12 +27,10 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
const convertParams = useConvertParameters();
|
const convertParams = useConvertParameters();
|
||||||
const convertOperation = useConvertOperation();
|
const convertOperation = useConvertOperation();
|
||||||
|
|
||||||
// Endpoint validation
|
|
||||||
const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled(
|
const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled(
|
||||||
convertParams.getEndpointName()
|
convertParams.getEndpointName()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Auto-scroll to bottom when content grows
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
if (scrollContainerRef.current) {
|
if (scrollContainerRef.current) {
|
||||||
scrollContainerRef.current.scrollTo({
|
scrollContainerRef.current.scrollTo({
|
||||||
@ -42,13 +40,11 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate state variables first
|
|
||||||
const hasFiles = selectedFiles.length > 0;
|
const hasFiles = selectedFiles.length > 0;
|
||||||
const hasResults = convertOperation.downloadUrl !== null;
|
const hasResults = convertOperation.downloadUrl !== null;
|
||||||
const filesCollapsed = hasFiles;
|
const filesCollapsed = hasFiles;
|
||||||
const settingsCollapsed = hasResults;
|
const settingsCollapsed = hasResults;
|
||||||
|
|
||||||
// Auto-detect extension when files change - now with smart detection
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedFiles.length > 0) {
|
if (selectedFiles.length > 0) {
|
||||||
convertParams.analyzeFileTypes(selectedFiles);
|
convertParams.analyzeFileTypes(selectedFiles);
|
||||||
@ -62,17 +58,15 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
onPreviewFile?.(null);
|
onPreviewFile?.(null);
|
||||||
}, [convertParams.parameters, selectedFiles]);
|
}, [convertParams.parameters, selectedFiles]);
|
||||||
|
|
||||||
// Auto-scroll when settings step becomes visible (files selected)
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hasFiles) {
|
if (hasFiles) {
|
||||||
setTimeout(scrollToBottom, 100); // Small delay to ensure DOM update
|
setTimeout(scrollToBottom, 100);
|
||||||
}
|
}
|
||||||
}, [hasFiles]);
|
}, [hasFiles]);
|
||||||
|
|
||||||
// Auto-scroll when results appear
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hasResults) {
|
if (hasResults) {
|
||||||
setTimeout(scrollToBottom, 100); // Small delay to ensure DOM update
|
setTimeout(scrollToBottom, 100);
|
||||||
}
|
}
|
||||||
}, [hasResults]);
|
}, [hasResults]);
|
||||||
|
|
||||||
@ -116,7 +110,6 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
<div className="h-full max-h-screen overflow-y-auto" ref={scrollContainerRef}>
|
<div className="h-full max-h-screen overflow-y-auto" ref={scrollContainerRef}>
|
||||||
<ToolStepContainer>
|
<ToolStepContainer>
|
||||||
<Stack gap="sm" p="sm">
|
<Stack gap="sm" p="sm">
|
||||||
{/* Files Step */}
|
|
||||||
<ToolStep
|
<ToolStep
|
||||||
title={t("convert.files", "Files")}
|
title={t("convert.files", "Files")}
|
||||||
isVisible={true}
|
isVisible={true}
|
||||||
@ -130,7 +123,6 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
/>
|
/>
|
||||||
</ToolStep>
|
</ToolStep>
|
||||||
|
|
||||||
{/* Settings Step */}
|
|
||||||
<ToolStep
|
<ToolStep
|
||||||
title={t("convert.settings", "Settings")}
|
title={t("convert.settings", "Settings")}
|
||||||
isVisible={true}
|
isVisible={true}
|
||||||
@ -161,7 +153,6 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
</ToolStep>
|
</ToolStep>
|
||||||
|
|
||||||
{/* Results Step */}
|
|
||||||
<ToolStep
|
<ToolStep
|
||||||
title={t("convert.results", "Results")}
|
title={t("convert.results", "Results")}
|
||||||
isVisible={hasResults}
|
isVisible={hasResults}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user