LibreChat/client/src/components/Chat/Messages/Content/Image.tsx
Danny Avila 2e63e32382
🐼 feat: Add Flux Image Generation Tool (#6147)
* 🔧 fix: Log warning for aborted operations in AgentClient

* ci: Remove unused saveMessageToDatabase mock in FakeClient initialization

* ci: test actual implementation of saveMessageToDatabase

* refactor: Change log level from warning to error for aborted operations in AgentClient

* refactor: Add className prop to Image component for customizable styling, use theme selectors

* feat: FLUX Image Generation tool
2025-03-02 13:19:53 -05:00

95 lines
2.8 KiB
TypeScript

import React, { useState, useRef, useMemo } from 'react';
import { LazyLoadImage } from 'react-lazy-load-image-component';
import * as Dialog from '@radix-ui/react-dialog';
import DialogImage from './DialogImage';
import { cn } from '~/utils';
const scaleImage = ({
originalWidth,
originalHeight,
containerRef,
}: {
originalWidth?: number;
originalHeight?: number;
containerRef: React.RefObject<HTMLDivElement>;
}) => {
const containerWidth = containerRef.current?.offsetWidth ?? 0;
if (containerWidth === 0 || originalWidth == null || originalHeight == null) {
return { width: 'auto', height: 'auto' };
}
const aspectRatio = originalWidth / originalHeight;
const scaledWidth = Math.min(containerWidth, originalWidth);
const scaledHeight = scaledWidth / aspectRatio;
return { width: `${scaledWidth}px`, height: `${scaledHeight}px` };
};
const Image = ({
imagePath,
altText,
height,
width,
placeholderDimensions,
className,
}: {
imagePath: string;
altText: string;
height: number;
width: number;
placeholderDimensions?: {
height?: string;
width?: string;
};
className?: string;
}) => {
const [isLoaded, setIsLoaded] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
const handleImageLoad = () => setIsLoaded(true);
const { width: scaledWidth, height: scaledHeight } = useMemo(
() =>
scaleImage({
originalWidth: Number(placeholderDimensions?.width?.split('px')[0] ?? width),
originalHeight: Number(placeholderDimensions?.height?.split('px')[0] ?? height),
containerRef,
}),
[placeholderDimensions, height, width],
);
return (
<Dialog.Root>
<div ref={containerRef}>
<div
className={cn(
'relative mt-1 flex h-auto w-full max-w-lg items-center justify-center overflow-hidden bg-surface-active-alt text-text-secondary-alt',
className,
)}
>
<Dialog.Trigger asChild>
<button type="button" aria-haspopup="dialog" aria-expanded="false">
<LazyLoadImage
alt={altText}
onLoad={handleImageLoad}
visibleByDefault={true}
className={cn(
'opacity-100 transition-opacity duration-100',
isLoaded ? 'opacity-100' : 'opacity-0',
)}
src={imagePath}
style={{
width: scaledWidth,
height: 'auto',
color: 'transparent',
}}
placeholder={<div style={{ width: scaledWidth, height: scaledHeight }} />}
/>
</button>
</Dialog.Trigger>
</div>
</div>
{isLoaded && <DialogImage src={imagePath} height={height} width={width} />}
</Dialog.Root>
);
};
export default Image;