mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-20 18:30:15 +01:00
✨ 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:
parent
d6c0121b19
commit
020995514e
23 changed files with 92 additions and 43 deletions
|
|
@ -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;
|
||||
|
|
|
|||
6
client/src/common/a11y.ts
Normal file
6
client/src/common/a11y.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export interface AnnounceOptions {
|
||||
message: string;
|
||||
isStatus?: boolean;
|
||||
}
|
||||
|
||||
export const MESSAGE_UPDATE_INTERVAL = 7000;
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
export * from './a11y';
|
||||
export * from './artifacts';
|
||||
export * from './types';
|
||||
export * from './assistants-types';
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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 '';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue