From 020995514e667ec0b7e550e34f6b61fda62c62e6 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Tue, 10 Sep 2024 19:00:27 -0400 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20v0.7.5-rc2=20(#3976)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ v0.7.5-rc2 * docs: update README * refactor(settings): Update rememberForkOption default value * a11y: proper screen reader announcements for content blocks * Update version to 0.7.423 in package-lock.json and packages/data-provider/package.json * chore: rename rememberForkOption -> rememberDefaultFork to apply new default value * fix: headlessui menu stealing focus from Settings Dialog when pressing Enter --- Dockerfile | 2 +- Dockerfile.multi | 2 +- README.md | 4 +-- api/package.json | 2 +- client/package.json | 2 +- client/src/Providers/AnnouncerContext.tsx | 6 +--- client/src/common/a11y.ts | 6 ++++ client/src/common/index.ts | 1 + client/src/components/Conversations/Fork.tsx | 2 +- client/src/components/Nav/AccountSettings.tsx | 4 ++- client/src/components/Nav/NavLink.tsx | 6 ++-- .../Nav/SettingsTabs/Chat/ForkSettings.tsx | 6 ++-- client/src/hooks/SSE/useEventHandlers.ts | 14 ++++++--- client/src/hooks/SSE/useStepHandler.ts | 30 +++++++++++++++---- client/src/store/settings.ts | 4 +-- client/src/utils/messages.ts | 20 +++++++++++++ e2e/jestSetup.js | 2 +- index.html | 2 +- package-lock.json | 10 +++---- package.json | 2 +- packages/data-provider/package.json | 2 +- packages/data-provider/src/config.ts | 4 +-- prettier.config.js | 2 +- 23 files changed, 92 insertions(+), 43 deletions(-) create mode 100644 client/src/common/a11y.ts diff --git a/Dockerfile b/Dockerfile index e8530fb58c9..0793f0de11d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# v0.7.5-rc1 +# v0.7.5-rc2 # Base node image FROM node:20-alpine AS node diff --git a/Dockerfile.multi b/Dockerfile.multi index 175563597ae..a32183d82f3 100644 --- a/Dockerfile.multi +++ b/Dockerfile.multi @@ -1,5 +1,5 @@ # Dockerfile.multi -# v0.7.5-rc1 +# v0.7.5-rc2 # Base for all builds FROM node:20-alpine AS base diff --git a/README.md b/README.md index f1d92b50dae..50ccd252b9c 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,10 @@ - 🖥️ UI matching ChatGPT, including Dark mode, Streaming, and latest updates - 🤖 AI model selection: - - OpenAI, Azure OpenAI, BingAI, ChatGPT, Google Vertex AI, Anthropic (Claude), Plugins, Assistants API (including Azure Assistants) + - Anthropic (Claude), AWS Bedrock, OpenAI, Azure OpenAI, BingAI, ChatGPT, Google Vertex AI, Plugins, Assistants API (including Azure Assistants) - ✅ Compatible across both **[Remote & Local AI services](https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints):** - groq, Ollama, Cohere, Mistral AI, Apple MLX, koboldcpp, OpenRouter, together.ai, Perplexity, ShuttleAI, and more -- 🪄 Generative UI with [Code Artifacts](https://youtu.be/GfTj7O4gmd0?si=WJbdnemZpJzBrJo3) +- 🪄 Generative UI with **[Code Artifacts](https://youtu.be/GfTj7O4gmd0?si=WJbdnemZpJzBrJo3)** - Create React, HTML code, and Mermaid diagrams right in chat - 💾 Create, Save, & Share Custom Presets - 🔀 Switch between AI Endpoints and Presets, mid-chat diff --git a/api/package.json b/api/package.json index 75df20b2dd2..dc9b8c092f6 100644 --- a/api/package.json +++ b/api/package.json @@ -1,6 +1,6 @@ { "name": "@librechat/backend", - "version": "v0.7.5-rc1", + "version": "v0.7.5-rc2", "description": "", "scripts": { "start": "echo 'please run this from the root directory'", diff --git a/client/package.json b/client/package.json index 9ea26134598..2419d667740 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@librechat/frontend", - "version": "v0.7.5-rc1", + "version": "v0.7.5-rc2", "description": "", "type": "module", "scripts": { diff --git a/client/src/Providers/AnnouncerContext.tsx b/client/src/Providers/AnnouncerContext.tsx index 34437d4e305..a45cbd20897 100644 --- a/client/src/Providers/AnnouncerContext.tsx +++ b/client/src/Providers/AnnouncerContext.tsx @@ -1,10 +1,6 @@ // AnnouncerContext.tsx import React from 'react'; - -export interface AnnounceOptions { - message: string; - isStatus?: boolean; -} +import type { AnnounceOptions } from '~/common'; interface AnnouncerContextType { announceAssertive: (options: AnnounceOptions) => void; diff --git a/client/src/common/a11y.ts b/client/src/common/a11y.ts new file mode 100644 index 00000000000..0a0e56eab2f --- /dev/null +++ b/client/src/common/a11y.ts @@ -0,0 +1,6 @@ +export interface AnnounceOptions { + message: string; + isStatus?: boolean; +} + +export const MESSAGE_UPDATE_INTERVAL = 7000; diff --git a/client/src/common/index.ts b/client/src/common/index.ts index 29739c7bd8f..85dda0700cb 100644 --- a/client/src/common/index.ts +++ b/client/src/common/index.ts @@ -1,3 +1,4 @@ +export * from './a11y'; export * from './artifacts'; export * from './types'; export * from './assistants-types'; diff --git a/client/src/components/Conversations/Fork.tsx b/client/src/components/Conversations/Fork.tsx index 6f109dd987f..7eb47d66f8f 100644 --- a/client/src/components/Conversations/Fork.tsx +++ b/client/src/components/Conversations/Fork.tsx @@ -116,7 +116,7 @@ export default function Fork({ const [forkSetting, setForkSetting] = useRecoilState(store.forkSetting); const [activeSetting, setActiveSetting] = useState(optionLabels.default); const [splitAtTarget, setSplitAtTarget] = useRecoilState(store.splitAtTarget); - const [rememberGlobal, setRememberGlobal] = useRecoilState(store.rememberForkOption); + const [rememberGlobal, setRememberGlobal] = useRecoilState(store.rememberDefaultFork); const forkConvo = useForkConvoMutation({ onSuccess: (data) => { if (data) { diff --git a/client/src/components/Nav/AccountSettings.tsx b/client/src/components/Nav/AccountSettings.tsx index f1fd026f9b0..dfed968e72b 100644 --- a/client/src/components/Nav/AccountSettings.tsx +++ b/client/src/components/Nav/AccountSettings.tsx @@ -120,7 +120,9 @@ function AccountSettings() { className={focus ? 'bg-surface-hover' : ''} svg={() => } text={localize('com_nav_settings')} - clickHandler={() => setShowSettings(true)} + clickHandler={() => { + setTimeout(() => setShowSettings(true), 50); + }} /> )} diff --git a/client/src/components/Nav/NavLink.tsx b/client/src/components/Nav/NavLink.tsx index 598f2df4299..be77ed2c4a1 100644 --- a/client/src/components/Nav/NavLink.tsx +++ b/client/src/components/Nav/NavLink.tsx @@ -1,10 +1,10 @@ -import { FC, forwardRef } from 'react'; +import React, { FC, forwardRef } from 'react'; import { cn } from '~/utils/'; interface Props { svg: () => JSX.Element; text: string; - clickHandler?: () => void; + clickHandler?: React.MouseEventHandler; className?: string; disabled?: boolean; } @@ -13,7 +13,7 @@ const NavLink: FC = forwardRef((props, ref) => const { svg, text, clickHandler, disabled, className = '' } = props; const defaultProps: { className: string; - onClick?: () => void; + onClick?: React.MouseEventHandler; } = { className: cn( 'w-full flex gap-2 rounded p-2.5 text-sm cursor-pointer group items-center transition-colors duration-200 text-text-primary hover:bg-surface-hover', diff --git a/client/src/components/Nav/SettingsTabs/Chat/ForkSettings.tsx b/client/src/components/Nav/SettingsTabs/Chat/ForkSettings.tsx index 26ce56fcd76..71c0a26e69c 100644 --- a/client/src/components/Nav/SettingsTabs/Chat/ForkSettings.tsx +++ b/client/src/components/Nav/SettingsTabs/Chat/ForkSettings.tsx @@ -9,7 +9,7 @@ export const ForkSettings = () => { const localize = useLocalize(); const [forkSetting, setForkSetting] = useRecoilState(store.forkSetting); const [splitAtTarget, setSplitAtTarget] = useRecoilState(store.splitAtTarget); - const [remember, setRemember] = useRecoilState(store.rememberForkOption); + const [remember, setRemember] = useRecoilState(store.rememberDefaultFork); const forkOptions = [ { value: ForkOptions.DIRECT_PATH, label: localize('com_ui_fork_visible') }, @@ -39,11 +39,11 @@ export const ForkSettings = () => {
{localize('com_ui_fork_default')}
diff --git a/client/src/hooks/SSE/useEventHandlers.ts b/client/src/hooks/SSE/useEventHandlers.ts index ea341c39b14..89f2e9af607 100644 --- a/client/src/hooks/SSE/useEventHandlers.ts +++ b/client/src/hooks/SSE/useEventHandlers.ts @@ -23,6 +23,7 @@ import type { TGenTitleMutation } from '~/data-provider'; import { scrollToEnd, addConversation, + getAllContentText, deleteConversation, updateConversation, getConversationById, @@ -30,6 +31,7 @@ import { import useContentHandler from '~/hooks/SSE/useContentHandler'; import useStepHandler from '~/hooks/SSE/useStepHandler'; import { useAuthContext } from '~/hooks/AuthContext'; +import { MESSAGE_UPDATE_INTERVAL } from '~/common'; import { useLiveAnnouncer } from '~/Providers'; import store from '~/store'; @@ -55,8 +57,6 @@ export type EventHandlerParams = { resetLatestMessage?: Resetter; }; -const MESSAGE_UPDATE_INTERVAL = 7000; - export default function useEventHandlers({ genTitle, setMessages, @@ -78,7 +78,13 @@ export default function useEventHandlers({ const { token } = useAuthContext(); const contentHandler = useContentHandler({ setMessages, getMessages }); - const stepHandler = useStepHandler({ setMessages, getMessages }); + const stepHandler = useStepHandler({ + setMessages, + getMessages, + announcePolite, + setIsSubmitting, + lastAnnouncementTimeRef, + }); const messageHandler = useCallback( (data: string | undefined, submission: EventSubmission) => { @@ -356,7 +362,7 @@ export default function useEventHandlers({ }); announcePolite({ - message: responseMessage?.text ?? '', + message: getAllContentText(responseMessage), }); /* Update messages; if assistants endpoint, client doesn't receive responseMessage */ diff --git a/client/src/hooks/SSE/useStepHandler.ts b/client/src/hooks/SSE/useStepHandler.ts index 40b5f185980..ae57e8daf6b 100644 --- a/client/src/hooks/SSE/useStepHandler.ts +++ b/client/src/hooks/SSE/useStepHandler.ts @@ -1,17 +1,22 @@ import { useCallback, useRef } from 'react'; -import { StepTypes, ContentTypes, ToolCallTypes } from 'librechat-data-provider'; +import { StepTypes, ContentTypes, ToolCallTypes, getNonEmptyValue } from 'librechat-data-provider'; import type { Agents, - PartMetadata, TMessage, - TMessageContentParts, + PartMetadata, EventSubmission, + TMessageContentParts, } from 'librechat-data-provider'; -import { getNonEmptyValue } from 'librechat-data-provider'; +import type { SetterOrUpdater } from 'recoil'; +import type { AnnounceOptions } from '~/common'; +import { MESSAGE_UPDATE_INTERVAL } from '~/common'; type TUseStepHandler = { + announcePolite: (options: AnnounceOptions) => void; setMessages: (messages: TMessage[]) => void; getMessages: () => TMessage[] | undefined; + setIsSubmitting: SetterOrUpdater; + lastAnnouncementTimeRef: React.MutableRefObject; }; type TStepEvent = { @@ -28,7 +33,13 @@ type AllContentTypes = | ContentTypes.IMAGE_URL | ContentTypes.ERROR; -export default function useStepHandler({ setMessages, getMessages }: TUseStepHandler) { +export default function useStepHandler({ + setMessages, + getMessages, + setIsSubmitting, + announcePolite, + lastAnnouncementTimeRef, +}: TUseStepHandler) { const toolCallIdMap = useRef(new Map()); const messageMap = useRef(new Map()); const stepMap = useRef(new Map()); @@ -112,6 +123,13 @@ export default function useStepHandler({ setMessages, getMessages }: TUseStepHan ({ event, data }: TStepEvent, submission: EventSubmission) => { const messages = getMessages() || []; const { userMessage } = submission; + setIsSubmitting(true); + + const currentTime = Date.now(); + if (currentTime - lastAnnouncementTimeRef.current > MESSAGE_UPDATE_INTERVAL) { + announcePolite({ message: 'composing', isStatus: true }); + lastAnnouncementTimeRef.current = currentTime; + } if (event === 'on_run_step') { const runStep = data as Agents.RunStep; @@ -249,6 +267,6 @@ export default function useStepHandler({ setMessages, getMessages }: TUseStepHan stepMap.current.clear(); }; }, - [getMessages, stepMap, messageMap, setMessages, toolCallIdMap], + [getMessages, setIsSubmitting, lastAnnouncementTimeRef, announcePolite, setMessages], ); } diff --git a/client/src/store/settings.ts b/client/src/store/settings.ts index fa75615cf7b..d131df5551f 100644 --- a/client/src/store/settings.ts +++ b/client/src/store/settings.ts @@ -1,5 +1,5 @@ import { atom } from 'recoil'; -import { SettingsViews } from 'librechat-data-provider'; +import { SettingsViews, LocalStorageKeys } from 'librechat-data-provider'; import { atomWithLocalStorage } from '~/store/utils'; import type { TOptionSettings } from '~/common'; @@ -32,7 +32,7 @@ const localStorageAtoms = { forkSetting: atomWithLocalStorage('forkSetting', ''), splitAtTarget: atomWithLocalStorage('splitAtTarget', false), - rememberForkOption: atomWithLocalStorage('rememberForkOption', true), + rememberDefaultFork: atomWithLocalStorage(LocalStorageKeys.REMEMBER_FORK_OPTION, false), // Beta features settings modularChat: atomWithLocalStorage('modularChat', true), diff --git a/client/src/utils/messages.ts b/client/src/utils/messages.ts index 035c1d7e277..507618a2c73 100644 --- a/client/src/utils/messages.ts +++ b/client/src/utils/messages.ts @@ -40,6 +40,26 @@ export const getLatestText = (message?: TMessage | null, includeIndex?: boolean) return ''; }; +export const getAllContentText = (message?: TMessage | null): string => { + if (!message) { + return ''; + } + + if (message.text) { + return message.text; + } + + if (message.content && message.content.length > 0) { + return message.content + .filter((part) => part.type === ContentTypes.TEXT) + .map((part) => (typeof part.text === 'string' ? part.text : part.text.value) ?? '') + .filter((text) => text.length > 0) + .join('\n'); + } + + return ''; +}; + export const getTextKey = (message?: TMessage | null, convoId?: string | null) => { if (!message) { return ''; diff --git a/e2e/jestSetup.js b/e2e/jestSetup.js index 35a507d11e8..b6dcd1372f5 100644 --- a/e2e/jestSetup.js +++ b/e2e/jestSetup.js @@ -1,3 +1,3 @@ -// v0.7.5-rc1 +// v0.7.5-rc2 // See .env.test.example for an example of the '.env.test' file. require('dotenv').config({ path: './e2e/.env.test' }); diff --git a/index.html b/index.html index 29699ef1243..77e880828ac 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ - + diff --git a/package-lock.json b/package-lock.json index 44d59720268..1e6fc082d74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "LibreChat", - "version": "v0.7.5-rc1", + "version": "v0.7.5-rc2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "LibreChat", - "version": "v0.7.5-rc1", + "version": "v0.7.5-rc2", "license": "ISC", "workspaces": [ "api", @@ -40,7 +40,7 @@ }, "api": { "name": "@librechat/backend", - "version": "v0.7.5-rc1", + "version": "v0.7.5-rc2", "license": "ISC", "dependencies": { "@anthropic-ai/sdk": "^0.16.1", @@ -1220,7 +1220,7 @@ }, "client": { "name": "@librechat/frontend", - "version": "v0.7.5-rc1", + "version": "v0.7.5-rc2", "license": "ISC", "dependencies": { "@ariakit/react": "^0.4.8", @@ -36399,7 +36399,7 @@ }, "packages/data-provider": { "name": "librechat-data-provider", - "version": "0.7.422", + "version": "0.7.423", "license": "ISC", "dependencies": { "@types/js-yaml": "^4.0.9", diff --git a/package.json b/package.json index f67533df8d3..d0f6688845b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "LibreChat", - "version": "v0.7.5-rc1", + "version": "v0.7.5-rc2", "description": "", "workspaces": [ "api", diff --git a/packages/data-provider/package.json b/packages/data-provider/package.json index 36ffa583012..d029ea727d4 100644 --- a/packages/data-provider/package.json +++ b/packages/data-provider/package.json @@ -1,6 +1,6 @@ { "name": "librechat-data-provider", - "version": "0.7.422", + "version": "0.7.423", "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 597d264ccf1..49c79d11f20 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -1060,7 +1060,7 @@ export enum TTSProviders { /** Enum for app-wide constants */ export enum Constants { /** Key for the app's version. */ - VERSION = 'v0.7.5-rc1', + VERSION = 'v0.7.5-rc2', /** Key for the Custom Config's version (librechat.yaml). */ CONFIG_VERSION = '1.1.7', /** Standard value for the first message's `parentMessageId` value, to indicate no parent exists. */ @@ -1107,7 +1107,7 @@ export enum LocalStorageKeys { /** Key for the last selected fork setting */ FORK_SETTING = 'forkSetting', /** Key for remembering the last selected option, instead of manually selecting */ - REMEMBER_FORK_OPTION = 'rememberForkOption', + REMEMBER_FORK_OPTION = 'rememberDefaultFork', /** Key for remembering the split at target fork option modifier */ FORK_SPLIT_AT_TARGET = 'splitAtTarget', /** Key for saving text drafts */ diff --git a/prettier.config.js b/prettier.config.js index d6f5ba07740..d01e0f609b6 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -1,4 +1,4 @@ -// v0.7.5-rc1 +// v0.7.5-rc2 module.exports = { tailwindConfig: './client/tailwind.config.cjs', printWidth: 100,