mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-07 11:08:52 +01:00
* 💻 feat: deeper MCP UI integration in the chat UI using plugins --------- Co-authored-by: Samuel Path <samuel.path@shopify.com> Co-authored-by: Pierre-Luc Godin <pierreluc.godin@shopify.com> * 💻 refactor: Migrate MCP UI resources from index-based to ID-based referencing - Replace index-based resource markers with stable resource IDs - Update plugin to parse \ui{resourceId} format instead of \ui0 - Refactor components to use useMessagesOperations instead of useSubmitMessage - Add ShareMessagesProvider for UI resources in share view - Add useConversationUIResources hook for cross-turn resource lookups - Update parsers to generate resource IDs from content hashes - Update all tests to use resource IDs instead of indices - Add sandbox permissions for iframe popups - Remove deprecated MCP tool context instructions --------- Co-authored-by: Pierre-Luc Godin <pierreluc.godin@shopify.com>
74 lines
1.9 KiB
TypeScript
74 lines
1.9 KiB
TypeScript
import { atom, selectorFamily } from 'recoil';
|
|
import { TAttachment } from 'librechat-data-provider';
|
|
import { atomWithLocalStorage } from './utils';
|
|
import { BadgeItem } from '~/common';
|
|
|
|
const hideBannerHint = atomWithLocalStorage('hideBannerHint', [] as string[]);
|
|
|
|
const messageAttachmentsMap = atom<Record<string, TAttachment[] | undefined>>({
|
|
key: 'messageAttachmentsMap',
|
|
default: {},
|
|
});
|
|
|
|
/**
|
|
* Selector to get attachments for a specific conversation.
|
|
*/
|
|
const conversationAttachmentsSelector = selectorFamily<
|
|
Record<string, TAttachment[]>,
|
|
string | undefined
|
|
>({
|
|
key: 'conversationAttachments',
|
|
get:
|
|
(conversationId) =>
|
|
({ get }) => {
|
|
if (!conversationId) {
|
|
return {};
|
|
}
|
|
|
|
const attachmentsMap = get(messageAttachmentsMap);
|
|
const result: Record<string, TAttachment[]> = {};
|
|
|
|
// Filter to only include attachments for this conversation
|
|
Object.entries(attachmentsMap).forEach(([messageId, attachments]) => {
|
|
if (!attachments) {
|
|
return;
|
|
}
|
|
|
|
const relevantAttachments = attachments.filter(
|
|
(attachment) => attachment.conversationId === conversationId,
|
|
);
|
|
|
|
if (relevantAttachments.length > 0) {
|
|
result[messageId] = relevantAttachments;
|
|
}
|
|
});
|
|
|
|
return result;
|
|
},
|
|
});
|
|
|
|
const queriesEnabled = atom<boolean>({
|
|
key: 'queriesEnabled',
|
|
default: true,
|
|
});
|
|
|
|
const isEditingBadges = atom<boolean>({
|
|
key: 'isEditingBadges',
|
|
default: false,
|
|
});
|
|
|
|
const chatBadges = atomWithLocalStorage<Pick<BadgeItem, 'id'>[]>('chatBadges', [
|
|
// When adding new badges, make sure to add them to useChatBadges.ts as well and add them as last item
|
|
// DO NOT CHANGE THE ORDER OF THE BADGES ALREADY IN THE ARRAY
|
|
{ id: '1' },
|
|
// { id: '2' },
|
|
]);
|
|
|
|
export default {
|
|
hideBannerHint,
|
|
messageAttachmentsMap,
|
|
conversationAttachmentsSelector,
|
|
queriesEnabled,
|
|
isEditingBadges,
|
|
chatBadges,
|
|
};
|