diff --git a/frontend/src/core/tools/Merge.tsx b/frontend/src/core/tools/Merge.tsx index e725291db..05d358f70 100644 --- a/frontend/src/core/tools/Merge.tsx +++ b/frontend/src/core/tools/Merge.tsx @@ -26,6 +26,57 @@ const Merge = (props: BaseToolProps) => { props, { minFiles: 2 } ); + const naturalCompare = useCallback((a: string, b: string): number => { + const isDigit = (char: string) => char >= '0' && char <= '9'; + + const getChunk = (s: string, length: number, marker: number): { chunk: string; newMarker: number } => { + let chunk = ''; + const c = s.charAt(marker); + chunk += c; + marker++; + + if (isDigit(c)) { + while (marker < length && isDigit(s.charAt(marker))) { + chunk += s.charAt(marker); + marker++; + } + } else { + while (marker < length && !isDigit(s.charAt(marker))) { + chunk += s.charAt(marker); + marker++; + } + } + return { chunk, newMarker: marker }; + }; + + const len1 = a.length; + const len2 = b.length; + let marker1 = 0; + let marker2 = 0; + + while (marker1 < len1 && marker2 < len2) { + const { chunk: chunk1, newMarker: newMarker1 } = getChunk(a, len1, marker1); + marker1 = newMarker1; + + const { chunk: chunk2, newMarker: newMarker2 } = getChunk(b, len2, marker2); + marker2 = newMarker2; + + let result: number; + if (isDigit(chunk1.charAt(0)) && isDigit(chunk2.charAt(0))) { + const num1 = parseInt(chunk1, 10); + const num2 = parseInt(chunk2, 10); + result = num1 - num2; + } else { + result = chunk1.localeCompare(chunk2); + } + + if (result !== 0) { + return result; + } + } + + return len1 - len2; + }, []); // Custom file sorting logic for merge tool const sortFiles = useCallback((sortType: 'filename' | 'dateModified', ascending: boolean = true) => { @@ -33,7 +84,7 @@ const Merge = (props: BaseToolProps) => { let comparison = 0; switch (sortType) { case 'filename': - comparison = stubA.name.localeCompare(stubB.name); + comparison = naturalCompare(stubA.name, stubB.name); break; case 'dateModified': comparison = stubA.lastModified - stubB.lastModified; @@ -45,7 +96,7 @@ const Merge = (props: BaseToolProps) => { const selectedIds = sortedStubs.map(record => record.id); const deselectedIds = fileIds.filter(id => !selectedIds.includes(id)); reorderFiles([...selectedIds, ...deselectedIds]); - }, [selectedFileStubs, fileIds, reorderFiles]); + }, [selectedFileStubs, fileIds, reorderFiles, naturalCompare]); return createToolFlow({ files: {