From 53bf948a048daf75ccef9e487998721e8a463b08 Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Tue, 18 Jun 2024 14:44:50 -0700 Subject: [PATCH] ChatMessage: extract avatars --- src/apps/chat/components/ChatDrawerItem.tsx | 2 +- .../chat/components/message/ChatMessage.tsx | 115 ++--------------- .../components/message/CleanerMessage.tsx | 2 +- .../chat/components/message/messageUtils.tsx | 118 ++++++++++++++++++ 4 files changed, 128 insertions(+), 109 deletions(-) create mode 100644 src/apps/chat/components/message/messageUtils.tsx diff --git a/src/apps/chat/components/ChatDrawerItem.tsx b/src/apps/chat/components/ChatDrawerItem.tsx index 76a7b974a..3ae18572b 100644 --- a/src/apps/chat/components/ChatDrawerItem.tsx +++ b/src/apps/chat/components/ChatDrawerItem.tsx @@ -21,7 +21,7 @@ import { InlineTextarea } from '~/common/components/InlineTextarea'; import { isDeepEqual } from '~/common/util/jsUtils'; import { useChatStore } from '~/common/stores/chat/store-chats'; -import { ANIM_BUSY_TYPING } from './message/ChatMessage'; +import { ANIM_BUSY_TYPING } from './message/messageUtils'; import { CHAT_NOVEL_TITLE } from '../AppChat'; diff --git a/src/apps/chat/components/message/ChatMessage.tsx b/src/apps/chat/components/message/ChatMessage.tsx index 26db2b604..31cda3e2d 100644 --- a/src/apps/chat/components/message/ChatMessage.tsx +++ b/src/apps/chat/components/message/ChatMessage.tsx @@ -3,7 +3,7 @@ import { useShallow } from 'zustand/react/shallow'; import TimeAgo from 'react-timeago'; import type { SxProps } from '@mui/joy/styles/types'; -import { Avatar, Box, ButtonGroup, CircularProgress, IconButton, ListDivider, ListItem, ListItemDecorator, MenuItem, Switch, Tooltip, Typography } from '@mui/joy'; +import { Box, ButtonGroup, CircularProgress, IconButton, ListDivider, ListItem, ListItemDecorator, MenuItem, Switch, Tooltip, Typography } from '@mui/joy'; import { ClickAwayListener, Popper } from '@mui/base'; import AccountTreeOutlinedIcon from '@mui/icons-material/AccountTreeOutlined'; import CheckRoundedIcon from '@mui/icons-material/CheckRounded'; @@ -12,35 +12,31 @@ import CloseRoundedIcon from '@mui/icons-material/CloseRounded'; import ContentCopyIcon from '@mui/icons-material/ContentCopy'; import DifferenceIcon from '@mui/icons-material/Difference'; import EditRoundedIcon from '@mui/icons-material/EditRounded'; -import Face6Icon from '@mui/icons-material/Face6'; import ForkRightIcon from '@mui/icons-material/ForkRight'; import FormatPaintOutlinedIcon from '@mui/icons-material/FormatPaintOutlined'; import MoreVertIcon from '@mui/icons-material/MoreVert'; import RecordVoiceOverOutlinedIcon from '@mui/icons-material/RecordVoiceOverOutlined'; import ReplayIcon from '@mui/icons-material/Replay'; import ReplyRoundedIcon from '@mui/icons-material/ReplyRounded'; -import SettingsSuggestIcon from '@mui/icons-material/SettingsSuggest'; -import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; import StarOutlineRoundedIcon from '@mui/icons-material/StarOutlineRounded'; import StarRoundedIcon from '@mui/icons-material/StarRounded'; import TelegramIcon from '@mui/icons-material/Telegram'; import VerticalAlignBottomIcon from '@mui/icons-material/VerticalAlignBottom'; -import { SystemPurposeId, SystemPurposes } from '../../../../data'; - import { ChatBeamIcon } from '~/common/components/icons/ChatBeamIcon'; import { CloseableMenu } from '~/common/components/CloseableMenu'; -import { createTextContentFragment, DMessage, DMessageAttachmentFragment, DMessageContentFragment, DMessageFragment, DMessageFragmentId, DMessageId, DMessageRole, DMessageUserFlag, messageFragmentsReduceText, messageHasUserFlag } from '~/common/stores/chat/chat.message'; import { KeyStroke } from '~/common/components/KeyStroke'; import { adjustContentScaling, themeScalingMap, themeZIndexPageBar } from '~/common/app.theme'; import { animationColorRainbow } from '~/common/util/animUtils'; import { copyToClipboard } from '~/common/util/clipboardUtils'; +import { createTextContentFragment, DMessage, DMessageAttachmentFragment, DMessageContentFragment, DMessageFragment, DMessageFragmentId, DMessageId, DMessageUserFlag, messageFragmentsReduceText, messageHasUserFlag } from '~/common/stores/chat/chat.message'; import { prettyBaseModel } from '~/common/util/modelUtils'; import { useUIPreferencesStore } from '~/common/state/store-ui'; import { AttachmentFragments } from './fragments-attachments/AttachmentFragments'; import { ContentFragments } from './fragments-content/ContentFragments'; import { ReplyToBubble } from './ReplyToBubble'; +import { avatarIconSx, makeMessageAvatar, messageBackground, personaColumnSx } from './messageUtils'; import { useChatShowTextDiff } from '../../store-app-chat'; @@ -52,100 +48,6 @@ const BUBBLE_MIN_TEXT_LENGTH = 3; // Enable the hover button to copy the whole message. The Copy button is also available in Blocks, or in the Avatar Menu. const ENABLE_COPY_MESSAGE_OVERLAY: boolean = false; -// Animations -const ANIM_BUSY_DOWNLOADING = 'https://i.giphy.com/26u6dIwIphLj8h10A.webp'; // hourglass: https://i.giphy.com/TFSxpAIYz5inJGuY8f.webp, small-lq: https://i.giphy.com/131tNuGktpXGhy.webp, floppy: https://i.giphy.com/RxR1KghIie2iI.webp -const ANIM_BUSY_PAINTING = 'https://i.giphy.com/media/5t9ujj9cMisyVjUZ0m/giphy.webp'; -const ANIM_BUSY_THINKING = 'https://i.giphy.com/media/l44QzsOLXxcrigdgI/giphy.webp'; -export const ANIM_BUSY_TYPING = 'https://i.giphy.com/media/jJxaUysjzO9ri/giphy.webp'; - - -export function messageBackground(messageRole: DMessageRole | string, wasEdited: boolean, isAssistantIssue: boolean): string { - switch (messageRole) { - case 'user': - return 'primary.plainHoverBg'; // was .background.level1 - case 'assistant': - return isAssistantIssue ? 'danger.softBg' : 'background.surface'; - case 'system': - return wasEdited ? 'warning.softHoverBg' : 'neutral.softBg'; - default: - return '#ff0000'; - } -} - -const avatarIconSx = { - width: 36, - height: 36, -}; - -const personaSx: SxProps = { - // make this stick to the top of the screen - position: 'sticky', - top: 0, - - // flexBasis: 0, // this won't let the item grow - minWidth: { xs: 50, md: 64 }, - maxWidth: 80, - textAlign: 'center', - // layout - display: 'flex', - flexDirection: 'column', - alignItems: 'center', -}; - - -export function makeMessageAvatar(messageAvatarUrl: string | null, messageRole: DMessageRole | string, messageOriginLLM: string | undefined, messagePurposeId: SystemPurposeId | string | undefined, messageSender: string, messageIncomplete: boolean, larger?: boolean): React.JSX.Element { - if (typeof messageAvatarUrl === 'string' && messageAvatarUrl) - return ; - - const mascotSx = larger ? { width: 64, height: 64 } : avatarIconSx; - switch (messageRole) { - case 'system': - return ; // https://em-content.zobj.net/thumbs/120/apple/325/robot_1f916.png - - case 'user': - return ; // https://www.svgrepo.com/show/306500/openai.svg - - case 'assistant': - const isDownload = messageOriginLLM === 'web'; - const isTextToImage = messageOriginLLM === 'DALL·E' || messageOriginLLM === 'Prodia'; - const isReact = messageOriginLLM?.startsWith('react-'); - - // animation on incomplete messages - if (messageIncomplete) - return ; - - // icon: text-to-image - if (isTextToImage) - return ; - - // purpose symbol (if present) - const symbol = SystemPurposes[messagePurposeId as SystemPurposeId]?.symbol; - if (symbol) - return - {symbol} - ; - - // default assistant avatar - return ; // https://mui.com/static/images/avatar/2.jpg - } - return ; -} export type ChatMessageTextContentEditState = { [fragmentId: DMessageFragmentId]: string }; @@ -234,7 +136,6 @@ export function ChatMessage(props: { const couldSpeak = couldImagine; const attachmentFragments = messageFragments.filter(f => f.ft === 'attachment') as DMessageAttachmentFragment[]; - const hasAttachments = attachmentFragments.length > 0; // TODO: fix the diffing @@ -553,7 +454,7 @@ export function ChatMessage(props: { {/* Editing: Apply */} {isEditingText && ( - + @@ -567,7 +468,7 @@ export function ChatMessage(props: { {/* Avatar (Persona) */} {showAvatar && !isEditingText && ( - + {/* Persona Avatar or Menu Button */} @@ -673,9 +574,9 @@ export function ChatMessage(props: { {/* Editing: Cancel */} {isEditingText && ( - + - + diff --git a/src/apps/chat/components/message/CleanerMessage.tsx b/src/apps/chat/components/message/CleanerMessage.tsx index 440d0bba5..6d2afb0ce 100644 --- a/src/apps/chat/components/message/CleanerMessage.tsx +++ b/src/apps/chat/components/message/CleanerMessage.tsx @@ -7,8 +7,8 @@ import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; import { DMessage, messageFragmentsReduceText } from '~/common/stores/chat/chat.message'; import { TokenBadgeMemo } from '../composer/TokenBadge'; -import { makeMessageAvatar, messageBackground } from './ChatMessage'; import { isErrorChatMessage } from './explainServiceErrors'; +import { makeMessageAvatar, messageBackground } from './messageUtils'; /** diff --git a/src/apps/chat/components/message/messageUtils.tsx b/src/apps/chat/components/message/messageUtils.tsx new file mode 100644 index 000000000..831726064 --- /dev/null +++ b/src/apps/chat/components/message/messageUtils.tsx @@ -0,0 +1,118 @@ +import * as React from 'react'; + +import type { SxProps } from '@mui/joy/styles/types'; +import { Avatar, Box } from '@mui/joy'; +import Face6Icon from '@mui/icons-material/Face6'; +import FormatPaintOutlinedIcon from '@mui/icons-material/FormatPaintOutlined'; +import SettingsSuggestIcon from '@mui/icons-material/SettingsSuggest'; +import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; + +import { SystemPurposeId, SystemPurposes } from '../../../../data'; + +import type { DMessageRole } from '~/common/stores/chat/chat.message'; +import { animationColorRainbow } from '~/common/util/animUtils'; + + +// Animations +const ANIM_BUSY_DOWNLOADING = 'https://i.giphy.com/26u6dIwIphLj8h10A.webp'; // hourglass: https://i.giphy.com/TFSxpAIYz5inJGuY8f.webp, small-lq: https://i.giphy.com/131tNuGktpXGhy.webp, floppy: https://i.giphy.com/RxR1KghIie2iI.webp +const ANIM_BUSY_PAINTING = 'https://i.giphy.com/media/5t9ujj9cMisyVjUZ0m/giphy.webp'; +const ANIM_BUSY_THINKING = 'https://i.giphy.com/media/l44QzsOLXxcrigdgI/giphy.webp'; +export const ANIM_BUSY_TYPING = 'https://i.giphy.com/media/jJxaUysjzO9ri/giphy.webp'; + + +export const personaColumnSx: SxProps = { + // make this stick to the top of the screen + position: 'sticky', + top: 0, + + // flexBasis: 0, // this won't let the item grow + minWidth: { xs: 50, md: 64 }, + maxWidth: 80, + textAlign: 'center', + // layout + display: 'flex', + flexDirection: 'column', + alignItems: 'center', +}; + +export const avatarIconSx = { + width: 36, + height: 36, +} as const; + + +export function makeMessageAvatar( + messageAvatarUrl: string | null, + messageRole: DMessageRole | string, + messageOriginLLM: string | undefined, + messagePurposeId: SystemPurposeId | string | undefined, + messageSender: string, + messageIncomplete: boolean, + larger?: boolean, +): React.JSX.Element { + if (typeof messageAvatarUrl === 'string' && messageAvatarUrl) + return ; + + const mascotSx = larger ? { width: 48, height: 48 } : avatarIconSx; + switch (messageRole) { + case 'system': + return ; // https://em-content.zobj.net/thumbs/120/apple/325/robot_1f916.png + + case 'user': + return ; // https://www.svgrepo.com/show/306500/openai.svg + + case 'assistant': + const isDownload = messageOriginLLM === 'web'; + const isTextToImage = messageOriginLLM === 'DALL·E' || messageOriginLLM === 'Prodia'; + const isReact = messageOriginLLM?.startsWith('react-'); + + // animation on incomplete messages + if (messageIncomplete) + return ; + + // icon: text-to-image + if (isTextToImage) + return ; + + // purpose symbol (if present) + const symbol = SystemPurposes[messagePurposeId as SystemPurposeId]?.symbol; + if (symbol) + return + {symbol} + ; + + // default assistant avatar + return ; // https://mui.com/static/images/avatar/2.jpg + } + return ; +} + + +export function messageBackground(messageRole: DMessageRole | string, wasEdited: boolean, isAssistantIssue: boolean): string { + switch (messageRole) { + case 'user': + return 'primary.plainHoverBg'; // was .background.level1 + case 'assistant': + return isAssistantIssue ? 'danger.softBg' : 'background.surface'; + case 'system': + return wasEdited ? 'warning.softHoverBg' : 'neutral.softBg'; + default: + return '#ff0000'; + } +}