mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
feat: Add lazy-loaded Mermaid diagram support with loading fallback and zoom features
This commit is contained in:
parent
4136dda7c7
commit
f67dd1b1b7
5 changed files with 446 additions and 4 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { memo, useMemo, useRef, useEffect } from 'react';
|
import React, { memo, useMemo, useRef, useEffect, lazy, Suspense } from 'react';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { PermissionTypes, Permissions } from 'librechat-data-provider';
|
import { PermissionTypes, Permissions } from 'librechat-data-provider';
|
||||||
import { useToastContext, useCodeBlockContext } from '~/Providers';
|
import { useToastContext, useCodeBlockContext } from '~/Providers';
|
||||||
|
|
@ -9,6 +9,16 @@ import useLocalize from '~/hooks/useLocalize';
|
||||||
import { handleDoubleClick } from '~/utils';
|
import { handleDoubleClick } from '~/utils';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
|
// Loading fallback component for lazy-loaded Mermaid diagrams
|
||||||
|
const MermaidLoadingFallback = memo(() => {
|
||||||
|
const localize = useLocalize();
|
||||||
|
return (
|
||||||
|
<div className="my-4 rounded-lg border border-border-light bg-surface-primary p-4 text-center text-text-secondary dark:border-border-heavy dark:bg-surface-primary-alt">
|
||||||
|
{localize('com_ui_loading_diagram')}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
type TCodeProps = {
|
type TCodeProps = {
|
||||||
inline?: boolean;
|
inline?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|
@ -23,6 +33,7 @@ export const code: React.ElementType = memo(({ className, children }: TCodeProps
|
||||||
const match = /language-(\w+)/.exec(className ?? '');
|
const match = /language-(\w+)/.exec(className ?? '');
|
||||||
const lang = match && match[1];
|
const lang = match && match[1];
|
||||||
const isMath = lang === 'math';
|
const isMath = lang === 'math';
|
||||||
|
const isMermaid = lang === 'mermaid';
|
||||||
const isSingleLine = typeof children === 'string' && children.split('\n').length === 1;
|
const isSingleLine = typeof children === 'string' && children.split('\n').length === 1;
|
||||||
|
|
||||||
const { getNextIndex, resetCounter } = useCodeBlockContext();
|
const { getNextIndex, resetCounter } = useCodeBlockContext();
|
||||||
|
|
@ -34,6 +45,13 @@ export const code: React.ElementType = memo(({ className, children }: TCodeProps
|
||||||
|
|
||||||
if (isMath) {
|
if (isMath) {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
|
} else if (isMermaid && typeof children === 'string') {
|
||||||
|
const SandpackMermaidDiagram = lazy(() => import('./SandpackMermaidDiagram'));
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<MermaidLoadingFallback />}>
|
||||||
|
<SandpackMermaidDiagram content={children} />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
} else if (isSingleLine) {
|
} else if (isSingleLine) {
|
||||||
return (
|
return (
|
||||||
<code onDoubleClick={handleDoubleClick} className={className}>
|
<code onDoubleClick={handleDoubleClick} className={className}>
|
||||||
|
|
@ -55,9 +73,17 @@ export const code: React.ElementType = memo(({ className, children }: TCodeProps
|
||||||
export const codeNoExecution: React.ElementType = memo(({ className, children }: TCodeProps) => {
|
export const codeNoExecution: React.ElementType = memo(({ className, children }: TCodeProps) => {
|
||||||
const match = /language-(\w+)/.exec(className ?? '');
|
const match = /language-(\w+)/.exec(className ?? '');
|
||||||
const lang = match && match[1];
|
const lang = match && match[1];
|
||||||
|
const isMermaid = lang === 'mermaid';
|
||||||
|
|
||||||
if (lang === 'math') {
|
if (lang === 'math') {
|
||||||
return children;
|
return children;
|
||||||
|
} else if (isMermaid && typeof children === 'string') {
|
||||||
|
const SandpackMermaidDiagram = lazy(() => import('./SandpackMermaidDiagram'));
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<MermaidLoadingFallback />}>
|
||||||
|
<SandpackMermaidDiagram content={children} />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
} else if (typeof children === 'string' && children.split('\n').length === 1) {
|
} else if (typeof children === 'string' && children.split('\n').length === 1) {
|
||||||
return (
|
return (
|
||||||
<code onDoubleClick={handleDoubleClick} className={className}>
|
<code onDoubleClick={handleDoubleClick} className={className}>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,289 @@
|
||||||
|
import React, { memo, useMemo, useEffect } from 'react';
|
||||||
|
import { SandpackPreview, SandpackProvider } from '@codesandbox/sandpack-react/unstyled';
|
||||||
|
import dedent from 'dedent';
|
||||||
|
import { cn } from '~/utils';
|
||||||
|
import { sharedOptions } from '~/utils/artifacts';
|
||||||
|
|
||||||
|
interface SandpackMermaidDiagramProps {
|
||||||
|
content: string;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimal dependencies for Mermaid only
|
||||||
|
const mermaidDependencies = {
|
||||||
|
mermaid: '^11.8.1',
|
||||||
|
'react-zoom-pan-pinch': '^3.7.0',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Lean mermaid template with inline SVG icons
|
||||||
|
const leanMermaidTemplate = dedent`
|
||||||
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
import {
|
||||||
|
TransformWrapper,
|
||||||
|
TransformComponent,
|
||||||
|
ReactZoomPanPinchRef,
|
||||||
|
} from "react-zoom-pan-pinch";
|
||||||
|
import mermaid from "mermaid";
|
||||||
|
|
||||||
|
// Inline SVG icons
|
||||||
|
const ZoomInIcon = () => (
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
|
<circle cx="11" cy="11" r="8"/>
|
||||||
|
<path d="m21 21-4.35-4.35"/>
|
||||||
|
<line x1="11" y1="8" x2="11" y2="14"/>
|
||||||
|
<line x1="8" y1="11" x2="14" y2="11"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
const ZoomOutIcon = () => (
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
|
<circle cx="11" cy="11" r="8"/>
|
||||||
|
<path d="m21 21-4.35-4.35"/>
|
||||||
|
<line x1="8" y1="11" x2="14" y2="11"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
const ResetIcon = () => (
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
|
<polyline points="1 4 1 10 7 10"/>
|
||||||
|
<polyline points="23 20 23 14 17 14"/>
|
||||||
|
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
interface MermaidDiagramProps {
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MermaidDiagram: React.FC<MermaidDiagramProps> = ({ content }) => {
|
||||||
|
const mermaidRef = useRef<HTMLDivElement>(null);
|
||||||
|
const transformRef = useRef<ReactZoomPanPinchRef>(null);
|
||||||
|
const [isRendered, setIsRendered] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
mermaid.initialize({
|
||||||
|
startOnLoad: false,
|
||||||
|
theme: "default",
|
||||||
|
securityLevel: "loose",
|
||||||
|
flowchart: {
|
||||||
|
useMaxWidth: true,
|
||||||
|
htmlLabels: true,
|
||||||
|
curve: "basis",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderDiagram = async () => {
|
||||||
|
if (mermaidRef.current) {
|
||||||
|
try {
|
||||||
|
const id = "mermaid-" + Date.now();
|
||||||
|
const { svg } = await mermaid.render(id, content);
|
||||||
|
mermaidRef.current.innerHTML = svg;
|
||||||
|
|
||||||
|
const svgElement = mermaidRef.current.querySelector("svg");
|
||||||
|
if (svgElement) {
|
||||||
|
svgElement.style.width = "100%";
|
||||||
|
svgElement.style.height = "100%";
|
||||||
|
}
|
||||||
|
setIsRendered(true);
|
||||||
|
setError(null);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Mermaid rendering error:", err);
|
||||||
|
setError(err.message || "Failed to render diagram");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
renderDiagram();
|
||||||
|
}, [content]);
|
||||||
|
|
||||||
|
const handleZoomIn = () => {
|
||||||
|
if (transformRef.current) {
|
||||||
|
transformRef.current.zoomIn(0.2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleZoomOut = () => {
|
||||||
|
if (transformRef.current) {
|
||||||
|
transformRef.current.zoomOut(0.2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
if (transformRef.current) {
|
||||||
|
transformRef.current.resetTransform();
|
||||||
|
transformRef.current.centerView(1, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '16px', color: '#ef4444', backgroundColor: '#fee2e2', borderRadius: '8px', border: '1px solid #fecaca' }}>
|
||||||
|
<strong>Error:</strong> {error}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ position: 'relative', height: '100%', width: '100%', backgroundColor: '#f9fafb' }}>
|
||||||
|
<TransformWrapper
|
||||||
|
ref={transformRef}
|
||||||
|
initialScale={1}
|
||||||
|
minScale={0.1}
|
||||||
|
maxScale={4}
|
||||||
|
wheel={{ step: 0.1 }}
|
||||||
|
centerOnInit={true}
|
||||||
|
>
|
||||||
|
<TransformComponent
|
||||||
|
wrapperStyle={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref={mermaidRef}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
minHeight: '300px',
|
||||||
|
padding: '20px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</TransformComponent>
|
||||||
|
</TransformWrapper>
|
||||||
|
|
||||||
|
{isRendered && (
|
||||||
|
<div style={{ position: 'absolute', bottom: '8px', right: '8px', display: 'flex', gap: '8px' }}>
|
||||||
|
<button
|
||||||
|
onClick={handleZoomIn}
|
||||||
|
style={{
|
||||||
|
padding: '8px',
|
||||||
|
backgroundColor: 'white',
|
||||||
|
border: '1px solid #e5e7eb',
|
||||||
|
borderRadius: '6px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
title="Zoom in"
|
||||||
|
>
|
||||||
|
<ZoomInIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleZoomOut}
|
||||||
|
style={{
|
||||||
|
padding: '8px',
|
||||||
|
backgroundColor: 'white',
|
||||||
|
border: '1px solid #e5e7eb',
|
||||||
|
borderRadius: '6px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
title="Zoom out"
|
||||||
|
>
|
||||||
|
<ZoomOutIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleReset}
|
||||||
|
style={{
|
||||||
|
padding: '8px',
|
||||||
|
backgroundColor: 'white',
|
||||||
|
border: '1px solid #e5e7eb',
|
||||||
|
borderRadius: '6px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
title="Reset zoom"
|
||||||
|
>
|
||||||
|
<ResetIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MermaidDiagram;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const wrapLeanMermaidDiagram = (content: string) => {
|
||||||
|
return dedent`
|
||||||
|
import React from 'react';
|
||||||
|
import MermaidDiagram from './MermaidDiagram';
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const content = \`${content.replace(/`/g, '\\`')}\`;
|
||||||
|
return <MermaidDiagram content={content} />;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getLeanMermaidFiles = (content: string) => {
|
||||||
|
return {
|
||||||
|
'/App.tsx': wrapLeanMermaidDiagram(content),
|
||||||
|
'/MermaidDiagram.tsx': leanMermaidTemplate,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const SandpackMermaidDiagram = memo(({ content, className }: SandpackMermaidDiagramProps) => {
|
||||||
|
const files = useMemo(() => getLeanMermaidFiles(content), [content]);
|
||||||
|
const sandpackProps = useMemo(
|
||||||
|
() => ({
|
||||||
|
customSetup: {
|
||||||
|
dependencies: mermaidDependencies,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Force iframe to respect container height
|
||||||
|
useEffect(() => {
|
||||||
|
const fixIframeHeight = () => {
|
||||||
|
const container = document.querySelector('.sandpack-mermaid-diagram');
|
||||||
|
if (container) {
|
||||||
|
const iframe = container.querySelector('iframe');
|
||||||
|
if (iframe && iframe.style.height && iframe.style.height !== '100%') {
|
||||||
|
iframe.style.height = '100%';
|
||||||
|
iframe.style.minHeight = '100%';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initial fix
|
||||||
|
fixIframeHeight();
|
||||||
|
|
||||||
|
// Fix on any DOM changes
|
||||||
|
const observer = new MutationObserver(fixIframeHeight);
|
||||||
|
const container = document.querySelector('.sandpack-mermaid-diagram');
|
||||||
|
if (container) {
|
||||||
|
observer.observe(container, {
|
||||||
|
attributes: true,
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
attributeFilter: ['style'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, [content]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SandpackProvider files={files} options={sharedOptions} template="react-ts" {...sandpackProps}>
|
||||||
|
<SandpackPreview
|
||||||
|
showOpenInCodeSandbox={false}
|
||||||
|
showRefreshButton={false}
|
||||||
|
showSandpackErrorOverlay={true}
|
||||||
|
/>
|
||||||
|
</SandpackProvider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
SandpackMermaidDiagram.displayName = 'SandpackMermaidDiagram';
|
||||||
|
|
||||||
|
export default SandpackMermaidDiagram;
|
||||||
|
|
@ -857,6 +857,7 @@
|
||||||
"com_ui_librechat_code_api_subtitle": "Secure. Multi-language. Input/Output Files.",
|
"com_ui_librechat_code_api_subtitle": "Secure. Multi-language. Input/Output Files.",
|
||||||
"com_ui_librechat_code_api_title": "Run AI Code",
|
"com_ui_librechat_code_api_title": "Run AI Code",
|
||||||
"com_ui_loading": "Loading...",
|
"com_ui_loading": "Loading...",
|
||||||
|
"com_ui_loading_diagram": "Loading diagram...",
|
||||||
"com_ui_locked": "Locked",
|
"com_ui_locked": "Locked",
|
||||||
"com_ui_logo": "{{0}} Logo",
|
"com_ui_logo": "{{0}} Logo",
|
||||||
"com_ui_low": "Low",
|
"com_ui_low": "Low",
|
||||||
|
|
|
||||||
|
|
@ -371,4 +371,130 @@ p.whitespace-pre-wrap a, li a {
|
||||||
|
|
||||||
.dark p.whitespace-pre-wrap a, .dark li a {
|
.dark p.whitespace-pre-wrap a, .dark li a {
|
||||||
color: #52a0ff;
|
color: #52a0ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* .sandpack-mermaid-diagram {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram > div {
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: 100% !important;
|
||||||
|
flex: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram .sp-wrapper {
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: inherit !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram .sp-stack {
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: inherit !important;
|
||||||
|
flex: 1 !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram .sp-preview {
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: inherit !important;
|
||||||
|
flex: 1 !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram .sp-preview-container {
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: inherit !important;
|
||||||
|
flex: 1 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
position: relative !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram .sp-preview-iframe {
|
||||||
|
position: absolute !important;
|
||||||
|
top: 0 !important;
|
||||||
|
left: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: 100% !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram .sp-preview-actions {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram .sp-preview-container::after {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram [style*="height: 346px"] {
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram iframe[style*="height"] {
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram [style*="height:"] {
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram iframe {
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: 100% !important;
|
||||||
|
position: absolute !important;
|
||||||
|
top: 0 !important;
|
||||||
|
left: 0 !important;
|
||||||
|
right: 0 !important;
|
||||||
|
bottom: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram .sp-stack {
|
||||||
|
max-height: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram .sp-wrapper,
|
||||||
|
.sandpack-mermaid-diagram .sp-stack,
|
||||||
|
.sandpack-mermaid-diagram .sp-preview,
|
||||||
|
.sandpack-mermaid-diagram .sp-preview-container {
|
||||||
|
max-height: none !important;
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram .p-4 > div {
|
||||||
|
height: 100% !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram .sp-wrapper {
|
||||||
|
height: 100% !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram iframe[style*="height"] {
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram [style*="height:"] {
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandpack-mermaid-diagram .sp-wrapper,
|
||||||
|
.sandpack-mermaid-diagram .sp-stack,
|
||||||
|
.sandpack-mermaid-diagram .sp-preview,
|
||||||
|
.sandpack-mermaid-diagram .sp-preview-container {
|
||||||
|
max-height: none !important;
|
||||||
|
} */
|
||||||
|
|
@ -113,8 +113,8 @@ export default defineConfig(({ command }) => ({
|
||||||
if (id.includes('i18next') || id.includes('react-i18next')) {
|
if (id.includes('i18next') || id.includes('react-i18next')) {
|
||||||
return 'i18n';
|
return 'i18n';
|
||||||
}
|
}
|
||||||
if (id.includes('lodash')) {
|
if (id.includes('node_modules/lodash-es')) {
|
||||||
return 'utilities';
|
return 'lodash-es';
|
||||||
}
|
}
|
||||||
if (id.includes('date-fns')) {
|
if (id.includes('date-fns')) {
|
||||||
return 'date-utils';
|
return 'date-utils';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue