diff --git a/client/src/components/Chat/Messages/Content/MessageContent.tsx b/client/src/components/Chat/Messages/Content/MessageContent.tsx index 5f03fc1efe..06d3715fa8 100644 --- a/client/src/components/Chat/Messages/Content/MessageContent.tsx +++ b/client/src/components/Chat/Messages/Content/MessageContent.tsx @@ -1,15 +1,18 @@ import { Fragment, Suspense, useMemo } from 'react'; +import { useRecoilValue } from 'recoil'; import type { TMessage, TResPlugin } from 'librechat-data-provider'; import type { TMessageContentProps, TDisplayProps } from '~/common'; import Plugin from '~/components/Messages/Content/Plugin'; import Error from '~/components/Messages/Content/Error'; import { DelayedRender } from '~/components/ui'; import { useChatContext } from '~/Providers'; +import MarkdownLite from './MarkdownLite'; import EditMessage from './EditMessage'; import { useLocalize } from '~/hooks'; import Container from './Container'; import Markdown from './Markdown'; import { cn } from '~/utils'; +import store from '~/store'; export const ErrorMessage = ({ text, @@ -64,9 +67,9 @@ export const ErrorMessage = ({ ); }; -// Display Message Component const DisplayMessage = ({ text, isCreatedByUser, message, showCursor }: TDisplayProps) => { const { isSubmitting, latestMessage } = useChatContext(); + const enableUserMsgMarkdown = useRecoilValue(store.enableUserMsgMarkdown); const showCursorState = useMemo( () => showCursor === true && isSubmitting, [showCursor, isSubmitting], @@ -75,6 +78,18 @@ const DisplayMessage = ({ text, isCreatedByUser, message, showCursor }: TDisplay () => message.messageId === latestMessage?.messageId, [message.messageId, latestMessage?.messageId], ); + + let content: React.ReactElement; + if (!isCreatedByUser) { + content = ( + + ); + } else if (enableUserMsgMarkdown) { + content = ; + } else { + content = <>{text}; + } + return (
- {!isCreatedByUser ? ( - - ) : ( - <>{text} - )} + {content}
); diff --git a/client/src/components/Chat/Messages/Content/Parts/Text.tsx b/client/src/components/Chat/Messages/Content/Parts/Text.tsx index 7d0b386c81..59e8b32164 100644 --- a/client/src/components/Chat/Messages/Content/Parts/Text.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/Text.tsx @@ -1,7 +1,10 @@ -import { memo, useMemo } from 'react'; -import { useChatContext } from '~/Providers'; +import { memo, useMemo, ReactElement } from 'react'; +import { useRecoilValue } from 'recoil'; +import MarkdownLite from '~/components/Chat/Messages/Content/MarkdownLite'; import Markdown from '~/components/Chat/Messages/Content/Markdown'; +import { useChatContext } from '~/Providers'; import { cn } from '~/utils'; +import store from '~/store'; type TextPartProps = { text: string; @@ -10,14 +13,32 @@ type TextPartProps = { showCursor: boolean; }; +type ContentType = + | ReactElement> + | ReactElement> + | ReactElement; + const TextPart = memo(({ text, isCreatedByUser, messageId, showCursor }: TextPartProps) => { const { isSubmitting, latestMessage } = useChatContext(); + const enableUserMsgMarkdown = useRecoilValue(store.enableUserMsgMarkdown); const showCursorState = useMemo(() => showCursor && isSubmitting, [showCursor, isSubmitting]); const isLatestMessage = useMemo( () => messageId === latestMessage?.messageId, [messageId, latestMessage?.messageId], ); + const content: ContentType = useMemo(() => { + if (!isCreatedByUser) { + return ( + + ); + } else if (enableUserMsgMarkdown) { + return ; + } else { + return <>{text}; + } + }, [isCreatedByUser, enableUserMsgMarkdown, text, showCursorState, isLatestMessage]); + return (
- {!isCreatedByUser ? ( - - ) : ( - <>{text} - )} + {content}
); }); diff --git a/client/src/components/Chat/Messages/Content/SearchContent.tsx b/client/src/components/Chat/Messages/Content/SearchContent.tsx index a76d3fcf59..3f19c3e03c 100644 --- a/client/src/components/Chat/Messages/Content/SearchContent.tsx +++ b/client/src/components/Chat/Messages/Content/SearchContent.tsx @@ -22,12 +22,13 @@ const SearchContent = ({ message }: { message: TMessage }) => { key={`display-${messageId}-${idx}`} showCursor={false} isSubmitting={false} + isCreatedByUser={message.isCreatedByUser} + messageId={message.messageId} part={part} - message={message} /> ); })} - {message.unfinished && ( + {message.unfinished === true && ( diff --git a/client/src/components/Nav/SettingsTabs/General/General.tsx b/client/src/components/Nav/SettingsTabs/General/General.tsx index 66aed110b2..5f289cc3fc 100644 --- a/client/src/components/Nav/SettingsTabs/General/General.tsx +++ b/client/src/components/Nav/SettingsTabs/General/General.tsx @@ -1,9 +1,10 @@ import { useRecoilState } from 'recoil'; import Cookies from 'js-cookie'; -import React, { useContext, useCallback, useRef } from 'react'; +import React, { useContext, useCallback } from 'react'; import type { TDangerButtonProps } from '~/common'; -import { ThemeContext, useLocalize } from '~/hooks'; +import UserMsgMarkdownSwitch from './UserMsgMarkdownSwitch'; import HideSidePanelSwitch from './HideSidePanelSwitch'; +import { ThemeContext, useLocalize } from '~/hooks'; import AutoScrollSwitch from './AutoScrollSwitch'; import ArchivedChats from './ArchivedChats'; import { Dropdown } from '~/components/ui'; @@ -123,8 +124,6 @@ function General() { const [langcode, setLangcode] = useRecoilState(store.lang); - const contentRef = useRef(null); - const changeTheme = useCallback( (value: string) => { setTheme(value); @@ -156,6 +155,9 @@ function General() {
+
+ +
diff --git a/client/src/components/Nav/SettingsTabs/General/UserMsgMarkdownSwitch.tsx b/client/src/components/Nav/SettingsTabs/General/UserMsgMarkdownSwitch.tsx new file mode 100644 index 0000000000..411692c515 --- /dev/null +++ b/client/src/components/Nav/SettingsTabs/General/UserMsgMarkdownSwitch.tsx @@ -0,0 +1,35 @@ +import { useRecoilState } from 'recoil'; +import { Switch } from '~/components/ui'; +import { useLocalize } from '~/hooks'; +import store from '~/store'; + +export default function UserMsgMarkdownSwitch({ + onCheckedChange, +}: { + onCheckedChange?: (value: boolean) => void; +}) { + const localize = useLocalize(); + const [enableUserMsgMarkdown, setEnableUserMsgMarkdown] = useRecoilState( + store.enableUserMsgMarkdown, + ); + + const handleCheckedChange = (value: boolean) => { + setEnableUserMsgMarkdown(value); + if (onCheckedChange) { + onCheckedChange(value); + } + }; + + return ( +
+
{localize('com_nav_user_msg_markdown')}
+ +
+ ); +} diff --git a/client/src/localization/languages/Eng.ts b/client/src/localization/languages/Eng.ts index 27734cf1d8..d0b0950465 100644 --- a/client/src/localization/languages/Eng.ts +++ b/client/src/localization/languages/Eng.ts @@ -625,6 +625,7 @@ export default { com_nav_welcome_assistant: 'Please Select an Assistant', com_nav_welcome_message: 'How can I help you today?', com_nav_auto_scroll: 'Auto-Scroll to latest message on chat open', + com_nav_user_msg_markdown: 'Render user messages as markdown', com_nav_hide_panel: 'Hide right-most side panel', com_nav_modular_chat: 'Enable switching Endpoints mid-conversation', com_nav_latex_parsing: 'Parsing LaTeX in messages (may affect performance)', diff --git a/client/src/store/settings.ts b/client/src/store/settings.ts index d131df5551..0316336bd3 100644 --- a/client/src/store/settings.ts +++ b/client/src/store/settings.ts @@ -23,6 +23,10 @@ const localStorageAtoms = { autoScroll: atomWithLocalStorage('autoScroll', false), hideSidePanel: atomWithLocalStorage('hideSidePanel', false), fontSize: atomWithLocalStorage('fontSize', 'text-base'), + enableUserMsgMarkdown: atomWithLocalStorage( + LocalStorageKeys.ENABLE_USER_MSG_MARKDOWN, + true, + ), // Messages settings enterToSend: atomWithLocalStorage('enterToSend', true), diff --git a/package-lock.json b/package-lock.json index c152c3f9b0..46f8fb1040 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36680,7 +36680,7 @@ }, "packages/data-provider": { "name": "librechat-data-provider", - "version": "0.7.424", + "version": "0.7.425", "license": "ISC", "dependencies": { "@types/js-yaml": "^4.0.9", diff --git a/packages/data-provider/package.json b/packages/data-provider/package.json index 437f44f66e..dd265e3c13 100644 --- a/packages/data-provider/package.json +++ b/packages/data-provider/package.json @@ -1,6 +1,6 @@ { "name": "librechat-data-provider", - "version": "0.7.424", + "version": "0.7.425", "description": "data services for librechat apps", "main": "dist/index.js", "module": "dist/index.es.js", diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index 152b59598b..be0dfd3d8e 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -1119,6 +1119,8 @@ export enum LocalStorageKeys { FILES_DRAFT = 'filesDraft_', /** Key for last Selected Prompt Category */ LAST_PROMPT_CATEGORY = 'lastPromptCategory', + /** Key for rendering User Messages as Markdown */ + ENABLE_USER_MSG_MARKDOWN = 'enableUserMsgMarkdown', } export enum ForkOptions {