From f0e8cca5df818af3fa79534be4f2218431352f97 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Fri, 17 May 2024 18:13:32 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20feat:=20Shared=20Links=20(#2772)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ feat(types): add necessary types for shared link feature * ✨ feat: add shared links functions to data service Added functions for retrieving, creating, updating, and deleting shared links and shared messages. * ✨ feat: Add useGetSharedMessages hook to fetch shared messages by shareId Adds a new hook `useGetSharedMessages` which fetches shared messages based on the provided shareId. * ✨ feat: Add share schema and data access functions to API models * ✨ feat: Add share endpoint to API The GET /api/share/${shareId} is exposed to the public, so authentication is not required. Other paths require authentication. * ♻️ refactor(utils): generalize react-query cache manipulation functions Introduces generic functions for manipulating react-query cache entries, marking a refinement in how query cache data is managed. It aims to enhance the flexibility and reusability of the cache interaction patterns within our application. - Replaced specific index names with more generic terms in queries.ts, enhancing consistency across data handling functions. - Introduced new utility functions in collection.ts for adding, updating, and deleting data entries in an InfiniteData. These utility functions (`addData`, `updateData`, `deleteData`, `findPage`) are designed to be re-usable across different data types and collections. - Adapted existing conversation utility functions in convos.ts to leverage these new generic utilities. * ✨ feat(shared-link): add functions to manipulate shared link cache list implemented new utility functions to handle additions, updates, and deletions in the shared link cache list. * ✨ feat: Add mutations and queries for shared links * ✨ feat(shared-link): add `Share` button to conversation list - Added a share button in each conversation in the conversation list. - Implemented functionality where clicking the share button triggers a POST request to the API. - The API checks if a share link was already created for the conversation today; if so, it returns the existing link. - If no link was created for today, the API will create a new share link and return it. - Each click on the share button results in a new API request, following the specification similar to ChatGPT's share link feature. * ♻️ refactor(hooks): generalize useNavScrolling for broader use - Modified `useNavScrolling` to accept a generic type parameter `TData`, allowing it to be used with different data structures besides `ConversationListResponse`. - Updated instances in `Nav.tsx` and `ArchivedChatsTable.tsx` to explicitly specify `ConversationListResponse` as the type argument when invoking `useNavScrolling`. * ✨ feat(settings): add shared links listing table with delete functionality in settings - Integrated a delete button for each shared link in the table, allowing users to remove links as needed. * ♻️ refactor(components): separate `EndpointIcon` from `Icon` component for standalone use * ♻️ refactor: update useGetSharedMessages to return TSharedLink - Modified the useGetSharedMessages hook to return not only a list of TMessage but also the TSharedLink itself. - This change was necessary to support displaying the title and date in the Shared Message UI, which requires data from TSharedLink. * ✨ feat(shared link): add UI for displaying shared conversations without authentication - Implemented a new UI component to display shared conversations, designed to be accessible without requiring authentication. - Reused components from the authenticated Messages module where possible. Copied and adapted components that could not be directly reused to fit the non-authenticated context. * 🔧 chore: Add translations Translate labels only. Messages remain in English as they are possibly subject to change. * ♻️ refactor: add icon and tooltip props to EditMenuButton component * moved icon and popover to arguments so that EditMenuButton can be reused. * modified so that when a ShareButton is closed, the parent DropdownMenu is also closed. * ♻️irefactor: added DropdownMenu for Export and Share * ♻️ refactor: renamed component names more intuitive * More accurate naming of the dropdown menu. * When the export button is closed, the parent dropdown menu is also closed. * 🌍 chore: updated translations * 🐞 Fix: OpenID Profile Image Download (#2757) * Add fetch requirement Fixes - error: [openidStrategy] downloadImage: Error downloading image at URL "https://graph.microsoft.com/v1.0/me/photo/$value": TypeError: response.buffer is not a function * Update openidStrategy.js --------- Co-authored-by: Danny Avila * 🚑 fix(export): Issue exporting Conversation with Assistants (#2769) * 🚑 fix(export): use content as text if content is present in the message If the endpoint is assistants, the text of the message goes into content, not message.text. * refactor(ExportModel): TypeScript, remove unused code --------- Co-authored-by: Yuichi Ohneda * 📤style: export button icon (#2752) * refactor(ShareDialog): logic and styling * refactor(ExportAndShareMenu): imports order and icon update * chore: imports * chore: imports/render logic * feat: message branching * refactor: add optional config to useGetStartupConfig * refactor: disable endpoints query * chore: fix search view styling gradient in light mode * style: ShareView gradient styling * refactor(Share): use select queries * style: shared link table buttons * localization and dark text styling * style: fix clipboard button layout shift app-wide and add localization for copy code * support assistants message content in shared links, add useCopyToClipboard, add copy buttons to Search Messages and Shared Link Messages * add localizations * comparisons --------- Co-authored-by: Yuichi Ohneda Co-authored-by: bsu3338 Co-authored-by: Fuegovic <32828263+fuegovic@users.noreply.github.com> --- api/models/Share.js | 89 ++ api/models/schema/shareSchema.js | 38 + api/server/index.js | 1 + api/server/routes/index.js | 2 + api/server/routes/share.js | 75 ++ client/src/Providers/ShareContext.tsx | 5 + client/src/Providers/index.ts | 1 + .../components/Chat/ExportAndShareMenu.tsx | 63 ++ client/src/components/Chat/ExportButton.tsx | 65 +- client/src/components/Chat/Footer.tsx | 9 +- client/src/components/Chat/Header.tsx | 3 +- .../components/Chat/Messages/HoverButtons.tsx | 2 +- .../Chat/Messages/MinimalHoverButtons.tsx | 29 + .../Chat/Messages/SearchMessage.tsx | 2 + client/src/components/Conversations/Convo.tsx | 17 +- .../{EditMenuButton.tsx => DropDownMenu.tsx} | 29 +- .../components/Conversations/HoverToggle.tsx | 4 +- .../components/Conversations/ShareButton.tsx | 113 +++ .../components/Conversations/ShareDialog.tsx | 80 ++ .../Conversations/SharedLinkButton.tsx | 115 +++ .../components/Conversations/ToggleContext.ts | 1 + client/src/components/Endpoints/Icon.tsx | 164 +-- .../Endpoints/MessageEndpointIcon.tsx | 166 +++ .../components/Messages/Content/CodeBlock.tsx | 8 +- .../components/Messages/Content/Plugin.tsx | 3 + client/src/components/Nav/Nav.tsx | 3 +- .../components/Nav/SettingsTabs/Data/Data.tsx | 4 + .../Nav/SettingsTabs/Data/SharedLinkTable.tsx | 178 ++++ .../Nav/SettingsTabs/Data/SharedLinks.tsx | 29 + .../General/ArchivedChatsTable.tsx | 3 +- client/src/components/Share/Message.tsx | 100 ++ client/src/components/Share/MessageIcon.tsx | 71 ++ client/src/components/Share/MessagesView.tsx | 47 + client/src/components/Share/MultiMessage.tsx | 54 + client/src/components/Share/ShareView.tsx | 60 ++ client/src/data-provider/mutations.ts | 92 ++ client/src/data-provider/queries.ts | 35 +- client/src/hooks/Messages/index.ts | 1 + .../src/hooks/Messages/useCopyToClipboard.ts | 32 + .../src/hooks/Messages/useMessageHelpers.tsx | 25 +- client/src/hooks/Nav/useNavScrolling.ts | 4 +- client/src/localization/languages/Ar.ts | 108 ++ client/src/localization/languages/Br.ts | 78 ++ client/src/localization/languages/De.ts | 108 ++ client/src/localization/languages/Eng.ts | 23 + client/src/localization/languages/Es.ts | 108 ++ client/src/localization/languages/Fr.ts | 108 ++ client/src/localization/languages/He.ts | 78 ++ client/src/localization/languages/Id.ts | 78 ++ client/src/localization/languages/It.ts | 108 ++ client/src/localization/languages/Jp.ts | 109 ++ client/src/localization/languages/Ko.ts | 108 ++ client/src/localization/languages/Nl.ts | 78 ++ client/src/localization/languages/Pl.ts | 73 ++ client/src/localization/languages/Ru.ts | 108 ++ client/src/localization/languages/Sv.ts | 78 ++ client/src/localization/languages/Tr.ts | 78 ++ client/src/localization/languages/Vi.ts | 78 ++ client/src/localization/languages/Zh.ts | 123 +++ .../localization/languages/ZhTraditional.ts | 108 ++ client/src/routes/Search.tsx | 4 +- client/src/routes/ShareRoute.tsx | 5 + client/src/routes/index.tsx | 5 + client/src/utils/collection.ts | 70 ++ client/src/utils/convos.spec.ts | 8 +- client/src/utils/convos.ts | 85 +- client/src/utils/index.ts | 1 + client/src/utils/sharedLink.fakeData.ts | 955 ++++++++++++++++++ client/src/utils/sharedLink.spec.ts | 149 +++ client/src/utils/sharedLink.ts | 30 + packages/data-provider/src/api-endpoints.ts | 7 + packages/data-provider/src/data-service.ts | 28 + packages/data-provider/src/keys.ts | 2 + .../src/react-query/react-query-service.ts | 21 +- packages/data-provider/src/schemas.ts | 13 + packages/data-provider/src/types.ts | 22 +- packages/data-provider/src/types/mutations.ts | 10 + packages/data-provider/src/types/queries.ts | 25 +- 78 files changed, 4683 insertions(+), 317 deletions(-) create mode 100644 api/models/Share.js create mode 100644 api/models/schema/shareSchema.js create mode 100644 api/server/routes/share.js create mode 100644 client/src/Providers/ShareContext.tsx create mode 100644 client/src/components/Chat/ExportAndShareMenu.tsx create mode 100644 client/src/components/Chat/Messages/MinimalHoverButtons.tsx rename client/src/components/Conversations/{EditMenuButton.tsx => DropDownMenu.tsx} (71%) create mode 100644 client/src/components/Conversations/ShareButton.tsx create mode 100644 client/src/components/Conversations/ShareDialog.tsx create mode 100644 client/src/components/Conversations/SharedLinkButton.tsx create mode 100644 client/src/components/Endpoints/MessageEndpointIcon.tsx create mode 100644 client/src/components/Nav/SettingsTabs/Data/SharedLinkTable.tsx create mode 100644 client/src/components/Nav/SettingsTabs/Data/SharedLinks.tsx create mode 100644 client/src/components/Share/Message.tsx create mode 100644 client/src/components/Share/MessageIcon.tsx create mode 100644 client/src/components/Share/MessagesView.tsx create mode 100644 client/src/components/Share/MultiMessage.tsx create mode 100644 client/src/components/Share/ShareView.tsx create mode 100644 client/src/hooks/Messages/useCopyToClipboard.ts create mode 100644 client/src/routes/ShareRoute.tsx create mode 100644 client/src/utils/collection.ts create mode 100644 client/src/utils/sharedLink.fakeData.ts create mode 100644 client/src/utils/sharedLink.spec.ts create mode 100644 client/src/utils/sharedLink.ts diff --git a/api/models/Share.js b/api/models/Share.js new file mode 100644 index 00000000000..ea257eb0217 --- /dev/null +++ b/api/models/Share.js @@ -0,0 +1,89 @@ +const crypto = require('crypto'); +const { getMessages } = require('./Message'); +const SharedLink = require('./schema/shareSchema'); +const logger = require('~/config/winston'); + +module.exports = { + SharedLink, + getSharedMessages: async (shareId) => { + try { + const share = await SharedLink.findOne({ shareId }) + .populate({ + path: 'messages', + select: '-_id -__v -user', + }) + .select('-_id -__v -user') + .lean(); + + if (!share || !share.conversationId || !share.isPublic) { + return null; + } + + return share; + } catch (error) { + logger.error('[getShare] Error getting share link', error); + return { message: 'Error getting share link' }; + } + }, + + getSharedLinks: async (user, pageNumber = 1, pageSize = 25, isPublic = true) => { + const query = { user, isPublic }; + try { + const totalConvos = (await SharedLink.countDocuments(query)) || 1; + const totalPages = Math.ceil(totalConvos / pageSize); + const shares = await SharedLink.find(query) + .sort({ updatedAt: -1 }) + .skip((pageNumber - 1) * pageSize) + .limit(pageSize) + .select('-_id -__v -user') + .lean(); + + return { sharedLinks: shares, pages: totalPages, pageNumber, pageSize }; + } catch (error) { + logger.error('[getShareByPage] Error getting shares', error); + return { message: 'Error getting shares' }; + } + }, + + createSharedLink: async (user, { conversationId, ...shareData }) => { + const share = await SharedLink.findOne({ conversationId }).select('-_id -__v -user').lean(); + if (share) { + return share; + } + + try { + const shareId = crypto.randomUUID(); + const messages = await getMessages({ conversationId }); + const update = { ...shareData, shareId, messages, user }; + return await SharedLink.findOneAndUpdate({ conversationId: conversationId, user }, update, { + new: true, + upsert: true, + }); + } catch (error) { + logger.error('[saveShareMessage] Error saving conversation', error); + return { message: 'Error saving conversation' }; + } + }, + + updateSharedLink: async (user, { conversationId, ...shareData }) => { + const share = await SharedLink.findOne({ conversationId }).select('-_id -__v -user').lean(); + if (!share) { + return { message: 'Share not found' }; + } + // update messages to the latest + const messages = await getMessages({ conversationId }); + const update = { ...shareData, messages, user }; + return await SharedLink.findOneAndUpdate({ conversationId: conversationId, user }, update, { + new: true, + upsert: false, + }); + }, + + deleteSharedLink: async (user, { shareId }) => { + const share = await SharedLink.findOne({ shareId, user }); + if (!share) { + return { message: 'Share not found' }; + } + return await SharedLink.findOneAndDelete({ shareId, user }); + }, +}; diff --git a/api/models/schema/shareSchema.js b/api/models/schema/shareSchema.js new file mode 100644 index 00000000000..56ecec00c0d --- /dev/null +++ b/api/models/schema/shareSchema.js @@ -0,0 +1,38 @@ +const mongoose = require('mongoose'); + +const shareSchema = mongoose.Schema( + { + conversationId: { + type: String, + required: true, + }, + title: { + type: String, + index: true, + }, + user: { + type: String, + index: true, + }, + messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }], + shareId: { + type: String, + index: true, + }, + isPublic: { + type: Boolean, + default: false, + }, + isVisible: { + type: Boolean, + default: false, + }, + isAnonymous: { + type: Boolean, + default: true, + }, + }, + { timestamps: true }, +); + +module.exports = mongoose.model('SharedLink', shareSchema); diff --git a/api/server/index.js b/api/server/index.js index 4e85c508010..f47af6725ac 100644 --- a/api/server/index.js +++ b/api/server/index.js @@ -85,6 +85,7 @@ const startServer = async () => { app.use('/api/assistants', routes.assistants); app.use('/api/files', await routes.files.initialize()); app.use('/images/', validateImageRequest, routes.staticRoute); + app.use('/api/share', routes.share); app.use((req, res) => { res.status(404).sendFile(path.join(app.locals.paths.dist, 'index.html')); diff --git a/api/server/routes/index.js b/api/server/routes/index.js index 8b1ffd8fe8c..958cf0aed52 100644 --- a/api/server/routes/index.js +++ b/api/server/routes/index.js @@ -18,6 +18,7 @@ const config = require('./config'); const assistants = require('./assistants'); const files = require('./files'); const staticRoute = require('./static'); +const share = require('./share'); module.exports = { search, @@ -40,4 +41,5 @@ module.exports = { assistants, files, staticRoute, + share, }; diff --git a/api/server/routes/share.js b/api/server/routes/share.js new file mode 100644 index 00000000000..7fbdae23f1d --- /dev/null +++ b/api/server/routes/share.js @@ -0,0 +1,75 @@ +const express = require('express'); + +const { + getSharedMessages, + createSharedLink, + updateSharedLink, + getSharedLinks, + deleteSharedLink, +} = require('~/models/Share'); +const requireJwtAuth = require('~/server/middleware/requireJwtAuth'); +const router = express.Router(); + +/** + * Shared messages + * this route does not require authentication + */ +router.get('/:shareId', async (req, res) => { + const share = await getSharedMessages(req.params.shareId); + + if (share) { + res.status(200).json(share); + } else { + res.status(404).end(); + } +}); + +/** + * Shared links + */ +router.get('/', requireJwtAuth, async (req, res) => { + let pageNumber = req.query.pageNumber || 1; + pageNumber = parseInt(pageNumber, 10); + + if (isNaN(pageNumber) || pageNumber < 1) { + return res.status(400).json({ error: 'Invalid page number' }); + } + + let pageSize = req.query.pageSize || 25; + pageSize = parseInt(pageSize, 10); + + if (isNaN(pageSize) || pageSize < 1) { + return res.status(400).json({ error: 'Invalid page size' }); + } + const isPublic = req.query.isPublic === 'true'; + res.status(200).send(await getSharedLinks(req.user.id, pageNumber, pageSize, isPublic)); +}); + +router.post('/', requireJwtAuth, async (req, res) => { + const created = await createSharedLink(req.user.id, req.body); + if (created) { + res.status(200).json(created); + } else { + res.status(404).end(); + } +}); + +router.patch('/', requireJwtAuth, async (req, res) => { + const updated = await updateSharedLink(req.user.id, req.body); + if (updated) { + res.status(200).json(updated); + } else { + res.status(404).end(); + } +}); + +router.delete('/:shareId', requireJwtAuth, async (req, res) => { + const deleted = await deleteSharedLink(req.user.id, { shareId: req.params.shareId }); + if (deleted) { + res.status(200).json(deleted); + } else { + res.status(404).end(); + } +}); + +module.exports = router; diff --git a/client/src/Providers/ShareContext.tsx b/client/src/Providers/ShareContext.tsx new file mode 100644 index 00000000000..fc5a1db00aa --- /dev/null +++ b/client/src/Providers/ShareContext.tsx @@ -0,0 +1,5 @@ +import { createContext, useContext } from 'react'; +type TShareContext = { isSharedConvo?: boolean }; + +export const ShareContext = createContext({} as TShareContext); +export const useShareContext = () => useContext(ShareContext); diff --git a/client/src/Providers/index.ts b/client/src/Providers/index.ts index debfdeac046..4085113e1a8 100644 --- a/client/src/Providers/index.ts +++ b/client/src/Providers/index.ts @@ -1,6 +1,7 @@ export { default as ToastProvider } from './ToastContext'; export { default as AssistantsProvider } from './AssistantsContext'; export * from './ChatContext'; +export * from './ShareContext'; export * from './ToastContext'; export * from './SearchContext'; export * from './FileMapContext'; diff --git a/client/src/components/Chat/ExportAndShareMenu.tsx b/client/src/components/Chat/ExportAndShareMenu.tsx new file mode 100644 index 00000000000..f16dd44d25e --- /dev/null +++ b/client/src/components/Chat/ExportAndShareMenu.tsx @@ -0,0 +1,63 @@ +import { useState } from 'react'; +import { Upload } from 'lucide-react'; +import { useRecoilValue } from 'recoil'; +import { useLocation } from 'react-router-dom'; +import type { TConversation } from 'librechat-data-provider'; +import DropDownMenu from '../Conversations/DropDownMenu'; +import ShareButton from '../Conversations/ShareButton'; +import HoverToggle from '../Conversations/HoverToggle'; +import ExportButton from './ExportButton'; +import store from '~/store'; + +export default function ExportAndShareMenu() { + const location = useLocation(); + + const activeConvo = useRecoilValue(store.conversationByIndex(0)); + const globalConvo = useRecoilValue(store.conversation) ?? ({} as TConversation); + const [isPopoverActive, setIsPopoverActive] = useState(false); + let conversation: TConversation | null | undefined; + if (location.state?.from?.pathname.includes('/chat')) { + conversation = globalConvo; + } else { + conversation = activeConvo; + } + + const exportable = + conversation && + conversation.conversationId && + conversation.conversationId !== 'new' && + conversation.conversationId !== 'search'; + + if (!exportable) { + return <>; + } + + const isActiveConvo = exportable; + + return ( + + } + tooltip="Export/Share" + className="pointer-cursor relative z-50 flex h-[40px] min-w-4 flex-none flex-col items-center justify-center rounded-md border border-gray-100 bg-white px-3 text-left hover:bg-gray-50 focus:outline-none focus:ring-0 focus:ring-offset-0 radix-state-open:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:hover:bg-gray-700 dark:radix-state-open:bg-gray-700 sm:text-sm" + > + {conversation && conversation.conversationId && ( + <> + + + + )} + + + ); +} diff --git a/client/src/components/Chat/ExportButton.tsx b/client/src/components/Chat/ExportButton.tsx index 1c06b6960e5..4f5cca388df 100644 --- a/client/src/components/Chat/ExportButton.tsx +++ b/client/src/components/Chat/ExportButton.tsx @@ -1,68 +1,41 @@ import React from 'react'; import { useState } from 'react'; -import { useLocation } from 'react-router-dom'; import type { TConversation } from 'librechat-data-provider'; import { Upload } from 'lucide-react'; -import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '~/components/ui'; import { useLocalize } from '~/hooks'; import { ExportModal } from '../Nav'; -import { useRecoilValue } from 'recoil'; -import store from '~/store'; -function ExportButton() { +function ExportButton({ + conversation, + setPopoverActive, +}: { + conversation: TConversation; + setPopoverActive: (value: boolean) => void; +}) { const localize = useLocalize(); - const location = useLocation(); const [showExports, setShowExports] = useState(false); - const activeConvo = useRecoilValue(store.conversationByIndex(0)); - const globalConvo = useRecoilValue(store.conversation) ?? ({} as TConversation); - - let conversation: TConversation | null | undefined; - if (location.state?.from?.pathname.includes('/chat')) { - conversation = globalConvo; - } else { - conversation = activeConvo; - } - const clickHandler = () => { - if (exportable) { - setShowExports(true); - } + setShowExports(true); }; - const exportable = - conversation && - conversation.conversationId && - conversation.conversationId !== 'new' && - conversation.conversationId !== 'search'; + const onOpenChange = (value: boolean) => { + setShowExports(value); + setPopoverActive(value); + }; return ( <> - {exportable && ( -
- - - - - - - {localize('com_nav_export_conversation')} - - - -
- )} + {showExports && ( - + )} ); diff --git a/client/src/components/Chat/Footer.tsx b/client/src/components/Chat/Footer.tsx index f5ee1b1675b..cca7d9a7cf5 100644 --- a/client/src/components/Chat/Footer.tsx +++ b/client/src/components/Chat/Footer.tsx @@ -3,7 +3,7 @@ import { Constants } from 'librechat-data-provider'; import { useGetStartupConfig } from 'librechat-data-provider/react-query'; import { useLocalize } from '~/hooks'; -export default function Footer() { +export default function Footer({ className }: { className?: string }) { const { data: config } = useGetStartupConfig(); const localize = useLocalize(); @@ -52,7 +52,12 @@ export default function Footer() { ); return ( -
+
{footerElements.map((contentRender, index) => { const isLastElement = index === footerElements.length - 1; return ( diff --git a/client/src/components/Chat/Header.tsx b/client/src/components/Chat/Header.tsx index ab40ace0b04..0a0dcb166dc 100644 --- a/client/src/components/Chat/Header.tsx +++ b/client/src/components/Chat/Header.tsx @@ -6,6 +6,7 @@ import type { ContextType } from '~/common'; import { EndpointsMenu, ModelSpecsMenu, PresetsMenu, HeaderNewChat } from './Menus'; import HeaderOptions from './Input/HeaderOptions'; import ExportButton from './ExportButton'; +import ExportAndShareMenu from './ExportAndShareMenu'; const defaultInterface = getConfigDefaults().interface; @@ -28,7 +29,7 @@ export default function Header() { {} {interfaceConfig.presets && }
- +
{/* Empty div for spacing */}
diff --git a/client/src/components/Chat/Messages/HoverButtons.tsx b/client/src/components/Chat/Messages/HoverButtons.tsx index 18ec845e843..7a593202b42 100644 --- a/client/src/components/Chat/Messages/HoverButtons.tsx +++ b/client/src/components/Chat/Messages/HoverButtons.tsx @@ -87,7 +87,7 @@ export default function HoverButtons({ isCopied ? localize('com_ui_copied_to_clipboard') : localize('com_ui_copy_to_clipboard') } > - {isCopied ? : } + {isCopied ? : } {regenerateEnabled ? ( +
+ ); +} diff --git a/client/src/components/Chat/Messages/SearchMessage.tsx b/client/src/components/Chat/Messages/SearchMessage.tsx index 46829dad97a..53293359a49 100644 --- a/client/src/components/Chat/Messages/SearchMessage.tsx +++ b/client/src/components/Chat/Messages/SearchMessage.tsx @@ -1,6 +1,7 @@ import { useRecoilValue } from 'recoil'; import { useAuthContext, useLocalize } from '~/hooks'; import type { TMessageProps } from '~/common'; +import MinimalHoverButtons from '~/components/Chat/Messages/MinimalHoverButtons'; import Icon from '~/components/Chat/Messages/MessageIcon'; import SearchContent from './Content/SearchContent'; import SearchButtons from './SearchButtons'; @@ -50,6 +51,7 @@ export default function Message({ message }: Pick) { + diff --git a/client/src/components/Conversations/Convo.tsx b/client/src/components/Conversations/Convo.tsx index 34c006d6c47..efb1cb95c78 100644 --- a/client/src/components/Conversations/Convo.tsx +++ b/client/src/components/Conversations/Convo.tsx @@ -9,13 +9,14 @@ import { useConversations, useNavigateToConvo } from '~/hooks'; import { NotificationSeverity } from '~/common'; import { ArchiveIcon } from '~/components/svg'; import { useToastContext } from '~/Providers'; -import EditMenuButton from './EditMenuButton'; +import DropDownMenu from './DropDownMenu'; import ArchiveButton from './ArchiveButton'; import DeleteButton from './DeleteButton'; import RenameButton from './RenameButton'; import HoverToggle from './HoverToggle'; import { cn } from '~/utils'; import store from '~/store'; +import ShareButton from './ShareButton'; type KeyEvent = KeyboardEvent; @@ -124,7 +125,15 @@ export default function Conversation({ conversation, retainView, toggleNav, isLa isPopoverActive={isPopoverActive} setIsPopoverActive={setIsPopoverActive} > - + + + - + = ({ children }: EditMenuButtonProps) => { +const DropDownMenu: FC = ({ + children, + icon = , + tooltip = 'More', + className, +}: DropDownMenuProps) => { const localize = useLocalize(); - const { setPopoverActive } = useToggle(); + const { isPopoverActive, setPopoverActive } = useToggle(); return ( - setPopoverActive(open)}> + setPopoverActive(open)}>
= ({ children }: EditMenuButtonPro - - {localize('com_ui_more_options')} + {tooltip} @@ -57,4 +68,4 @@ const EditMenuButton: FC = ({ children }: EditMenuButtonPro ); }; -export default EditMenuButton; +export default DropDownMenu; diff --git a/client/src/components/Conversations/HoverToggle.tsx b/client/src/components/Conversations/HoverToggle.tsx index fab8bb2a262..a63b4b5a413 100644 --- a/client/src/components/Conversations/HoverToggle.tsx +++ b/client/src/components/Conversations/HoverToggle.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import { ToggleContext } from './ToggleContext'; import { cn } from '~/utils'; @@ -15,7 +15,7 @@ const HoverToggle = ({ }) => { const setPopoverActive = (value: boolean) => setIsPopoverActive(value); return ( - +
void; +}) { + const localize = useLocalize(); + const [share, setShare] = useState(null); + const [open, setOpen] = useState(false); + const [isUpdated, setIsUpdated] = useState(false); + + const classProp: { className?: string } = { + className: 'p-1 hover:text-black dark:hover:text-white', + }; + if (className) { + classProp.className = className; + } + const renderShareButton = () => { + if (appendLabel) { + return ( + <> + {localize('com_ui_share')} + + ); + } + return ( + + + + + + + + + {localize('com_ui_share')} + + + + ); + }; + + const buttons = share && ( + + ); + + const onOpenChange = (open: boolean) => { + setPopoverActive(open); + setOpen(open); + }; + return ( + + + + + + + + } + /> + + ); +} diff --git a/client/src/components/Conversations/ShareDialog.tsx b/client/src/components/Conversations/ShareDialog.tsx new file mode 100644 index 00000000000..094e53c322b --- /dev/null +++ b/client/src/components/Conversations/ShareDialog.tsx @@ -0,0 +1,80 @@ +import { useLocalize } from '~/hooks'; + +import { useCreateSharedLinkMutation } from '~/data-provider'; +import { useEffect, useState } from 'react'; +import { TSharedLink } from 'librechat-data-provider'; +import { useToastContext } from '~/Providers'; +import { NotificationSeverity } from '~/common'; +import { Spinner } from '~/components/svg'; + +export default function ShareDialog({ + conversationId, + title, + share, + setShare, + setDialogOpen, + isUpdated, +}: { + conversationId: string; + title: string; + share: TSharedLink | null; + setShare: (share: TSharedLink | null) => void; + setDialogOpen: (open: boolean) => void; + isUpdated: boolean; +}) { + const localize = useLocalize(); + const { showToast } = useToastContext(); + const { mutate, isLoading } = useCreateSharedLinkMutation(); + const [isNewSharedLink, setIsNewSharedLink] = useState(false); + + useEffect(() => { + if (isLoading || share) { + return; + } + const data = { + conversationId, + title, + isAnonymous: true, + }; + + mutate(data, { + onSuccess: (result) => { + setShare(result); + setIsNewSharedLink(!result.isPublic); + }, + onError: () => { + showToast({ + message: localize('com_ui_share_error'), + severity: NotificationSeverity.ERROR, + showIcon: true, + }); + setDialogOpen(false); + }, + }); + + // mutation.mutate should only be called once + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+
+ {(() => { + if (isLoading) { + return ; + } + + if (isUpdated) { + return isNewSharedLink + ? localize('com_ui_share_created_message') + : localize('com_ui_share_updated_message'); + } + + return share?.isPublic + ? localize('com_ui_share_update_message') + : localize('com_ui_share_create_message'); + })()} +
+
+ ); +} diff --git a/client/src/components/Conversations/SharedLinkButton.tsx b/client/src/components/Conversations/SharedLinkButton.tsx new file mode 100644 index 00000000000..4482ab56919 --- /dev/null +++ b/client/src/components/Conversations/SharedLinkButton.tsx @@ -0,0 +1,115 @@ +import { useState } from 'react'; +import copy from 'copy-to-clipboard'; +import { Copy, Link } from 'lucide-react'; +import { useUpdateSharedLinkMutation } from '~/data-provider'; +import type { TSharedLink } from 'librechat-data-provider'; +import { Spinner } from '~/components/svg'; +import { Button } from '~/components/ui'; +import { useLocalize } from '~/hooks'; + +export default function SharedLinkButton({ + conversationId, + share, + setShare, + isUpdated, + setIsUpdated, +}: { + conversationId: string; + share: TSharedLink; + setShare: (share: TSharedLink) => void; + isUpdated: boolean; + setIsUpdated: (isUpdated: boolean) => void; +}) { + const localize = useLocalize(); + const [isCopying, setIsCopying] = useState(false); + const { mutateAsync, isLoading } = useUpdateSharedLinkMutation(); + + const copyLink = () => { + if (!share) { + return; + } + setIsCopying(true); + const sharedLink = + window.location.protocol + '//' + window.location.host + '/share/' + share.shareId; + copy(sharedLink); + setTimeout(() => { + setIsCopying(false); + }, 1500); + }; + const updateSharedLink = async () => { + if (!share) { + return; + } + const result = await mutateAsync({ + shareId: share.shareId, + conversationId: conversationId, + isPublic: true, + isVisible: true, + isAnonymous: true, + }); + + if (result) { + setShare(result); + setIsUpdated(true); + copyLink(); + } + }; + const getHandler = () => { + if (isUpdated) { + return { + handler: () => { + copyLink(); + }, + label: ( + <> + + {localize('com_ui_copy_link')} + + ), + }; + } + if (share?.isPublic) { + return { + handler: async () => { + await updateSharedLink(); + }, + + label: ( + <> + + {localize('com_ui_update_link')} + + ), + }; + } + return { + handler: updateSharedLink, + label: ( + <> + + {localize('com_ui_create_link')} + + ), + }; + }; + + const handlers = getHandler(); + return ( + + ); +} diff --git a/client/src/components/Conversations/ToggleContext.ts b/client/src/components/Conversations/ToggleContext.ts index 75276dfabdd..af85b85149c 100644 --- a/client/src/components/Conversations/ToggleContext.ts +++ b/client/src/components/Conversations/ToggleContext.ts @@ -3,6 +3,7 @@ import { createContext, useContext } from 'react'; const defaultFunction: (value: boolean) => void = () => ({}); export const ToggleContext = createContext({ setPopoverActive: defaultFunction, + isPopoverActive: false, }); export const useToggle = () => useContext(ToggleContext); diff --git a/client/src/components/Endpoints/Icon.tsx b/client/src/components/Endpoints/Icon.tsx index eac5d60ceac..1f5bd17d7f6 100644 --- a/client/src/components/Endpoints/Icon.tsx +++ b/client/src/components/Endpoints/Icon.tsx @@ -1,36 +1,14 @@ -import { EModelEndpoint } from 'librechat-data-provider'; -import UnknownIcon from '~/components/Chat/Menus/Endpoints/UnknownIcon'; -import { - Plugin, - GPTIcon, - UserIcon, - PaLMIcon, - CodeyIcon, - GeminiIcon, - AssistantIcon, - AnthropicIcon, - AzureMinimalIcon, - CustomMinimalIcon, -} from '~/components/svg'; +import { UserIcon } from '~/components/svg'; import { useAuthContext } from '~/hooks/AuthContext'; import useAvatar from '~/hooks/Messages/useAvatar'; import useLocalize from '~/hooks/useLocalize'; import { IconProps } from '~/common'; import { cn } from '~/utils'; +import MessageEndpointIcon from './MessageEndpointIcon'; const Icon: React.FC = (props) => { const { user } = useAuthContext(); - const { - error, - button, - iconURL, - endpoint, - jailbreak, - size = 30, - model = '', - assistantName, - isCreatedByUser, - } = props; + const { size = 30, isCreatedByUser } = props; const avatarSrc = useAvatar(user); const localize = useLocalize(); @@ -65,141 +43,7 @@ const Icon: React.FC = (props) => {
); } - - const endpointIcons = { - [EModelEndpoint.assistants]: { - icon: props.iconURL ? ( -
-
- {assistantName} -
-
- ) : ( -
-
- -
-
- ), - name: endpoint, - }, - [EModelEndpoint.azureOpenAI]: { - icon: , - bg: 'linear-gradient(0.375turn, #61bde2, #4389d0)', - name: 'ChatGPT', - }, - [EModelEndpoint.openAI]: { - icon: , - bg: - typeof model === 'string' && model.toLowerCase().includes('gpt-4') ? '#AB68FF' : '#19C37D', - name: 'ChatGPT', - }, - [EModelEndpoint.gptPlugins]: { - icon: , - bg: `rgba(69, 89, 164, ${button ? 0.75 : 1})`, - name: 'Plugins', - }, - [EModelEndpoint.google]: { - icon: model?.toLowerCase()?.includes('code') ? ( - - ) : model?.toLowerCase()?.includes('gemini') ? ( - - ) : ( - - ), - name: model?.toLowerCase()?.includes('code') - ? 'Codey' - : model?.toLowerCase()?.includes('gemini') - ? 'Gemini' - : 'PaLM2', - }, - [EModelEndpoint.anthropic]: { - icon: , - bg: '#d09a74', - name: 'Claude', - }, - [EModelEndpoint.bingAI]: { - icon: jailbreak ? ( - Bing Icon - ) : ( - Sydney Icon - ), - name: jailbreak ? 'Sydney' : 'BingAI', - }, - [EModelEndpoint.chatGPTBrowser]: { - icon: , - bg: - typeof model === 'string' && model.toLowerCase().includes('gpt-4') - ? '#AB68FF' - : `rgba(0, 163, 255, ${button ? 0.75 : 1})`, - name: 'ChatGPT', - }, - [EModelEndpoint.custom]: { - icon: , - name: 'Custom', - }, - null: { icon: , bg: 'grey', name: 'N/A' }, - default: { - icon: ( -
-
- -
-
- ), - name: endpoint, - }, - }; - - let { icon, bg, name } = - endpoint && endpointIcons[endpoint] ? endpointIcons[endpoint] : endpointIcons.default; - - if (iconURL && endpointIcons[iconURL]) { - ({ icon, bg, name } = endpointIcons[iconURL]); - } - - if (endpoint === EModelEndpoint.assistants) { - return icon; - } - - return ( -
- {icon} - {error && ( - - ! - - )} -
- ); + return ; }; export default Icon; diff --git a/client/src/components/Endpoints/MessageEndpointIcon.tsx b/client/src/components/Endpoints/MessageEndpointIcon.tsx new file mode 100644 index 00000000000..43ea9e2e6a4 --- /dev/null +++ b/client/src/components/Endpoints/MessageEndpointIcon.tsx @@ -0,0 +1,166 @@ +import { EModelEndpoint } from 'librechat-data-provider'; +import UnknownIcon from '~/components/Chat/Menus/Endpoints/UnknownIcon'; +import { + Plugin, + GPTIcon, + PaLMIcon, + CodeyIcon, + GeminiIcon, + AssistantIcon, + AnthropicIcon, + AzureMinimalIcon, + CustomMinimalIcon, +} from '~/components/svg'; + +import { IconProps } from '~/common'; +import { cn } from '~/utils'; + +const MessageEndpointIcon: React.FC = (props) => { + const { + error, + button, + iconURL, + endpoint, + jailbreak, + size = 30, + model = '', + assistantName, + } = props; + + const endpointIcons = { + [EModelEndpoint.assistants]: { + icon: props.iconURL ? ( +
+
+ {assistantName} +
+
+ ) : ( +
+
+ +
+
+ ), + name: endpoint, + }, + [EModelEndpoint.azureOpenAI]: { + icon: , + bg: 'linear-gradient(0.375turn, #61bde2, #4389d0)', + name: 'ChatGPT', + }, + [EModelEndpoint.openAI]: { + icon: , + bg: + typeof model === 'string' && model.toLowerCase().includes('gpt-4') ? '#AB68FF' : '#19C37D', + name: 'ChatGPT', + }, + [EModelEndpoint.gptPlugins]: { + icon: , + bg: `rgba(69, 89, 164, ${button ? 0.75 : 1})`, + name: 'Plugins', + }, + [EModelEndpoint.google]: { + icon: model?.toLowerCase()?.includes('code') ? ( + + ) : model?.toLowerCase()?.includes('gemini') ? ( + + ) : ( + + ), + name: model?.toLowerCase()?.includes('code') + ? 'Codey' + : model?.toLowerCase()?.includes('gemini') + ? 'Gemini' + : 'PaLM2', + }, + [EModelEndpoint.anthropic]: { + icon: , + bg: '#d09a74', + name: 'Claude', + }, + [EModelEndpoint.bingAI]: { + icon: jailbreak ? ( + Bing Icon + ) : ( + Sydney Icon + ), + name: jailbreak ? 'Sydney' : 'BingAI', + }, + [EModelEndpoint.chatGPTBrowser]: { + icon: , + bg: + typeof model === 'string' && model.toLowerCase().includes('gpt-4') + ? '#AB68FF' + : `rgba(0, 163, 255, ${button ? 0.75 : 1})`, + name: 'ChatGPT', + }, + [EModelEndpoint.custom]: { + icon: , + name: 'Custom', + }, + null: { icon: , bg: 'grey', name: 'N/A' }, + default: { + icon: ( +
+
+ +
+
+ ), + name: endpoint, + }, + }; + + let { icon, bg, name } = + endpoint && endpointIcons[endpoint] ? endpointIcons[endpoint] : endpointIcons.default; + + if (iconURL && endpointIcons[iconURL]) { + ({ icon, bg, name } = endpointIcons[iconURL]); + } + + if (endpoint === EModelEndpoint.assistants) { + return icon; + } + + return ( +
+ {icon} + {error && ( + + ! + + )} +
+ ); +}; + +export default MessageEndpointIcon; diff --git a/client/src/components/Messages/Content/CodeBlock.tsx b/client/src/components/Messages/Content/CodeBlock.tsx index 3b58277067c..399a2454bbb 100644 --- a/client/src/components/Messages/Content/CodeBlock.tsx +++ b/client/src/components/Messages/Content/CodeBlock.tsx @@ -3,6 +3,7 @@ import { InfoIcon } from 'lucide-react'; import React, { useRef, useState, RefObject } from 'react'; import Clipboard from '~/components/svg/Clipboard'; import CheckMark from '~/components/svg/CheckMark'; +import useLocalize from '~/hooks/useLocalize'; import cn from '~/utils/cn'; type CodeBarProps = { @@ -18,6 +19,7 @@ type CodeBlockProps = Pick & { }; const CodeBar: React.FC = React.memo(({ lang, codeRef, error, plugin = null }) => { + const localize = useLocalize(); const [isCopied, setIsCopied] = useState(false); return (
@@ -41,13 +43,13 @@ const CodeBar: React.FC = React.memo(({ lang, codeRef, error, plug > {isCopied ? ( <> - - {error ? '' : 'Copied!'} + + {error ? '' : localize('com_ui_copied')} ) : ( <> - {error ? '' : 'Copy code'} + {error ? '' : localize('com_ui_copy_code')} )} diff --git a/client/src/components/Messages/Content/Plugin.tsx b/client/src/components/Messages/Content/Plugin.tsx index 0745e45a1e5..ccf56d68d1c 100644 --- a/client/src/components/Messages/Content/Plugin.tsx +++ b/client/src/components/Messages/Content/Plugin.tsx @@ -3,6 +3,7 @@ import { useCallback, memo, ReactNode } from 'react'; import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import type { TResPlugin, TInput } from 'librechat-data-provider'; import { ChevronDownIcon, LucideProps } from 'lucide-react'; +import { useShareContext } from '~/Providers'; import { cn, formatJSON } from '~/utils'; import { Spinner } from '~/components'; import CodeBlock from './CodeBlock'; @@ -31,7 +32,9 @@ type PluginProps = { }; const Plugin: React.FC = ({ plugin }) => { + const { isSharedConvo } = useShareContext(); const { data: plugins = {} } = useGetEndpointsQuery({ + enabled: !isSharedConvo, select: (data) => data?.gptPlugins?.plugins, }); diff --git a/client/src/components/Nav/Nav.tsx b/client/src/components/Nav/Nav.tsx index e9d7c66b96e..53fc727e774 100644 --- a/client/src/components/Nav/Nav.tsx +++ b/client/src/components/Nav/Nav.tsx @@ -19,6 +19,7 @@ import NavToggle from './NavToggle'; import NavLinks from './NavLinks'; import NewChat from './NewChat'; import { cn } from '~/utils'; +import { ConversationListResponse } from 'librechat-data-provider'; import store from '~/store'; const Nav = ({ navVisible, setNavVisible }) => { @@ -59,7 +60,7 @@ const Nav = ({ navVisible, setNavVisible }) => { { enabled: isAuthenticated }, ); - const { containerRef, moveToTop } = useNavScrolling({ + const { containerRef, moveToTop } = useNavScrolling({ setShowLoading, hasNextPage: searchQuery ? searchQueryRes.hasNextPage : hasNextPage, fetchNextPage: searchQuery ? searchQueryRes.fetchNextPage : fetchNextPage, diff --git a/client/src/components/Nav/SettingsTabs/Data/Data.tsx b/client/src/components/Nav/SettingsTabs/Data/Data.tsx index 65704d8c126..6e8b8c0c185 100644 --- a/client/src/components/Nav/SettingsTabs/Data/Data.tsx +++ b/client/src/components/Nav/SettingsTabs/Data/Data.tsx @@ -10,6 +10,7 @@ import { useConversation, useConversations, useOnClickOutside } from '~/hooks'; import ImportConversations from './ImportConversations'; import { ClearChatsButton } from './ClearChats'; import DangerButton from '../DangerButton'; +import SharedLinks from './SharedLinks'; export const RevokeKeysButton = ({ showText = true, @@ -107,6 +108,9 @@ function Data() {
+
+ +
diff --git a/client/src/components/Nav/SettingsTabs/Data/SharedLinkTable.tsx b/client/src/components/Nav/SettingsTabs/Data/SharedLinkTable.tsx new file mode 100644 index 00000000000..47f33dcdf38 --- /dev/null +++ b/client/src/components/Nav/SettingsTabs/Data/SharedLinkTable.tsx @@ -0,0 +1,178 @@ +import { useAuthContext, useLocalize, useNavScrolling } from '~/hooks'; +import { MessageSquare, Link as LinkIcon } from 'lucide-react'; +import { useMemo, useState, MouseEvent } from 'react'; +import { useDeleteSharedLinkMutation, useSharedLinksInfiniteQuery } from '~/data-provider'; + +import { cn } from '~/utils'; +import { + Spinner, + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, + TrashIcon, +} from '~/components'; +import { SharedLinksResponse, TSharedLink } from 'librechat-data-provider'; +import { Link } from 'react-router-dom'; + +function SharedLinkDeleteButton({ + shareId, + setIsDeleting, +}: { + shareId: string; + setIsDeleting: (isDeleting: boolean) => void; +}) { + const localize = useLocalize(); + const mutation = useDeleteSharedLinkMutation(); + + const handleDelete = async (e: MouseEvent) => { + e.preventDefault(); + if (mutation.isLoading) { + return; + } + setIsDeleting(true); + await mutation.mutateAsync({ shareId }); + setIsDeleting(false); + }; + return ( + + + + + + + + + {localize('com_ui_delete')} + + + + ); +} +function SourceChatButton({ conversationId }: { conversationId: string }) { + const localize = useLocalize(); + + return ( + + + + + + + + + {localize('com_nav_source_chat')} + + + + ); +} + +function ShareLinkRow({ sharedLink }: { sharedLink: TSharedLink }) { + const [isDeleting, setIsDeleting] = useState(false); + + return ( + + + + + {sharedLink.title} + + + +
+
+ {new Date(sharedLink.createdAt).toLocaleDateString('en-US', { + month: 'long', + day: 'numeric', + year: 'numeric', + })} +
+
+ {sharedLink.conversationId && ( + <> + +
+ +
+ + )} +
+
+ + + ); +} +export default function ShareLinkTable({ className }: { className?: string }) { + const localize = useLocalize(); + const { isAuthenticated } = useAuthContext(); + const [showLoading, setShowLoading] = useState(false); + + const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useSharedLinksInfiniteQuery( + { pageNumber: '1', isPublic: true }, + { enabled: isAuthenticated }, + ); + + const { containerRef } = useNavScrolling({ + setShowLoading, + hasNextPage: hasNextPage, + fetchNextPage: fetchNextPage, + isFetchingNextPage: isFetchingNextPage, + }); + + const sharedLinks = useMemo(() => data?.pages.flatMap((page) => page.sharedLinks) || [], [data]); + const classProp: { className?: string } = { + className: 'p-1 hover:text-black dark:hover:text-white', + }; + if (className) { + classProp.className = className; + } + + if (!sharedLinks || sharedLinks.length === 0) { + return
{localize('com_nav_shared_links_empty')}
; + } + + return ( +
+ + + + + + + + + {sharedLinks.map((sharedLink) => ( + + ))} + +
{localize('com_nav_shared_links_name')}{localize('com_nav_shared_links_date_shared')}
+ {(isFetchingNextPage || showLoading) && ( + + )} +
+ ); +} diff --git a/client/src/components/Nav/SettingsTabs/Data/SharedLinks.tsx b/client/src/components/Nav/SettingsTabs/Data/SharedLinks.tsx new file mode 100644 index 00000000000..f09384b30cd --- /dev/null +++ b/client/src/components/Nav/SettingsTabs/Data/SharedLinks.tsx @@ -0,0 +1,29 @@ +import { useLocalize } from '~/hooks'; +import { Dialog, DialogTrigger } from '~/components/ui'; +import DialogTemplate from '~/components/ui/DialogTemplate'; + +import ShareLinkTable from './SharedLinkTable'; + +export default function SharedLinks() { + const localize = useLocalize(); + + return ( +
+
{localize('com_nav_shared_links')}
+ + + + + + } + /> + +
+ ); +} diff --git a/client/src/components/Nav/SettingsTabs/General/ArchivedChatsTable.tsx b/client/src/components/Nav/SettingsTabs/General/ArchivedChatsTable.tsx index cff93b92eb2..4425cd203ee 100644 --- a/client/src/components/Nav/SettingsTabs/General/ArchivedChatsTable.tsx +++ b/client/src/components/Nav/SettingsTabs/General/ArchivedChatsTable.tsx @@ -6,6 +6,7 @@ import ArchiveButton from '~/components/Conversations/ArchiveButton'; import DeleteButton from '~/components/Conversations/DeleteButton'; import { Spinner } from '~/components/svg'; import { cn } from '~/utils'; +import { ConversationListResponse } from 'librechat-data-provider'; export default function ArchivedChatsTable({ className }: { className?: string }) { const localize = useLocalize(); @@ -17,7 +18,7 @@ export default function ArchivedChatsTable({ className }: { className?: string } { enabled: isAuthenticated }, ); - const { containerRef, moveToTop } = useNavScrolling({ + const { containerRef, moveToTop } = useNavScrolling({ setShowLoading, hasNextPage: hasNextPage, fetchNextPage: fetchNextPage, diff --git a/client/src/components/Share/Message.tsx b/client/src/components/Share/Message.tsx new file mode 100644 index 00000000000..e4784a2a156 --- /dev/null +++ b/client/src/components/Share/Message.tsx @@ -0,0 +1,100 @@ +import type { TMessageProps } from '~/common'; +import MinimalHoverButtons from '~/components/Chat/Messages/MinimalHoverButtons'; +import MessageContent from '~/components/Chat/Messages/Content/MessageContent'; +import SearchContent from '~/components/Chat/Messages/Content/SearchContent'; +import SiblingSwitch from '~/components/Chat/Messages/SiblingSwitch'; +import { Plugin } from '~/components/Messages/Content'; +import SubRow from '~/components/Chat/Messages/SubRow'; +// eslint-disable-next-line import/no-cycle +import MultiMessage from './MultiMessage'; +import { cn } from '~/utils'; + +import Icon from './MessageIcon'; +export default function Message(props: TMessageProps) { + const { + message, + siblingIdx, + siblingCount, + conversation, + setSiblingIdx, + currentEditId, + setCurrentEditId, + } = props; + + if (!message) { + return null; + } + + const { text, children, messageId = null, isCreatedByUser, error, unfinished } = message ?? {}; + + let messageLabel = ''; + if (isCreatedByUser) { + messageLabel = 'anonymous'; + } else { + messageLabel = message.sender; + } + + return ( + <> +
+
+
+
+
+
+
+ +
+
+
+
+
+
{messageLabel}
+
+
+ {/* Legacy Plugins */} + {message?.plugin && } + {message?.content ? ( + + ) : ( + ({})} + text={text ?? ''} + message={message} + isSubmitting={false} + enterEdit={() => ({})} + unfinished={!!unfinished} + isCreatedByUser={isCreatedByUser ?? true} + siblingIdx={siblingIdx ?? 0} + setSiblingIdx={setSiblingIdx ?? (() => ({}))} + /> + )} +
+
+ + + + +
+
+
+
+ + + ); +} diff --git a/client/src/components/Share/MessageIcon.tsx b/client/src/components/Share/MessageIcon.tsx new file mode 100644 index 00000000000..00beb84a686 --- /dev/null +++ b/client/src/components/Share/MessageIcon.tsx @@ -0,0 +1,71 @@ +import { useMemo } from 'react'; +import type { TMessage, TPreset, Assistant } from 'librechat-data-provider'; +import type { TMessageProps } from '~/common'; +import MessageEndpointIcon from '../Endpoints/MessageEndpointIcon'; +import ConvoIconURL from '~/components/Endpoints/ConvoIconURL'; +import { getIconEndpoint } from '~/utils'; +import { UserIcon } from '../svg'; + +export default function MessageIcon( + props: Pick & { + assistant?: false | Assistant; + }, +) { + const { message, conversation, assistant } = props; + + const assistantName = assistant ? (assistant.name as string | undefined) : ''; + const assistantAvatar = assistant ? (assistant.metadata?.avatar as string | undefined) : ''; + + const messageSettings = useMemo( + () => ({ + ...(conversation ?? {}), + ...({ + ...message, + iconURL: message?.iconURL ?? '', + } as TMessage), + }), + [conversation, message], + ); + + const iconURL = messageSettings?.iconURL; + let endpoint = messageSettings?.endpoint; + endpoint = getIconEndpoint({ endpointsConfig: undefined, iconURL, endpoint }); + + if (!message?.isCreatedByUser && iconURL && iconURL.includes('http')) { + return ( + + ); + } + + if (message?.isCreatedByUser) { + return ( +
+ +
+ ); + } + + return ( + + ); +} diff --git a/client/src/components/Share/MessagesView.tsx b/client/src/components/Share/MessagesView.tsx new file mode 100644 index 00000000000..436aec4a8c0 --- /dev/null +++ b/client/src/components/Share/MessagesView.tsx @@ -0,0 +1,47 @@ +import { useState } from 'react'; +import type { TMessage } from 'librechat-data-provider'; +import MultiMessage from './MultiMessage'; + +export default function MessagesView({ + messagesTree: _messagesTree, + conversationId, +}: { + messagesTree?: TMessage[] | null; + conversationId: string; +}) { + const [currentEditId, setCurrentEditId] = useState(-1); + return ( +
+
+
+
+ {(_messagesTree && _messagesTree?.length == 0) || _messagesTree === null ? ( +
+ Nothing found +
+ ) : ( + <> +
+ +
+ + )} +
+
+
+
+
+ ); +} diff --git a/client/src/components/Share/MultiMessage.tsx b/client/src/components/Share/MultiMessage.tsx new file mode 100644 index 00000000000..a62e9d384f5 --- /dev/null +++ b/client/src/components/Share/MultiMessage.tsx @@ -0,0 +1,54 @@ +import { useEffect } from 'react'; +import { useRecoilState } from 'recoil'; +import type { TMessageProps } from '~/common'; +// eslint-disable-next-line import/no-cycle +import Message from './Message'; +import store from '~/store'; + +export default function MultiMessage({ + // messageId is used recursively here + messageId, + messagesTree, + currentEditId, + setCurrentEditId, +}: TMessageProps) { + const [siblingIdx, setSiblingIdx] = useRecoilState(store.messagesSiblingIdxFamily(messageId)); + + const setSiblingIdxRev = (value: number) => { + setSiblingIdx((messagesTree?.length ?? 0) - value - 1); + }; + + useEffect(() => { + // reset siblingIdx when the tree changes, mostly when a new message is submitting. + setSiblingIdx(0); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [messagesTree?.length]); + + useEffect(() => { + if (messagesTree?.length && siblingIdx >= messagesTree?.length) { + setSiblingIdx(0); + } + }, [siblingIdx, messagesTree?.length, setSiblingIdx]); + + if (!(messagesTree && messagesTree?.length)) { + return null; + } + + const message = messagesTree[messagesTree.length - siblingIdx - 1]; + + if (!message) { + return null; + } + + return ( + + ); +} diff --git a/client/src/components/Share/ShareView.tsx b/client/src/components/Share/ShareView.tsx new file mode 100644 index 00000000000..71ff9fdf565 --- /dev/null +++ b/client/src/components/Share/ShareView.tsx @@ -0,0 +1,60 @@ +import { memo } from 'react'; +import { useParams } from 'react-router-dom'; +import { useGetSharedMessages } from 'librechat-data-provider/react-query'; +import { ShareContext } from '~/Providers'; +import { Spinner } from '~/components/svg'; +import MessagesView from './MessagesView'; +import { useLocalize } from '~/hooks'; +import { buildTree } from '~/utils'; +import Footer from '../Chat/Footer'; + +function SharedView() { + const localize = useLocalize(); + const { shareId } = useParams(); + const { data, isLoading } = useGetSharedMessages(shareId ?? ''); + const dataTree = data && buildTree({ messages: data.messages }); + const messagesTree = dataTree?.length === 0 ? null : dataTree ?? null; + + return ( + +
+
+
+ {isLoading ? ( +
+ +
+ ) : data && messagesTree && messagesTree.length !== 0 ? ( + <> +
+

{data.title}

+
+ {new Date(data.createdAt).toLocaleDateString('en-US', { + month: 'long', + day: 'numeric', + year: 'numeric', + })} +
+
+ + + + ) : ( +
+ {localize('com_ui_shared_link_not_found')} +
+ )} +
+
+
+
+
+
+
+ ); +} + +export default memo(SharedView); diff --git a/client/src/data-provider/mutations.ts b/client/src/data-provider/mutations.ts index ade95fb9022..436914706f0 100644 --- a/client/src/data-provider/mutations.ts +++ b/client/src/data-provider/mutations.ts @@ -7,6 +7,8 @@ import { updateConversation, deleteConversation, updateConvoFields, + deleteSharedLink, + addSharedLink, } from '~/utils'; import { dataService, MutationKeys, QueryKeys, defaultOrderQuery } from 'librechat-data-provider'; import { useSetRecoilState } from 'recoil'; @@ -117,6 +119,96 @@ export const useArchiveConversationMutation = ( ); }; +export const useCreateSharedLinkMutation = ( + options?: t.CreateSharedLinkOptions, +): UseMutationResult => { + const queryClient = useQueryClient(); + const { onSuccess, ..._options } = options || {}; + return useMutation((payload: t.TSharedLinkRequest) => dataService.createSharedLink(payload), { + onSuccess: (_data, vars, context) => { + if (!vars.conversationId) { + return; + } + + queryClient.setQueryData([QueryKeys.sharedLinks], (sharedLink) => { + if (!sharedLink) { + return sharedLink; + } + + // If the shared link is public, add it to the shared links cache list + if (vars.isPublic) { + return addSharedLink(sharedLink, _data); + } else { + return deleteSharedLink(sharedLink, _data.shareId); + } + }); + + queryClient.setQueryData([QueryKeys.sharedLinks, _data.shareId], _data); + + onSuccess?.(_data, vars, context); + }, + ...(_options || {}), + }); +}; + +export const useUpdateSharedLinkMutation = ( + options?: t.UpdateSharedLinkOptions, +): UseMutationResult => { + const queryClient = useQueryClient(); + const { onSuccess, ..._options } = options || {}; + return useMutation((payload: t.TSharedLinkRequest) => dataService.updateSharedLink(payload), { + onSuccess: (_data, vars, context) => { + if (!vars.conversationId) { + return; + } + + queryClient.setQueryData([QueryKeys.sharedLinks], (sharedLink) => { + if (!sharedLink) { + return sharedLink; + } + + // If the shared link is public, add it to the shared links cache list. + if (vars.isPublic) { + // Even if the SharedLink data exists in the database, it is not registered in the cache when isPublic is false. + // Therefore, when isPublic is true, use addSharedLink instead of updateSharedLink. + return addSharedLink(sharedLink, _data); + } else { + return deleteSharedLink(sharedLink, _data.shareId); + } + }); + + queryClient.setQueryData([QueryKeys.sharedLinks, _data.shareId], _data); + + onSuccess?.(_data, vars, context); + }, + ...(_options || {}), + }); +}; + +export const useDeleteSharedLinkMutation = ( + options?: t.DeleteSharedLinkOptions, +): UseMutationResult => { + const queryClient = useQueryClient(); + const { onSuccess, ..._options } = options || {}; + return useMutation(({ shareId }) => dataService.deleteSharedLink(shareId), { + onSuccess: (_data, vars, context) => { + if (!vars.shareId) { + return; + } + + queryClient.setQueryData([QueryKeys.sharedMessages, vars.shareId], null); + queryClient.setQueryData([QueryKeys.sharedLinks], (data) => { + if (!data) { + return data; + } + return deleteSharedLink(data, vars.shareId); + }); + onSuccess?.(_data, vars, context); + }, + ...(_options || {}), + }); +}; + export const useDeleteConversationMutation = ( options?: t.DeleteConversationOptions, ): UseMutationResult< diff --git a/client/src/data-provider/queries.ts b/client/src/data-provider/queries.ts index 47646361074..50302ad7edc 100644 --- a/client/src/data-provider/queries.ts +++ b/client/src/data-provider/queries.ts @@ -20,6 +20,8 @@ import type { AssistantDocument, TEndpointsConfig, TCheckUserKeyResponse, + SharedLinkListParams, + SharedLinksResponse, } from 'librechat-data-provider'; import { findPageForConversation, addFileToCache } from '~/utils'; @@ -92,10 +94,10 @@ export const useGetConvoIdQuery = ( return defaultQuery(); } - const { pageIndex, convIndex } = findPageForConversation(convosQuery, { conversationId: id }); + const { pageIndex, index } = findPageForConversation(convosQuery, { conversationId: id }); - if (pageIndex > -1 && convIndex > -1) { - return convosQuery.pages[pageIndex].conversations[convIndex]; + if (pageIndex > -1 && index > -1) { + return convosQuery.pages[pageIndex].conversations[index]; } return defaultQuery(); @@ -158,6 +160,33 @@ export const useConversationsInfiniteQuery = ( ); }; +export const useSharedLinksInfiniteQuery = ( + params?: SharedLinkListParams, + config?: UseInfiniteQueryOptions, +) => { + return useInfiniteQuery( + [QueryKeys.sharedLinks], + ({ pageParam = '' }) => + dataService.listSharedLinks({ + ...params, + pageNumber: pageParam?.toString(), + isPublic: params?.isPublic || true, + }), + { + getNextPageParam: (lastPage) => { + const currentPageNumber = Number(lastPage.pageNumber); + const totalPages = Number(lastPage.pages); // Convert totalPages to a number + // If the current page number is less than total pages, return the next page number + return currentPageNumber < totalPages ? currentPageNumber + 1 : undefined; + }, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config, + }, + ); +}; + /** * ASSISTANTS */ diff --git a/client/src/hooks/Messages/index.ts b/client/src/hooks/Messages/index.ts index 460967b54cd..43c10d8b51a 100644 --- a/client/src/hooks/Messages/index.ts +++ b/client/src/hooks/Messages/index.ts @@ -1,4 +1,5 @@ export { default as useAvatar } from './useAvatar'; export { default as useProgress } from './useProgress'; export { default as useMessageHelpers } from './useMessageHelpers'; +export { default as useCopyToClipboard } from './useCopyToClipboard'; export { default as useMessageScrolling } from './useMessageScrolling'; diff --git a/client/src/hooks/Messages/useCopyToClipboard.ts b/client/src/hooks/Messages/useCopyToClipboard.ts new file mode 100644 index 00000000000..1b20a14e329 --- /dev/null +++ b/client/src/hooks/Messages/useCopyToClipboard.ts @@ -0,0 +1,32 @@ +import { useCallback } from 'react'; +import copy from 'copy-to-clipboard'; +import { ContentTypes } from 'librechat-data-provider'; +import type { TMessage } from 'librechat-data-provider'; + +export default function useCopyToClipboard({ + text, + content, +}: Partial>) { + const copyToClipboard = useCallback( + (setIsCopied: React.Dispatch>) => { + setIsCopied(true); + let messageText = text ?? ''; + if (content) { + messageText = content.reduce((acc, curr, i) => { + if (curr.type === ContentTypes.TEXT) { + return acc + curr.text.value + (i === content.length - 1 ? '' : '\n'); + } + return acc; + }, ''); + } + copy(messageText ?? ''); + + setTimeout(() => { + setIsCopied(false); + }, 3000); + }, + [text, content], + ); + + return copyToClipboard; +} diff --git a/client/src/hooks/Messages/useMessageHelpers.tsx b/client/src/hooks/Messages/useMessageHelpers.tsx index bcf4949fc40..f3e42ac4cd3 100644 --- a/client/src/hooks/Messages/useMessageHelpers.tsx +++ b/client/src/hooks/Messages/useMessageHelpers.tsx @@ -1,8 +1,8 @@ -import copy from 'copy-to-clipboard'; import { useEffect, useRef, useCallback } from 'react'; -import { EModelEndpoint, ContentTypes } from 'librechat-data-provider'; +import { EModelEndpoint } from 'librechat-data-provider'; import type { TMessageProps } from '~/common'; import { useChatContext, useAssistantsMapContext } from '~/Providers'; +import useCopyToClipboard from './useCopyToClipboard'; export default function useMessageHelpers(props: TMessageProps) { const latestText = useRef(''); @@ -65,26 +65,7 @@ export default function useMessageHelpers(props: TMessageProps) { regenerate(message); }; - const copyToClipboard = useCallback( - (setIsCopied: React.Dispatch>) => { - setIsCopied(true); - let messageText = text ?? ''; - if (content) { - messageText = content.reduce((acc, curr, i) => { - if (curr.type === ContentTypes.TEXT) { - return acc + curr.text.value + (i === content.length - 1 ? '' : '\n'); - } - return acc; - }, ''); - } - copy(messageText ?? ''); - - setTimeout(() => { - setIsCopied(false); - }, 3000); - }, - [text, content], - ); + const copyToClipboard = useCopyToClipboard({ text, content }); return { ask, diff --git a/client/src/hooks/Nav/useNavScrolling.ts b/client/src/hooks/Nav/useNavScrolling.ts index ba45d3eabf4..635a7bc6a0c 100644 --- a/client/src/hooks/Nav/useNavScrolling.ts +++ b/client/src/hooks/Nav/useNavScrolling.ts @@ -3,7 +3,7 @@ import React, { useCallback, useEffect, useRef } from 'react'; import type { FetchNextPageOptions, InfiniteQueryObserverResult } from '@tanstack/react-query'; import type { ConversationListResponse } from 'librechat-data-provider'; -export default function useNavScrolling({ +export default function useNavScrolling({ hasNextPage, isFetchingNextPage, setShowLoading, @@ -14,7 +14,7 @@ export default function useNavScrolling({ setShowLoading: React.Dispatch>; fetchNextPage: ( options?: FetchNextPageOptions | undefined, - ) => Promise>; + ) => Promise>; }) { const scrollPositionRef = useRef(null); const containerRef = useRef(null); diff --git a/client/src/localization/languages/Ar.ts b/client/src/localization/languages/Ar.ts index ef8219c805f..8307181b26e 100644 --- a/client/src/localization/languages/Ar.ts +++ b/client/src/localization/languages/Ar.ts @@ -51,6 +51,17 @@ export default { com_ui_import_conversation_error: 'حدث خطأ أثناء استيراد محادثاتك', com_ui_confirm_action: 'تأكيد الإجراء', com_ui_chats: 'الدردشات', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete: 'حذف', com_ui_delete_conversation: 'حذف الدردشة؟', com_ui_delete_conversation_confirm: 'سيتم حذف هذا', @@ -254,6 +265,12 @@ export default { com_nav_export_recursive_or_sequential: 'التراجع أو التسلسل؟', com_nav_export_recursive: 'تكراري', com_nav_export_conversation: 'تصدير المحادثة', + com_nav_export: 'تصدير', + com_nav_shared_links: 'روابط مشتركة', + com_nav_shared_links_manage: 'الإدارة', + com_nav_shared_links_empty: 'ليس لديك أي روابط مشتركة.', + com_nav_shared_links_name: 'الاسم', + com_nav_shared_links_date_shared: 'تاريخ المشترك', com_nav_theme: 'المظهر', com_nav_theme_system: 'النظام', com_nav_theme_dark: 'داكن', @@ -280,6 +297,12 @@ export default { com_nav_setting_general: 'عام', com_nav_setting_data: 'تحكم في البيانات', /* The following are AI translated */ + com_ui_copied: 'تم النسخ', + com_ui_copy_code: 'نسخ الكود', + com_ui_copy_link: 'نسخ الرابط', + com_ui_update_link: 'رابط التحديث', + com_ui_create_link: 'إنشاء رابط', + com_nav_source_chat: 'عرض محادثة المصدر', com_ui_date_today: 'اليوم', com_ui_date_yesterday: 'أمس', com_ui_date_previous_7_days: 'الأيام السبعة السابقة', @@ -736,6 +759,43 @@ export const comparisons = { english: 'chats', translated: 'الدردشات', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete: { english: 'Delete', translated: 'حذف', @@ -1456,6 +1516,30 @@ export const comparisons = { english: 'Export conversation', translated: 'تصدير المحادثة', }, + com_nav_export: { + english: 'Export', + translated: 'تصدير', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'روابط مشتركة', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'الإدارة', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'ليس لديك أي روابط مشتركة.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'الاسم', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'تاريخ المشترك', + }, com_nav_theme: { english: 'Theme', translated: 'المظهر', @@ -1552,6 +1636,30 @@ export const comparisons = { english: 'Data controls', translated: 'تحكم في البيانات', }, + com_ui_copied: { + english: 'Copied!', + translated: 'تم النسخ', + }, + com_ui_copy_code: { + english: 'Copy code', + translated: 'نسخ الكود', + }, + com_ui_copy_link: { + english: 'Copy link', + translated: 'نسخ الرابط', + }, + com_ui_update_link: { + english: 'Update link', + translated: 'رابط التحديث', + }, + com_ui_create_link: { + english: 'Create link', + translated: 'إنشاء رابط', + }, + com_nav_source_chat: { + english: 'View source chat', + translated: 'عرض محادثة المصدر', + }, com_ui_date_today: { english: 'Today', translated: 'اليوم', diff --git a/client/src/localization/languages/Br.ts b/client/src/localization/languages/Br.ts index 81c0b037269..e571d2ec1e7 100644 --- a/client/src/localization/languages/Br.ts +++ b/client/src/localization/languages/Br.ts @@ -135,6 +135,17 @@ export default { com_ui_assistants_output: 'Saída dos Assistentes', com_ui_delete: 'Excluir', com_ui_create: 'Criar', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete_conversation: 'Excluir conversa?', com_ui_delete_conversation_confirm: 'Isso excluirá', com_ui_delete_assistant_confirm: @@ -417,6 +428,12 @@ export default { com_nav_export_recursive_or_sequential: 'Recursivo ou sequencial?', com_nav_export_recursive: 'Recursivo', com_nav_export_conversation: 'Exportar conversa', + com_nav_export: 'Exportar', + com_nav_shared_links: 'Links Compartilhados', + com_nav_shared_links_manage: 'Gerenciar', + com_nav_shared_links_empty: 'Você não tem nenhum link compartilhado.', + com_nav_shared_links_name: 'Nome', + com_nav_shared_links_date_shared: 'Data compartilhada', com_nav_my_files: 'Meus arquivos', com_nav_theme: 'Tema', com_nav_theme_system: 'Sistema', @@ -963,6 +980,43 @@ export const comparisons = { english: 'Create', translated: 'Criar', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete_conversation: { english: 'Delete chat?', translated: 'Excluir conversa?', @@ -1951,6 +2005,30 @@ export const comparisons = { english: 'Export conversation', translated: 'Exportar conversa', }, + com_nav_export: { + english: 'Export', + translated: 'Exportar', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'Links Compartilhados', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'Gerenciar', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'Você não tem nenhum link compartilhado.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'Nome', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'Data compartilhada', + }, com_nav_my_files: { english: 'My Files', translated: 'Meus arquivos', diff --git a/client/src/localization/languages/De.ts b/client/src/localization/languages/De.ts index ac6ecec7bcc..08e023d7ccd 100644 --- a/client/src/localization/languages/De.ts +++ b/client/src/localization/languages/De.ts @@ -148,6 +148,17 @@ export default { com_ui_assistants_output: 'Assistenten Ausgabe', com_ui_delete: 'Löschen', com_ui_create: 'Erstellen', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete_conversation: 'Chat löschen?', com_ui_delete_conversation_confirm: 'Damit wird gelöscht', com_ui_delete_assistant_confirm: @@ -431,6 +442,12 @@ export default { com_nav_export_recursive_or_sequential: 'Rekursiv oder sequentiell?', com_nav_export_recursive: 'Rekursiv', com_nav_export_conversation: 'Konversation exportieren', + com_nav_export: 'Exportieren', + com_nav_shared_links: 'Gemeinsame Links', + com_nav_shared_links_manage: 'Verwalten', + com_nav_shared_links_empty: 'Sie haben keine gemeinsam genutzten Links.', + com_nav_shared_links_name: 'Name', + com_nav_shared_links_date_shared: 'Datum geteilt', com_nav_my_files: 'Meine Dateien', com_nav_theme: 'Farbschema', com_nav_theme_system: 'System', @@ -464,6 +481,12 @@ export default { com_nav_setting_account: 'Konto', com_nav_language: 'Sprache', /* The following are AI Translated */ + com_ui_copied: 'Kopiert', + com_ui_copy_code: 'Code kopieren', + com_ui_copy_link: 'Link kopieren', + com_ui_update_link: 'Link aktualisieren', + com_ui_create_link: 'Link erstellen', + com_nav_source_chat: 'Quellchat anzeigen', com_ui_date_today: 'Heute', com_ui_date_yesterday: 'Gestern', com_ui_date_previous_7_days: 'Letzte 7 Tage', @@ -1102,6 +1125,43 @@ export const comparisons = { english: 'Create', translated: 'Erstellen', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete_conversation: { english: 'Delete chat?', translated: 'Chat löschen?', @@ -2097,6 +2157,30 @@ export const comparisons = { english: 'Export conversation', translated: 'Konversation exportieren', }, + com_nav_export: { + english: 'Export', + translated: 'Exportieren', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'Gemeinsame Links', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'Verwalten', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'Sie haben keine gemeinsam genutzten Links.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'Name', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'Datum geteilt', + }, com_nav_my_files: { english: 'My Files', translated: 'Meine Dateien', @@ -2221,6 +2305,30 @@ export const comparisons = { english: 'Language', translated: 'Sprache', }, + com_ui_copied: { + english: 'Copied!', + translated: 'Kopiert', + }, + com_ui_copy_code: { + english: 'Copy code', + translated: 'Code kopieren', + }, + com_ui_copy_link: { + english: 'Copy link', + translated: 'Link kopieren', + }, + com_ui_update_link: { + english: 'Update link', + translated: 'Link aktualisieren', + }, + com_ui_create_link: { + english: 'Create link', + translated: 'Link erstellen', + }, + com_nav_source_chat: { + english: 'View source chat', + translated: 'Quellchat anzeigen', + }, com_ui_date_today: { english: 'Today', translated: 'Heute', diff --git a/client/src/localization/languages/Eng.ts b/client/src/localization/languages/Eng.ts index 2007224fc17..f1b3374a25b 100644 --- a/client/src/localization/languages/Eng.ts +++ b/client/src/localization/languages/Eng.ts @@ -139,6 +139,8 @@ export default { com_ui_save: 'Save', com_ui_save_submit: 'Save & Submit', com_user_message: 'You', + com_ui_copied: 'Copied!', + com_ui_copy_code: 'Copy code', com_ui_copy_to_clipboard: 'Copy to clipboard', com_ui_copied_to_clipboard: 'Copied to clipboard', com_ui_fork: 'Fork', @@ -199,6 +201,20 @@ export default { com_ui_assistants_output: 'Assistants Output', com_ui_delete: 'Delete', com_ui_create: 'Create', + com_ui_share: 'Share', + com_ui_copy_link: 'Copy link', + com_ui_update_link: 'Update link', + com_ui_create_link: 'Create link', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete_conversation: 'Delete chat?', com_ui_delete_conversation_confirm: 'This will delete', com_ui_delete_assistant_confirm: @@ -480,6 +496,13 @@ export default { com_nav_export_recursive_or_sequential: 'Recursive or sequential?', com_nav_export_recursive: 'Recursive', com_nav_export_conversation: 'Export conversation', + com_nav_export: 'Export', + com_nav_shared_links: 'Shared links', + com_nav_shared_links_manage: 'Manage', + com_nav_shared_links_empty: 'You have no shared links.', + com_nav_shared_links_name: 'Name', + com_nav_shared_links_date_shared: 'Date shared', + com_nav_source_chat: 'View source chat', com_nav_my_files: 'My Files', com_nav_theme: 'Theme', com_nav_theme_system: 'System', diff --git a/client/src/localization/languages/Es.ts b/client/src/localization/languages/Es.ts index 66669791e5a..6b258b4337b 100644 --- a/client/src/localization/languages/Es.ts +++ b/client/src/localization/languages/Es.ts @@ -137,6 +137,17 @@ export default { com_ui_assistants_output: 'Salida de Asistentes', com_ui_delete: 'Eliminar', com_ui_create: 'Crear', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete_conversation: '¿Eliminar Chat?', com_ui_delete_conversation_confirm: 'Esto eliminará', com_ui_delete_assistant_confirm: @@ -423,6 +434,12 @@ export default { com_nav_export_recursive_or_sequential: '¿Recursivo o secuencial?', com_nav_export_recursive: 'Recursivo', com_nav_export_conversation: 'Exportar conversación', + com_nav_export: 'Exportar', + com_nav_shared_links: 'Links Compartidos', + com_nav_shared_links_manage: 'Gerenciar', + com_nav_shared_links_empty: 'Você não tem nenhum link compartilhado.', + com_nav_shared_links_name: 'Nome', + com_nav_shared_links_date_shared: 'Data compartilhada', com_nav_my_files: 'Mis archivos', com_nav_theme: 'Tema', com_nav_theme_system: 'Sistema', @@ -458,6 +475,12 @@ export default { com_nav_lang_auto: 'Detección automática', com_nav_lang_spanish: 'Español', /* The following are AI Translated */ + com_ui_copied: '¡Copiado!', + com_ui_copy_code: 'Copiar código', + com_ui_copy_link: 'Copiar enlace', + com_ui_update_link: 'Actualizar enlace', + com_ui_create_link: 'Crear enlace', + com_nav_source_chat: 'Ver chat de origen', com_ui_date_today: 'Hoy', com_ui_date_yesterday: 'Ayer', com_ui_date_previous_7_days: 'Últimos 7 días', @@ -1073,6 +1096,43 @@ export const comparisons = { english: 'Create', translated: 'Crear', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete_conversation: { english: 'Delete chat?', translated: '¿Eliminar Chat?', @@ -2069,6 +2129,30 @@ export const comparisons = { english: 'Export conversation', translated: 'Exportar conversación', }, + com_nav_export: { + english: 'Export', + translated: 'Exportar', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'Links Compartidos', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'Gerenciar', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'Você não tem nenhum link compartilhado.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'Nome', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'Data compartilhada', + }, com_nav_my_files: { english: 'My Files', translated: 'Mis archivos', @@ -2202,6 +2286,30 @@ export const comparisons = { english: 'Español', translated: 'Español', }, + com_ui_copied: { + english: 'Copied!', + translated: '¡Copiado!', + }, + com_ui_copy_code: { + english: 'Copy code', + translated: 'Copiar código', + }, + com_ui_copy_link: { + english: 'Copy link', + translated: 'Copiar enlace', + }, + com_ui_update_link: { + english: 'Update link', + translated: 'Actualizar enlace', + }, + com_ui_create_link: { + english: 'Create link', + translated: 'Crear enlace', + }, + com_nav_source_chat: { + english: 'View source chat', + translated: 'Ver chat de origen', + }, com_ui_date_today: { english: 'Today', translated: 'Hoy', diff --git a/client/src/localization/languages/Fr.ts b/client/src/localization/languages/Fr.ts index ea7ea122421..fb193547d91 100644 --- a/client/src/localization/languages/Fr.ts +++ b/client/src/localization/languages/Fr.ts @@ -65,6 +65,17 @@ export default { 'Une erreur s’est produite lors de l’importation de vos conversations', com_ui_confirm_action: 'Confirmer l\'action', com_ui_chats: 'discussions', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete: 'Supprimer', com_ui_delete_conversation: 'Supprimer la discussions?', com_ui_delete_conversation_confirm: 'Cela supprimera', @@ -318,6 +329,12 @@ export default { com_nav_export_recursive_or_sequential: 'Récursif ou séquentiel ?', com_nav_export_recursive: 'Récursif', com_nav_export_conversation: 'Exporter la conversation', + com_nav_export: 'Exporter', + com_nav_shared_links: 'Liens partagés', + com_nav_shared_links_manage: 'Gerenciar', + com_nav_shared_links_empty: 'Você não tem nenhum link compartilhado.', + com_nav_shared_links_name: 'Nome', + com_nav_shared_links_date_shared: 'Data compartilhada', com_nav_theme: 'Thème', com_nav_theme_system: 'Système', com_nav_theme_dark: 'Sombre', @@ -347,6 +364,12 @@ export default { com_nav_setting_data: 'Contrôles des données', com_nav_setting_account: 'Compte', /* The following are AI Translated */ + com_ui_copied: 'Copié !', + com_ui_copy_code: 'Copier le code', + com_ui_copy_link: 'Copier le lien', + com_ui_update_link: 'Mettre à jour le lien', + com_ui_create_link: 'Créer un lien', + com_nav_source_chat: 'Afficher la conversation source', com_ui_date_today: 'Aujourd\'hui', com_ui_date_yesterday: 'Hier', com_ui_date_previous_7_days: '7 derniers jours', @@ -788,6 +811,43 @@ export const comparisons = { english: 'chats', translated: 'discussions', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete: { english: 'Delete', translated: 'Supprimer', @@ -1670,6 +1730,30 @@ export const comparisons = { english: 'Export conversation', translated: 'Exporter la conversation', }, + com_nav_export: { + english: 'Export', + translated: 'Exporter', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'Liens partagés', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'Gerenciar', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'Você não tem nenhum link compartilhado.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'Nome', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'Data compartilhada', + }, com_nav_theme: { english: 'Theme', translated: 'Thème', @@ -1779,6 +1863,30 @@ export const comparisons = { english: 'Account', translated: 'Compte', }, + com_ui_copied: { + english: 'Copied!', + translated: 'Copié !', + }, + com_ui_copy_code: { + english: 'Copy code', + translated: 'Copier le code', + }, + com_ui_copy_link: { + english: 'Copy link', + translated: 'Copier le lien', + }, + com_ui_update_link: { + english: 'Update link', + translated: 'Mettre à jour le lien', + }, + com_ui_create_link: { + english: 'Create link', + translated: 'Créer un lien', + }, + com_nav_source_chat: { + english: 'View source chat', + translated: 'Afficher la conversation source', + }, com_ui_date_today: { english: 'Today', translated: 'Aujourd\'hui', diff --git a/client/src/localization/languages/He.ts b/client/src/localization/languages/He.ts index ca887e2042d..138b79e2a9c 100644 --- a/client/src/localization/languages/He.ts +++ b/client/src/localization/languages/He.ts @@ -91,6 +91,17 @@ export default { com_ui_assistant: 'סייען', com_ui_delete: 'מחק', com_ui_create: 'צור', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete_conversation: 'למחוק את השיחה (צאט)?', com_ui_delete_conversation_confirm: 'זה ימחק', com_ui_delete_assistant_confirm: @@ -345,6 +356,12 @@ export default { com_nav_export_recursive_or_sequential: 'רקורסיבי או רציף?', com_nav_export_recursive: 'רקורסיבי', com_nav_export_conversation: 'ייצא שיחה', + com_nav_export: 'ייצא', + com_nav_shared_links: 'קישורים משותפים', + com_nav_shared_links_manage: 'ניהול', + com_nav_shared_links_empty: 'אין לך קישורים משותפים.', + com_nav_shared_links_name: 'שם', + com_nav_shared_links_date_shared: 'תאריך שיתוף', com_nav_theme: 'נושא', com_nav_theme_system: 'מערכת', com_nav_theme_dark: 'כהה', @@ -723,6 +740,43 @@ export const comparisons = { english: 'Create', translated: 'צור', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete_conversation: { english: 'Delete chat?', translated: 'למחוק את השיחה (צאט)?', @@ -1659,6 +1713,30 @@ export const comparisons = { english: 'Export conversation', translated: 'ייצא שיחה', }, + com_nav_export: { + english: 'Export', + translated: 'ייצא', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'קישורים משותפים', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'ניהול', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'אין לך קישורים משותפים.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'שם', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'תאריך שיתוף', + }, com_nav_theme: { english: 'Theme', translated: 'נושא', diff --git a/client/src/localization/languages/Id.ts b/client/src/localization/languages/Id.ts index 16927392ae0..41219cca397 100644 --- a/client/src/localization/languages/Id.ts +++ b/client/src/localization/languages/Id.ts @@ -61,6 +61,17 @@ export default { com_ui_import_conversation_error: 'Terjadi kesalahan saat mengimpor percakapan Anda', com_ui_confirm_action: 'Konfirmasi Aksi', com_ui_chats: 'chat', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete: 'Hapus', com_ui_delete_conversation: 'Hapus chat?', com_ui_delete_conversation_confirm: 'Ini akan menghapus', @@ -304,6 +315,12 @@ export default { com_nav_export_recursive_or_sequential: 'Rekursif atau berurutan?', com_nav_export_recursive: 'Rekursif', com_nav_export_conversation: 'Ekspor percakapan', + com_nav_export: 'Ekspor', + com_nav_shared_links: 'Link berbagi', + com_nav_shared_links_manage: 'Pengeluaran', + com_nav_shared_links_empty: 'Anda tidak memiliki link berbagi.', + com_nav_shared_links_name: 'Nama', + com_nav_shared_links_date_shared: 'Tanggal berbagi', com_nav_theme: 'Tema', com_nav_theme_system: 'Sistem', com_nav_theme_dark: 'Gelap', @@ -576,6 +593,43 @@ export const comparisons = { english: 'chats', translated: 'chat', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete: { english: 'Delete', translated: 'Hapus', @@ -1457,6 +1511,30 @@ export const comparisons = { english: 'Export conversation', translated: 'Ekspor percakapan', }, + com_nav_export: { + english: 'Export', + translated: 'Ekspor', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'Link berbagi', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'Pengeluaran', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'Anda tidak memiliki link berbagi.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'Nama', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'Tanggal berbagi', + }, com_nav_theme: { english: 'Theme', translated: 'Tema', diff --git a/client/src/localization/languages/It.ts b/client/src/localization/languages/It.ts index 83daab8cc97..c5ea45bafe7 100644 --- a/client/src/localization/languages/It.ts +++ b/client/src/localization/languages/It.ts @@ -188,6 +188,17 @@ export default { com_ui_assistants_output: 'Output Assistenti', com_ui_delete: 'Elimina', com_ui_create: 'Crea', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete_conversation: 'Eliminare la chat?', com_ui_delete_conversation_confirm: 'Questo eliminerà', com_ui_rename: 'Rinominare', @@ -475,6 +486,12 @@ export default { com_nav_export_recursive_or_sequential: 'Ricorsivo o sequenziale?', com_nav_export_recursive: 'Ricorsivo', com_nav_export_conversation: 'Esporta conversazione', + com_nav_export: 'Esporta', + com_nav_shared_links: 'Link condivisi', + com_nav_shared_links_manage: 'Gestisci', + com_nav_shared_links_empty: 'Non hai link condivisi.', + com_nav_shared_links_name: 'Nome', + com_nav_shared_links_date_shared: 'Data condivisione', com_nav_my_files: 'I miei file', com_nav_theme: 'Tema', com_nav_theme_system: 'Sistema', @@ -508,6 +525,12 @@ export default { com_nav_setting_data: 'Controlli dati', com_nav_setting_account: 'Account', /* The following are AI Translated */ + com_ui_copied: 'Copiato!', + com_ui_copy_code: 'Copia codice', + com_ui_copy_link: 'Copia link', + com_ui_update_link: 'Aggiorna link', + com_ui_create_link: 'Crea link', + com_nav_source_chat: 'Visualizza chat sorgente', com_ui_date_today: 'Oggi', com_ui_date_yesterday: 'Ieri', com_ui_date_previous_7_days: 'Ultimi 7 giorni', @@ -1222,6 +1245,43 @@ export const comparisons = { english: 'Create', translated: 'Crea', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete_conversation: { english: 'Delete chat?', translated: 'Eliminare la chat?', @@ -2234,6 +2294,30 @@ export const comparisons = { english: 'Export conversation', translated: 'Esporta conversazione', }, + com_nav_export: { + english: 'Export', + translated: 'Esporta', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'Link condivisi', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'Gestisci', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'Non hai link condivisi.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'Nome', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'Data condivisione', + }, com_nav_my_files: { english: 'My Files', translated: 'I miei file', @@ -2359,6 +2443,30 @@ export const comparisons = { english: 'Account', translated: 'Account', }, + com_ui_copied: { + english: 'Copied!', + translated: 'Copiato!', + }, + com_ui_copy_code: { + english: 'Copy code', + translated: 'Copia codice', + }, + com_ui_copy_link: { + english: 'Copy link', + translated: 'Copia link', + }, + com_ui_update_link: { + english: 'Update link', + translated: 'Aggiorna link', + }, + com_ui_create_link: { + english: 'Create link', + translated: 'Crea link', + }, + com_nav_source_chat: { + english: 'View source chat', + translated: 'Visualizza chat sorgente', + }, com_ui_date_today: { english: 'Today', translated: 'Oggi', diff --git a/client/src/localization/languages/Jp.ts b/client/src/localization/languages/Jp.ts index 5a4227f649b..f99d2d8f478 100644 --- a/client/src/localization/languages/Jp.ts +++ b/client/src/localization/languages/Jp.ts @@ -147,6 +147,18 @@ export default { com_ui_assistants_output: 'Assistantsの出力', com_ui_delete: '削除', com_ui_create: '作成', + com_ui_share: '共有', + com_ui_share_link_to_chat: 'チャットへの共有リンク', + com_ui_share_error: 'チャットの共有リンクの共有中にエラーが発生しました', + com_ui_share_create_message: + 'あなたの名前と共有リンクを作成した後のメッセージは、共有されません。', + com_ui_share_created_message: + 'チャットへの公開された共有リンクが作成されました。設定から以前共有したチャットを管理できます。', + com_ui_share_update_message: + 'あなたの名前、カスタム指示、共有リンクを作成した後のメッセージは、共有されません。', + com_ui_share_updated_message: + 'チャットへの公開された共有リンクが更新されました。設定から以前共有したチャットを管理できます。', + com_ui_shared_link_not_found: '共有リンクが見つかりません', com_ui_delete_conversation: 'チャットを削除しますか?', com_ui_delete_conversation_confirm: 'このチャットは削除されます。', com_ui_delete_assistant_confirm: 'このアシスタントを削除しますか? この操作は元に戻せません。', @@ -422,6 +434,12 @@ export default { com_nav_export_recursive_or_sequential: '再帰的? or 順次的?', com_nav_export_recursive: '再帰的', com_nav_export_conversation: '会話をエクスポートする', + com_nav_export: 'エクスポート', + com_nav_shared_links: '共有リンク', + com_nav_shared_links_manage: '管理', + com_nav_shared_links_empty: '共有リンクはありません。', + com_nav_shared_links_name: 'タイトル', + com_nav_shared_links_date_shared: '共有日', com_nav_my_files: 'My Files', com_nav_theme: 'テーマ', com_nav_theme_system: 'システム', @@ -455,6 +473,12 @@ export default { com_nav_setting_data: 'データ管理', com_nav_setting_account: 'アカウント', /* The following are AI translated */ + com_ui_copied: 'コピーしました', + com_ui_copy_code: 'コードをコピーする', + com_ui_copy_link: 'リンクをコピー', + com_ui_update_link: 'リンクを更新する', + com_ui_create_link: 'リンクを作成する', + com_nav_source_chat: 'ソースチャットを表示する', com_ui_date_today: '今日', com_ui_date_yesterday: '昨日', com_ui_date_previous_7_days: '過去7日間', @@ -1098,6 +1122,43 @@ export const comparisons = { english: 'Create', translated: '作成', }, + com_ui_share: { + english: 'Share', + translated: '共有', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'チャットへの共有リンク', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'チャットの共有リンクの共有中にエラーが発生しました', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'あなたの名前と共有リンクを作成した後のメッセージは、共有されません。', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'チャットへの公開された共有リンクが作成されました。設定から以前共有したチャットを管理できます。', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'あなたの名前、カスタム指示、共有リンクを作成した後のメッセージは、共有されません。', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'チャットへの公開された共有リンクが更新されました。設定から以前共有したチャットを管理できます。', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: '共有リンクが見つかりません', + }, com_ui_delete_conversation: { english: 'Delete chat?', translated: 'チャットを削除しますか?', @@ -2087,6 +2148,30 @@ export const comparisons = { english: 'Export conversation', translated: '会話をエクスポートする', }, + com_nav_export: { + english: 'Export', + translated: 'エクスポート', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: '共有リンク', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: '管理', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: '共有リンクはありません。', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'タイトル', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: '共有日', + }, com_nav_my_files: { english: 'My Files', translated: 'My Files', @@ -2211,6 +2296,30 @@ export const comparisons = { english: 'Account', translated: 'アカウント', }, + com_ui_copied: { + english: 'Copied!', + translated: 'コピーしました', + }, + com_ui_copy_code: { + english: 'Copy code', + translated: 'コードをコピーする', + }, + com_ui_copy_link: { + english: 'Copy link', + translated: 'リンクをコピー', + }, + com_ui_update_link: { + english: 'Update link', + translated: 'リンクを更新する', + }, + com_ui_create_link: { + english: 'Create link', + translated: 'リンクを作成する', + }, + com_nav_source_chat: { + english: 'View source chat', + translated: 'ソースチャットを表示する', + }, com_ui_date_today: { english: 'Today', translated: '今日', diff --git a/client/src/localization/languages/Ko.ts b/client/src/localization/languages/Ko.ts index d84bde4cee2..ef0aef5f8e9 100644 --- a/client/src/localization/languages/Ko.ts +++ b/client/src/localization/languages/Ko.ts @@ -50,6 +50,17 @@ export default { com_ui_import_conversation_error: '대화를 가져오는 동안 오류가 발생했습니다', com_ui_confirm_action: '작업 확인', com_ui_chats: '채팅', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete: '삭제', com_ui_delete_conversation: '채팅을 삭제하시겠습니까?', com_ui_delete_conversation_confirm: '이 채팅이 삭제됩니다', @@ -234,6 +245,12 @@ export default { com_nav_export_recursive_or_sequential: '재귀적 또는 순차적?', com_nav_export_recursive: '재귀적', com_nav_export_conversation: '대화 내보내기', + com_nav_export: '내보내기', + com_nav_shared_links: '공유 링크', + com_nav_shared_links_manage: '관리', + com_nav_shared_links_empty: '공유 링크가 없습니다.', + com_nav_shared_links_name: '이름', + com_nav_shared_links_date_shared: '공유 날짜', com_nav_theme: '테마', com_nav_theme_system: '시스템', com_nav_theme_dark: '다크', @@ -261,6 +278,12 @@ export default { com_nav_setting_general: '일반', com_nav_setting_data: '데이터 제어', /* The following are AI Translated */ + com_ui_copied: '복사됨', + com_ui_copy_code: '코드 복사', + com_ui_copy_link: '링크 복사', + com_ui_update_link: '링크 업데이트', + com_ui_create_link: '링크 만들기', + com_nav_source_chat: '소스 채팅 보기', com_ui_date_today: '오늘', com_ui_date_yesterday: '어제', com_ui_date_previous_7_days: '지난 7일', @@ -729,6 +752,43 @@ export const comparisons = { english: 'chats', translated: '채팅', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete: { english: 'Delete', translated: '삭제', @@ -1397,6 +1457,30 @@ export const comparisons = { english: 'Export conversation', translated: '대화 내보내기', }, + com_nav_export: { + english: 'Export', + translated: '내보내기', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: '공유 링크', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: '관리', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: '공유 링크가 없습니다.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: '이름', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: '공유 날짜', + }, com_nav_theme: { english: 'Theme', translated: '테마', @@ -1497,6 +1581,30 @@ export const comparisons = { english: 'Data controls', translated: '데이터 제어', }, + com_ui_copied: { + english: 'Copied!', + translated: '복사됨', + }, + com_ui_copy_code: { + english: 'Copy code', + translated: '코드 복사', + }, + com_ui_copy_link: { + english: 'Copy link', + translated: '링크 복사', + }, + com_ui_update_link: { + english: 'Update link', + translated: '링크 업데이트', + }, + com_ui_create_link: { + english: 'Create link', + translated: '링크 만들기', + }, + com_nav_source_chat: { + english: 'View source chat', + translated: '소스 채팅 보기', + }, com_ui_date_today: { english: 'Today', translated: '오늘', diff --git a/client/src/localization/languages/Nl.ts b/client/src/localization/languages/Nl.ts index 647830dacbf..f1b79fa1e18 100644 --- a/client/src/localization/languages/Nl.ts +++ b/client/src/localization/languages/Nl.ts @@ -54,6 +54,17 @@ export default { 'Er is een fout opgetreden bij het importeren van je gesprekken', com_ui_confirm_action: 'Bevestig actie', com_ui_chats: 'chats', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete: 'Verwijderen', com_ui_delete_conversation: 'Chat verwijderen?', com_ui_delete_conversation_confirm: 'Hiermee wordt', @@ -261,6 +272,12 @@ export default { com_nav_export_recursive_or_sequential: 'Recursief of sequentieel?', com_nav_export_recursive: 'Recursief', com_nav_export_conversation: 'Conversatie exporteren', + com_nav_export: 'Exporteren', + com_nav_shared_links: 'Gedeelde links', + com_nav_shared_links_manage: 'Beheren', + com_nav_shared_links_empty: 'U hebt geen gedeeld links.', + com_nav_shared_links_name: 'Naam', + com_nav_shared_links_date_shared: 'Datum gedeeld', com_nav_theme: 'Thema', com_nav_theme_system: 'Systeem', com_nav_theme_dark: 'Donker', @@ -494,6 +511,43 @@ export const comparisons = { english: 'chats', translated: 'chats', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete: { english: 'Delete', translated: 'Verwijderen', @@ -1220,6 +1274,30 @@ export const comparisons = { english: 'Export conversation', translated: 'Conversatie exporteren', }, + com_nav_export: { + english: 'Export', + translated: 'Exporteren', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'Gedeelde links', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'Beheren', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'U hebt geen gedeeld links.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'Naam', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'Datum gedeeld', + }, com_nav_theme: { english: 'Theme', translated: 'Thema', diff --git a/client/src/localization/languages/Pl.ts b/client/src/localization/languages/Pl.ts index 944cab7a418..fd16636c003 100644 --- a/client/src/localization/languages/Pl.ts +++ b/client/src/localization/languages/Pl.ts @@ -30,6 +30,17 @@ export default { com_ui_entries: 'wpisów', com_ui_pay_per_call: 'Wszystkie rozmowy z AI w jednym miejscu. Płatność za połączenie, a nie za miesiąc', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_rename: 'Zmień nazwę', com_ui_archive: 'Archiwum', com_ui_archive_error: 'Nie udało się archiwizować rozmowy', @@ -192,6 +203,11 @@ export default { com_nav_export_recursive_or_sequential: 'Rekurencyjny czy sekwencyjny?', com_nav_export_recursive: 'Rekurencyjny', com_nav_export_conversation: 'Eksportuj konwersację', + com_nav_shared_links: 'Linki udostępnione', + com_nav_shared_links_manage: 'Beheren', + com_nav_shared_links_empty: 'U hebt geen gedeeld links.', + com_nav_shared_links_name: 'Naam', + com_nav_shared_links_date_shared: 'Datum gedeeld', com_nav_theme: 'Motyw', com_nav_theme_system: 'Domyślny', com_nav_theme_dark: 'Ciemny', @@ -321,6 +337,43 @@ export const comparisons = { english: 'All AI conversations in one place. Pay per call and not per month', translated: 'Wszystkie rozmowy z AI w jednym miejscu. Płatność za połączenie, a nie za miesiąc', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_rename: { english: 'Rename', translated: 'Zmień nazwę', @@ -898,6 +951,26 @@ export const comparisons = { english: 'Export conversation', translated: 'Eksportuj konwersację', }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'Linki udostępnione', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'Beheren', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'U hebt geen gedeeld links.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'Naam', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'Datum gedeeld', + }, com_nav_theme: { english: 'Theme', translated: 'Motyw', diff --git a/client/src/localization/languages/Ru.ts b/client/src/localization/languages/Ru.ts index 26d44264e4f..0c5b2eabd97 100644 --- a/client/src/localization/languages/Ru.ts +++ b/client/src/localization/languages/Ru.ts @@ -66,6 +66,17 @@ export default { com_ui_preview: 'Предпросмотр', com_ui_upload: 'Загрузить', com_ui_connect: 'Подключить', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete_conversation: 'Удалить чат?', com_ui_delete_conversation_confirm: 'Будет удален следующий чат: ', com_ui_rename: 'Переименовать', @@ -302,6 +313,12 @@ export default { com_nav_export_recursive_or_sequential: 'Рекурсивно или последовательно?', com_nav_export_recursive: 'Рекурсивно', com_nav_export_conversation: 'Экспортировать разговор', + com_nav_export: 'Экспорт', + com_nav_shared_links: 'Связываемые ссылки', + com_nav_shared_links_manage: 'Управление', + com_nav_shared_links_empty: 'У вас нет связываемых ссылок.', + com_nav_shared_links_name: 'Naam', + com_nav_shared_links_date_shared: 'Datum gedeeld', com_nav_my_files: 'Мои файлы', com_nav_theme: 'Тема', com_nav_theme_system: 'Системная', @@ -364,6 +381,12 @@ export default { com_ui_upload_error: 'Произошла ошибка при загрузке вашего файла', com_user_message: 'Вы', /* The following are AI Translated */ + com_ui_copied: 'Скопировано', + com_ui_copy_code: 'Копировать код', + com_ui_copy_link: 'Копировать ссылку', + com_ui_update_link: 'Обновить ссылку', + com_ui_create_link: 'Создать ссылку', + com_nav_source_chat: 'Просмотреть исходный чат', com_ui_date_today: 'Сегодня', com_ui_date_yesterday: 'Вчера', com_ui_date_previous_7_days: 'Предыдущие 7 дней', @@ -800,6 +823,43 @@ export const comparisons = { english: 'Connect', translated: 'Подключить', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete_conversation: { english: 'Delete chat?', translated: 'Удалить чат?', @@ -1635,6 +1695,30 @@ export const comparisons = { english: 'Export conversation', translated: 'Экспортировать разговор', }, + com_nav_export: { + english: 'Export', + translated: 'Экспорт', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'Связываемые ссылки', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'Управление', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'У вас нет связываемых ссылок.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'Naam', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'Datum gedeeld', + }, com_nav_my_files: { english: 'My Files', translated: 'Мои файлы', @@ -1864,6 +1948,30 @@ export const comparisons = { english: 'You', translated: 'Вы', }, + com_ui_copied: { + english: 'Copied!', + translated: 'Скопировано', + }, + com_ui_copy_code: { + english: 'Copy code', + translated: 'Копировать код', + }, + com_ui_copy_link: { + english: 'Copy link', + translated: 'Копировать ссылку', + }, + com_ui_update_link: { + english: 'Update link', + translated: 'Обновить ссылку', + }, + com_ui_create_link: { + english: 'Create link', + translated: 'Создать ссылку', + }, + com_nav_source_chat: { + english: 'View source chat', + translated: 'Просмотреть исходный чат', + }, com_ui_date_today: { english: 'Today', translated: 'Сегодня', diff --git a/client/src/localization/languages/Sv.ts b/client/src/localization/languages/Sv.ts index d5debae8244..87e11d19142 100644 --- a/client/src/localization/languages/Sv.ts +++ b/client/src/localization/languages/Sv.ts @@ -51,6 +51,17 @@ export default { com_ui_import_conversation_error: 'Det uppstod ett fel vid import av dina konversationer', com_ui_confirm_action: 'Bekräfta åtgärd', com_ui_chats: 'chattar', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete: 'Radera', com_ui_delete_conversation: 'Radera chatt?', com_ui_delete_conversation_confirm: 'Detta kommer att radera', @@ -249,6 +260,12 @@ export default { com_nav_export_recursive_or_sequential: 'Rekursiv eller sekventiell?', com_nav_export_recursive: 'Rekursiv', com_nav_export_conversation: 'Exportera konversation', + com_nav_export: 'Exportera', + com_nav_shared_links: 'Delade länkar', + com_nav_shared_links_manage: 'Hantera', + com_nav_shared_links_empty: 'Du har inga delade länkar.', + com_nav_shared_links_name: 'Namn', + com_nav_shared_links_date_shared: 'Datum delad', com_nav_theme: 'Tema', com_nav_theme_system: 'System', com_nav_theme_dark: 'Mörkt', @@ -474,6 +491,43 @@ export const comparisons = { english: 'chats', translated: 'chattar', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete: { english: 'Delete', translated: 'Radera', @@ -1185,6 +1239,30 @@ export const comparisons = { english: 'Export conversation', translated: 'Exportera konversation', }, + com_nav_export: { + english: 'Export', + translated: 'Exportera', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'Delade länkar', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'Hantera', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'Du har inga delade länkar.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'Namn', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'Datum delad', + }, com_nav_theme: { english: 'Theme', translated: 'Tema', diff --git a/client/src/localization/languages/Tr.ts b/client/src/localization/languages/Tr.ts index 959145dfc51..a7c545bbe04 100644 --- a/client/src/localization/languages/Tr.ts +++ b/client/src/localization/languages/Tr.ts @@ -53,6 +53,17 @@ export default { com_ui_import_conversation_error: 'Sohbetlerinizi içe aktarırken bir hata oluştu', com_ui_confirm_action: 'İşlemi Onayla', com_ui_chats: 'sohbetler', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete: 'Sil', com_ui_delete_conversation: 'Sohbet silinecek?', com_ui_delete_conversation_confirm: 'Bu silinecek', @@ -277,6 +288,12 @@ export default { com_nav_export_recursive_or_sequential: 'Yinelemeli mi yoksa sıralı mı?', com_nav_export_recursive: 'Yinelemeli', com_nav_export_conversation: 'Konuşmayı dışa aktar', + com_nav_export: 'Dışa Aktar', + com_nav_shared_links: 'Paylaşılan linkler', + com_nav_shared_links_manage: 'Ynetmek', + com_nav_shared_links_empty: 'Paylaşılan linkleriniz yok.', + com_nav_shared_links_name: 'İsim', + com_nav_shared_links_date_shared: 'Paylaşılan tarih', com_nav_theme: 'Tema', com_nav_theme_system: 'Sistem', com_nav_theme_dark: 'Koyu', @@ -510,6 +527,43 @@ export const comparisons = { english: 'chats', translated: 'sohbetler', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete: { english: 'Delete', translated: 'Sil', @@ -1302,6 +1356,30 @@ export const comparisons = { english: 'Export conversation', translated: 'Konuşmayı dışa aktar', }, + com_nav_export: { + english: 'Export', + translated: 'Dışa Aktar', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'Paylaşılan linkler', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'Ynetmek', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'Paylaşılan linkleriniz yok.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'İsim', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'Paylaşılan tarih', + }, com_nav_theme: { english: 'Theme', translated: 'Tema', diff --git a/client/src/localization/languages/Vi.ts b/client/src/localization/languages/Vi.ts index 9229afc2e9c..2b24a4ad8de 100644 --- a/client/src/localization/languages/Vi.ts +++ b/client/src/localization/languages/Vi.ts @@ -53,6 +53,17 @@ export default { com_ui_import_conversation_error: 'Đã xảy ra lỗi khi nhập khẩu cuộc trò chuyện của bạn', com_ui_confirm_action: 'Xác nhận hành động', com_ui_chats: 'cuộc trò chuyện', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete: 'Xóa', com_ui_delete_conversation: 'Xóa cuộc trò chuyện?', com_ui_delete_conversation_confirm: 'Điều này sẽ xóa', @@ -254,6 +265,12 @@ export default { com_nav_export_recursive_or_sequential: 'Đệ quy hay tuần tự?', com_nav_export_recursive: 'Đệ quy', com_nav_export_conversation: 'Xuất cuộc trò chuyện', + com_nav_export: 'Xuất', + com_nav_shared_links: 'Liên kết được chia sẻ', + com_nav_shared_links_manage: 'Quản l', + com_nav_shared_links_empty: 'Bạn không có link được chia sẻ.', + com_nav_shared_links_name: 'Tên', + com_nav_shared_links_date_shared: 'Ngày chia sẻ', com_nav_theme: 'Chủ đề', com_nav_theme_system: 'Hệ thống', com_nav_theme_dark: 'Tối', @@ -473,6 +490,43 @@ export const comparisons = { english: 'chats', translated: 'cuộc trò chuyện', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete: { english: 'Delete', translated: 'Xóa', @@ -1193,6 +1247,30 @@ export const comparisons = { english: 'Export conversation', translated: 'Xuất cuộc trò chuyện', }, + com_nav_export: { + english: 'Export', + translated: 'Xuất', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: 'Liên kết được chia sẻ', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: 'Quản l', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: 'Bạn không có link được chia sẻ.', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: 'Tên', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: 'Ngày chia sẻ', + }, com_nav_theme: { english: 'Theme', translated: 'Chủ đề', diff --git a/client/src/localization/languages/Zh.ts b/client/src/localization/languages/Zh.ts index e88fc148857..4df8edc696b 100644 --- a/client/src/localization/languages/Zh.ts +++ b/client/src/localization/languages/Zh.ts @@ -132,6 +132,17 @@ export default { com_ui_assistants_output: '助手输出', com_ui_delete: '删除', com_ui_create: '创建', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete_conversation: '删除对话?', com_ui_delete_conversation_confirm: '这将删除', com_ui_delete_assistant_confirm: '确定要删除此助手吗?该操作无法撤销。', @@ -386,6 +397,12 @@ export default { com_nav_export_recursive_or_sequential: '递归或顺序?', com_nav_export_recursive: '递归', com_nav_export_conversation: '导出对话', + com_nav_export: '导出', + com_nav_shared_links: '共享链接', + com_nav_shared_links_manage: '管理', + com_nav_shared_links_empty: '您没有共享链接。', + com_nav_shared_links_name: '名称', + com_nav_shared_links_date_shared: '共享日期', com_nav_my_files: '我的文件', com_nav_theme: '主题', com_nav_theme_system: '跟随系统设置', @@ -417,6 +434,15 @@ export default { com_nav_setting_data: '数据管理', com_nav_setting_account: '账户', /* The following are AI Translated */ + com_ui_date_october: '十月', + com_ui_date_november: '十一月', + com_ui_date_december: '十二月', + com_ui_copied: '已复制!', + com_ui_copy_code: '复制代码', + com_ui_copy_link: '复制链接', + com_ui_update_link: '更新链接', + com_ui_create_link: '创建链接', + com_nav_source_chat: '查看源代码对话', com_ui_date_today: '今天', com_ui_date_yesterday: '昨天', com_ui_date_previous_7_days: '过去7天', @@ -1022,6 +1048,43 @@ export const comparisons = { english: 'Create', translated: '创建', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete_conversation: { english: 'Delete chat?', translated: '删除对话?', @@ -1991,6 +2054,30 @@ export const comparisons = { english: 'Export conversation', translated: '导出对话', }, + com_nav_export: { + english: 'Export', + translated: '导出', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: '共享链接', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: '管理', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: '您没有共享链接。', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: '名称', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: '共享日期', + }, com_nav_my_files: { english: 'My Files', translated: '我的文件', @@ -2111,6 +2198,42 @@ export const comparisons = { english: 'Account', translated: '账户', }, + com_ui_date_october: { + english: 'October', + translated: '十月', + }, + com_ui_date_november: { + english: 'November', + translated: '十一月', + }, + com_ui_date_december: { + english: 'December', + translated: '十二月', + }, + com_ui_copied: { + english: 'Copied!', + translated: '已复制!', + }, + com_ui_copy_code: { + english: 'Copy code', + translated: '复制代码', + }, + com_ui_copy_link: { + english: 'Copy link', + translated: '复制链接', + }, + com_ui_update_link: { + english: 'Update link', + translated: '更新链接', + }, + com_ui_create_link: { + english: 'Create link', + translated: '创建链接', + }, + com_nav_source_chat: { + english: 'View source chat', + translated: '查看源代码对话', + }, com_ui_date_today: { english: 'Today', translated: '今天', diff --git a/client/src/localization/languages/ZhTraditional.ts b/client/src/localization/languages/ZhTraditional.ts index 2331cbb609f..23a59dc3112 100644 --- a/client/src/localization/languages/ZhTraditional.ts +++ b/client/src/localization/languages/ZhTraditional.ts @@ -50,6 +50,17 @@ export default { com_ui_import_conversation_error: '導入對話時發生錯誤', com_ui_confirm_action: '確認操作', com_ui_chats: '對話', + com_ui_share: 'Share', + com_ui_share_link_to_chat: 'Share link to chat', + com_ui_share_error: 'There was an error sharing the chat link', + com_ui_share_create_message: 'Your name and any messages you add after sharing stay private.', + com_ui_share_created_message: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + com_ui_share_update_message: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + com_ui_share_updated_message: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + com_ui_shared_link_not_found: 'Shared link not found', com_ui_delete: '刪除', com_ui_delete_conversation: '刪除對話?', com_ui_delete_conversation_confirm: '這將刪除', @@ -240,6 +251,12 @@ export default { com_nav_export_recursive_or_sequential: '遞迴還是序列?', com_nav_export_recursive: '遞迴', com_nav_export_conversation: '匯出對話', + com_nav_export: '匯出', + com_nav_shared_links: '共享連結', + com_nav_shared_links_manage: '管理', + com_nav_shared_links_empty: '您沒有任何共享連結。', + com_nav_shared_links_name: '名称', + com_nav_shared_links_date_shared: '共享日期', com_nav_theme: '主題', com_nav_theme_system: '跟隨系統設定', com_nav_theme_dark: '深色', @@ -266,6 +283,12 @@ export default { com_nav_setting_general: '一般', com_nav_setting_data: '資料控制', /* The following are AI translated */ + com_ui_copied: '已複製!', + com_ui_copy_code: '複製程式碼', + com_ui_copy_link: '複製連結', + com_ui_update_link: '更新連結', + com_ui_create_link: '創建連結', + com_nav_source_chat: '檢視原始對話', com_ui_date_today: '今天', com_ui_date_yesterday: '昨天', com_ui_date_previous_7_days: '前 7 天', @@ -711,6 +734,43 @@ export const comparisons = { english: 'chats', translated: '對話', }, + com_ui_share: { + english: 'Share', + translated: 'Share', + }, + com_ui_share_link_to_chat: { + english: 'Share link to chat', + translated: 'Share link to chat', + }, + com_ui_share_error: { + english: 'There was an error sharing the chat link', + translated: 'There was an error sharing the chat link', + }, + com_ui_share_create_message: { + english: 'Your name and any messages you add after sharing stay private.', + translated: 'Your name and any messages you add after sharing stay private.', + }, + com_ui_share_created_message: { + english: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been created. Manage previously shared chats at any time via Settings.', + }, + com_ui_share_update_message: { + english: 'Your name, custom instructions, and any messages you add after sharing stay private.', + translated: + 'Your name, custom instructions, and any messages you add after sharing stay private.', + }, + com_ui_share_updated_message: { + english: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + translated: + 'A public link to your chat has been updated. Manage previously shared chats at any time via Settings.', + }, + com_ui_shared_link_not_found: { + english: 'Shared link not found', + translated: 'Shared link not found', + }, com_ui_delete: { english: 'Delete', translated: '刪除', @@ -1427,6 +1487,30 @@ export const comparisons = { english: 'Export conversation', translated: '匯出對話', }, + com_nav_export: { + english: 'Export', + translated: '匯出', + }, + com_nav_shared_links: { + english: 'Shared links', + translated: '共享連結', + }, + com_nav_shared_links_manage: { + english: 'Manage', + translated: '管理', + }, + com_nav_shared_links_empty: { + english: 'You have no shared links.', + translated: '您沒有任何共享連結。', + }, + com_nav_shared_links_name: { + english: 'Name', + translated: '名称', + }, + com_nav_shared_links_date_shared: { + english: 'Date shared', + translated: '共享日期', + }, com_nav_theme: { english: 'Theme', translated: '主題', @@ -1527,6 +1611,30 @@ export const comparisons = { english: 'Data controls', translated: '資料控制', }, + com_ui_copied: { + english: 'Copied!', + translated: '已複製!', + }, + com_ui_copy_code: { + english: 'Copy code', + translated: '複製程式碼', + }, + com_ui_copy_link: { + english: 'Copy link', + translated: '複製連結', + }, + com_ui_update_link: { + english: 'Update link', + translated: '更新連結', + }, + com_ui_create_link: { + english: 'Create link', + translated: '創建連結', + }, + com_nav_source_chat: { + english: 'View source chat', + translated: '檢視原始對話', + }, com_ui_date_today: { english: 'Today', translated: '今天', diff --git a/client/src/routes/Search.tsx b/client/src/routes/Search.tsx index a1e0fdff295..40ca1afc8b2 100644 --- a/client/src/routes/Search.tsx +++ b/client/src/routes/Search.tsx @@ -30,13 +30,13 @@ export default function Search() { return ( {(messages && messages?.length == 0) || messages === null ? ( -
+
{localize('com_ui_nothing_found')}
) : ( messages?.map((message) => ) )} -
+
); } diff --git a/client/src/routes/ShareRoute.tsx b/client/src/routes/ShareRoute.tsx new file mode 100644 index 00000000000..a9d41cf35cb --- /dev/null +++ b/client/src/routes/ShareRoute.tsx @@ -0,0 +1,5 @@ +import ShareView from '~/components/Share/ShareView'; + +export default function ShareRoute() { + return ; +} diff --git a/client/src/routes/index.tsx b/client/src/routes/index.tsx index 58fb524ad03..89576fc47d1 100644 --- a/client/src/routes/index.tsx +++ b/client/src/routes/index.tsx @@ -7,6 +7,7 @@ import { ApiErrorWatcher, } from '~/components/Auth'; import { AuthContextProvider } from '~/hooks/AuthContext'; +import ShareRoute from './ShareRoute'; import ChatRoute from './ChatRoute'; import Search from './Search'; import Root from './Root'; @@ -31,6 +32,10 @@ export const router = createBrowserRouter([ path: 'reset-password', element: , }, + { + path: 'share/:shareId', + element: , + }, { element: , children: [ diff --git a/client/src/utils/collection.ts b/client/src/utils/collection.ts new file mode 100644 index 00000000000..6d8b1c691aa --- /dev/null +++ b/client/src/utils/collection.ts @@ -0,0 +1,70 @@ +import { InfiniteData } from '@tanstack/react-query'; + +export const addData = ( + data: InfiniteData, + collectionName: string, + newData: TData, + findIndex: (page: TCollection) => number, +) => { + const dataJson = JSON.parse(JSON.stringify(data)) as InfiniteData; + const { pageIndex, index } = findPage(data, findIndex); + + if (pageIndex !== -1 && index !== -1) { + return updateData(data, collectionName, newData, findIndex); + } + dataJson.pages[0][collectionName].unshift({ + ...newData, + updatedAt: new Date().toISOString(), + }); + + return dataJson; +}; + +export function findPage(data: InfiniteData, findIndex: (page: TData) => number) { + for (let pageIndex = 0; pageIndex < data.pages.length; pageIndex++) { + const page = data.pages[pageIndex]; + const index = findIndex(page); + if (index !== -1) { + return { pageIndex, index }; + } + } + return { pageIndex: -1, index: -1 }; // Not found +} + +export const updateData = ( + data: InfiniteData, + collectionName: string, + updatedData: TData, + findIndex: (page: TCollection) => number, +) => { + const newData = JSON.parse(JSON.stringify(data)) as InfiniteData; + const { pageIndex, index } = findPage(data, findIndex); + + if (pageIndex !== -1 && index !== -1) { + // Remove the data from its current position + newData.pages[pageIndex][collectionName].splice(index, 1); + // Add the updated data to the top of the first page + newData.pages[0][collectionName].unshift({ + ...updatedData, + updatedAt: new Date().toISOString(), + }); + } + + return newData; +}; + +export const deleteData = ( + data: TData, + collectionName: string, + findIndex: (page: TCollection) => number, +): TData => { + const newData = JSON.parse(JSON.stringify(data)); + const { pageIndex, index } = findPage(newData, findIndex); + + if (pageIndex !== -1 && index !== -1) { + // Delete the data from its current page + newData.pages[pageIndex][collectionName].splice(index, 1); + } + + return newData; +}; diff --git a/client/src/utils/convos.spec.ts b/client/src/utils/convos.spec.ts index 6382ad351fd..be5b60708e1 100644 --- a/client/src/utils/convos.spec.ts +++ b/client/src/utils/convos.spec.ts @@ -146,11 +146,11 @@ describe('Conversation Utilities', () => { }, ], }; - const { pageIndex, convIndex } = findPageForConversation(data as ConversationData, { + const { pageIndex, index } = findPageForConversation(data as ConversationData, { conversationId: '2', }); expect(pageIndex).toBe(0); - expect(convIndex).toBe(1); + expect(index).toBe(1); }); }); }); @@ -219,11 +219,11 @@ describe('Conversation Utilities with Fake Data', () => { describe('findPageForConversation', () => { it('finds the correct page and index for a given conversation in fake data', () => { const targetConversation = convoData.pages[0].conversations[0]; - const { pageIndex, convIndex } = findPageForConversation(convoData, { + const { pageIndex, index } = findPageForConversation(convoData, { conversationId: targetConversation.conversationId as string, }); expect(pageIndex).toBeGreaterThanOrEqual(0); - expect(convIndex).toBeGreaterThanOrEqual(0); + expect(index).toBeGreaterThanOrEqual(0); }); }); }); diff --git a/client/src/utils/convos.ts b/client/src/utils/convos.ts index d572481e2fe..6f152a48510 100644 --- a/client/src/utils/convos.ts +++ b/client/src/utils/convos.ts @@ -14,8 +14,12 @@ import type { ConversationData, ConversationUpdater, GroupedConversations, + ConversationListResponse, } from 'librechat-data-provider'; +import { addData, deleteData, updateData, findPage } from './collection'; +import { InfiniteData } from '@tanstack/react-query'; + export const dateKeys = { today: 'com_ui_date_today', yesterday: 'com_ui_date_yesterday', @@ -105,52 +109,39 @@ export const groupConversationsByDate = (conversations: TConversation[]): Groupe return Object.entries(sortedGroups); }; -export const addConversation: ConversationUpdater = (data, newConversation) => { - const newData = JSON.parse(JSON.stringify(data)) as ConversationData; - const { pageIndex, convIndex } = findPageForConversation(newData, newConversation); - - if (pageIndex !== -1 && convIndex !== -1) { - return updateConversation(data, newConversation); - } - newData.pages[0].conversations.unshift({ - ...newConversation, - updatedAt: new Date().toISOString(), - }); - - return newData; +export const addConversation = ( + data: InfiniteData, + newConversation: TConversation, +): ConversationData => { + return addData( + data, + 'conversations', + newConversation, + (page) => + page.conversations.findIndex((c) => c.conversationId === newConversation.conversationId), + ); }; export function findPageForConversation( data: ConversationData, conversation: TConversation | { conversationId: string }, ) { - for (let pageIndex = 0; pageIndex < data.pages.length; pageIndex++) { - const page = data.pages[pageIndex]; - const convIndex = page.conversations.findIndex( - (c) => c.conversationId === conversation.conversationId, - ); - if (convIndex !== -1) { - return { pageIndex, convIndex }; - } - } - return { pageIndex: -1, convIndex: -1 }; // Not found + return findPage(data, (page) => + page.conversations.findIndex((c) => c.conversationId === conversation.conversationId), + ); } -export const updateConversation: ConversationUpdater = (data, updatedConversation) => { - const newData = JSON.parse(JSON.stringify(data)); - const { pageIndex, convIndex } = findPageForConversation(newData, updatedConversation); - - if (pageIndex !== -1 && convIndex !== -1) { - // Remove the conversation from its current position - newData.pages[pageIndex].conversations.splice(convIndex, 1); - // Add the updated conversation to the top of the first page - newData.pages[0].conversations.unshift({ - ...updatedConversation, - updatedAt: new Date().toISOString(), - }); - } - - return newData; +export const updateConversation = ( + data: InfiniteData, + newConversation: TConversation, +): ConversationData => { + return updateData( + data, + 'conversations', + newConversation, + (page) => + page.conversations.findIndex((c) => c.conversationId === newConversation.conversationId), + ); }; export const updateConvoFields: ConversationUpdater = ( @@ -158,13 +149,13 @@ export const updateConvoFields: ConversationUpdater = ( updatedConversation: Partial & Pick, ): ConversationData => { const newData = JSON.parse(JSON.stringify(data)); - const { pageIndex, convIndex } = findPageForConversation( + const { pageIndex, index } = findPageForConversation( newData, updatedConversation as { conversationId: string }, ); - if (pageIndex !== -1 && convIndex !== -1) { - const deleted = newData.pages[pageIndex].conversations.splice(convIndex, 1); + if (pageIndex !== -1 && index !== -1) { + const deleted = newData.pages[pageIndex].conversations.splice(index, 1); const oldConversation = deleted[0] as TConversation; newData.pages[0].conversations.unshift({ @@ -181,15 +172,9 @@ export const deleteConversation = ( data: ConversationData, conversationId: string, ): ConversationData => { - const newData = JSON.parse(JSON.stringify(data)); - const { pageIndex, convIndex } = findPageForConversation(newData, { conversationId }); - - if (pageIndex !== -1 && convIndex !== -1) { - // Delete the conversation from its current page - newData.pages[pageIndex].conversations.splice(convIndex, 1); - } - - return newData; + return deleteData(data, 'conversations', (page) => + page.conversations.findIndex((c) => c.conversationId === conversationId), + ); }; export const getConversationById = ( diff --git a/client/src/utils/index.ts b/client/src/utils/index.ts index 1be9a66a285..ffb7e4fb32a 100644 --- a/client/src/utils/index.ts +++ b/client/src/utils/index.ts @@ -7,6 +7,7 @@ export * from './presets'; export * from './textarea'; export * from './languages'; export * from './endpoints'; +export * from './sharedLink'; export { default as cn } from './cn'; export { default as buildTree } from './buildTree'; export { default as getLoginError } from './getLoginError'; diff --git a/client/src/utils/sharedLink.fakeData.ts b/client/src/utils/sharedLink.fakeData.ts new file mode 100644 index 00000000000..947a26fe2de --- /dev/null +++ b/client/src/utils/sharedLink.fakeData.ts @@ -0,0 +1,955 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck +import type { SharedLinkListData } from 'librechat-data-provider'; + +const today = new Date(); +today.setDate(today.getDate() - 3); + +export const sharedLinkData: SharedLinkListData = { + pages: [ + { + sharedLinks: [ + { + conversationId: '7a327f49-0850-4741-b5da-35373e751256', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-04T04:31:04.897Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'b3c2e29b131c464182b483c4', + '6dc217152a134ac1826fc46c', + '483658114d104691b2501fbf', + 'cfb8467cfd30438e8268cf92', + ], + shareId: '62f850ad-a0d8-48a5-b439-2d1dbaba291c', + title: 'Test Shared Link 1', + updatedAt: '2024-04-11T11:10:42.329Z', + }, + { + conversationId: '1777ad5f-5e53-4847-be49-86f66c649ac6', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-05T05:59:31.571Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'bc53fda136ba46fb965260b8', + '138b83d659c84250904feb53', + '1c750ffab31546bd85b81360', + '7db87f183e4d489fae0b5161', + '64ee2004479644b7b5ffd2ea', + '4dd2b9a0704c4ae79688292e', + '25394c2bb2ee40feaf67836f', + '838ed537d9054780a3d9f272', + '300728390f8c4021a6c066ca', + 'ea30b637cb8f463192523919', + ], + shareId: '1f43f69f-0562-4129-b181-3c37df0df43e', + title: 'Test Shared Link 2', + updatedAt: '2024-04-16T17:52:40.250Z', + }, + { + conversationId: 'a9682067-a7c9-4375-8efb-6b8fa1c71def', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-03T08:23:35.147Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'bb4fe223548b480eae6d64af', + '420ef02293d0470b96980e7b', + 'ae0ffbb27e13418fbd63e7c2', + '43df3ea55cfb4219b1630518', + 'c4fb3be788404058a4c9780d', + '6ee6a5833b1d4849a95be890', + '0b8a3ecf5ca5449b9bdc0ed8', + 'a3daed97f0e5432a8b6031c0', + '6a7d10c55c9a46cfbd08d6d2', + '216d40fa813a44059bd01ab6', + ], + shareId: 'e84d2642-9b3a-4e20-b92a-11a37eebe33f', + title: 'Test Shared Link 3', + updatedAt: '2024-02-06T04:21:17.065Z', + }, + { + conversationId: 'b61f9a0a-6d5d-4d0e-802b-4c1866428816', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-06T19:25:45.708Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: ['00aad718514044dda8e044ec', '8cb3b67fccd64b8c8ac0abbb'], + shareId: '9011e12a-b2fe-4003-9623-bf1b5f80396b', + title: 'Test Shared Link 4', + updatedAt: '2024-03-21T22:37:32.704Z', + }, + { + conversationId: '4ac3fd9e-437b-4988-b870-29cacf28abef', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-03T15:45:11.220Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '6b05f825ca7747f294f2ac64', + '871ee06fb6a141879ca1cb25', + '47b05821c6134a3b9f21072e', + ], + shareId: '51d3ab25-195e-47d0-a5e3-d0694ece776a', + title: 'Test Shared Link 5', + updatedAt: '2024-04-03T23:20:11.213Z', + }, + { + conversationId: '6ed26de8-3310-4abb-b561-4bdae9400aac', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-22T19:12:14.995Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'ac2929efa82b4cd78aae02d6', + '4266450abc7b41a59887e99d', + '95df3c7c802c40e0b643bb96', + 'f21038af46074e51a2c4bd87', + '3f064bc8589c435786a92bcb', + ], + shareId: 'c3bc13ed-190a-4ffa-8a05-50f8dad3c83e', + title: 'Test Shared Link 6', + updatedAt: '2024-04-25T19:55:25.785Z', + }, + { + conversationId: 'b3c0aaca-ee76-42a2-b53b-5e85baca2f91', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-04T00:37:12.929Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '5a44ebd0bf05418e98cc9e5d', + '88b93127aef74bfb94666ac1', + 'bf654993c34743c9a5a1b76c', + '2514259bd702491e924da475', + '60dbbf91a6734aa081e082cd', + '11efabaa3a8f4df8bf85410b', + '3f5bbf38abdb42efa65a8740', + '5b9dd8246dde41ae9ebd57c4', + ], + shareId: '871d41fe-fb8a-41d4-8460-8bb93fb8aa98', + title: 'Test Shared Link 7', + updatedAt: '2024-03-13T14:34:26.790Z', + }, + { + conversationId: '2071122a-57cc-4f16-baa8-1e8af3e23522', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-01-24T03:22:58.012Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '8c94aad21caa45d6acb863c8', + 'c10e4e0bfe554a94920093ba', + '2e4c2e2238f24f63b08440bc', + '05bacd00320342298f9f439f', + 'c8b7750a7d8a4e2fbdc2630b', + 'a84573fea668476a87207979', + '6ab15a1b96c24798b1bddd6f', + 'b699d8e42324493eae95ca44', + ], + shareId: 'f90f738a-b0ac-4dba-bb39-ad3d77919a21', + title: 'Test Shared Link 8', + updatedAt: '2024-01-22T11:09:51.834Z', + }, + { + conversationId: 'ee06374d-4452-4fbe-a1c0-5dbc327638f9', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-03T19:24:21.281Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '297d0827c81a4da0a881561a', + '3131ef1b3c484542b0db1f92', + 'e8879a50340c49449e970dbc', + 'fe598327a93b4b0399055edd', + 'acc7a2a24e204325befffbcd', + '6ec3c6450e124cbf808c8839', + '714e3443f62045aaaff17f93', + '014be593aaad41cab54a1c44', + ], + shareId: '0fc91bab-083d-449f-add3-1e32146b6c4a', + title: 'Test Shared Link 9', + updatedAt: '2024-03-14T00:52:52.345Z', + }, + { + conversationId: '0d2f6880-cacf-4f7b-930e-35881df1cdea', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-02-14T03:18:45.587Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: ['1d045c1cf37742a6a979e21b'], + shareId: 'd87deb62-b993-476c-b520-104b08fd7445', + title: 'Test Shared Link 10', + updatedAt: '2024-03-26T18:38:41.222Z', + }, + { + conversationId: '1fe437fd-68f0-4e3e-81a9-ca9a0fa8220a', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-02-16T19:55:23.412Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'a28b5f27c95e4700bcd158dc', + '6e85f0a8b6ae4107a5819317', + 'fa5b863c91224a0098aebd64', + 'b73811a510e54acebe348371', + 'f3f7f7d7b69a485da727f9c2', + '81d82df3098c4e359d29703f', + ], + shareId: '704a1a9c-5366-4f55-b69e-670a374f4326', + title: 'Test Shared Link 11', + updatedAt: '2024-04-11T05:00:25.349Z', + }, + { + conversationId: '50465c8e-102f-4f94-88c2-9cf607a6c336', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-02-05T21:57:52.289Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'a64886199ab641c29eb6fdaf', + '9c16497010354cf385d4cc1d', + '36cdeb4d1e4f45078edfe28a', + 'a11f4ea78fa44f57bfc5bfc6', + 'dea42fcfe7a544feb5debc26', + 'ece0d630cd89420ca80ffe25', + '719165a5d80644ae8fae9498', + 'f27111921a10470982f522b2', + '10b78255f7a24b6192e67693', + ], + shareId: 'e47eaf30-c1ed-4cc2-b2b8-8cdec4b1ea2f', + title: 'Test Shared Link 12', + updatedAt: '2024-02-07T15:43:21.110Z', + }, + { + conversationId: '1834f551-0a68-4bc7-a66a-21a234462d24', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-23T02:58:52.653Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'cb5d19d986194f779a6f47fd', + '72159d6668f347f99398aec9', + 'cbe535213d664a6280d9a19e', + '8dccceadcb3a44148962ba47', + ], + shareId: '976b55cb-d305-40f8-ae06-ae516f4e49f5', + title: 'Test Shared Link 13', + updatedAt: '2024-05-02T10:21:05.190Z', + }, + { + conversationId: 'd8175b2f-f7c0-4f61-850d-f524bf8a84df', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-09T09:04:10.576Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '0f3708fc670d46998b1294d5', + '794520b9cee84c23bff01d5a', + 'b05d2af2d37c426a970d8326', + 'bd4239e379284d01acb9aaf4', + 'e6265cfbbd88420781b27248', + '5262193aef7c426cafe2ee85', + '848569e2ca4843beaf64efc4', + '99f3b438241c4454a6784ac2', + '111d346fbeae4806bdf23490', + 'fe4bde34e1a143f1a12fa628', + ], + shareId: '928eb0a8-e0ea-470d-8b6a-92e0981d61b0', + title: 'Test Shared Link 14', + updatedAt: '2024-04-15T18:00:13.094Z', + }, + { + conversationId: '281984c0-fed0-4428-8e50-c7c93cba4ae0', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-23T23:26:41.956Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '7e781fd08408426795c243e7', + '3c6d729fd3524a65a7b2a5e3', + '53bdbec6ee6148e78795d6e1', + '46f8170f28684ccc8ee56f33', + '3350d9aa7c814c89af6d3640', + ], + shareId: '7a251af6-1ad3-4b24-830c-21b38124f325', + title: 'Test Shared Link 15', + updatedAt: '2024-03-18T16:33:35.498Z', + }, + { + conversationId: '09610b11-6087-4d15-b163-e1bc958f2e82', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-02-05T20:00:36.159Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: ['6dce61720af24c70926efe87'], + shareId: '2b389d5e-eb24-4b29-a8e1-c0545cdfa1fc', + title: 'Test Shared Link 16', + updatedAt: '2024-02-23T05:49:50.020Z', + }, + { + conversationId: '0c388322-905c-4c57-948c-1ba9614fdc2f', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-05T00:03:20.078Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'e3755ff2cf9f403c9d20901f', + 'e32733b8da1440ec9d9dc2df', + 'e2870d0361634d4f867e1e57', + '2e504afb8675434bb9f58cb5', + 'ea38d76735c54f94bf378ed3', + '8712cda1bfc8480eba6c65aa', + '3f43a655706f4032a9e1efb4', + '3f890f8279f4436da2a7d767', + '4ca7616c04404391a7cfc94f', + 'd3e176a831ff48e49debabce', + ], + shareId: '2866400b-bcb9-43a4-8cbf-6597959f8c55', + title: 'Test Shared Link 17', + updatedAt: '2024-03-16T02:53:06.642Z', + }, + { + conversationId: '5ac2b90a-63f8-4388-919b-40a1c1fea874', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-01-21T15:30:37.893Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '734a0427c6224fca87e2a89d', + '6af13387ddf0495d9c6ebad9', + '02a93d5659f343678b12b932', + '8af2f028c5114286a3339075', + '3a8bec13fc574fb9a9f938e2', + '6f4aa482286548b7b42668e6', + 'c1d4f94a2eaf4e44b94c5834', + '442d9491b51d49fcab60366d', + '82a115a84b2a4457942ca6cf', + '152d8c2894a0454d9248c9f5', + ], + shareId: 'e76f6a90-06f3-4846-8e3d-987d37af27b5', + title: 'Test Shared Link 18', + updatedAt: '2024-01-27T06:25:27.032Z', + }, + { + conversationId: '01521fef-aa0b-4670-857d-f19bfc0ce664', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-01T21:46:40.674Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '222cf562d8e24b1b954395c2', + 'c6f299f588c24905b771e623', + 'f023f30fd4d9472c9bf60b84', + 'e4929e3f14d748a18656f1be', + 'a01f453fcb0a49b5b488a22c', + '4ceee6b365ab4386bacb4d27', + 'c2cab81da0be4c6e97f11f92', + '644c32d10f2f4e2086d5e04d', + '5225d1286db14cc6a47fdea5', + 'c821ebb220ae495b98f2e17f', + ], + shareId: '1b2d8bf5-ff90-478a-bdf6-ea622fb4875a', + title: 'Test Shared Link 19', + updatedAt: '2024-02-25T15:52:56.189Z', + }, + { + conversationId: '54f5d332-efc7-4062-9e1d-c70c3dbbc964', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-01-29T15:57:22.808Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '49771038e2dd4de0a28b19f2', + '0debd4ad13de4db9a65fe589', + 'a9c8e6e34c34486ca27b7c88', + 'd7b0ace0438146789e8b1899', + ], + shareId: '4f5eea7d-b3a8-4b72-ad1e-a4d516c582c2', + title: 'Test Shared Link 20', + updatedAt: '2024-03-18T13:12:10.828Z', + }, + { + conversationId: '99dabf25-46a5-43bb-8274-715c64e56032', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-05T03:35:11.327Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: ['965a55515727404eb70dec8f'], + shareId: '2360b7c1-20d7-46b9-919d-65576a899ab9', + title: 'Test Shared Link 21', + updatedAt: '2024-04-17T11:22:12.800Z', + }, + { + conversationId: '1e2ffc1a-3546-460e-819c-689eb88940c6', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-22T08:40:32.663Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '131f4b03ad3d4e90803a203d', + '7f55262c554f4d97a8fef0ec', + '341e8fea28e241fc8b5a2398', + ], + shareId: 'f3e370ed-420c-4579-a033-e18743b49485', + title: 'Test Shared Link 22', + updatedAt: '2024-04-07T22:06:07.162Z', + }, + { + conversationId: '14510a0c-01cc-4bfb-8336-3388573ac4d8', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-08T08:20:28.619Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '022f87b1bf0d4e4688970daa', + '42519e8f3603496faae0969c', + 'abc29ac88d66485aa11e4b58', + ], + shareId: '0f46f1fd-95d3-4a6f-a5aa-ae5338dc5337', + title: 'Test Shared Link 23', + updatedAt: '2024-03-06T12:05:33.679Z', + }, + { + conversationId: '2475594e-10dc-4d6a-aa58-5ce727a36271', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-04T07:43:46.952Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '5d0cd8bef4c241aba5d822a8', + 'a19669a364d84ab5bbafbe0c', + '336686022ea6456b9a63879d', + '3323c9b85acc4ffba35aad04', + 'bf15e8860a01474cb4744842', + '5a055eb825ed4173910fffd5', + '36a5e683ad144ec68c2a8ce0', + '8bc1d5590a594fa1afc18ee1', + 'f86444b60bea437ba0d0ef8e', + '5be768788d984723aef5c9a0', + ], + shareId: 'b742f35c-e6a3-4fa4-b35d-abab4528d7d6', + title: 'Test Shared Link 24', + updatedAt: '2024-03-27T15:31:10.930Z', + }, + { + conversationId: 'ddb5a61c-82fe-4cc7-a2b0-c34b8c393b28', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-02-15T02:06:45.901Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '059d7ae5405a42af9c52171d', + '303efd2e676e4fe7aa9fa9d0', + '9f459c2e6a23411ea4a3e153', + '6036a3785adc4b7caa7ea22b', + '65251979d0c64d1f8821b3d9', + '25fdeb5ed99d42cca3041e08', + '61baa25e4e3d42a3aefd6c16', + '91dc4578fee749aeb352b5ea', + 'd52daca5afb84e7890d5d9ad', + ], + shareId: '13106e5f-1b5f-4ed4-963d-790e61c1f4c8', + title: 'Test Shared Link 25', + updatedAt: '2024-02-05T08:39:45.847Z', + }, + { + conversationId: 'df09c89b-0b0d-429c-9c93-b5f4d51ef1ec', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-28T07:50:10.724Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '7f007af305ee4f2197c125d3', + '318b26abfe864dbf9f557bf9', + '0c4709b489ac4211b9f22874', + '8940f9ab45f44b56911819d5', + 'b47ec3aa0cf7413fa446f19b', + '3857f85f492f4e11aa0ea377', + ], + shareId: '31bbafa4-2051-4a20-883b-2f8557c46116', + title: 'Test Shared Link 26', + updatedAt: '2024-02-01T19:52:32.986Z', + }, + { + conversationId: '856a4d54-54f7-483f-9b4e-7b798920be25', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-14T08:57:03.592Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'b5afc1f3569d44378bc7539d', + 'e54804329577443d8685d3b1', + '7b10204ad48c464aac2b752a', + '8e96d562d33b4d6b85f2269e', + 'cd844644f15d4dbdb5772a3b', + '91f5159278ca420c8a0097b2', + '5f8cf34736df4cca962635c1', + '96e2169ddcf5408fb793aeb6', + '988d96959afb4ec08cd3cec4', + '173398cdf05d4838aeb5ad9f', + ], + shareId: '88c159a0-0273-4798-9d21-f95bd650bd30', + title: 'Test Shared Link 27', + updatedAt: '2024-05-08T20:07:46.345Z', + }, + { + conversationId: '41ee3f3f-36a5-4139-993a-1c4d7d055ccb', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-02-26T10:08:29.943Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: ['883cc3286240405ba558f322', '7ca7809f471e481fa9944795'], + shareId: '97dc26aa-c909-4a9c-91be-b605d25b9cf3', + title: 'Test Shared Link 28', + updatedAt: '2024-04-06T17:36:05.767Z', + }, + { + conversationId: '79e30f91-9b87-484c-8a12-6e4c6e8973d4', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-05-07T05:28:58.595Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'a8ac347785504b51bdad7ea7', + 'ce85321aecf64355b0362f8c', + '21a462895f37474d8d6acdfd', + '095d9104011e4534bda93294', + '503b6e27677c457289366a8d', + '1738d52a60004c9ba6f0c9ec', + 'a157fe44a67f4882a507941b', + '40e30dc275394eb4b9921db0', + 'f4ed9f2fb08640fcbacaa6a7', + 'bbac358328864dc2bfaa39da', + ], + shareId: 'aa36fc45-2a73-4fa2-a500-2a9148fca67d', + title: 'Test Shared Link 29', + updatedAt: '2024-01-26T16:45:59.269Z', + }, + { + conversationId: 'f5eaa000-3657-43d4-bc55-538108723b83', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-02-22T15:51:31.330Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'a87cfce565844b4ba9230dc5', + '426723bc4c22425e9bdf4b7b', + '73be5795469a444b8f1eca88', + '75a87212574a4cfc80d7d4e3', + '80f982dfc3e94535aed6e7d4', + '86d036c912c142ca8ec0f45a', + 'e3435fbbd4d2443eba30e97d', + 'e451e124aa964398b596af5d', + '1a13913f55e9442e8b5d7816', + ], + shareId: 'fe0f7ea2-74d2-40ba-acb2-437e61fc3bef', + title: 'Test Shared Link 30', + updatedAt: '2024-02-27T13:29:04.060Z', + }, + { + conversationId: 'a1ad92b4-6fac-44be-bad6-7648aeeba7af', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-10T09:32:22.242Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '5931cd94fbcd4fbcbaa20b91', + '0bc5f38ccc4f4b88afa42aed', + '7b4375d65f3f4524a79cb5f0', + 'd2ce098360ce4d19b6961017', + '847f5ee8d2df49a0ba1fd8a7', + '6164a71770c745ea8142a37c', + 'e98a0f1e15c846ac9b113608', + '5297d7df09b44d088cf80da5', + '62260b3f62ba423aa5c1962c', + '21fffc89d1d54e0190819384', + ], + shareId: 'ee5ae35d-540d-4a01-a938-ee7ee97b15ce', + title: 'Test Shared Link 31', + updatedAt: '2024-02-26T03:37:24.862Z', + }, + { + conversationId: '1e502d46-c710-4848-9bf2-674c08e51d9c', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-09T08:37:01.082Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'adb4bfb7657d4d7d92e82edf', + '70bdd81466e0408399b415d3', + 'ef99511981dc4c3baa18d372', + ], + shareId: 'b4fd8b63-7265-4825-89a4-9cebcbaadeee', + title: 'Test Shared Link 32', + updatedAt: '2024-02-27T04:32:40.654Z', + }, + { + conversationId: 'd1a43c39-f05e-4c6e-a8c2-0fcca9cb8928', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-26T15:03:25.546Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '07c070ab8a7541fea96b131c', + 'eb89cc57bcbb47ecb497cd5f', + '651999e46e734837b24c2500', + '608f9fbbbbb645e6b32d7d46', + ], + shareId: '5a4cf7d0-0abb-48c1-8e70-9f4ee3220dc4', + title: 'Test Shared Link 33', + updatedAt: '2024-04-06T21:39:51.521Z', + }, + { + conversationId: 'e549be2b-2623-42a3-8315-a8e35a7776b3', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-02-23T21:40:32.151Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'aa4e0b65650544589afb5961', + '160841178c0944e88de83956', + '234ac16af26d48a7875ee643', + ], + shareId: 'b083f048-2803-407e-b54a-89261db87ade', + title: 'Test Shared Link 34', + updatedAt: '2024-03-14T12:16:32.984Z', + }, + { + conversationId: '39f415ea-48f2-4bb2-b6f8-c2cf2d5fe42a', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-08T19:02:27.141Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'c77e79bb92b64d36a72d5a4d', + 'ea236310a9ba4b27a2217f09', + 'b25c46f2d23542f6b9d94de9', + ], + shareId: 'a9871169-7012-4206-b35c-7d207309a0f5', + title: 'Test Shared Link 35', + updatedAt: '2024-04-21T04:00:58.151Z', + }, + { + conversationId: 'c0d00265-12c4-45d0-a8bd-95d6e1bda769', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-14T09:50:55.476Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '63cdf78acd0449cf90237b29', + 'b93d82d7612b49fc98f0c211', + 'e56afe7e6e1e478d858a96d0', + '09344c8d22e74ce9b1d615cc', + ], + shareId: 'aa1262ab-54c9-406a-a97f-e2636266cf3e', + title: 'Test Shared Link 36', + updatedAt: '2024-03-24T15:53:36.021Z', + }, + { + conversationId: '5114b13d-8050-4e29-a2fd-85c776aec055', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-01-20T20:39:54.322Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'd397dc5c136a4c7da44e2fb9', + 'a3cd29243629450b87852a85', + '9dd1e0e918844a37ba8dc955', + 'ec2a73f7efe344fe85709c22', + '4d4702651869476b8ae397fd', + '8447430fd4f34aab82921018', + '8d804ee086734d6192b59995', + '29d6ccba37234bb8bd280977', + '31ec4f8c28cc4c21828ecef8', + '8ea630045b5847ec92651f4a', + ], + shareId: '2021fcab-7000-4840-9a8c-f0a1cb1ce8fa', + title: 'Test Shared Link 37', + updatedAt: '2024-04-08T02:09:33.732Z', + }, + { + conversationId: 'afa796fe-c8c1-411d-98d1-a8c8c8550412', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-01-16T23:58:11.179Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '8f54ee5871494f1b9f13f314', + '7778849398db40eb950952fb', + '65977e5d9e12445cb1cd9a54', + '8dba76884b09490a91b1aff9', + '2f6cc465171742b8a529daa3', + '1775b24fe2e94cd89dd6164e', + '780d980e59274240837c0bff', + ], + shareId: '9bb78460-0a26-4df7-be54-99b904b8084a', + title: 'Test Shared Link 38', + updatedAt: '2024-04-22T00:33:47.525Z', + }, + { + conversationId: 'c70fc447-acfc-4b57-84aa-2d8abcc3c5a5', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-24T11:39:14.696Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'b659ff86f9284ae1a40bee94', + '35bce7b6b2124db491f116c4', + 'cf0bad6c2623413babb33e65', + '26c6ce4d46614c86941d5429', + 'fba6517fc3434c188d8e1471', + '3e37398cc2ea4e50920d6271', + 'fd8584b1cf8145c88697b89d', + '8e433df0ada34e2280d4bd91', + 'fc52f80a6df24df5baccb657', + '95cdf9b05b8f4a81a70a37e9', + ], + shareId: '0664b078-8f29-41ff-8c1c-85172c659195', + title: 'Test Shared Link 39', + updatedAt: '2024-03-29T10:22:50.815Z', + }, + { + conversationId: '32ccaa36-cc46-4c84-888d-a86bf9b1d79c', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-02-04T03:13:19.399Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '8a0cfa8f5e874cf089f91b2e', + 'e9a72907ac9b4e88a8cfa737', + 'aa328aaf978944e18727a967', + '8786577a76b24415920d87a0', + 'ee05127d35ec415a85554406', + ], + shareId: 'a0018d28-52a8-4d31-8884-037cf9037eb7', + title: 'Test Shared Link 40', + updatedAt: '2024-01-30T03:26:15.920Z', + }, + { + conversationId: '2d8f1f40-b0e8-4629-937a-dee5036cb0bb', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-18T15:32:59.697Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '64475ed4f6234326a1104ca2', + 'db0db3ee92e14afaba6db75b', + '1f28a30501a94e3d896c261b', + 'de2eb08823db401d8262d3f3', + '254c32efae97476b954d8dc4', + 'dda42e4e74144cb69e395392', + '85bfe89de9e643fb8d5fa8ff', + '2f52e060a8b645928d0bf594', + ], + shareId: '9740b59b-cd84-461d-9fd7-2e1903b844b2', + title: 'Test Shared Link 41', + updatedAt: '2024-04-23T15:48:54.690Z', + }, + { + conversationId: '5180353f-23a9-48af-8ed0-b05983ef87d1', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-02-15T10:45:51.373Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '012b97e2df45475b93ad1e37', + '23d5042117a142f5a12762d5', + '8eb8cbca953d4ec18108f6d8', + 'ba506914203442339cd81d25', + '88c3b40cd0ae43d2b670ee41', + '0dd8fe241f5c4ea88730652c', + '80e3d1d7c26c489c9c8741fe', + '317a47a138c6499db73679f0', + '6497260d6a174f799cb56fd5', + ], + shareId: 'a6eaf23e-6e99-4e96-8222-82149c48803b', + title: 'Test Shared Link 42', + updatedAt: '2024-02-24T12:08:27.344Z', + }, + { + conversationId: 'cf3f2919-1840-4f6a-b350-f73f02ba6e90', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-02-14T06:20:45.439Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'ba3b939f8a3443f99f37b296', + 'b2039c988b3841c6b4ccb436', + '89ea6e1d4b3f440bb867d740', + '270210838a724aeb87e9bbe9', + '02dd6b2f185247d9888d5be1', + '6458fe13ee1c470ba33fb931', + ], + shareId: '765042c0-144d-4f7b-9953-0553ed438717', + title: 'Test Shared Link 43', + updatedAt: '2024-04-11T05:23:05.750Z', + }, + { + conversationId: '8efb71ee-7984-409a-b27c-aeb2650d78ba', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-01-28T16:41:04.100Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'cc60d9e2cbb7494481138833', + '1fb8d220b888475ba6c59fd3', + '5fd97817ab25451bb7ac22f5', + '9e8f7765a1bc4ab495da9081', + '4d5997d3c8744aaeb8c96964', + 'd438acb0f7704201857d6916', + 'b5106745d89f4a3fada8cd11', + '3b41562ce727411a83f44cdf', + '627f8f77feb843848145fc5f', + '6bee635eb10443ae9eef20ab', + ], + shareId: 'ed0fe440-479d-4c79-a494-0f461612c474', + title: 'Test Shared Link 44', + updatedAt: '2024-04-15T12:41:00.324Z', + }, + { + conversationId: '7cdd42a6-67bb-48c8-b8c3-bb55cbaa3905', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-24T23:13:42.892Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'a944f461ca094d1c80bea677', + 'bd4b516b51a84285846343b4', + '442f6b4c27f647199279e49c', + 'e672974b3cf74cd3b85537f9', + ], + shareId: '9439972e-226c-4386-910a-e629eb7019c3', + title: 'Test Shared Link 45', + updatedAt: '2024-01-17T07:42:21.103Z', + }, + { + conversationId: '595bab25-e5c1-4bd0-99c1-a099391adb87', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-13T05:58:33.171Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'c39942615fdf435cb22369b5', + '0ec24a7328424a78b7dcecaf', + '335373a769fd43a5833eac16', + '22905090a44f4bf8b6f415f8', + ], + shareId: '18501e23-3fc5-436d-a9aa-ccde7c5c9074', + title: 'Test Shared Link 46', + updatedAt: '2024-02-05T04:34:42.323Z', + }, + { + conversationId: '822a650b-2971-441a-9cb0-b2ecabf7b3ba', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-20T10:29:20.771Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'ed566d1ffd51494e9a069f32', + '3ca9a8bbfb7c43e49e4898d7', + '6534186966784f0fba45c1ab', + '8a9e394dda8542d4a4db1140', + '002d883a1c344de0beb794b3', + '61e9e872aa854288a4ac9694', + '11e465cb875746aaa5894327', + 'ead6b00c855f4907ac5070af', + ], + shareId: 'aaaf89e4-eb3d-45f8-9e24-f370d777d8f7', + title: 'Test Shared Link 47', + updatedAt: '2024-04-29T03:11:47.109Z', + }, + { + conversationId: 'ce68ce26-07fc-4448-9239-f1925cfaaa72', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-03-15T15:04:08.691Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + 'a1851d231ee748e59ed43494', + '363372c828d8443b81abffd4', + '0b2e97210bd14e229ddb6641', + ], + shareId: 'f4de7c43-c058-43f5-bdab-0854d939dfb9', + title: 'Test Shared Link 48', + updatedAt: '2024-03-05T11:43:00.177Z', + }, + { + conversationId: '3fafe417-b5f8-4cc8-ac8e-897ebef836bd', + user: '662dbe728ca96f444c6f69f4', + createdAt: '2024-04-20T05:34:57.880Z', + isAnonymous: true, + isPublic: true, + isVisible: true, + messages: [ + '876337c495ca40c080b65c1d', + 'b5e914ac15ff439a9836a9ea', + 'cb6379d0a9ad442291d78c14', + '529424b650a4478ba012cf40', + '99ff1ed49cb2483bbd970730', + '0f0e215e179f4cfba56c7b03', + '210940fbe4c745d183358ed1', + '99246c796c7a44c2ae85a549', + 'a2b967556867499eb437674a', + ], + shareId: '79ec3716-ea2e-4045-8a82-056d63ebc939', + title: 'Test Shared Link 49', + updatedAt: '2024-03-19T08:01:13.445Z', + }, + ], + pages: 49, + pageNumber: '1', + pageSize: 25, + }, + ], + pageParams: [null], +}; diff --git a/client/src/utils/sharedLink.spec.ts b/client/src/utils/sharedLink.spec.ts new file mode 100644 index 00000000000..1ead5ee2bec --- /dev/null +++ b/client/src/utils/sharedLink.spec.ts @@ -0,0 +1,149 @@ +import { sharedLinkData } from './sharedLink.fakeData'; +import { addSharedLink, updateSharedLink, deleteSharedLink } from './sharedLink'; + +import type { TSharedLink, SharedLinkListData } from 'librechat-data-provider'; + +describe('Shared Link Utilities', () => { + describe('addSharedLink', () => { + it('adds a new shared link to the top of the list', () => { + const data = { pages: [{ sharedLinks: [] }] }; + const newSharedLink = { shareId: 'new', updatedAt: '2023-04-02T12:00:00Z' }; + const newData = addSharedLink( + data as unknown as SharedLinkListData, + newSharedLink as TSharedLink, + ); + expect(newData.pages[0].sharedLinks).toHaveLength(1); + expect(newData.pages[0].sharedLinks[0].shareId).toBe('new'); + }); + it('does not add a shared link but updates it if it already exists', () => { + const data = { + pages: [ + { + sharedLinks: [ + { shareId: '1', updatedAt: '2023-04-01T12:00:00Z' }, + { shareId: '2', updatedAt: '2023-04-01T13:00:00Z' }, + ], + }, + ], + }; + const newSharedLink = { shareId: '2', updatedAt: '2023-04-02T12:00:00Z' }; + const newData = addSharedLink( + data as unknown as SharedLinkListData, + newSharedLink as TSharedLink, + ); + expect(newData.pages[0].sharedLinks).toHaveLength(2); + expect(newData.pages[0].sharedLinks[0].shareId).toBe('2'); + }); + }); + + describe('updateSharedLink', () => { + it('updates an existing shared link and moves it to the top', () => { + const initialData = { + pages: [ + { + sharedLinks: [ + { shareId: '1', updatedAt: '2023-04-01T12:00:00Z' }, + { shareId: '2', updatedAt: '2023-04-01T13:00:00Z' }, + ], + }, + ], + }; + const updatedSharedLink = { shareId: '1', updatedAt: '2023-04-02T12:00:00Z' }; + const newData = updateSharedLink( + initialData as unknown as SharedLinkListData, + updatedSharedLink as TSharedLink, + ); + expect(newData.pages[0].sharedLinks).toHaveLength(2); + expect(newData.pages[0].sharedLinks[0].shareId).toBe('1'); + }); + it('does not update a shared link if it does not exist', () => { + const initialData = { + pages: [ + { + sharedLinks: [ + { shareId: '1', updatedAt: '2023-04-01T12:00:00Z' }, + { shareId: '2', updatedAt: '2023-04-01T13:00:00Z' }, + ], + }, + ], + }; + const updatedSharedLink = { shareId: '3', updatedAt: '2023-04-02T12:00:00Z' }; + const newData = updateSharedLink( + initialData as unknown as SharedLinkListData, + updatedSharedLink as TSharedLink, + ); + expect(newData.pages[0].sharedLinks).toHaveLength(2); + expect(newData.pages[0].sharedLinks[0].shareId).toBe('1'); + }); + }); + + describe('deleteSharedLink', () => { + it('removes a shared link by id', () => { + const initialData = { + pages: [ + { + sharedLinks: [ + { shareId: '1', updatedAt: '2023-04-01T12:00:00Z' }, + { shareId: '2', updatedAt: '2023-04-01T13:00:00Z' }, + ], + }, + ], + }; + const newData = deleteSharedLink(initialData as unknown as SharedLinkListData, '1'); + expect(newData.pages[0].sharedLinks).toHaveLength(1); + expect(newData.pages[0].sharedLinks[0].shareId).not.toBe('1'); + }); + + it('does not remove a shared link if it does not exist', () => { + const initialData = { + pages: [ + { + sharedLinks: [ + { shareId: '1', updatedAt: '2023-04-01T12:00:00Z' }, + { shareId: '2', updatedAt: '2023-04-01T13:00:00Z' }, + ], + }, + ], + }; + const newData = deleteSharedLink(initialData as unknown as SharedLinkListData, '3'); + expect(newData.pages[0].sharedLinks).toHaveLength(2); + }); + }); +}); + +describe('Shared Link Utilities with Fake Data', () => { + describe('addSharedLink', () => { + it('adds a new shared link to the existing fake data', () => { + const newSharedLink = { + shareId: 'new', + updatedAt: new Date().toISOString(), + } as TSharedLink; + const initialLength = sharedLinkData.pages[0].sharedLinks.length; + const newData = addSharedLink(sharedLinkData, newSharedLink); + expect(newData.pages[0].sharedLinks.length).toBe(initialLength + 1); + expect(newData.pages[0].sharedLinks[0].shareId).toBe('new'); + }); + }); + + describe('updateSharedLink', () => { + it('updates an existing shared link within fake data', () => { + const updatedSharedLink = { + ...sharedLinkData.pages[0].sharedLinks[0], + title: 'Updated Title', + }; + const newData = updateSharedLink(sharedLinkData, updatedSharedLink); + expect(newData.pages[0].sharedLinks[0].title).toBe('Updated Title'); + }); + }); + + describe('deleteSharedLink', () => { + it('removes a shared link by id from fake data', () => { + const shareIdToDelete = sharedLinkData.pages[0].sharedLinks[0].shareId as string; + const newData = deleteSharedLink(sharedLinkData, shareIdToDelete); + const deletedDataExists = newData.pages[0].sharedLinks.some( + (c) => c.shareId === shareIdToDelete, + ); + expect(deletedDataExists).toBe(false); + }); + }); +}); diff --git a/client/src/utils/sharedLink.ts b/client/src/utils/sharedLink.ts new file mode 100644 index 00000000000..4eb2a7bc995 --- /dev/null +++ b/client/src/utils/sharedLink.ts @@ -0,0 +1,30 @@ +import { SharedLinkListData, SharedLinkListResponse, TSharedLink } from 'librechat-data-provider'; +import { addData, deleteData, updateData } from './collection'; +import { InfiniteData } from '@tanstack/react-query'; + +export const addSharedLink = ( + data: InfiniteData, + newSharedLink: TSharedLink, +): SharedLinkListData => { + return addData(data, 'sharedLinks', newSharedLink, (page) => + page.sharedLinks.findIndex((c) => c.shareId === newSharedLink.shareId), + ); +}; + +export const updateSharedLink = ( + data: InfiniteData, + newSharedLink: TSharedLink, +): SharedLinkListData => { + return updateData( + data, + 'sharedLinks', + newSharedLink, + (page) => page.sharedLinks.findIndex((c) => c.shareId === newSharedLink.shareId), + ); +}; + +export const deleteSharedLink = (data: SharedLinkListData, shareId: string): SharedLinkListData => { + return deleteData(data, 'sharedLinks', (page) => + page.sharedLinks.findIndex((c) => c.shareId === shareId), + ); +}; diff --git a/packages/data-provider/src/api-endpoints.ts b/packages/data-provider/src/api-endpoints.ts index 2e56efb8d0f..e1fdd55fb06 100644 --- a/packages/data-provider/src/api-endpoints.ts +++ b/packages/data-provider/src/api-endpoints.ts @@ -7,6 +7,13 @@ export const userPlugins = () => '/api/user/plugins'; export const messages = (conversationId: string, messageId?: string) => `/api/messages/${conversationId}${messageId ? `/${messageId}` : ''}`; +const shareRoot = '/api/share'; +export const shareMessages = (shareId: string) => `${shareRoot}/${shareId}`; +export const getSharedLinks = (pageNumber: string, isPublic: boolean) => + `${shareRoot}?pageNumber=${pageNumber}&isPublic=${isPublic}`; +export const createSharedLink = shareRoot; +export const updateSharedLink = shareRoot; + const keysEndpoint = '/api/keys'; export const keys = () => keysEndpoint; diff --git a/packages/data-provider/src/data-service.ts b/packages/data-provider/src/data-service.ts index b3346190963..55fe8f459b1 100644 --- a/packages/data-provider/src/data-service.ts +++ b/packages/data-provider/src/data-service.ts @@ -31,6 +31,34 @@ export function getMessagesByConvoId(conversationId: string): Promise { + return request.get(endpoints.shareMessages(shareId)); +} + +export const listSharedLinks = ( + params?: q.SharedLinkListParams, +): Promise => { + const pageNumber = params?.pageNumber || '1'; // Default to page 1 if not provided + const isPublic = params?.isPublic || true; // Default to true if not provided + return request.get(endpoints.getSharedLinks(pageNumber, isPublic)); +}; + +export function getSharedLink(shareId: string): Promise { + return request.get(endpoints.shareMessages(shareId)); +} + +export function createSharedLink(payload: t.TSharedLinkRequest): Promise { + return request.post(endpoints.createSharedLink, payload); +} + +export function updateSharedLink(payload: t.TSharedLinkRequest): Promise { + return request.patch(endpoints.updateSharedLink, payload); +} + +export function deleteSharedLink(shareId: string): Promise { + return request.delete(endpoints.shareMessages(shareId)); +} + export function updateMessage(payload: t.TUpdateMessageRequest): Promise { const { conversationId, messageId, text } = payload; if (!conversationId) { diff --git a/packages/data-provider/src/keys.ts b/packages/data-provider/src/keys.ts index aa664f8c39a..13f894ac8ad 100644 --- a/packages/data-provider/src/keys.ts +++ b/packages/data-provider/src/keys.ts @@ -1,5 +1,7 @@ export enum QueryKeys { messages = 'messages', + sharedMessages = 'sharedMessages', + sharedLinks = 'sharedLinks', allConversations = 'allConversations', archivedConversations = 'archivedConversations', searchConversations = 'searchConversations', diff --git a/packages/data-provider/src/react-query/react-query-service.ts b/packages/data-provider/src/react-query/react-query-service.ts index bb898939ea1..796cf6c79b1 100644 --- a/packages/data-provider/src/react-query/react-query-service.ts +++ b/packages/data-provider/src/react-query/react-query-service.ts @@ -60,6 +60,22 @@ export const useGetMessagesByConvoId = ( ); }; +export const useGetSharedMessages = ( + shareId: string, + config?: UseQueryOptions, +): QueryObserverResult => { + return useQuery( + [QueryKeys.sharedMessages, shareId], + () => dataService.getSharedMessages(shareId), + { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config, + }, + ); +}; + export const useGetUserBalance = ( config?: UseQueryOptions, ): QueryObserverResult => { @@ -398,7 +414,9 @@ export const useUpdateUserPluginsMutation = (): UseMutationResult< }); }; -export const useGetStartupConfig = (): QueryObserverResult => { +export const useGetStartupConfig = ( + config?: UseQueryOptions, +): QueryObserverResult => { return useQuery( [QueryKeys.startupConfig], () => dataService.getStartupConfig(), @@ -406,6 +424,7 @@ export const useGetStartupConfig = (): QueryObserverResult => refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, + ...config, }, ); }; diff --git a/packages/data-provider/src/schemas.ts b/packages/data-provider/src/schemas.ts index 53f8535f100..942b2f8fd1e 100644 --- a/packages/data-provider/src/schemas.ts +++ b/packages/data-provider/src/schemas.ts @@ -381,6 +381,19 @@ export type TConversation = z.infer & { presetOverride?: Partial; }; +export const tSharedLinkSchema = z.object({ + conversationId: z.string(), + shareId: z.string(), + messages: z.array(z.string()), + isAnonymous: z.boolean(), + isPublic: z.boolean(), + isVisible: z.boolean(), + title: z.string(), + createdAt: z.string(), + updatedAt: z.string(), +}); +export type TSharedLink = z.infer; + export const openAISchema = tConversationSchema .pick({ model: true, diff --git a/packages/data-provider/src/types.ts b/packages/data-provider/src/types.ts index 895b763fe6e..5efdece1623 100644 --- a/packages/data-provider/src/types.ts +++ b/packages/data-provider/src/types.ts @@ -1,5 +1,12 @@ import OpenAI from 'openai'; -import type { TResPlugin, TMessage, TConversation, EModelEndpoint, ImageDetail } from './schemas'; +import type { + TResPlugin, + TMessage, + TConversation, + EModelEndpoint, + ImageDetail, + TSharedLink, +} from './schemas'; import type { TSpecsConfig } from './models'; export type TOpenAIMessage = OpenAI.Chat.ChatCompletionMessageParam; export type TOpenAIFunction = OpenAI.Chat.ChatCompletionCreateParams.Function; @@ -133,6 +140,19 @@ export type TArchiveConversationRequest = { export type TArchiveConversationResponse = TConversation; +export type TSharedMessagesResponse = Omit & { + messages: TMessage[]; +}; +export type TSharedLinkRequest = Partial< + Omit +> & { + conversationId: string; +}; + +export type TSharedLinkResponse = TSharedLink; +export type TSharedLinksResponse = TSharedLink[]; +export type TDeleteSharedLinkResponse = TSharedLink; + export type TForkConvoRequest = { messageId: string; conversationId: string; diff --git a/packages/data-provider/src/types/mutations.ts b/packages/data-provider/src/types/mutations.ts index 57b65ab5756..421d0dd42cf 100644 --- a/packages/data-provider/src/types/mutations.ts +++ b/packages/data-provider/src/types/mutations.ts @@ -90,3 +90,13 @@ export type DeleteConversationOptions = MutationOptions< >; export type ForkConvoOptions = MutationOptions; + +export type CreateSharedLinkOptions = MutationOptions< + types.TSharedLink, + Partial +>; +export type UpdateSharedLinkOptions = MutationOptions< + types.TSharedLink, + Partial +>; +export type DeleteSharedLinkOptions = MutationOptions; diff --git a/packages/data-provider/src/types/queries.ts b/packages/data-provider/src/types/queries.ts index 18e56ecca1e..ad4c4db89e9 100644 --- a/packages/data-provider/src/types/queries.ts +++ b/packages/data-provider/src/types/queries.ts @@ -1,5 +1,5 @@ import type { InfiniteData } from '@tanstack/react-query'; -import type { TMessage, TConversation } from '../schemas'; +import type { TMessage, TConversation, TSharedLink } from '../schemas'; export type Conversation = { id: string; createdAt: number; @@ -16,7 +16,7 @@ export type ConversationListParams = { order?: 'asc' | 'desc'; pageNumber: string; // Add this line conversationId?: string; - isArchived: boolean; + isArchived?: boolean; }; // Type for the response from the conversation list API @@ -33,3 +33,24 @@ export type ConversationUpdater = ( data: ConversationData, conversation: TConversation, ) => ConversationData; + +export type SharedMessagesResponse = Omit & { + messages: TMessage[]; +}; +export type SharedLinkListParams = Omit & { + isPublic?: boolean; +}; + +export type SharedLinksResponse = Omit & { + sharedLinks: TSharedLink[]; +}; + +// Type for the response from the conversation list API +export type SharedLinkListResponse = { + sharedLinks: TSharedLink[]; + pageNumber: string; + pageSize: string | number; + pages: string | number; +}; + +export type SharedLinkListData = InfiniteData;