Skip to content

Commit

Permalink
✏️ feat: LaTeX parsing for Messages (danny-avila#1585)
Browse files Browse the repository at this point in the history
* feat: Beta features tab in Settings and LaTeX Parsing toggle

* feat: LaTex parsing with spec
  • Loading branch information
danny-avila authored Jan 18, 2024
1 parent 264c171 commit 3aed0e9
Show file tree
Hide file tree
Showing 17 changed files with 290 additions and 21 deletions.
12 changes: 9 additions & 3 deletions client/src/components/Chat/Messages/Content/Markdown.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useRecoilValue } from 'recoil';
import React, { useState, useEffect } from 'react';
import type { TMessage } from 'librechat-data-provider';
import rehypeHighlight from 'rehype-highlight';
Expand All @@ -8,9 +9,10 @@ import rehypeKatex from 'rehype-katex';
import remarkMath from 'remark-math';
import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
import { useChatContext } from '~/Providers';
import { langSubset, validateIframe } from '~/utils';
import CodeBlock from '~/components/Messages/Content/CodeBlock';
import { langSubset, validateIframe, processLaTeX } from '~/utils';
import { useChatContext } from '~/Providers';
import store from '~/store';

type TCodeProps = {
inline: boolean;
Expand Down Expand Up @@ -42,11 +44,15 @@ const p = React.memo(({ children }: { children: React.ReactNode }) => {
const Markdown = React.memo(({ content, message, showCursor }: TContentProps) => {
const [cursor, setCursor] = useState('█');
const { isSubmitting, latestMessage } = useChatContext();
const LaTeXParsing = useRecoilValue<boolean>(store.LaTeXParsing);

const isInitializing = content === '<span className="result-streaming">█</span>';

const { isEdited, messageId } = message ?? {};
const isLatestMessage = messageId === latestMessage?.messageId;
const currentContent = content?.replace('z-index: 1;', '') ?? '';

const _content = content?.replace('z-index: 1;', '') ?? '';
const currentContent = LaTeXParsing ? processLaTeX(_content) : _content;

useEffect(() => {
let timer1: NodeJS.Timeout, timer2: NodeJS.Timeout;
Expand Down
26 changes: 20 additions & 6 deletions client/src/components/Nav/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as Tabs from '@radix-ui/react-tabs';
import { SettingsTabValues } from 'librechat-data-provider';
import type { TDialogProps } from '~/common';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '~/components/ui';
import { GearIcon, DataIcon, UserIcon } from '~/components/svg';
import { General, Data, Account } from './SettingsTabs';
import { GearIcon, DataIcon, UserIcon, ExperimentIcon } from '~/components/svg';
import { General, Beta, Data, Account } from './SettingsTabs';
import { useMediaQuery, useLocalize } from '~/hooks';
import { cn } from '~/utils';

Expand All @@ -23,7 +24,7 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
</DialogHeader>
<div className="px-6">
<Tabs.Root
defaultValue="general"
defaultValue={SettingsTabValues.GENERAL}
className="flex flex-col gap-10 md:flex-row"
orientation="vertical"
>
Expand All @@ -44,7 +45,7 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
? 'flex-1 items-center justify-center text-sm dark:text-gray-500 dark:radix-state-active:text-white'
: '',
)}
value="general"
value={SettingsTabValues.GENERAL}
>
<GearIcon />
{localize('com_nav_setting_general')}
Expand All @@ -56,7 +57,19 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
? 'flex-1 items-center justify-center text-sm dark:text-gray-500 dark:radix-state-active:text-white'
: '',
)}
value="data"
value={SettingsTabValues.BETA}
>
<ExperimentIcon />
{localize('com_nav_setting_beta')}
</Tabs.Trigger>
<Tabs.Trigger
className={cn(
'group my-1 flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-black radix-state-active:bg-gray-100 radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-800',
isSmallScreen
? 'flex-1 items-center justify-center text-sm dark:text-gray-500 dark:radix-state-active:text-white'
: '',
)}
value={SettingsTabValues.DATA}
>
<DataIcon />
{localize('com_nav_setting_data')}
Expand All @@ -68,13 +81,14 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
? 'flex-1 items-center justify-center text-sm dark:text-gray-500 dark:radix-state-active:text-white'
: '',
)}
value="account"
value={SettingsTabValues.ACCOUNT}
>
<UserIcon />
{localize('com_nav_setting_account')}
</Tabs.Trigger>
</Tabs.List>
<General />
<Beta />
<Data />
<Account />
</Tabs.Root>
Expand Down
9 changes: 7 additions & 2 deletions client/src/components/Nav/SettingsTabs/Account/Account.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import React from 'react';
import * as Tabs from '@radix-ui/react-tabs';
import { SettingsTabValues } from 'librechat-data-provider';
import Avatar from './Avatar';
import React from 'react';

function Account() {
return (
<Tabs.Content value="account" role="tabpanel" className="w-full md:min-h-[300px]">
<Tabs.Content
value={SettingsTabValues.ACCOUNT}
role="tabpanel"
className="w-full md:min-h-[300px]"
>
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-300">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<Avatar />
Expand Down
26 changes: 26 additions & 0 deletions client/src/components/Nav/SettingsTabs/Beta/Beta.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { memo } from 'react';
import * as Tabs from '@radix-ui/react-tabs';
import { SettingsTabValues } from 'librechat-data-provider';
import LaTeXParsing from './LaTeXParsing';
import ModularChat from './ModularChat';

function Beta() {
return (
<Tabs.Content
value={SettingsTabValues.BETA}
role="tabpanel"
className="w-full md:min-h-[300px]"
>
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-300">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<ModularChat />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<LaTeXParsing />
</div>
</div>
</Tabs.Content>
);
}

export default memo(Beta);
33 changes: 33 additions & 0 deletions client/src/components/Nav/SettingsTabs/Beta/LaTeXParsing.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useRecoilState } from 'recoil';
import { Switch } from '~/components/ui';
import { useLocalize } from '~/hooks';
import store from '~/store';

export default function LaTeXParsingSwitch({
onCheckedChange,
}: {
onCheckedChange?: (value: boolean) => void;
}) {
const [LaTeXParsing, setLaTeXParsing] = useRecoilState<boolean>(store.LaTeXParsing);
const localize = useLocalize();

const handleCheckedChange = (value: boolean) => {
setLaTeXParsing(value);
if (onCheckedChange) {
onCheckedChange(value);
}
};

return (
<div className="flex items-center justify-between">
<div>{localize('com_nav_latex_parsing')} </div>
<Switch
id="LaTeXParsing"
checked={LaTeXParsing}
onCheckedChange={handleCheckedChange}
className="ml-4 mt-2"
data-testid="LaTeXParsing"
/>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ export default function ModularChatSwitch({

return (
<div className="flex items-center justify-between">
<div>
{`[${localize('com_ui_experimental')}]`} {localize('com_nav_modular_chat')}{' '}
</div>
<div>{localize('com_nav_modular_chat')} </div>
<Switch
id="modularChat"
checked={modularChat}
Expand Down
7 changes: 6 additions & 1 deletion client/src/components/Nav/SettingsTabs/Data/Data.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
useRevokeAllUserKeysMutation,
useRevokeUserKeyMutation,
} from 'librechat-data-provider/react-query';
import { SettingsTabValues } from 'librechat-data-provider';
import React, { useState, useCallback, useRef } from 'react';
import { useOnClickOutside } from '~/hooks';
import DangerButton from '../DangerButton';
Expand Down Expand Up @@ -66,7 +67,11 @@ export const RevokeKeysButton = ({

function Data() {
return (
<Tabs.Content value="data" role="tabpanel" className="w-full md:min-h-[300px]">
<Tabs.Content
value={SettingsTabValues.DATA}
role="tabpanel"
className="w-full md:min-h-[300px]"
>
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-300">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<RevokeKeysButton all={true} />
Expand Down
7 changes: 2 additions & 5 deletions client/src/components/Nav/SettingsTabs/General/General.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useRecoilState } from 'recoil';
import * as Tabs from '@radix-ui/react-tabs';
import { SettingsTabValues } from 'librechat-data-provider';
import React, { useState, useContext, useCallback, useRef } from 'react';
import { useClearConversationsMutation } from 'librechat-data-provider/react-query';
import {
Expand All @@ -14,7 +15,6 @@ import type { TDangerButtonProps } from '~/common';
import AutoScrollSwitch from './AutoScrollSwitch';
import { Dropdown } from '~/components/ui';
import DangerButton from '../DangerButton';
import ModularChat from './ModularChat';
import store from '~/store';

export const ThemeSelector = ({
Expand Down Expand Up @@ -167,7 +167,7 @@ function General() {

return (
<Tabs.Content
value="general"
value={SettingsTabValues.GENERAL}
role="tabpanel"
className="w-full md:min-h-[300px]"
ref={contentRef}
Expand All @@ -190,9 +190,6 @@ function General() {
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<AutoScrollSwitch />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<ModularChat />
</div>
</div>
</Tabs.Content>
);
Expand Down
1 change: 1 addition & 0 deletions client/src/components/Nav/SettingsTabs/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { default as General } from './General/General';
export { ClearChatsButton } from './General/General';
export { default as Data } from './Data/Data';
export { default as Beta } from './Beta/Beta';
export { RevokeKeysButton } from './Data/Data';
export { default as Account } from './Account/Account';
27 changes: 27 additions & 0 deletions client/src/components/svg/ExperimentIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export default function ExperimentIcon() {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="icon-sm"
>
<path
d="M9 3H15M9 3V9.2759C9 9.74377 8.83597 10.1968 8.53644 10.5563L4.85085 14.979C4.30108 15.6387 4 16.4703 4 17.3291V17.3291C4 19.3565 5.64353 21 7.67094 21H16.3291C18.3565 21 20 19.3565 20 17.3291V17.3291C20 16.4703 19.6989 15.6387 19.1492 14.979L15.4636 10.5563C15.164 10.1968 15 9.74377 15 9.2759V3M9 3H8M15 3H16"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
></path>
<path
d="M5 14.774C11.5 12.839 12.15 16.7089 18 14"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
></path>
</svg>
);
}
1 change: 1 addition & 0 deletions client/src/components/svg/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ export { default as GoogleMinimalIcon } from './GoogleMinimalIcon';
export { default as AnthropicMinimalIcon } from './AnthropicMinimalIcon';
export { default as SendMessageIcon } from './SendMessageIcon';
export { default as UserIcon } from './UserIcon';
export { default as ExperimentIcon } from './ExperimentIcon';
5 changes: 4 additions & 1 deletion client/src/localization/languages/Eng.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default {
com_ui_limitation_harmful_biased:
'May occasionally produce harmful instructions or biased content',
com_ui_limitation_limited_2021: 'Limited knowledge of world and events after 2021',
com_ui_experimental: 'Experimental',
com_ui_experimental: 'Experimental Features',
com_ui_input: 'Input',
com_ui_close: 'Close',
com_ui_model: 'Model',
Expand Down Expand Up @@ -265,6 +265,8 @@ export default {
com_nav_welcome_message: 'How can I help you today?',
com_nav_auto_scroll: 'Auto-scroll to Newest on Open',
com_nav_modular_chat: 'Enable switching Endpoints mid-conversation',
com_nav_latex_parsing:
'Toggle parsing LaTeX in messages. Enabled by default but may affect performance on mobile or longer conversations.',
com_nav_profile_picture: 'Profile Picture',
com_nav_change_picture: 'Change picture',
com_nav_plugin_store: 'Plugin store',
Expand Down Expand Up @@ -303,6 +305,7 @@ export default {
com_nav_settings: 'Settings',
com_nav_search_placeholder: 'Search messages',
com_nav_setting_general: 'General',
com_nav_setting_beta: 'Beta features',
com_nav_setting_data: 'Data controls',
com_nav_setting_account: 'Account',
com_nav_language: 'Language',
Expand Down
20 changes: 20 additions & 0 deletions client/src/store/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,25 @@ const modularChat = atom<boolean>({
] as const,
});

const LaTeXParsing = atom<boolean>({
key: 'LaTeXParsing',
default: true,
effects: [
({ setSelf, onSet }) => {
const savedValue = localStorage.getItem('LaTeXParsing');
if (savedValue != null) {
setSelf(savedValue === 'true');
}

onSet((newValue: unknown) => {
if (typeof newValue === 'boolean') {
localStorage.setItem('LaTeXParsing', newValue.toString());
}
});
},
] as const,
});

export default {
abortScroll,
optionSettings,
Expand All @@ -78,4 +97,5 @@ export default {
showPopover,
autoScroll,
modularChat,
LaTeXParsing,
};
1 change: 1 addition & 0 deletions client/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './json';
export * from './files';
export * from './latex';
export * from './presets';
export * from './languages';
export * from './endpoints';
Expand Down
Loading

0 comments on commit 3aed0e9

Please sign in to comment.