Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions client/src/components/Chat/Messages/Content/EditMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { useRef, useEffect, useCallback, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { useForm } from 'react-hook-form';
import { Alert, Button, TextareaAutosize } from '@librechat/client';
import { useUpdateMessageMutation } from 'librechat-data-provider/react-query';
import type { TEditProps } from '~/common';
import { useMessagesOperations, useMessagesConversation } from '~/Providers';
import { useMessagePartsHost } from '~/Providers/MessagePartsHostContext';
import useGetAddedConvo from '~/hooks/Conversations/useGetAddedConvo';
import { cn, isSubmittableMessage } from '~/utils';
import { findRerunParent } from './rerunParent';
import { useLocalize } from '~/hooks';
import Container from './Container';
import EditFiles from './EditFiles';
import store from '~/store';

const EditMessage = ({
text,
Expand All @@ -25,8 +23,8 @@ const EditMessage = ({
const saveButtonRef = useRef<HTMLButtonElement | null>(null);
const submitButtonRef = useRef<HTMLButtonElement | null>(null);
const [saveError, setSaveError] = useState(false);
const { conversation } = useMessagesConversation();
const { getMessages, setMessages } = useMessagesOperations();
const { useChatDirection, useMessagesEditing } = useMessagePartsHost();
const { conversation, getMessages, setMessages } = useMessagesEditing();

const textAreaRef = useRef<HTMLTextAreaElement | null>(null);

Expand All @@ -46,8 +44,7 @@ const EditMessage = ({
const updateMessageMutation = useUpdateMessageMutation(conversationId ?? '');
const localize = useLocalize();

const chatDirection = useRecoilValue(store.chatDirection).toLowerCase();
const isRTL = chatDirection === 'rtl';
const isRTL = useChatDirection() === 'rtl';

const getAddedConvo = useGetAddedConvo();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useState, useEffect, useCallback, useMemo } from 'react';
import copy from 'copy-to-clipboard';
import { Download } from 'lucide-react';
import { useRecoilValue } from 'recoil';
import {
Button,
OGDialog,
Expand All @@ -24,10 +23,10 @@ import {
useSharedFileDownload,
} from '~/data-provider';
import { getDownloadFilename, logger, sortPagesByRelevance, triggerDownload } from '~/utils';
import { useMessagePartsHost } from '~/Providers/MessagePartsHostContext';
import CopyButton from '~/components/Messages/Content/CopyButton';
import { useFileMapContext, useShareContext } from '~/Providers';
import { useShareContext } from '~/Providers';
import { useLocalize } from '~/hooks';
import store from '~/store';

interface FilePreviewDialogProps {
open: boolean;
Expand Down Expand Up @@ -109,9 +108,10 @@ export default function FilePreviewDialog({
triggerRef,
}: FilePreviewDialogProps) {
const localize = useLocalize();
const user = useRecoilValue(store.user);
const { useUser, useFileMap } = useMessagePartsHost();
const user = useUser();
const { shareId } = useShareContext();
const fileMap = useFileMapContext();
const fileMap = useFileMap();
const showExtractedText = shouldUseExtractedTextPreview(
deliveryPath ?? (!shareId && fileId ? fileMap?.[fileId]?.llmDeliveryPath : undefined),
);
Expand Down
5 changes: 3 additions & 2 deletions client/src/components/Chat/Messages/Content/Files.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useMemo, useState, useCallback, memo } from 'react';
import type { TFile, TMessage } from 'librechat-data-provider';
import { useMessagePartsHost } from '~/Providers/MessagePartsHostContext';
import FileContainer from '~/components/Chat/Input/Files/FileContainer';
import { usesImagePreview, hydrateFileDeliveryMetadata } from '~/utils';
import { useFileMapContext, useShareContext } from '~/Providers';
import FilePreviewDialog from './FilePreviewDialog';
import { useShareContext } from '~/Providers';
import Image from './Image';

const Files = ({ message }: { message?: TMessage }) => {
const fileMap = useFileMapContext();
const fileMap = useMessagePartsHost().useFileMap();
const { shareId } = useShareContext();
const files = useMemo(
() => hydrateFileDeliveryMetadata(message?.files, undefined, shareId ? undefined : fileMap),
Expand Down
18 changes: 7 additions & 11 deletions client/src/components/Chat/Messages/Content/SearchContent.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Suspense, useMemo, Fragment } from 'react';
import { useRecoilValue } from 'recoil';
import { DelayedRender } from '@librechat/client';
import { ContentTypes } from 'librechat-data-provider';
import { MessagePartIndex } from '@librechat/chat/react';
import type {
Agents,
TMessage,
Expand All @@ -11,12 +11,12 @@ import type {
} from 'librechat-data-provider';
import type { ReactNode, ReactElement } from 'react';
import { ErrorSourceProvider } from '~/components/Messages/Content/Error/source';
import { MessageContext, SearchContext, useMessageContext } from '~/Providers';
import { useMessagePartsHost } from '~/Providers/MessagePartsHostContext';
import { ErrorMessage, UnfinishedMessage } from './MessageContent';
import { cn, mapAttachments } from '~/utils';
import { SearchContext } from '~/Providers';
import MarkdownLite from './MarkdownLite';
import { AgentUpdate } from './Parts';
import store from '~/store';
import Part from './Part';

/**
Expand All @@ -39,8 +39,7 @@ const SearchContent = ({
/** Author icon + label re-rendered before content that resumes after an inline steer. */
authorHeader?: ReactNode;
}) => {
const enableUserMsgMarkdown = useRecoilValue(store.enableUserMsgMarkdown);
const messageContext = useMessageContext();
const { enableUserMsgMarkdown } = useMessagePartsHost().useUserTextPreferences();
const { messageId } = message;

const attachmentMap = useMemo(() => mapAttachments(attachments ?? []), [attachments]);
Expand Down Expand Up @@ -79,16 +78,13 @@ const SearchContent = ({
/>
);
/** An error part resolves the agent a handoff made active from its own position,
* which it reads from `MessageContext`; persisted content is compacted, so `idx` is
* which it reads from the message context; persisted content is compacted, so `idx` is
* that position in `message.content`. */
const rendered: ReactElement =
part.type === ContentTypes.ERROR ? (
<MessageContext.Provider
key={`display-${messageId}-${idx}`}
value={{ ...messageContext, partIndex: idx }}
>
<MessagePartIndex key={`display-${messageId}-${idx}`} index={idx}>
{partElement}
</MessageContext.Provider>
</MessagePartIndex>
) : (
partElement
);
Expand Down
2 changes: 1 addition & 1 deletion packages/chat/src/react.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** React bindings for the core `Chat`: providers and `useChat`. Populated as the hooks move in. */
export { MessageContext, useMessageContext } from './react/message';
export { MessageContext, MessagePartIndex, useMessageContext } from './react/message';
export {
useMessagePartsHost,
MessagePartsHostProvider,
Expand Down
37 changes: 37 additions & 0 deletions packages/chat/src/react/__tests__/message.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* @jest-environment jsdom
*/
import React from 'react';
import { renderHook } from '@testing-library/react';
import { MessageContext, MessagePartIndex, useMessageContext } from '../message';

const surrounding: MessageContext = {
messageId: 'message-1',
conversationId: 'convo-1',
isExpanded: true,
isSubmitting: false,
isLatestMessage: true,
partIndex: 0,
};

describe('MessagePartIndex', () => {
it('provides the surrounding message at the part position', () => {
const { result } = renderHook(() => useMessageContext(), {
wrapper: ({ children }: { children: React.ReactNode }) => (
<MessageContext.Provider value={surrounding}>
<MessagePartIndex index={3}>{children}</MessagePartIndex>
</MessageContext.Provider>
),
});
expect(result.current).toEqual({ ...surrounding, partIndex: 3 });
});

it('sets only the position outside a message', () => {
const { result } = renderHook(() => useMessageContext(), {
wrapper: ({ children }: { children: React.ReactNode }) => (
<MessagePartIndex index={1}>{children}</MessagePartIndex>
),
});
expect(result.current).toEqual({ partIndex: 1 });
});
});
17 changes: 15 additions & 2 deletions packages/chat/src/react/message.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createContext, useContext } from 'react';
import type { Context } from 'react';
import { createContext, createElement, useContext, useMemo } from 'react';
import type { Context, ReactNode } from 'react';

/** The message a part renders in, as the view rendering it reports. */
export type MessageContext = {
Expand All @@ -18,3 +18,16 @@ export const MessageContext: Context<MessageContext> = createContext<MessageCont
{} as MessageContext,
);
export const useMessageContext = (): MessageContext => useContext(MessageContext);

/** Provides the surrounding message to one part, at that part's position in the message's content. */
export function MessagePartIndex({
index,
children,
}: {
index: number;
children?: ReactNode;
}): ReactNode {
const message = useMessageContext();
const value = useMemo(() => ({ ...message, partIndex: index }), [message, index]);
return createElement(MessageContext.Provider, { value }, children);
}
Loading