From fb6f96689bfe5508009b8b9b59567b0c19bd5842 Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Thu, 22 Feb 2024 10:36:49 -0800 Subject: [PATCH] Improve Image warning & style. Closes #419 --- src/common/app.theme.ts | 4 + src/modules/blocks/BlocksRenderer.tsx | 10 +- src/modules/blocks/RenderImage.tsx | 152 +++++++++++++++---------- src/modules/blocks/code/RenderCode.tsx | 2 +- 4 files changed, 106 insertions(+), 62 deletions(-) diff --git a/src/common/app.theme.ts b/src/common/app.theme.ts index db95526cf..8a919f237 100644 --- a/src/common/app.theme.ts +++ b/src/common/app.theme.ts @@ -153,6 +153,7 @@ interface ContentScalingOptions { // BlocksRenderer blockCodeFontSize: string; blockFontSize: string; + blockImageGap: number; blockLineHeight: string | number; // ChatMessage chatMessagePadding: number; @@ -165,6 +166,7 @@ export const themeScalingMap: Record = { xs: { blockCodeFontSize: '0.75rem', blockFontSize: 'xs', + blockImageGap: 1, blockLineHeight: 1.666667, chatMessagePadding: 1.25, chatDrawerItemSx: { '--ListItem-minHeight': '2.25rem', fontSize: 'sm' }, // 36px @@ -173,6 +175,7 @@ export const themeScalingMap: Record = { sm: { blockCodeFontSize: '0.75rem', blockFontSize: 'sm', + blockImageGap: 1.5, blockLineHeight: 1.714286, chatMessagePadding: 1.5, chatDrawerItemSx: { '--ListItem-minHeight': '2.25rem', fontSize: 'sm' }, @@ -181,6 +184,7 @@ export const themeScalingMap: Record = { md: { blockCodeFontSize: '0.875rem', blockFontSize: 'md', + blockImageGap: 2, blockLineHeight: 1.75, chatMessagePadding: 2, chatDrawerItemSx: { '--ListItem-minHeight': '2.5rem', fontSize: 'md' }, // 40px diff --git a/src/modules/blocks/BlocksRenderer.tsx b/src/modules/blocks/BlocksRenderer.tsx index b4d2f5449..0a9423c48 100644 --- a/src/modules/blocks/BlocksRenderer.tsx +++ b/src/modules/blocks/BlocksRenderer.tsx @@ -108,6 +108,14 @@ export function BlocksRenderer(props: { } ), [fromAssistant, props.contentScaling, props.specialDiagramMode]); + const scaledImageSx: SxProps = React.useMemo(() => ( + { + fontSize: themeScalingMap[props.contentScaling]?.blockFontSize ?? undefined, + lineHeight: themeScalingMap[props.contentScaling]?.blockLineHeight ?? 1.75, + marginBottom: themeScalingMap[props.contentScaling]?.blockImageGap ?? 1.5, + } + ), [props.contentScaling]); + const scaledTypographySx: SxProps = React.useMemo(() => ( { fontSize: themeScalingMap[props.contentScaling]?.blockFontSize ?? undefined, @@ -186,7 +194,7 @@ export function BlocksRenderer(props: { : block.type === 'code' ? : block.type === 'image' - ? + ? : block.type === 'latex' ? : block.type === 'diff' diff --git a/src/modules/blocks/RenderImage.tsx b/src/modules/blocks/RenderImage.tsx index eff7ab871..a52d313be 100644 --- a/src/modules/blocks/RenderImage.tsx +++ b/src/modules/blocks/RenderImage.tsx @@ -1,10 +1,14 @@ import * as React from 'react'; import type { SxProps } from '@mui/joy/styles/types'; -import { Alert, Box, IconButton, Tooltip, Typography } from '@mui/joy'; +import { Alert, Box, IconButton, Sheet } from '@mui/joy'; +import CloseRoundedIcon from '@mui/icons-material/CloseRounded'; +import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import ReplayIcon from '@mui/icons-material/Replay'; +import WarningRoundedIcon from '@mui/icons-material/WarningRounded'; +import { GoodTooltip } from '~/common/components/GoodTooltip'; import { Link } from '~/common/components/Link'; import type { ImageBlock } from './blocks'; @@ -71,74 +75,102 @@ export const RenderImage = (props: { noTooltip?: boolean, onRunAgain?: (e: React.MouseEvent) => void, sx?: SxProps, }) => { + + // state + const [infoOpen, setInfoOpen] = React.useState(false); + const [showAlert, setShowAlert] = React.useState(true); + + + // derived state const { url, alt } = props.imageBlock; - const imageUrls = url.split('\n'); + const isTempDalleUrl = url.startsWith('https://oaidalle'); - return imageUrls.map((url, index) => { - // display a notice for temporary images DallE - const isTempDalleUrl = url.startsWith('https://oaidalle'); + return ( + - return 0 || !props.isFirst) ? 1.5 : 0, - // p: 1, border: '1px solid', borderColor: 'divider', borderRadius: 1, - minWidth: 128, minHeight: 128, - boxShadow: 'md', - backgroundColor: 'neutral.solidBg', - '& picture': { display: 'flex' }, - '& img': { maxWidth: '100%', maxHeight: '100%' }, - '&:hover > .overlay-buttons': { opacity: 1 }, - ...props.sx, - }} - > + - {isTempDalleUrl && - ⚠️ Temporary Image - This image will be deleted from the OpenAI servers in one hour. Please save it to your device. - {/**/} - {/* The following is the re-written DALL·E prompt that generated this image.*/} - {/**/} - } - - {alt} - - + // layout + position: 'relative', + display: 'grid', + justifyContent: 'center', + alignItems: 'center', + + '& picture': { display: 'flex' }, + '& img': { maxWidth: '100%', maxHeight: '100%' }, + '&:hover > .overlay-buttons': { opacity: 1 }, + + ...props.sx, + }} + > + + {/* External Image */} + + {alt + + + {/* Information */} + {!!alt && infoOpen && ( + + {alt} + + )} + + {/* (overlay) Image Buttons */} + + {!!props.onRunAgain && ( + + + + + + )} + + {!!alt && ( + + setInfoOpen(open => !open)}> + + + + )} + + + + + + + + + + {/* Dalle Warning notice */} + {isTempDalleUrl && showAlert && ( + } + endDecorator={ + setShowAlert(on => !on)} sx={{ my: -0.5 }}> + + } sx={{ - maxWidth: { sm: '90vw', md: '70vw' }, - boxShadow: 'md', + mx: 0.5, + ...props.sx, }} > - {`Generated - - ) : ( - Generated Image +
+ Please Save Locally · OpenAI will delete this image link from their servers one hour after creation. +
+
)} - {/* Image Buttons */} - - {!!props.onRunAgain && ( - - - - - - )} - - - - - - -
; - }); + + ); }; \ No newline at end of file diff --git a/src/modules/blocks/code/RenderCode.tsx b/src/modules/blocks/code/RenderCode.tsx index 28bc512be..258f2e4b3 100644 --- a/src/modules/blocks/code/RenderCode.tsx +++ b/src/modules/blocks/code/RenderCode.tsx @@ -54,7 +54,7 @@ async function fetchPlantUmlSvg(plantUmlCode: string): Promise { export const overlayButtonsSx: SxProps = { position: 'absolute', top: 0, right: 0, zIndex: 10, display: 'flex', flexDirection: 'row', gap: 1, - opacity: 0, transition: 'opacity 0.2s', + opacity: 0, transition: 'opacity 0.15s', // '& > button': { // backgroundColor: 'background.level2', // backdropFilter: 'blur(12px)',