v0.7.5-rc2 (#3976)

*  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
This commit is contained in:
Danny Avila 2024-09-10 19:00:27 -04:00 committed by GitHub
parent d6c0121b19
commit 020995514e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 92 additions and 43 deletions

View file

@ -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;

View file

@ -0,0 +1,6 @@
export interface AnnounceOptions {
message: string;
isStatus?: boolean;
}
export const MESSAGE_UPDATE_INTERVAL = 7000;

View file

@ -1,3 +1,4 @@
export * from './a11y';
export * from './artifacts';
export * from './types';
export * from './assistants-types';

View file

@ -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) {

View file

@ -120,7 +120,9 @@ function AccountSettings() {
className={focus ? 'bg-surface-hover' : ''}
svg={() => <GearIcon className="icon-md" />}
text={localize('com_nav_settings')}
clickHandler={() => setShowSettings(true)}
clickHandler={() => {
setTimeout(() => setShowSettings(true), 50);
}}
/>
)}
</MenuItem>

View file

@ -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<HTMLButtonElement>;
className?: string;
disabled?: boolean;
}
@ -13,7 +13,7 @@ const NavLink: FC<Props> = forwardRef<HTMLButtonElement, Props>((props, ref) =>
const { svg, text, clickHandler, disabled, className = '' } = props;
const defaultProps: {
className: string;
onClick?: () => void;
onClick?: React.MouseEventHandler<HTMLButtonElement>;
} = {
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',

View file

@ -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<boolean>(store.rememberForkOption);
const [remember, setRemember] = useRecoilState<boolean>(store.rememberDefaultFork);
const forkOptions = [
{ value: ForkOptions.DIRECT_PATH, label: localize('com_ui_fork_visible') },
@ -39,11 +39,11 @@ export const ForkSettings = () => {
<div className="flex items-center justify-between">
<div> {localize('com_ui_fork_default')} </div>
<Switch
id="rememberForkOption"
id="rememberDefaultFork"
checked={remember}
onCheckedChange={setRemember}
className="ml-4 mt-2"
data-testid="rememberForkOption"
data-testid="rememberDefaultFork"
/>
</div>
</div>

View file

@ -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 */

View file

@ -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<boolean>;
lastAnnouncementTimeRef: React.MutableRefObject<number>;
};
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<string, string | undefined>());
const messageMap = useRef(new Map<string, TMessage>());
const stepMap = useRef(new Map<string, Agents.RunStep>());
@ -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],
);
}

View file

@ -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),

View file

@ -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 '';