From 88e5a2f23b6d187159bb362e4ad048c8b24c58ed Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Mon, 1 Jun 2026 21:10:01 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=89=20refactor:=20Reduce=20Frontend=20?= =?UTF-8?q?Build=20Warning=20Noise=20(#13463)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Artifacts/Mermaid.tsx | 43 +++++++++++++------ .../Files/__tests__/useFileHandling.test.ts | 16 +++++++ client/src/hooks/Files/useFileHandling.ts | 32 +++++++------- client/vite.config.ts | 5 ++- 4 files changed, 67 insertions(+), 29 deletions(-) diff --git a/client/src/components/Artifacts/Mermaid.tsx b/client/src/components/Artifacts/Mermaid.tsx index 5eb55be3ae..9d54285cb6 100644 --- a/client/src/components/Artifacts/Mermaid.tsx +++ b/client/src/components/Artifacts/Mermaid.tsx @@ -1,5 +1,4 @@ import React, { useEffect, useRef, useState, useCallback } from 'react'; -import mermaid from 'mermaid'; import { Button } from '@librechat/client'; import { ZoomIn, ZoomOut, RotateCcw } from 'lucide-react'; import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch'; @@ -11,6 +10,16 @@ interface MermaidDiagramProps { isDarkMode?: boolean; } +let mermaidPromise: Promise | null = null; + +const loadMermaid = () => { + if (!mermaidPromise) { + mermaidPromise = import('mermaid').then((mod) => mod.default); + } + + return mermaidPromise; +}; + const MermaidDiagram: React.FC = ({ content, isDarkMode = true }) => { const mermaidRef = useRef(null); const transformRef = useRef(null); @@ -19,19 +28,23 @@ const MermaidDiagram: React.FC = ({ content, isDarkMode = t const bgColor = isDarkMode ? '#212121' : '#FFFFFF'; useEffect(() => { - mermaid.initialize({ - startOnLoad: false, - theme, - securityLevel: 'sandbox', - flowchart: artifactFlowchartConfig, - }); + let isMounted = true; const renderDiagram = async () => { - if (!mermaidRef.current) { - return; - } - try { + const mermaid = await loadMermaid(); + + mermaid.initialize({ + startOnLoad: false, + theme, + securityLevel: 'sandbox', + flowchart: artifactFlowchartConfig, + }); + + if (!mermaidRef.current) { + return; + } + const { svg } = await mermaid.render('mermaid-diagram', content); mermaidRef.current.innerHTML = svg; @@ -40,7 +53,9 @@ const MermaidDiagram: React.FC = ({ content, isDarkMode = t svgElement.style.width = '100%'; svgElement.style.height = '100%'; } - setIsRendered(true); + if (isMounted) { + setIsRendered(true); + } } catch (error) { console.error('Mermaid rendering error:', error); if (mermaidRef.current) { @@ -50,6 +65,10 @@ const MermaidDiagram: React.FC = ({ content, isDarkMode = t }; renderDiagram(); + + return () => { + isMounted = false; + }; }, [content, theme]); const centerAndFitDiagram = useCallback(() => { diff --git a/client/src/hooks/Files/__tests__/useFileHandling.test.ts b/client/src/hooks/Files/__tests__/useFileHandling.test.ts index fdb098e2d5..1583c1d021 100644 --- a/client/src/hooks/Files/__tests__/useFileHandling.test.ts +++ b/client/src/hooks/Files/__tests__/useFileHandling.test.ts @@ -98,6 +98,8 @@ jest.mock('~/utils', () => ({ })); const mockValidateFiles = jest.requireMock('~/utils').validateFiles; +const mockProcessFileForUpload = jest.requireMock('~/utils/heicConverter') + .processFileForUpload as jest.Mock; describe('useFileHandling', () => { beforeEach(() => { @@ -109,6 +111,20 @@ describe('useFileHandling', () => { const loadHook = async () => (await import('../useFileHandling')).default; describe('endpointOverride', () => { + it('checks possible image uploads for HEIC conversion without HEIC metadata', async () => { + const useFileHandling = await loadHook(); + const { result } = renderHook(() => useFileHandling()); + + const imageFile = new File(['maybe-heic'], 'photo.jpg', { type: 'image/jpeg' }); + + await act(async () => { + await result.current.handleFiles([imageFile]); + }); + + expect(mockProcessFileForUpload).toHaveBeenCalledTimes(1); + expect(mockProcessFileForUpload).toHaveBeenCalledWith(imageFile, 0.9, expect.any(Function)); + }); + it('uses conversation endpoint when no override is provided', async () => { mockConversation = { conversationId: 'convo-1', diff --git a/client/src/hooks/Files/useFileHandling.ts b/client/src/hooks/Files/useFileHandling.ts index 94f26039b6..9a26c219b9 100644 --- a/client/src/hooks/Files/useFileHandling.ts +++ b/client/src/hooks/Files/useFileHandling.ts @@ -20,7 +20,6 @@ import { logger, validateFiles, cachePreview, getCachedPreview, removePreviewEnt import { useGetFileConfig, useUploadFileMutation } from '~/data-provider'; import useLocalize, { TranslationKeys } from '~/hooks/useLocalize'; import { useDelayedUploadToast } from './useDelayedUploadToast'; -import { processFileForUpload } from '~/utils/heicConverter'; import { useChatContext } from '~/Providers/ChatContext'; import store, { ephemeralAgentByConvoId } from '~/store'; import useClientResize from './useClientResize'; @@ -339,11 +338,16 @@ const useFileHandlingCore = (params: UseFileHandling | undefined, fileState: Fil // Add file immediately to show in UI addFile(initialExtendedFile); + const originalFileName = originalFile.name.toLowerCase(); + // Check if HEIC conversion is needed and show toast const isHEIC = originalFile.type === 'image/heic' || originalFile.type === 'image/heif' || - originalFile.name.toLowerCase().match(/\.(heic|heif)$/); + /\.(heic|heif)$/.test(originalFileName); + const isPossibleImage = + originalFile.type.startsWith('image/') || + /\.(avif|bmp|gif|heic|heif|jpe?g|png|svg|tiff?|webp)$/.test(originalFileName); if (isHEIC) { showToast({ @@ -353,19 +357,17 @@ const useFileHandlingCore = (params: UseFileHandling | undefined, fileState: Fil }); } - // Process file for HEIC conversion if needed - const heicProcessedFile = await processFileForUpload( - originalFile, - 0.9, - (conversionProgress) => { - // Update progress during HEIC conversion (0.1 to 0.5 range for conversion) - const adjustedProgress = 0.1 + conversionProgress * 0.4; - replaceFile({ - ...initialExtendedFile, - progress: adjustedProgress, - }); - }, - ); + const heicProcessedFile = isPossibleImage + ? await import('~/utils/heicConverter').then(({ processFileForUpload }) => + processFileForUpload(originalFile, 0.9, (conversionProgress) => { + const adjustedProgress = 0.1 + conversionProgress * 0.4; + replaceFile({ + ...initialExtendedFile, + progress: adjustedProgress, + }); + }), + ) + : originalFile; let finalProcessedFile = heicProcessedFile; diff --git a/client/vite.config.ts b/client/vite.config.ts index 060fdf7b1c..04f91983dc 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -33,6 +33,7 @@ const backendPort = (process.env.BACKEND_PORT && Number(process.env.BACKEND_PORT const backendURL = process.env.HOST ? `http://${process.env.HOST}:${backendPort}` : `http://localhost:${backendPort}`; +const buildSourceMap = process.env.NODE_ENV === 'development'; export default defineConfig(({ command }) => ({ base: '', @@ -124,14 +125,14 @@ export default defineConfig(({ command }) => ({ ], }, }), - sourcemapExclude({ excludeNodeModules: true }), + ...(buildSourceMap ? [sourcemapExclude({ excludeNodeModules: true })] : []), compression({ threshold: 10240, }), ], publicDir: command === 'serve' ? './public' : false, build: { - sourcemap: process.env.NODE_ENV === 'development', + sourcemap: buildSourceMap, outDir: './dist', minify: 'terser', rolldownOptions: {