diff --git a/client/src/components/Chat/Header.tsx b/client/src/components/Chat/Header.tsx index 0390eb428d..8a9bd80c23 100644 --- a/client/src/components/Chat/Header.tsx +++ b/client/src/components/Chat/Header.tsx @@ -37,20 +37,36 @@ export default function Header() {
- {!navVisible && } - {!navVisible && } - {} - {interfaceConfig.presets === true && interfaceConfig.modelSelect && } - {hasAccessToBookmarks === true && } - {hasAccessToMultiConvo === true && } - {isSmallScreen && ( - <> - - - - )} +
+ + +
+
+ + {interfaceConfig.presets === true && interfaceConfig.modelSelect && } + {hasAccessToBookmarks === true && } + {hasAccessToMultiConvo === true && } + {isSmallScreen && ( + <> + + + + )} +
{!isSmallScreen && (
diff --git a/client/src/components/Chat/Messages/Content/DialogImage.tsx b/client/src/components/Chat/Messages/Content/DialogImage.tsx index 2080e69fca..d41f482eb0 100644 --- a/client/src/components/Chat/Messages/Content/DialogImage.tsx +++ b/client/src/components/Chat/Messages/Content/DialogImage.tsx @@ -1,7 +1,48 @@ -import { X, ArrowDownToLine } from 'lucide-react'; -import { Button, OGDialog, OGDialogContent } from '~/components'; +import { useState, useEffect } from 'react'; +import { X, ArrowDownToLine, PanelLeftOpen, PanelLeftClose } from 'lucide-react'; +import { Button, OGDialog, OGDialogContent, TooltipAnchor } from '~/components'; +import { useLocalize } from '~/hooks'; + +export default function DialogImage({ isOpen, onOpenChange, src = '', downloadImage, args }) { + const localize = useLocalize(); + const [isPromptOpen, setIsPromptOpen] = useState(false); + const [imageSize, setImageSize] = useState(null); + + const getImageSize = async (url: string) => { + try { + const response = await fetch(url, { method: 'HEAD' }); + const contentLength = response.headers.get('Content-Length'); + + if (contentLength) { + const bytes = parseInt(contentLength, 10); + return formatFileSize(bytes); + } + + const fullResponse = await fetch(url); + const blob = await fullResponse.blob(); + return formatFileSize(blob.size); + } catch (error) { + console.error('Error getting image size:', error); + return null; + } + }; + + const formatFileSize = (bytes: number): string => { + if (bytes === 0) return '0 Bytes'; + + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + }; + + useEffect(() => { + if (isOpen && src) { + getImageSize(src).then(setImageSize); + } + }, [isOpen, src]); -export default function DialogImage({ isOpen, onOpenChange, src = '', downloadImage }) { return ( -
- - +
+ onOpenChange(false)} + variant="ghost" + className="h-10 w-10 p-0 hover:bg-surface-hover" + > + + + } + /> +
+ downloadImage()} variant="ghost" className="h-10 w-10 p-0"> + + + } + /> + setIsPromptOpen(!isPromptOpen)} + variant="ghost" + className="h-10 w-10 p-0" + > + {isPromptOpen ? ( + + ) : ( + + )} + + } + /> +
- - + + {/* Main content area with image */} +
+
Uploaded image - - +
+
+ + {/* Side Panel */} +
+
+
+

+ {localize('com_ui_image_details')} +

+
+
+ +
+ {/* Prompt Section */} +
+

+ {localize('com_ui_prompt')} +

+
+

+ {args?.prompt || 'No prompt available'} +

+
+
+ + {/* Generation Settings */} +
+

+ {localize('com_ui_generation_settings')} +

+
+
+ {localize('com_ui_size')}: + + {args?.size || 'Unknown'} + +
+
+ + {localize('com_ui_quality')}: + + + {args?.quality || 'Standard'} + +
+
+ + {localize('com_ui_file_size')}: + + + {imageSize || 'Loading...'} + +
+
+
+
+
+
); diff --git a/client/src/components/Chat/Messages/Content/Image.tsx b/client/src/components/Chat/Messages/Content/Image.tsx index 4350926182..85c3fdb3f2 100644 --- a/client/src/components/Chat/Messages/Content/Image.tsx +++ b/client/src/components/Chat/Messages/Content/Image.tsx @@ -11,6 +11,7 @@ const Image = ({ width, placeholderDimensions, className, + args, }: { imagePath: string; altText: string; @@ -21,6 +22,13 @@ const Image = ({ width?: string; }; className?: string; + args?: { + prompt?: string; + quality?: 'low' | 'medium' | 'high'; + size?: string; + style?: string; + [key: string]: unknown; + }; }) => { const [isOpen, setIsOpen] = useState(false); const [isLoaded, setIsLoaded] = useState(false); @@ -91,6 +99,7 @@ const Image = ({ onOpenChange={setIsOpen} src={imagePath} downloadImage={downloadImage} + args={args} /> )}
diff --git a/client/src/components/Chat/Messages/Content/Parts/OpenAIImageGen/OpenAIImageGen.tsx b/client/src/components/Chat/Messages/Content/Parts/OpenAIImageGen/OpenAIImageGen.tsx index bf09e5a7c3..e13e68312d 100644 --- a/client/src/components/Chat/Messages/Content/Parts/OpenAIImageGen/OpenAIImageGen.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/OpenAIImageGen/OpenAIImageGen.tsx @@ -32,8 +32,17 @@ export default function OpenAIImageGen({ let height: number | undefined; let quality: 'low' | 'medium' | 'high' = 'high'; + // Parse args if it's a string + let parsedArgs; try { - const argsObj = typeof _args === 'string' ? JSON.parse(_args) : _args; + parsedArgs = typeof _args === 'string' ? JSON.parse(_args) : _args; + } catch (error) { + console.error('Error parsing args:', error); + parsedArgs = {}; + } + + try { + const argsObj = parsedArgs; if (argsObj && typeof argsObj.size === 'string') { const [w, h] = argsObj.size.split('x').map((v: string) => parseInt(v, 10)); @@ -197,6 +206,7 @@ export default function OpenAIImageGen({ width={Number(dimensions.width?.split('px')[0])} height={Number(dimensions.height?.split('px')[0])} placeholderDimensions={{ width: dimensions.width, height: dimensions.height }} + args={parsedArgs} />
diff --git a/client/src/components/Nav/Nav.tsx b/client/src/components/Nav/Nav.tsx index 7c300b8b8d..d425468ca6 100644 --- a/client/src/components/Nav/Nav.tsx +++ b/client/src/components/Nav/Nav.tsx @@ -30,7 +30,7 @@ const NavMask = memo( id="mobile-nav-mask-toggle" role="button" tabIndex={0} - className={`nav-mask ${navVisible ? 'active' : ''}`} + className={`nav-mask transition-opacity duration-500 ease-in-out ${navVisible ? 'active opacity-100' : 'opacity-0'}`} onClick={toggleNavVisible} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { @@ -186,18 +186,19 @@ const Nav = memo(
-
+