🚀 feat: Shared Links (#2772)

*  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<TCollection>. 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 <danacordially@gmail.com>

* 🚑 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 <ohneda@gmail.com>

* 📤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 <ohneda@gmail.com>
Co-authored-by: bsu3338 <bsu3338@users.noreply.github.com>
Co-authored-by: Fuegovic <32828263+fuegovic@users.noreply.github.com>
This commit is contained in:
Danny Avila 2024-05-17 18:13:32 -04:00 committed by GitHub
parent 38ad36c1c5
commit f0e8cca5df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
78 changed files with 4683 additions and 317 deletions

89
api/models/Share.js Normal file
View file

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

View file

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

View file

@ -85,6 +85,7 @@ const startServer = async () => {
app.use('/api/assistants', routes.assistants); app.use('/api/assistants', routes.assistants);
app.use('/api/files', await routes.files.initialize()); app.use('/api/files', await routes.files.initialize());
app.use('/images/', validateImageRequest, routes.staticRoute); app.use('/images/', validateImageRequest, routes.staticRoute);
app.use('/api/share', routes.share);
app.use((req, res) => { app.use((req, res) => {
res.status(404).sendFile(path.join(app.locals.paths.dist, 'index.html')); res.status(404).sendFile(path.join(app.locals.paths.dist, 'index.html'));

View file

@ -18,6 +18,7 @@ const config = require('./config');
const assistants = require('./assistants'); const assistants = require('./assistants');
const files = require('./files'); const files = require('./files');
const staticRoute = require('./static'); const staticRoute = require('./static');
const share = require('./share');
module.exports = { module.exports = {
search, search,
@ -40,4 +41,5 @@ module.exports = {
assistants, assistants,
files, files,
staticRoute, staticRoute,
share,
}; };

View file

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

View file

@ -0,0 +1,5 @@
import { createContext, useContext } from 'react';
type TShareContext = { isSharedConvo?: boolean };
export const ShareContext = createContext<TShareContext>({} as TShareContext);
export const useShareContext = () => useContext(ShareContext);

View file

@ -1,6 +1,7 @@
export { default as ToastProvider } from './ToastContext'; export { default as ToastProvider } from './ToastContext';
export { default as AssistantsProvider } from './AssistantsContext'; export { default as AssistantsProvider } from './AssistantsContext';
export * from './ChatContext'; export * from './ChatContext';
export * from './ShareContext';
export * from './ToastContext'; export * from './ToastContext';
export * from './SearchContext'; export * from './SearchContext';
export * from './FileMapContext'; export * from './FileMapContext';

View file

@ -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 (
<HoverToggle
isActiveConvo={!!isActiveConvo}
isPopoverActive={isPopoverActive}
setIsPopoverActive={setIsPopoverActive}
>
<DropDownMenu
icon={<Upload />}
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 && (
<>
<ExportButton conversation={conversation} setPopoverActive={setIsPopoverActive} />
<ShareButton
conversationId={conversation.conversationId}
title={conversation.title ?? ''}
appendLabel={true}
className="mb-[3.5px]"
setPopoverActive={setIsPopoverActive}
/>
</>
)}
</DropDownMenu>
</HoverToggle>
);
}

View file

@ -1,68 +1,41 @@
import React from 'react'; import React from 'react';
import { useState } from 'react'; import { useState } from 'react';
import { useLocation } from 'react-router-dom';
import type { TConversation } from 'librechat-data-provider'; import type { TConversation } from 'librechat-data-provider';
import { Upload } from 'lucide-react'; import { Upload } from 'lucide-react';
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '~/components/ui';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
import { ExportModal } from '../Nav'; 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 localize = useLocalize();
const location = useLocation();
const [showExports, setShowExports] = useState(false); 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 = () => { const clickHandler = () => {
if (exportable) {
setShowExports(true); setShowExports(true);
}
}; };
const exportable = const onOpenChange = (value: boolean) => {
conversation && setShowExports(value);
conversation.conversationId && setPopoverActive(value);
conversation.conversationId !== 'new' && };
conversation.conversationId !== 'search';
return ( return (
<> <>
{exportable && (
<div className="flex gap-1 gap-2 pr-1">
<TooltipProvider delayDuration={50}>
<Tooltip>
<TooltipTrigger asChild>
<button <button
className="btn btn-neutral btn-small relative flex h-9 w-9 items-center justify-center whitespace-nowrap rounded-lg"
onClick={clickHandler} onClick={clickHandler}
className="group m-1.5 flex w-full cursor-pointer items-center gap-2 rounded p-2.5 text-sm hover:bg-gray-200 focus-visible:bg-gray-200 focus-visible:outline-0 radix-disabled:pointer-events-none radix-disabled:opacity-50 dark:hover:bg-gray-600 dark:focus-visible:bg-gray-600"
> >
<div className="flex w-full items-center justify-center gap-2"> <Upload size={16} /> {localize('com_nav_export')}
<Upload size={16} />
</div>
</button> </button>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={5}>
{localize('com_nav_export_conversation')}
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
)}
{showExports && ( {showExports && (
<ExportModal open={showExports} onOpenChange={setShowExports} conversation={conversation} /> <ExportModal open={showExports} onOpenChange={onOpenChange} conversation={conversation} />
)} )}
</> </>
); );

View file

@ -3,7 +3,7 @@ import { Constants } from 'librechat-data-provider';
import { useGetStartupConfig } from 'librechat-data-provider/react-query'; import { useGetStartupConfig } from 'librechat-data-provider/react-query';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
export default function Footer() { export default function Footer({ className }: { className?: string }) {
const { data: config } = useGetStartupConfig(); const { data: config } = useGetStartupConfig();
const localize = useLocalize(); const localize = useLocalize();
@ -52,7 +52,12 @@ export default function Footer() {
); );
return ( return (
<div className="relative flex items-center justify-center gap-2 px-2 py-2 text-xs text-gray-600 dark:text-gray-300 md:px-[60px]"> <div
className={
className ||
'relative flex items-center justify-center gap-2 px-2 py-2 text-xs text-gray-600 dark:text-gray-300 md:px-[60px]'
}
>
{footerElements.map((contentRender, index) => { {footerElements.map((contentRender, index) => {
const isLastElement = index === footerElements.length - 1; const isLastElement = index === footerElements.length - 1;
return ( return (

View file

@ -6,6 +6,7 @@ import type { ContextType } from '~/common';
import { EndpointsMenu, ModelSpecsMenu, PresetsMenu, HeaderNewChat } from './Menus'; import { EndpointsMenu, ModelSpecsMenu, PresetsMenu, HeaderNewChat } from './Menus';
import HeaderOptions from './Input/HeaderOptions'; import HeaderOptions from './Input/HeaderOptions';
import ExportButton from './ExportButton'; import ExportButton from './ExportButton';
import ExportAndShareMenu from './ExportAndShareMenu';
const defaultInterface = getConfigDefaults().interface; const defaultInterface = getConfigDefaults().interface;
@ -28,7 +29,7 @@ export default function Header() {
{<HeaderOptions interfaceConfig={interfaceConfig} />} {<HeaderOptions interfaceConfig={interfaceConfig} />}
{interfaceConfig.presets && <PresetsMenu />} {interfaceConfig.presets && <PresetsMenu />}
</div> </div>
<ExportButton /> <ExportAndShareMenu />
</div> </div>
{/* Empty div for spacing */} {/* Empty div for spacing */}
<div /> <div />

View file

@ -87,7 +87,7 @@ export default function HoverButtons({
isCopied ? localize('com_ui_copied_to_clipboard') : localize('com_ui_copy_to_clipboard') isCopied ? localize('com_ui_copied_to_clipboard') : localize('com_ui_copy_to_clipboard')
} }
> >
{isCopied ? <CheckMark /> : <Clipboard />} {isCopied ? <CheckMark className="h-[18px] w-[18px]" /> : <Clipboard />}
</button> </button>
{regenerateEnabled ? ( {regenerateEnabled ? (
<button <button

View file

@ -0,0 +1,29 @@
import { useState } from 'react';
import type { TMessage } from 'librechat-data-provider';
import { useLocalize, useCopyToClipboard } from '~/hooks';
import { Clipboard, CheckMark } from '~/components/svg';
type THoverButtons = {
message: TMessage;
};
export default function MinimalHoverButtons({ message }: THoverButtons) {
const localize = useLocalize();
const [isCopied, setIsCopied] = useState(false);
const copyToClipboard = useCopyToClipboard({ text: message.text, content: message.content });
return (
<div className="visible mt-0 flex justify-center gap-1 self-end text-gray-400 lg:justify-start">
<button
className="ml-0 flex items-center gap-1.5 rounded-md p-1 text-xs hover:text-gray-900 dark:text-gray-400/70 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400 md:group-hover:visible md:group-[.final-completion]:visible"
onClick={() => copyToClipboard(setIsCopied)}
type="button"
title={
isCopied ? localize('com_ui_copied_to_clipboard') : localize('com_ui_copy_to_clipboard')
}
>
{isCopied ? <CheckMark className="h-[18px] w-[18px]" /> : <Clipboard />}
</button>
</div>
);
}

View file

@ -1,6 +1,7 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { useAuthContext, useLocalize } from '~/hooks'; import { useAuthContext, useLocalize } from '~/hooks';
import type { TMessageProps } from '~/common'; import type { TMessageProps } from '~/common';
import MinimalHoverButtons from '~/components/Chat/Messages/MinimalHoverButtons';
import Icon from '~/components/Chat/Messages/MessageIcon'; import Icon from '~/components/Chat/Messages/MessageIcon';
import SearchContent from './Content/SearchContent'; import SearchContent from './Content/SearchContent';
import SearchButtons from './SearchButtons'; import SearchButtons from './SearchButtons';
@ -50,6 +51,7 @@ export default function Message({ message }: Pick<TMessageProps, 'message'>) {
</div> </div>
</div> </div>
<SubRow classes="text-xs"> <SubRow classes="text-xs">
<MinimalHoverButtons message={message} />
<SearchButtons message={message} /> <SearchButtons message={message} />
</SubRow> </SubRow>
</div> </div>

View file

@ -9,13 +9,14 @@ import { useConversations, useNavigateToConvo } from '~/hooks';
import { NotificationSeverity } from '~/common'; import { NotificationSeverity } from '~/common';
import { ArchiveIcon } from '~/components/svg'; import { ArchiveIcon } from '~/components/svg';
import { useToastContext } from '~/Providers'; import { useToastContext } from '~/Providers';
import EditMenuButton from './EditMenuButton'; import DropDownMenu from './DropDownMenu';
import ArchiveButton from './ArchiveButton'; import ArchiveButton from './ArchiveButton';
import DeleteButton from './DeleteButton'; import DeleteButton from './DeleteButton';
import RenameButton from './RenameButton'; import RenameButton from './RenameButton';
import HoverToggle from './HoverToggle'; import HoverToggle from './HoverToggle';
import { cn } from '~/utils'; import { cn } from '~/utils';
import store from '~/store'; import store from '~/store';
import ShareButton from './ShareButton';
type KeyEvent = KeyboardEvent<HTMLInputElement>; type KeyEvent = KeyboardEvent<HTMLInputElement>;
@ -124,7 +125,15 @@ export default function Conversation({ conversation, retainView, toggleNav, isLa
isPopoverActive={isPopoverActive} isPopoverActive={isPopoverActive}
setIsPopoverActive={setIsPopoverActive} setIsPopoverActive={setIsPopoverActive}
> >
<EditMenuButton> <DropDownMenu>
<ShareButton
conversationId={conversationId}
title={title}
appendLabel={true}
className="mb-[3.5px]"
setPopoverActive={setIsPopoverActive}
/>
<RenameButton <RenameButton
renaming={renaming} renaming={renaming}
onRename={onRename} onRename={onRename}
@ -140,7 +149,7 @@ export default function Conversation({ conversation, retainView, toggleNav, isLa
appendLabel={true} appendLabel={true}
className="group m-1.5 mt-[3.5px] flex w-full cursor-pointer items-center gap-2 rounded p-2.5 text-sm hover:bg-gray-200 focus-visible:bg-gray-200 focus-visible:outline-0 radix-disabled:pointer-events-none radix-disabled:opacity-50 dark:hover:bg-gray-600 dark:focus-visible:bg-gray-600" className="group m-1.5 mt-[3.5px] flex w-full cursor-pointer items-center gap-2 rounded p-2.5 text-sm hover:bg-gray-200 focus-visible:bg-gray-200 focus-visible:outline-0 radix-disabled:pointer-events-none radix-disabled:opacity-50 dark:hover:bg-gray-600 dark:focus-visible:bg-gray-600"
/> />
</EditMenuButton> </DropDownMenu>
<ArchiveButton <ArchiveButton
className="z-50 hover:text-black dark:hover:text-white" className="z-50 hover:text-black dark:hover:text-white"
conversationId={conversationId} conversationId={conversationId}
@ -156,7 +165,7 @@ export default function Conversation({ conversation, retainView, toggleNav, isLa
onClick={clickHandler} onClick={clickHandler}
className={cn( className={cn(
isActiveConvo || isPopoverActive isActiveConvo || isPopoverActive
? 'group relative mt-2 flex cursor-pointer items-center gap-2 break-all rounded-lg rounded-lg bg-gray-200 px-2 py-2 active:opacity-50 dark:bg-gray-700' ? 'group relative mt-2 flex cursor-pointer items-center gap-2 break-all rounded-lg bg-gray-200 px-2 py-2 active:opacity-50 dark:bg-gray-700'
: 'group relative mt-2 flex grow cursor-pointer items-center gap-2 overflow-hidden whitespace-nowrap break-all rounded-lg rounded-lg px-2 py-2 hover:bg-gray-200 active:opacity-50 dark:hover:bg-gray-700', : 'group relative mt-2 flex grow cursor-pointer items-center gap-2 overflow-hidden whitespace-nowrap break-all rounded-lg rounded-lg px-2 py-2 hover:bg-gray-200 active:opacity-50 dark:hover:bg-gray-700',
!isActiveConvo && !renaming ? 'peer-hover:bg-gray-200 dark:peer-hover:bg-gray-800' : '', !isActiveConvo && !renaming ? 'peer-hover:bg-gray-200 dark:peer-hover:bg-gray-800' : '',
)} )}

View file

@ -1,4 +1,4 @@
import type { FC } from 'react'; import { cloneElement, type FC } from 'react';
import { DotsIcon } from '~/components/svg'; import { DotsIcon } from '~/components/svg';
import { Content, Portal, Root, Trigger } from '@radix-ui/react-popover'; import { Content, Portal, Root, Trigger } from '@radix-ui/react-popover';
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '~/components/ui'; import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '~/components/ui';
@ -6,15 +6,23 @@ import { useToggle } from './ToggleContext';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
import { cn } from '~/utils'; import { cn } from '~/utils';
type EditMenuButtonProps = { type DropDownMenuProps = {
children: React.ReactNode; children: React.ReactNode;
icon?: React.ReactElement;
tooltip?: string;
className?: string;
}; };
const EditMenuButton: FC<EditMenuButtonProps> = ({ children }: EditMenuButtonProps) => { const DropDownMenu: FC<DropDownMenuProps> = ({
children,
icon = <DotsIcon />,
tooltip = 'More',
className,
}: DropDownMenuProps) => {
const localize = useLocalize(); const localize = useLocalize();
const { setPopoverActive } = useToggle(); const { isPopoverActive, setPopoverActive } = useToggle();
return ( return (
<Root onOpenChange={(open) => setPopoverActive(open)}> <Root open={isPopoverActive} onOpenChange={(open) => setPopoverActive(open)}>
<Trigger asChild> <Trigger asChild>
<div <div
className={cn( className={cn(
@ -29,12 +37,15 @@ const EditMenuButton: FC<EditMenuButtonProps> = ({ children }: EditMenuButtonPro
<TooltipProvider delayDuration={500}> <TooltipProvider delayDuration={500}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button type="button" className=""> <button type="button" className={className}>
<DotsIcon className="h-[18px] w-[18px] flex-shrink-0 text-gray-500 hover:text-gray-400 dark:text-gray-300 dark:hover:text-gray-400" /> {cloneElement(icon, {
className:
'h-[18px] w-[18px] flex-shrink-0 text-gray-500 hover:text-gray-400 dark:text-gray-300 dark:hover:text-gray-400',
})}
</button> </button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side="top" sideOffset={0}> <TooltipContent side="top" sideOffset={0}>
{localize('com_ui_more_options')} {tooltip}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
@ -57,4 +68,4 @@ const EditMenuButton: FC<EditMenuButtonProps> = ({ children }: EditMenuButtonPro
); );
}; };
export default EditMenuButton; export default DropDownMenu;

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React from 'react';
import { ToggleContext } from './ToggleContext'; import { ToggleContext } from './ToggleContext';
import { cn } from '~/utils'; import { cn } from '~/utils';
@ -15,7 +15,7 @@ const HoverToggle = ({
}) => { }) => {
const setPopoverActive = (value: boolean) => setIsPopoverActive(value); const setPopoverActive = (value: boolean) => setIsPopoverActive(value);
return ( return (
<ToggleContext.Provider value={{ setPopoverActive }}> <ToggleContext.Provider value={{ isPopoverActive, setPopoverActive }}>
<div <div
className={cn( className={cn(
'peer absolute bottom-0 right-0 top-0 items-center gap-1.5 rounded-r-lg from-gray-500 from-gray-900 pl-2 pr-2 dark:text-white', 'peer absolute bottom-0 right-0 top-0 items-center gap-1.5 rounded-r-lg from-gray-500 from-gray-900 pl-2 pr-2 dark:text-white',

View file

@ -0,0 +1,113 @@
import { useState } from 'react';
import {
Dialog,
Tooltip,
DialogTrigger,
TooltipContent,
TooltipTrigger,
TooltipProvider,
} from '~/components/ui';
import { Share2Icon } from 'lucide-react';
import type { TSharedLink } from 'librechat-data-provider';
import DialogTemplate from '~/components/ui/DialogTemplate';
import SharedLinkButton from './SharedLinkButton';
import ShareDialog from './ShareDialog';
import { useLocalize } from '~/hooks';
import { cn } from '~/utils';
export default function ShareButton({
conversationId,
title,
className,
appendLabel = false,
setPopoverActive,
}: {
conversationId: string;
title: string;
className?: string;
appendLabel?: boolean;
setPopoverActive: (isActive: boolean) => void;
}) {
const localize = useLocalize();
const [share, setShare] = useState<TSharedLink | null>(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 (
<>
<Share2Icon className="h-4 w-4" /> {localize('com_ui_share')}
</>
);
}
return (
<TooltipProvider delayDuration={250}>
<Tooltip>
<TooltipTrigger asChild>
<span>
<Share2Icon />
</span>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={0}>
{localize('com_ui_share')}
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
};
const buttons = share && (
<SharedLinkButton
share={share}
conversationId={conversationId}
setShare={setShare}
isUpdated={isUpdated}
setIsUpdated={setIsUpdated}
/>
);
const onOpenChange = (open: boolean) => {
setPopoverActive(open);
setOpen(open);
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogTrigger asChild>
<button
className={cn(
'group m-1.5 flex w-full cursor-pointer items-center gap-2 rounded p-2.5 text-sm hover:bg-gray-200 focus-visible:bg-gray-200 focus-visible:outline-0 radix-disabled:pointer-events-none radix-disabled:opacity-50 dark:hover:bg-gray-600 dark:focus-visible:bg-gray-600',
className,
)}
>
{renderShareButton()}
</button>
</DialogTrigger>
<DialogTemplate
buttons={buttons}
showCloseButton={true}
showCancelButton={false}
title={localize('com_ui_share_link_to_chat')}
className="max-w-[550px]"
main={
<>
<ShareDialog
setDialogOpen={setOpen}
conversationId={conversationId}
title={title}
share={share}
setShare={setShare}
isUpdated={isUpdated}
/>
</>
}
/>
</Dialog>
);
}

View file

@ -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 (
<div>
<div className="h-full py-2 text-gray-400 dark:text-gray-200">
{(() => {
if (isLoading) {
return <Spinner className="m-auto h-14 animate-spin" />;
}
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');
})()}
</div>
</div>
);
}

View file

@ -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: (
<>
<Copy className="mr-2 h-4 w-4" />
{localize('com_ui_copy_link')}
</>
),
};
}
if (share?.isPublic) {
return {
handler: async () => {
await updateSharedLink();
},
label: (
<>
<Link className="mr-2 h-4 w-4" />
{localize('com_ui_update_link')}
</>
),
};
}
return {
handler: updateSharedLink,
label: (
<>
<Link className="mr-2 h-4 w-4" />
{localize('com_ui_create_link')}
</>
),
};
};
const handlers = getHandler();
return (
<Button
disabled={isLoading || isCopying}
onClick={() => {
handlers.handler();
}}
className="min-w-32 whitespace-nowrap bg-green-500 text-white hover:bg-green-600 dark:bg-green-600 dark:text-white dark:hover:bg-green-800"
>
{isCopying && (
<>
<Copy className="mr-2 h-4 w-4" />
{localize('com_ui_copied')}
</>
)}
{!isCopying && !isLoading && handlers.label}
{!isCopying && isLoading && <Spinner className="h-4 w-4" />}
</Button>
);
}

View file

@ -3,6 +3,7 @@ import { createContext, useContext } from 'react';
const defaultFunction: (value: boolean) => void = () => ({}); const defaultFunction: (value: boolean) => void = () => ({});
export const ToggleContext = createContext({ export const ToggleContext = createContext({
setPopoverActive: defaultFunction, setPopoverActive: defaultFunction,
isPopoverActive: false,
}); });
export const useToggle = () => useContext(ToggleContext); export const useToggle = () => useContext(ToggleContext);

View file

@ -1,36 +1,14 @@
import { EModelEndpoint } from 'librechat-data-provider'; import { UserIcon } from '~/components/svg';
import UnknownIcon from '~/components/Chat/Menus/Endpoints/UnknownIcon';
import {
Plugin,
GPTIcon,
UserIcon,
PaLMIcon,
CodeyIcon,
GeminiIcon,
AssistantIcon,
AnthropicIcon,
AzureMinimalIcon,
CustomMinimalIcon,
} from '~/components/svg';
import { useAuthContext } from '~/hooks/AuthContext'; import { useAuthContext } from '~/hooks/AuthContext';
import useAvatar from '~/hooks/Messages/useAvatar'; import useAvatar from '~/hooks/Messages/useAvatar';
import useLocalize from '~/hooks/useLocalize'; import useLocalize from '~/hooks/useLocalize';
import { IconProps } from '~/common'; import { IconProps } from '~/common';
import { cn } from '~/utils'; import { cn } from '~/utils';
import MessageEndpointIcon from './MessageEndpointIcon';
const Icon: React.FC<IconProps> = (props) => { const Icon: React.FC<IconProps> = (props) => {
const { user } = useAuthContext(); const { user } = useAuthContext();
const { const { size = 30, isCreatedByUser } = props;
error,
button,
iconURL,
endpoint,
jailbreak,
size = 30,
model = '',
assistantName,
isCreatedByUser,
} = props;
const avatarSrc = useAvatar(user); const avatarSrc = useAvatar(user);
const localize = useLocalize(); const localize = useLocalize();
@ -65,141 +43,7 @@ const Icon: React.FC<IconProps> = (props) => {
</div> </div>
); );
} }
return <MessageEndpointIcon {...props} />;
const endpointIcons = {
[EModelEndpoint.assistants]: {
icon: props.iconURL ? (
<div className="relative flex h-6 w-6 items-center justify-center">
<div
title={assistantName}
style={{
width: size,
height: size,
}}
className={cn('overflow-hidden rounded-full', props.className ?? '')}
>
<img
className="shadow-stroke h-full w-full object-cover"
src={props.iconURL}
alt={assistantName}
style={{ height: '80', width: '80' }}
/>
</div>
</div>
) : (
<div className="h-6 w-6">
<div className="shadow-stroke flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
<AssistantIcon className="h-2/3 w-2/3 text-gray-400" />
</div>
</div>
),
name: endpoint,
},
[EModelEndpoint.azureOpenAI]: {
icon: <AzureMinimalIcon size={size * 0.5555555555555556} />,
bg: 'linear-gradient(0.375turn, #61bde2, #4389d0)',
name: 'ChatGPT',
},
[EModelEndpoint.openAI]: {
icon: <GPTIcon size={size * 0.5555555555555556} />,
bg:
typeof model === 'string' && model.toLowerCase().includes('gpt-4') ? '#AB68FF' : '#19C37D',
name: 'ChatGPT',
},
[EModelEndpoint.gptPlugins]: {
icon: <Plugin size={size * 0.7} />,
bg: `rgba(69, 89, 164, ${button ? 0.75 : 1})`,
name: 'Plugins',
},
[EModelEndpoint.google]: {
icon: model?.toLowerCase()?.includes('code') ? (
<CodeyIcon size={size * 0.75} />
) : model?.toLowerCase()?.includes('gemini') ? (
<GeminiIcon size={size * 0.7} />
) : (
<PaLMIcon size={size * 0.7} />
),
name: model?.toLowerCase()?.includes('code')
? 'Codey'
: model?.toLowerCase()?.includes('gemini')
? 'Gemini'
: 'PaLM2',
},
[EModelEndpoint.anthropic]: {
icon: <AnthropicIcon size={size * 0.5555555555555556} />,
bg: '#d09a74',
name: 'Claude',
},
[EModelEndpoint.bingAI]: {
icon: jailbreak ? (
<img src="/assets/bingai-jb.png" alt="Bing Icon" />
) : (
<img src="/assets/bingai.png" alt="Sydney Icon" />
),
name: jailbreak ? 'Sydney' : 'BingAI',
},
[EModelEndpoint.chatGPTBrowser]: {
icon: <GPTIcon size={size * 0.5555555555555556} />,
bg:
typeof model === 'string' && model.toLowerCase().includes('gpt-4')
? '#AB68FF'
: `rgba(0, 163, 255, ${button ? 0.75 : 1})`,
name: 'ChatGPT',
},
[EModelEndpoint.custom]: {
icon: <CustomMinimalIcon size={size * 0.7} />,
name: 'Custom',
},
null: { icon: <GPTIcon size={size * 0.7} />, bg: 'grey', name: 'N/A' },
default: {
icon: (
<div className="h-6 w-6">
<div className="overflow-hidden rounded-full">
<UnknownIcon
iconURL={props.iconURL}
endpoint={endpoint ?? ''}
className="h-full w-full object-contain"
context="message"
/>
</div>
</div>
),
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 (
<div
title={name}
style={{
background: bg || 'transparent',
width: size,
height: size,
}}
className={cn(
'relative flex h-9 w-9 items-center justify-center rounded-sm p-1 text-white',
props.className || '',
)}
>
{icon}
{error && (
<span className="absolute right-0 top-[20px] -mr-2 flex h-3 w-3 items-center justify-center rounded-full border border-white bg-red-500 text-[10px] text-white">
!
</span>
)}
</div>
);
}; };
export default Icon; export default Icon;

View file

@ -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<IconProps> = (props) => {
const {
error,
button,
iconURL,
endpoint,
jailbreak,
size = 30,
model = '',
assistantName,
} = props;
const endpointIcons = {
[EModelEndpoint.assistants]: {
icon: props.iconURL ? (
<div className="relative flex h-6 w-6 items-center justify-center">
<div
title={assistantName}
style={{
width: size,
height: size,
}}
className={cn('overflow-hidden rounded-full', props.className ?? '')}
>
<img
className="shadow-stroke h-full w-full object-cover"
src={props.iconURL}
alt={assistantName}
style={{ height: '80', width: '80' }}
/>
</div>
</div>
) : (
<div className="h-6 w-6">
<div className="shadow-stroke flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
<AssistantIcon className="h-2/3 w-2/3 text-gray-400" />
</div>
</div>
),
name: endpoint,
},
[EModelEndpoint.azureOpenAI]: {
icon: <AzureMinimalIcon size={size * 0.5555555555555556} />,
bg: 'linear-gradient(0.375turn, #61bde2, #4389d0)',
name: 'ChatGPT',
},
[EModelEndpoint.openAI]: {
icon: <GPTIcon size={size * 0.5555555555555556} />,
bg:
typeof model === 'string' && model.toLowerCase().includes('gpt-4') ? '#AB68FF' : '#19C37D',
name: 'ChatGPT',
},
[EModelEndpoint.gptPlugins]: {
icon: <Plugin size={size * 0.7} />,
bg: `rgba(69, 89, 164, ${button ? 0.75 : 1})`,
name: 'Plugins',
},
[EModelEndpoint.google]: {
icon: model?.toLowerCase()?.includes('code') ? (
<CodeyIcon size={size * 0.75} />
) : model?.toLowerCase()?.includes('gemini') ? (
<GeminiIcon size={size * 0.7} />
) : (
<PaLMIcon size={size * 0.7} />
),
name: model?.toLowerCase()?.includes('code')
? 'Codey'
: model?.toLowerCase()?.includes('gemini')
? 'Gemini'
: 'PaLM2',
},
[EModelEndpoint.anthropic]: {
icon: <AnthropicIcon size={size * 0.5555555555555556} />,
bg: '#d09a74',
name: 'Claude',
},
[EModelEndpoint.bingAI]: {
icon: jailbreak ? (
<img src="/assets/bingai-jb.png" alt="Bing Icon" />
) : (
<img src="/assets/bingai.png" alt="Sydney Icon" />
),
name: jailbreak ? 'Sydney' : 'BingAI',
},
[EModelEndpoint.chatGPTBrowser]: {
icon: <GPTIcon size={size * 0.5555555555555556} />,
bg:
typeof model === 'string' && model.toLowerCase().includes('gpt-4')
? '#AB68FF'
: `rgba(0, 163, 255, ${button ? 0.75 : 1})`,
name: 'ChatGPT',
},
[EModelEndpoint.custom]: {
icon: <CustomMinimalIcon size={size * 0.7} />,
name: 'Custom',
},
null: { icon: <GPTIcon size={size * 0.7} />, bg: 'grey', name: 'N/A' },
default: {
icon: (
<div className="h-6 w-6">
<div className="overflow-hidden rounded-full">
<UnknownIcon
iconURL={props.iconURL}
endpoint={endpoint ?? ''}
className="h-full w-full object-contain"
context="message"
/>
</div>
</div>
),
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 (
<div
title={name}
style={{
background: bg || 'transparent',
width: size,
height: size,
}}
className={cn(
'relative flex h-9 w-9 items-center justify-center rounded-sm p-1 text-white',
props.className || '',
)}
>
{icon}
{error && (
<span className="absolute right-0 top-[20px] -mr-2 flex h-3 w-3 items-center justify-center rounded-full border border-white bg-red-500 text-[10px] text-white">
!
</span>
)}
</div>
);
};
export default MessageEndpointIcon;

View file

@ -3,6 +3,7 @@ import { InfoIcon } from 'lucide-react';
import React, { useRef, useState, RefObject } from 'react'; import React, { useRef, useState, RefObject } from 'react';
import Clipboard from '~/components/svg/Clipboard'; import Clipboard from '~/components/svg/Clipboard';
import CheckMark from '~/components/svg/CheckMark'; import CheckMark from '~/components/svg/CheckMark';
import useLocalize from '~/hooks/useLocalize';
import cn from '~/utils/cn'; import cn from '~/utils/cn';
type CodeBarProps = { type CodeBarProps = {
@ -18,6 +19,7 @@ type CodeBlockProps = Pick<CodeBarProps, 'lang' | 'plugin' | 'error'> & {
}; };
const CodeBar: React.FC<CodeBarProps> = React.memo(({ lang, codeRef, error, plugin = null }) => { const CodeBar: React.FC<CodeBarProps> = React.memo(({ lang, codeRef, error, plugin = null }) => {
const localize = useLocalize();
const [isCopied, setIsCopied] = useState(false); const [isCopied, setIsCopied] = useState(false);
return ( return (
<div className="relative flex items-center rounded-tl-md rounded-tr-md bg-gray-700 px-4 py-2 font-sans text-xs text-gray-200 dark:bg-gray-700"> <div className="relative flex items-center rounded-tl-md rounded-tr-md bg-gray-700 px-4 py-2 font-sans text-xs text-gray-200 dark:bg-gray-700">
@ -41,13 +43,13 @@ const CodeBar: React.FC<CodeBarProps> = React.memo(({ lang, codeRef, error, plug
> >
{isCopied ? ( {isCopied ? (
<> <>
<CheckMark /> <CheckMark className="h-[18px] w-[18px]" />
{error ? '' : 'Copied!'} {error ? '' : localize('com_ui_copied')}
</> </>
) : ( ) : (
<> <>
<Clipboard /> <Clipboard />
{error ? '' : 'Copy code'} {error ? '' : localize('com_ui_copy_code')}
</> </>
)} )}
</button> </button>

View file

@ -3,6 +3,7 @@ import { useCallback, memo, ReactNode } from 'react';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { TResPlugin, TInput } from 'librechat-data-provider'; import type { TResPlugin, TInput } from 'librechat-data-provider';
import { ChevronDownIcon, LucideProps } from 'lucide-react'; import { ChevronDownIcon, LucideProps } from 'lucide-react';
import { useShareContext } from '~/Providers';
import { cn, formatJSON } from '~/utils'; import { cn, formatJSON } from '~/utils';
import { Spinner } from '~/components'; import { Spinner } from '~/components';
import CodeBlock from './CodeBlock'; import CodeBlock from './CodeBlock';
@ -31,7 +32,9 @@ type PluginProps = {
}; };
const Plugin: React.FC<PluginProps> = ({ plugin }) => { const Plugin: React.FC<PluginProps> = ({ plugin }) => {
const { isSharedConvo } = useShareContext();
const { data: plugins = {} } = useGetEndpointsQuery({ const { data: plugins = {} } = useGetEndpointsQuery({
enabled: !isSharedConvo,
select: (data) => data?.gptPlugins?.plugins, select: (data) => data?.gptPlugins?.plugins,
}); });

View file

@ -19,6 +19,7 @@ import NavToggle from './NavToggle';
import NavLinks from './NavLinks'; import NavLinks from './NavLinks';
import NewChat from './NewChat'; import NewChat from './NewChat';
import { cn } from '~/utils'; import { cn } from '~/utils';
import { ConversationListResponse } from 'librechat-data-provider';
import store from '~/store'; import store from '~/store';
const Nav = ({ navVisible, setNavVisible }) => { const Nav = ({ navVisible, setNavVisible }) => {
@ -59,7 +60,7 @@ const Nav = ({ navVisible, setNavVisible }) => {
{ enabled: isAuthenticated }, { enabled: isAuthenticated },
); );
const { containerRef, moveToTop } = useNavScrolling({ const { containerRef, moveToTop } = useNavScrolling<ConversationListResponse>({
setShowLoading, setShowLoading,
hasNextPage: searchQuery ? searchQueryRes.hasNextPage : hasNextPage, hasNextPage: searchQuery ? searchQueryRes.hasNextPage : hasNextPage,
fetchNextPage: searchQuery ? searchQueryRes.fetchNextPage : fetchNextPage, fetchNextPage: searchQuery ? searchQueryRes.fetchNextPage : fetchNextPage,

View file

@ -10,6 +10,7 @@ import { useConversation, useConversations, useOnClickOutside } from '~/hooks';
import ImportConversations from './ImportConversations'; import ImportConversations from './ImportConversations';
import { ClearChatsButton } from './ClearChats'; import { ClearChatsButton } from './ClearChats';
import DangerButton from '../DangerButton'; import DangerButton from '../DangerButton';
import SharedLinks from './SharedLinks';
export const RevokeKeysButton = ({ export const RevokeKeysButton = ({
showText = true, showText = true,
@ -107,6 +108,9 @@ function Data() {
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600"> <div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<ImportConversations /> <ImportConversations />
</div> </div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<SharedLinks />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600"> <div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<RevokeKeysButton all={true} /> <RevokeKeysButton all={true} />
</div> </div>

View file

@ -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<HTMLButtonElement>) => {
e.preventDefault();
if (mutation.isLoading) {
return;
}
setIsDeleting(true);
await mutation.mutateAsync({ shareId });
setIsDeleting(false);
};
return (
<TooltipProvider delayDuration={250}>
<Tooltip>
<TooltipTrigger asChild>
<span onClick={handleDelete}>
<TrashIcon />
</span>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={0}>
{localize('com_ui_delete')}
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}
function SourceChatButton({ conversationId }: { conversationId: string }) {
const localize = useLocalize();
return (
<TooltipProvider delayDuration={250}>
<Tooltip>
<TooltipTrigger asChild>
<Link to={`/c/${conversationId}`} target="_blank" rel="noreferrer">
<MessageSquare className="h-4 w-4 hover:text-gray-300" />
</Link>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={0}>
{localize('com_nav_source_chat')}
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}
function ShareLinkRow({ sharedLink }: { sharedLink: TSharedLink }) {
const [isDeleting, setIsDeleting] = useState(false);
return (
<tr
key={sharedLink.conversationId}
className="border-b border-gray-200 text-sm font-normal dark:border-white/10"
>
<td
className={cn(
'flex items-center py-3 text-blue-800/70 dark:text-blue-500',
isDeleting && 'opacity-50',
)}
>
<Link to={`/share/${sharedLink.shareId}`} target="_blank" rel="noreferrer" className="flex">
<LinkIcon className="mr-1 h-5 w-5" />
{sharedLink.title}
</Link>
</td>
<td className="p-3">
<div className="flex justify-between">
<div className={cn('flex justify-start dark:text-gray-200', isDeleting && 'opacity-50')}>
{new Date(sharedLink.createdAt).toLocaleDateString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
})}
</div>
<div
className={cn(
'flex items-center justify-end gap-3 text-gray-400',
isDeleting && 'opacity-50',
)}
>
{sharedLink.conversationId && (
<>
<SourceChatButton conversationId={sharedLink.conversationId} />
<div className={cn('h-4 w-4 cursor-pointer', !isDeleting && 'hover:text-gray-300')}>
<SharedLinkDeleteButton
shareId={sharedLink.shareId}
setIsDeleting={setIsDeleting}
/>
</div>
</>
)}
</div>
</div>
</td>
</tr>
);
}
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<SharedLinksResponse>({
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 <div className="text-gray-300">{localize('com_nav_shared_links_empty')}</div>;
}
return (
<div
className={cn(
'grid w-full gap-2',
'-mr-2 flex-1 flex-col overflow-y-auto pr-2 transition-opacity duration-500',
'max-h-[350px]',
)}
ref={containerRef}
>
<table className="table-fixed text-left">
<thead className="sticky top-0 bg-white dark:bg-gray-700">
<tr className="border-b border-gray-200 text-sm font-semibold text-gray-500 dark:border-white/10 dark:text-gray-200">
<th className="p-3">{localize('com_nav_shared_links_name')}</th>
<th className="p-3">{localize('com_nav_shared_links_date_shared')}</th>
</tr>
</thead>
<tbody>
{sharedLinks.map((sharedLink) => (
<ShareLinkRow key={sharedLink.shareId} sharedLink={sharedLink} />
))}
</tbody>
</table>
{(isFetchingNextPage || showLoading) && (
<Spinner className={cn('m-1 mx-auto mb-4 h-4 w-4 text-black dark:text-white')} />
)}
</div>
);
}

View file

@ -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 (
<div className="flex items-center justify-between">
<div> {localize('com_nav_shared_links')} </div>
<Dialog>
<DialogTrigger asChild>
<button className="btn btn-neutral relative ">
{localize('com_nav_shared_links_manage')}
</button>
</DialogTrigger>
<DialogTemplate
title={localize('com_nav_shared_links')}
className="max-w-[1000px]"
showCancelButton={false}
main={<ShareLinkTable />}
/>
</Dialog>
</div>
);
}

View file

@ -6,6 +6,7 @@ import ArchiveButton from '~/components/Conversations/ArchiveButton';
import DeleteButton from '~/components/Conversations/DeleteButton'; import DeleteButton from '~/components/Conversations/DeleteButton';
import { Spinner } from '~/components/svg'; import { Spinner } from '~/components/svg';
import { cn } from '~/utils'; import { cn } from '~/utils';
import { ConversationListResponse } from 'librechat-data-provider';
export default function ArchivedChatsTable({ className }: { className?: string }) { export default function ArchivedChatsTable({ className }: { className?: string }) {
const localize = useLocalize(); const localize = useLocalize();
@ -17,7 +18,7 @@ export default function ArchivedChatsTable({ className }: { className?: string }
{ enabled: isAuthenticated }, { enabled: isAuthenticated },
); );
const { containerRef, moveToTop } = useNavScrolling({ const { containerRef, moveToTop } = useNavScrolling<ConversationListResponse>({
setShowLoading, setShowLoading,
hasNextPage: hasNextPage, hasNextPage: hasNextPage,
fetchNextPage: fetchNextPage, fetchNextPage: fetchNextPage,

View file

@ -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 (
<>
<div className="text-token-text-primary w-full border-0 bg-transparent dark:border-0 dark:bg-transparent">
<div className="m-auto justify-center p-4 py-2 text-base md:gap-6 ">
<div className="final-completion group mx-auto flex flex-1 gap-3 text-base md:max-w-3xl md:px-5 lg:max-w-[40rem] lg:px-1 xl:max-w-[48rem] xl:px-5">
<div className="relative flex flex-shrink-0 flex-col items-end">
<div>
<div className="pt-0.5">
<div className="flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
<Icon message={message} conversation={conversation} />
</div>
</div>
</div>
</div>
<div
className={cn('relative flex w-11/12 flex-col', isCreatedByUser ? '' : 'agent-turn')}
>
<div className="select-none font-semibold">{messageLabel}</div>
<div className="flex-col gap-1 md:gap-3">
<div className="flex max-w-full flex-grow flex-col gap-0">
{/* Legacy Plugins */}
{message?.plugin && <Plugin plugin={message?.plugin} />}
{message?.content ? (
<SearchContent message={message} />
) : (
<MessageContent
edit={false}
error={error}
isLast={false}
ask={() => ({})}
text={text ?? ''}
message={message}
isSubmitting={false}
enterEdit={() => ({})}
unfinished={!!unfinished}
isCreatedByUser={isCreatedByUser ?? true}
siblingIdx={siblingIdx ?? 0}
setSiblingIdx={setSiblingIdx ?? (() => ({}))}
/>
)}
</div>
</div>
<SubRow classes="text-xs">
<SiblingSwitch
siblingIdx={siblingIdx}
siblingCount={siblingCount}
setSiblingIdx={setSiblingIdx}
/>
<MinimalHoverButtons message={message} />
</SubRow>
</div>
</div>
</div>
</div>
<MultiMessage
key={messageId}
messageId={messageId}
messagesTree={children ?? []}
currentEditId={currentEditId}
setCurrentEditId={setCurrentEditId}
/>
</>
);
}

View file

@ -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<TMessageProps, 'message' | 'conversation'> & {
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 (
<ConvoIconURL
preset={messageSettings as typeof messageSettings & TPreset}
context="message"
assistantAvatar={assistantAvatar}
assistantName={assistantName}
/>
);
}
if (message?.isCreatedByUser) {
return (
<div
style={{
backgroundColor: 'rgb(121, 137, 255)',
width: '20px',
height: '20px',
boxShadow: 'rgba(240, 246, 252, 0.1) 0px 0px 0px 1px',
}}
className="relative flex h-9 w-9 items-center justify-center rounded-sm p-1 text-white"
>
<UserIcon />
</div>
);
}
return (
<MessageEndpointIcon
{...messageSettings}
endpoint={endpoint}
iconURL={!assistant ? undefined : assistantAvatar}
model={message?.model ?? conversation?.model}
assistantName={assistantName}
size={28.8}
/>
);
}

View file

@ -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<number | string | null>(-1);
return (
<div className="flex-1 pb-[50px]">
<div className="dark:gpt-dark-gray relative h-full">
<div
style={{
height: '100%',
overflowY: 'auto',
width: '100%',
}}
>
<div className="flex flex-col pb-9 text-sm dark:bg-transparent">
{(_messagesTree && _messagesTree?.length == 0) || _messagesTree === null ? (
<div className="flex w-full items-center justify-center gap-1 bg-gray-50 p-3 text-sm text-gray-500 dark:border-gray-800/50 dark:bg-gray-800 dark:text-gray-300">
Nothing found
</div>
) : (
<>
<div>
<MultiMessage
key={conversationId} // avoid internal state mixture
messagesTree={_messagesTree}
messageId={conversationId ?? null}
setCurrentEditId={setCurrentEditId}
currentEditId={currentEditId ?? null}
/>
</div>
</>
)}
<div className="dark:gpt-dark-gray group h-0 w-full flex-shrink-0 dark:border-gray-800/50" />
</div>
</div>
</div>
</div>
);
}

View file

@ -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 (
<Message
key={message.messageId}
message={message}
currentEditId={currentEditId}
setCurrentEditId={setCurrentEditId}
siblingIdx={messagesTree.length - siblingIdx - 1}
siblingCount={messagesTree.length}
setSiblingIdx={setSiblingIdxRev}
/>
);
}

View file

@ -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 (
<ShareContext.Provider value={{ isSharedConvo: true }}>
<div
className="relative flex w-full grow overflow-hidden bg-white dark:bg-gray-800"
style={{ paddingBottom: '50px' }}
>
<div className="transition-width relative flex h-full w-full flex-1 flex-col items-stretch overflow-hidden bg-white pt-0 dark:bg-gray-800">
<div className="flex h-full flex-col" role="presentation" tabIndex={0}>
{isLoading ? (
<div className="flex h-screen items-center justify-center">
<Spinner className="" />
</div>
) : data && messagesTree && messagesTree.length !== 0 ? (
<>
<div className="final-completion group mx-auto flex min-w-[40rem] flex-col gap-3 pb-6 pt-4 md:max-w-3xl md:px-5 lg:max-w-[40rem] lg:px-1 xl:max-w-[48rem] xl:px-5">
<h1 className="text-4xl font-bold dark:text-white">{data.title}</h1>
<div className="border-b pb-6 text-base text-gray-300">
{new Date(data.createdAt).toLocaleDateString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
})}
</div>
</div>
<MessagesView messagesTree={messagesTree} conversationId={data.conversationId} />
</>
) : (
<div className="flex h-screen items-center justify-center">
{localize('com_ui_shared_link_not_found')}
</div>
)}
<div className="w-full border-t-0 pl-0 pt-2 dark:border-white/20 md:w-[calc(100%-.5rem)] md:border-t-0 md:border-transparent md:pl-0 md:pt-0 md:dark:border-transparent">
<Footer className="fixed bottom-0 left-0 right-0 z-50 flex items-center justify-center gap-2 bg-gradient-to-t from-gray-50 to-transparent px-2 pb-2 pt-8 text-xs text-gray-600 dark:from-gray-800 dark:text-gray-300 md:px-[60px]" />
</div>
</div>
</div>
</div>
</ShareContext.Provider>
);
}
export default memo(SharedView);

View file

@ -7,6 +7,8 @@ import {
updateConversation, updateConversation,
deleteConversation, deleteConversation,
updateConvoFields, updateConvoFields,
deleteSharedLink,
addSharedLink,
} from '~/utils'; } from '~/utils';
import { dataService, MutationKeys, QueryKeys, defaultOrderQuery } from 'librechat-data-provider'; import { dataService, MutationKeys, QueryKeys, defaultOrderQuery } from 'librechat-data-provider';
import { useSetRecoilState } from 'recoil'; import { useSetRecoilState } from 'recoil';
@ -117,6 +119,96 @@ export const useArchiveConversationMutation = (
); );
}; };
export const useCreateSharedLinkMutation = (
options?: t.CreateSharedLinkOptions,
): UseMutationResult<t.TSharedLinkResponse, unknown, t.TSharedLinkRequest, unknown> => {
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<t.SharedLinkListData>([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<t.TSharedLinkResponse, unknown, t.TSharedLinkRequest, unknown> => {
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<t.SharedLinkListData>([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<t.TDeleteSharedLinkResponse, unknown, { shareId: string }, unknown> => {
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<t.SharedLinkListData>([QueryKeys.sharedLinks], (data) => {
if (!data) {
return data;
}
return deleteSharedLink(data, vars.shareId);
});
onSuccess?.(_data, vars, context);
},
...(_options || {}),
});
};
export const useDeleteConversationMutation = ( export const useDeleteConversationMutation = (
options?: t.DeleteConversationOptions, options?: t.DeleteConversationOptions,
): UseMutationResult< ): UseMutationResult<

View file

@ -20,6 +20,8 @@ import type {
AssistantDocument, AssistantDocument,
TEndpointsConfig, TEndpointsConfig,
TCheckUserKeyResponse, TCheckUserKeyResponse,
SharedLinkListParams,
SharedLinksResponse,
} from 'librechat-data-provider'; } from 'librechat-data-provider';
import { findPageForConversation, addFileToCache } from '~/utils'; import { findPageForConversation, addFileToCache } from '~/utils';
@ -92,10 +94,10 @@ export const useGetConvoIdQuery = (
return defaultQuery(); return defaultQuery();
} }
const { pageIndex, convIndex } = findPageForConversation(convosQuery, { conversationId: id }); const { pageIndex, index } = findPageForConversation(convosQuery, { conversationId: id });
if (pageIndex > -1 && convIndex > -1) { if (pageIndex > -1 && index > -1) {
return convosQuery.pages[pageIndex].conversations[convIndex]; return convosQuery.pages[pageIndex].conversations[index];
} }
return defaultQuery(); return defaultQuery();
@ -158,6 +160,33 @@ export const useConversationsInfiniteQuery = (
); );
}; };
export const useSharedLinksInfiniteQuery = (
params?: SharedLinkListParams,
config?: UseInfiniteQueryOptions<SharedLinksResponse, unknown>,
) => {
return useInfiniteQuery<SharedLinksResponse, unknown>(
[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 * ASSISTANTS
*/ */

View file

@ -1,4 +1,5 @@
export { default as useAvatar } from './useAvatar'; export { default as useAvatar } from './useAvatar';
export { default as useProgress } from './useProgress'; export { default as useProgress } from './useProgress';
export { default as useMessageHelpers } from './useMessageHelpers'; export { default as useMessageHelpers } from './useMessageHelpers';
export { default as useCopyToClipboard } from './useCopyToClipboard';
export { default as useMessageScrolling } from './useMessageScrolling'; export { default as useMessageScrolling } from './useMessageScrolling';

View file

@ -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<Pick<TMessage, 'text' | 'content'>>) {
const copyToClipboard = useCallback(
(setIsCopied: React.Dispatch<React.SetStateAction<boolean>>) => {
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;
}

View file

@ -1,8 +1,8 @@
import copy from 'copy-to-clipboard';
import { useEffect, useRef, useCallback } from 'react'; 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 type { TMessageProps } from '~/common';
import { useChatContext, useAssistantsMapContext } from '~/Providers'; import { useChatContext, useAssistantsMapContext } from '~/Providers';
import useCopyToClipboard from './useCopyToClipboard';
export default function useMessageHelpers(props: TMessageProps) { export default function useMessageHelpers(props: TMessageProps) {
const latestText = useRef<string | number>(''); const latestText = useRef<string | number>('');
@ -65,26 +65,7 @@ export default function useMessageHelpers(props: TMessageProps) {
regenerate(message); regenerate(message);
}; };
const copyToClipboard = useCallback( const copyToClipboard = useCopyToClipboard({ text, content });
(setIsCopied: React.Dispatch<React.SetStateAction<boolean>>) => {
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 { return {
ask, ask,

View file

@ -3,7 +3,7 @@ import React, { useCallback, useEffect, useRef } from 'react';
import type { FetchNextPageOptions, InfiniteQueryObserverResult } from '@tanstack/react-query'; import type { FetchNextPageOptions, InfiniteQueryObserverResult } from '@tanstack/react-query';
import type { ConversationListResponse } from 'librechat-data-provider'; import type { ConversationListResponse } from 'librechat-data-provider';
export default function useNavScrolling({ export default function useNavScrolling<TData>({
hasNextPage, hasNextPage,
isFetchingNextPage, isFetchingNextPage,
setShowLoading, setShowLoading,
@ -14,7 +14,7 @@ export default function useNavScrolling({
setShowLoading: React.Dispatch<React.SetStateAction<boolean>>; setShowLoading: React.Dispatch<React.SetStateAction<boolean>>;
fetchNextPage: ( fetchNextPage: (
options?: FetchNextPageOptions | undefined, options?: FetchNextPageOptions | undefined,
) => Promise<InfiniteQueryObserverResult<ConversationListResponse, unknown>>; ) => Promise<InfiniteQueryObserverResult<TData, unknown>>;
}) { }) {
const scrollPositionRef = useRef<number | null>(null); const scrollPositionRef = useRef<number | null>(null);
const containerRef = useRef<HTMLDivElement | null>(null); const containerRef = useRef<HTMLDivElement | null>(null);

View file

@ -51,6 +51,17 @@ export default {
com_ui_import_conversation_error: 'حدث خطأ أثناء استيراد محادثاتك', com_ui_import_conversation_error: 'حدث خطأ أثناء استيراد محادثاتك',
com_ui_confirm_action: 'تأكيد الإجراء', com_ui_confirm_action: 'تأكيد الإجراء',
com_ui_chats: 'الدردشات', 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: 'حذف',
com_ui_delete_conversation: 'حذف الدردشة؟', com_ui_delete_conversation: 'حذف الدردشة؟',
com_ui_delete_conversation_confirm: 'سيتم حذف هذا', com_ui_delete_conversation_confirm: 'سيتم حذف هذا',
@ -254,6 +265,12 @@ export default {
com_nav_export_recursive_or_sequential: 'التراجع أو التسلسل؟', com_nav_export_recursive_or_sequential: 'التراجع أو التسلسل؟',
com_nav_export_recursive: 'تكراري', com_nav_export_recursive: 'تكراري',
com_nav_export_conversation: 'تصدير المحادثة', 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: 'المظهر',
com_nav_theme_system: 'النظام', com_nav_theme_system: 'النظام',
com_nav_theme_dark: 'داكن', com_nav_theme_dark: 'داكن',
@ -280,6 +297,12 @@ export default {
com_nav_setting_general: 'عام', com_nav_setting_general: 'عام',
com_nav_setting_data: 'تحكم في البيانات', com_nav_setting_data: 'تحكم في البيانات',
/* The following are AI translated */ /* 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_today: 'اليوم',
com_ui_date_yesterday: 'أمس', com_ui_date_yesterday: 'أمس',
com_ui_date_previous_7_days: 'الأيام السبعة السابقة', com_ui_date_previous_7_days: 'الأيام السبعة السابقة',
@ -736,6 +759,43 @@ export const comparisons = {
english: 'chats', english: 'chats',
translated: 'الدردشات', 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: { com_ui_delete: {
english: 'Delete', english: 'Delete',
translated: 'حذف', translated: 'حذف',
@ -1456,6 +1516,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'تصدير المحادثة', 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: { com_nav_theme: {
english: 'Theme', english: 'Theme',
translated: 'المظهر', translated: 'المظهر',
@ -1552,6 +1636,30 @@ export const comparisons = {
english: 'Data controls', english: 'Data controls',
translated: 'تحكم في البيانات', 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: { com_ui_date_today: {
english: 'Today', english: 'Today',
translated: 'اليوم', translated: 'اليوم',

View file

@ -135,6 +135,17 @@ export default {
com_ui_assistants_output: 'Saída dos Assistentes', com_ui_assistants_output: 'Saída dos Assistentes',
com_ui_delete: 'Excluir', com_ui_delete: 'Excluir',
com_ui_create: 'Criar', 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: 'Excluir conversa?',
com_ui_delete_conversation_confirm: 'Isso excluirá', com_ui_delete_conversation_confirm: 'Isso excluirá',
com_ui_delete_assistant_confirm: com_ui_delete_assistant_confirm:
@ -417,6 +428,12 @@ export default {
com_nav_export_recursive_or_sequential: 'Recursivo ou sequencial?', com_nav_export_recursive_or_sequential: 'Recursivo ou sequencial?',
com_nav_export_recursive: 'Recursivo', com_nav_export_recursive: 'Recursivo',
com_nav_export_conversation: 'Exportar conversa', 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_my_files: 'Meus arquivos',
com_nav_theme: 'Tema', com_nav_theme: 'Tema',
com_nav_theme_system: 'Sistema', com_nav_theme_system: 'Sistema',
@ -963,6 +980,43 @@ export const comparisons = {
english: 'Create', english: 'Create',
translated: 'Criar', 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: { com_ui_delete_conversation: {
english: 'Delete chat?', english: 'Delete chat?',
translated: 'Excluir conversa?', translated: 'Excluir conversa?',
@ -1951,6 +2005,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'Exportar conversa', 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: { com_nav_my_files: {
english: 'My Files', english: 'My Files',
translated: 'Meus arquivos', translated: 'Meus arquivos',

View file

@ -148,6 +148,17 @@ export default {
com_ui_assistants_output: 'Assistenten Ausgabe', com_ui_assistants_output: 'Assistenten Ausgabe',
com_ui_delete: 'Löschen', com_ui_delete: 'Löschen',
com_ui_create: 'Erstellen', 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: 'Chat löschen?',
com_ui_delete_conversation_confirm: 'Damit wird gelöscht', com_ui_delete_conversation_confirm: 'Damit wird gelöscht',
com_ui_delete_assistant_confirm: com_ui_delete_assistant_confirm:
@ -431,6 +442,12 @@ export default {
com_nav_export_recursive_or_sequential: 'Rekursiv oder sequentiell?', com_nav_export_recursive_or_sequential: 'Rekursiv oder sequentiell?',
com_nav_export_recursive: 'Rekursiv', com_nav_export_recursive: 'Rekursiv',
com_nav_export_conversation: 'Konversation exportieren', 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_my_files: 'Meine Dateien',
com_nav_theme: 'Farbschema', com_nav_theme: 'Farbschema',
com_nav_theme_system: 'System', com_nav_theme_system: 'System',
@ -464,6 +481,12 @@ export default {
com_nav_setting_account: 'Konto', com_nav_setting_account: 'Konto',
com_nav_language: 'Sprache', com_nav_language: 'Sprache',
/* The following are AI Translated */ /* 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_today: 'Heute',
com_ui_date_yesterday: 'Gestern', com_ui_date_yesterday: 'Gestern',
com_ui_date_previous_7_days: 'Letzte 7 Tage', com_ui_date_previous_7_days: 'Letzte 7 Tage',
@ -1102,6 +1125,43 @@ export const comparisons = {
english: 'Create', english: 'Create',
translated: 'Erstellen', 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: { com_ui_delete_conversation: {
english: 'Delete chat?', english: 'Delete chat?',
translated: 'Chat löschen?', translated: 'Chat löschen?',
@ -2097,6 +2157,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'Konversation exportieren', 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: { com_nav_my_files: {
english: 'My Files', english: 'My Files',
translated: 'Meine Dateien', translated: 'Meine Dateien',
@ -2221,6 +2305,30 @@ export const comparisons = {
english: 'Language', english: 'Language',
translated: 'Sprache', 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: { com_ui_date_today: {
english: 'Today', english: 'Today',
translated: 'Heute', translated: 'Heute',

View file

@ -139,6 +139,8 @@ export default {
com_ui_save: 'Save', com_ui_save: 'Save',
com_ui_save_submit: 'Save & Submit', com_ui_save_submit: 'Save & Submit',
com_user_message: 'You', com_user_message: 'You',
com_ui_copied: 'Copied!',
com_ui_copy_code: 'Copy code',
com_ui_copy_to_clipboard: 'Copy to clipboard', com_ui_copy_to_clipboard: 'Copy to clipboard',
com_ui_copied_to_clipboard: 'Copied to clipboard', com_ui_copied_to_clipboard: 'Copied to clipboard',
com_ui_fork: 'Fork', com_ui_fork: 'Fork',
@ -199,6 +201,20 @@ export default {
com_ui_assistants_output: 'Assistants Output', com_ui_assistants_output: 'Assistants Output',
com_ui_delete: 'Delete', com_ui_delete: 'Delete',
com_ui_create: 'Create', 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: 'Delete chat?',
com_ui_delete_conversation_confirm: 'This will delete', com_ui_delete_conversation_confirm: 'This will delete',
com_ui_delete_assistant_confirm: com_ui_delete_assistant_confirm:
@ -480,6 +496,13 @@ export default {
com_nav_export_recursive_or_sequential: 'Recursive or sequential?', com_nav_export_recursive_or_sequential: 'Recursive or sequential?',
com_nav_export_recursive: 'Recursive', com_nav_export_recursive: 'Recursive',
com_nav_export_conversation: 'Export conversation', 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_my_files: 'My Files',
com_nav_theme: 'Theme', com_nav_theme: 'Theme',
com_nav_theme_system: 'System', com_nav_theme_system: 'System',

View file

@ -137,6 +137,17 @@ export default {
com_ui_assistants_output: 'Salida de Asistentes', com_ui_assistants_output: 'Salida de Asistentes',
com_ui_delete: 'Eliminar', com_ui_delete: 'Eliminar',
com_ui_create: 'Crear', 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: '¿Eliminar Chat?',
com_ui_delete_conversation_confirm: 'Esto eliminará', com_ui_delete_conversation_confirm: 'Esto eliminará',
com_ui_delete_assistant_confirm: com_ui_delete_assistant_confirm:
@ -423,6 +434,12 @@ export default {
com_nav_export_recursive_or_sequential: '¿Recursivo o secuencial?', com_nav_export_recursive_or_sequential: '¿Recursivo o secuencial?',
com_nav_export_recursive: 'Recursivo', com_nav_export_recursive: 'Recursivo',
com_nav_export_conversation: 'Exportar conversación', 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_my_files: 'Mis archivos',
com_nav_theme: 'Tema', com_nav_theme: 'Tema',
com_nav_theme_system: 'Sistema', com_nav_theme_system: 'Sistema',
@ -458,6 +475,12 @@ export default {
com_nav_lang_auto: 'Detección automática', com_nav_lang_auto: 'Detección automática',
com_nav_lang_spanish: 'Español', com_nav_lang_spanish: 'Español',
/* The following are AI Translated */ /* 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_today: 'Hoy',
com_ui_date_yesterday: 'Ayer', com_ui_date_yesterday: 'Ayer',
com_ui_date_previous_7_days: 'Últimos 7 días', com_ui_date_previous_7_days: 'Últimos 7 días',
@ -1073,6 +1096,43 @@ export const comparisons = {
english: 'Create', english: 'Create',
translated: 'Crear', 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: { com_ui_delete_conversation: {
english: 'Delete chat?', english: 'Delete chat?',
translated: '¿Eliminar Chat?', translated: '¿Eliminar Chat?',
@ -2069,6 +2129,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'Exportar conversación', 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: { com_nav_my_files: {
english: 'My Files', english: 'My Files',
translated: 'Mis archivos', translated: 'Mis archivos',
@ -2202,6 +2286,30 @@ export const comparisons = {
english: 'Español', english: 'Español',
translated: '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: { com_ui_date_today: {
english: 'Today', english: 'Today',
translated: 'Hoy', translated: 'Hoy',

View file

@ -65,6 +65,17 @@ export default {
'Une erreur sest produite lors de limportation de vos conversations', 'Une erreur sest produite lors de limportation de vos conversations',
com_ui_confirm_action: 'Confirmer l\'action', com_ui_confirm_action: 'Confirmer l\'action',
com_ui_chats: 'discussions', 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: 'Supprimer',
com_ui_delete_conversation: 'Supprimer la discussions?', com_ui_delete_conversation: 'Supprimer la discussions?',
com_ui_delete_conversation_confirm: 'Cela supprimera', 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_or_sequential: 'Récursif ou séquentiel ?',
com_nav_export_recursive: 'Récursif', com_nav_export_recursive: 'Récursif',
com_nav_export_conversation: 'Exporter la conversation', 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: 'Thème',
com_nav_theme_system: 'Système', com_nav_theme_system: 'Système',
com_nav_theme_dark: 'Sombre', com_nav_theme_dark: 'Sombre',
@ -347,6 +364,12 @@ export default {
com_nav_setting_data: 'Contrôles des données', com_nav_setting_data: 'Contrôles des données',
com_nav_setting_account: 'Compte', com_nav_setting_account: 'Compte',
/* The following are AI Translated */ /* 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_today: 'Aujourd\'hui',
com_ui_date_yesterday: 'Hier', com_ui_date_yesterday: 'Hier',
com_ui_date_previous_7_days: '7 derniers jours', com_ui_date_previous_7_days: '7 derniers jours',
@ -788,6 +811,43 @@ export const comparisons = {
english: 'chats', english: 'chats',
translated: 'discussions', 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: { com_ui_delete: {
english: 'Delete', english: 'Delete',
translated: 'Supprimer', translated: 'Supprimer',
@ -1670,6 +1730,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'Exporter la 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: { com_nav_theme: {
english: 'Theme', english: 'Theme',
translated: 'Thème', translated: 'Thème',
@ -1779,6 +1863,30 @@ export const comparisons = {
english: 'Account', english: 'Account',
translated: 'Compte', 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: { com_ui_date_today: {
english: 'Today', english: 'Today',
translated: 'Aujourd\'hui', translated: 'Aujourd\'hui',

View file

@ -91,6 +91,17 @@ export default {
com_ui_assistant: 'סייען', com_ui_assistant: 'סייען',
com_ui_delete: 'מחק', com_ui_delete: 'מחק',
com_ui_create: 'צור', 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: 'למחוק את השיחה (צאט)?',
com_ui_delete_conversation_confirm: 'זה ימחק', com_ui_delete_conversation_confirm: 'זה ימחק',
com_ui_delete_assistant_confirm: com_ui_delete_assistant_confirm:
@ -345,6 +356,12 @@ export default {
com_nav_export_recursive_or_sequential: 'רקורסיבי או רציף?', com_nav_export_recursive_or_sequential: 'רקורסיבי או רציף?',
com_nav_export_recursive: 'רקורסיבי', com_nav_export_recursive: 'רקורסיבי',
com_nav_export_conversation: 'ייצא שיחה', 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: 'נושא',
com_nav_theme_system: 'מערכת', com_nav_theme_system: 'מערכת',
com_nav_theme_dark: 'כהה', com_nav_theme_dark: 'כהה',
@ -723,6 +740,43 @@ export const comparisons = {
english: 'Create', english: 'Create',
translated: 'צור', 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: { com_ui_delete_conversation: {
english: 'Delete chat?', english: 'Delete chat?',
translated: 'למחוק את השיחה (צאט)?', translated: 'למחוק את השיחה (צאט)?',
@ -1659,6 +1713,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'ייצא שיחה', 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: { com_nav_theme: {
english: 'Theme', english: 'Theme',
translated: 'נושא', translated: 'נושא',

View file

@ -61,6 +61,17 @@ export default {
com_ui_import_conversation_error: 'Terjadi kesalahan saat mengimpor percakapan Anda', com_ui_import_conversation_error: 'Terjadi kesalahan saat mengimpor percakapan Anda',
com_ui_confirm_action: 'Konfirmasi Aksi', com_ui_confirm_action: 'Konfirmasi Aksi',
com_ui_chats: 'chat', 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: 'Hapus',
com_ui_delete_conversation: 'Hapus chat?', com_ui_delete_conversation: 'Hapus chat?',
com_ui_delete_conversation_confirm: 'Ini akan menghapus', 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_or_sequential: 'Rekursif atau berurutan?',
com_nav_export_recursive: 'Rekursif', com_nav_export_recursive: 'Rekursif',
com_nav_export_conversation: 'Ekspor percakapan', 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: 'Tema',
com_nav_theme_system: 'Sistem', com_nav_theme_system: 'Sistem',
com_nav_theme_dark: 'Gelap', com_nav_theme_dark: 'Gelap',
@ -576,6 +593,43 @@ export const comparisons = {
english: 'chats', english: 'chats',
translated: 'chat', 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: { com_ui_delete: {
english: 'Delete', english: 'Delete',
translated: 'Hapus', translated: 'Hapus',
@ -1457,6 +1511,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'Ekspor percakapan', 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: { com_nav_theme: {
english: 'Theme', english: 'Theme',
translated: 'Tema', translated: 'Tema',

View file

@ -188,6 +188,17 @@ export default {
com_ui_assistants_output: 'Output Assistenti', com_ui_assistants_output: 'Output Assistenti',
com_ui_delete: 'Elimina', com_ui_delete: 'Elimina',
com_ui_create: 'Crea', 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: 'Eliminare la chat?',
com_ui_delete_conversation_confirm: 'Questo eliminerà', com_ui_delete_conversation_confirm: 'Questo eliminerà',
com_ui_rename: 'Rinominare', com_ui_rename: 'Rinominare',
@ -475,6 +486,12 @@ export default {
com_nav_export_recursive_or_sequential: 'Ricorsivo o sequenziale?', com_nav_export_recursive_or_sequential: 'Ricorsivo o sequenziale?',
com_nav_export_recursive: 'Ricorsivo', com_nav_export_recursive: 'Ricorsivo',
com_nav_export_conversation: 'Esporta conversazione', 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_my_files: 'I miei file',
com_nav_theme: 'Tema', com_nav_theme: 'Tema',
com_nav_theme_system: 'Sistema', com_nav_theme_system: 'Sistema',
@ -508,6 +525,12 @@ export default {
com_nav_setting_data: 'Controlli dati', com_nav_setting_data: 'Controlli dati',
com_nav_setting_account: 'Account', com_nav_setting_account: 'Account',
/* The following are AI Translated */ /* 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_today: 'Oggi',
com_ui_date_yesterday: 'Ieri', com_ui_date_yesterday: 'Ieri',
com_ui_date_previous_7_days: 'Ultimi 7 giorni', com_ui_date_previous_7_days: 'Ultimi 7 giorni',
@ -1222,6 +1245,43 @@ export const comparisons = {
english: 'Create', english: 'Create',
translated: 'Crea', 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: { com_ui_delete_conversation: {
english: 'Delete chat?', english: 'Delete chat?',
translated: 'Eliminare la chat?', translated: 'Eliminare la chat?',
@ -2234,6 +2294,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'Esporta conversazione', 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: { com_nav_my_files: {
english: 'My Files', english: 'My Files',
translated: 'I miei file', translated: 'I miei file',
@ -2359,6 +2443,30 @@ export const comparisons = {
english: 'Account', english: 'Account',
translated: '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: { com_ui_date_today: {
english: 'Today', english: 'Today',
translated: 'Oggi', translated: 'Oggi',

View file

@ -147,6 +147,18 @@ export default {
com_ui_assistants_output: 'Assistantsの出力', com_ui_assistants_output: 'Assistantsの出力',
com_ui_delete: '削除', com_ui_delete: '削除',
com_ui_create: '作成', 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: 'チャットを削除しますか?',
com_ui_delete_conversation_confirm: 'このチャットは削除されます。', com_ui_delete_conversation_confirm: 'このチャットは削除されます。',
com_ui_delete_assistant_confirm: 'このアシスタントを削除しますか? この操作は元に戻せません。', com_ui_delete_assistant_confirm: 'このアシスタントを削除しますか? この操作は元に戻せません。',
@ -422,6 +434,12 @@ export default {
com_nav_export_recursive_or_sequential: '再帰的? or 順次的?', com_nav_export_recursive_or_sequential: '再帰的? or 順次的?',
com_nav_export_recursive: '再帰的', com_nav_export_recursive: '再帰的',
com_nav_export_conversation: '会話をエクスポートする', 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_my_files: 'My Files',
com_nav_theme: 'テーマ', com_nav_theme: 'テーマ',
com_nav_theme_system: 'システム', com_nav_theme_system: 'システム',
@ -455,6 +473,12 @@ export default {
com_nav_setting_data: 'データ管理', com_nav_setting_data: 'データ管理',
com_nav_setting_account: 'アカウント', com_nav_setting_account: 'アカウント',
/* The following are AI translated */ /* 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_today: '今日',
com_ui_date_yesterday: '昨日', com_ui_date_yesterday: '昨日',
com_ui_date_previous_7_days: '過去7日間', com_ui_date_previous_7_days: '過去7日間',
@ -1098,6 +1122,43 @@ export const comparisons = {
english: 'Create', english: 'Create',
translated: '作成', 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: { com_ui_delete_conversation: {
english: 'Delete chat?', english: 'Delete chat?',
translated: 'チャットを削除しますか?', translated: 'チャットを削除しますか?',
@ -2087,6 +2148,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: '会話をエクスポートする', 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: { com_nav_my_files: {
english: 'My Files', english: 'My Files',
translated: 'My Files', translated: 'My Files',
@ -2211,6 +2296,30 @@ export const comparisons = {
english: 'Account', english: 'Account',
translated: 'アカウント', 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: { com_ui_date_today: {
english: 'Today', english: 'Today',
translated: '今日', translated: '今日',

View file

@ -50,6 +50,17 @@ export default {
com_ui_import_conversation_error: '대화를 가져오는 동안 오류가 발생했습니다', com_ui_import_conversation_error: '대화를 가져오는 동안 오류가 발생했습니다',
com_ui_confirm_action: '작업 확인', com_ui_confirm_action: '작업 확인',
com_ui_chats: '채팅', 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: '삭제',
com_ui_delete_conversation: '채팅을 삭제하시겠습니까?', com_ui_delete_conversation: '채팅을 삭제하시겠습니까?',
com_ui_delete_conversation_confirm: '이 채팅이 삭제됩니다', com_ui_delete_conversation_confirm: '이 채팅이 삭제됩니다',
@ -234,6 +245,12 @@ export default {
com_nav_export_recursive_or_sequential: '재귀적 또는 순차적?', com_nav_export_recursive_or_sequential: '재귀적 또는 순차적?',
com_nav_export_recursive: '재귀적', com_nav_export_recursive: '재귀적',
com_nav_export_conversation: '대화 내보내기', 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: '테마',
com_nav_theme_system: '시스템', com_nav_theme_system: '시스템',
com_nav_theme_dark: '다크', com_nav_theme_dark: '다크',
@ -261,6 +278,12 @@ export default {
com_nav_setting_general: '일반', com_nav_setting_general: '일반',
com_nav_setting_data: '데이터 제어', com_nav_setting_data: '데이터 제어',
/* The following are AI Translated */ /* 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_today: '오늘',
com_ui_date_yesterday: '어제', com_ui_date_yesterday: '어제',
com_ui_date_previous_7_days: '지난 7일', com_ui_date_previous_7_days: '지난 7일',
@ -729,6 +752,43 @@ export const comparisons = {
english: 'chats', english: 'chats',
translated: '채팅', 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: { com_ui_delete: {
english: 'Delete', english: 'Delete',
translated: '삭제', translated: '삭제',
@ -1397,6 +1457,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: '대화 내보내기', 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: { com_nav_theme: {
english: 'Theme', english: 'Theme',
translated: '테마', translated: '테마',
@ -1497,6 +1581,30 @@ export const comparisons = {
english: 'Data controls', english: 'Data controls',
translated: '데이터 제어', 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: { com_ui_date_today: {
english: 'Today', english: 'Today',
translated: '오늘', translated: '오늘',

View file

@ -54,6 +54,17 @@ export default {
'Er is een fout opgetreden bij het importeren van je gesprekken', 'Er is een fout opgetreden bij het importeren van je gesprekken',
com_ui_confirm_action: 'Bevestig actie', com_ui_confirm_action: 'Bevestig actie',
com_ui_chats: 'chats', 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: 'Verwijderen',
com_ui_delete_conversation: 'Chat verwijderen?', com_ui_delete_conversation: 'Chat verwijderen?',
com_ui_delete_conversation_confirm: 'Hiermee wordt', 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_or_sequential: 'Recursief of sequentieel?',
com_nav_export_recursive: 'Recursief', com_nav_export_recursive: 'Recursief',
com_nav_export_conversation: 'Conversatie exporteren', 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: 'Thema',
com_nav_theme_system: 'Systeem', com_nav_theme_system: 'Systeem',
com_nav_theme_dark: 'Donker', com_nav_theme_dark: 'Donker',
@ -494,6 +511,43 @@ export const comparisons = {
english: 'chats', english: 'chats',
translated: '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: { com_ui_delete: {
english: 'Delete', english: 'Delete',
translated: 'Verwijderen', translated: 'Verwijderen',
@ -1220,6 +1274,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'Conversatie exporteren', 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: { com_nav_theme: {
english: 'Theme', english: 'Theme',
translated: 'Thema', translated: 'Thema',

View file

@ -30,6 +30,17 @@ export default {
com_ui_entries: 'wpisów', com_ui_entries: 'wpisów',
com_ui_pay_per_call: com_ui_pay_per_call:
'Wszystkie rozmowy z AI w jednym miejscu. Płatność za połączenie, a nie za miesiąc', '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_rename: 'Zmień nazwę',
com_ui_archive: 'Archiwum', com_ui_archive: 'Archiwum',
com_ui_archive_error: 'Nie udało się archiwizować rozmowy', 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_or_sequential: 'Rekurencyjny czy sekwencyjny?',
com_nav_export_recursive: 'Rekurencyjny', com_nav_export_recursive: 'Rekurencyjny',
com_nav_export_conversation: 'Eksportuj konwersację', 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: 'Motyw',
com_nav_theme_system: 'Domyślny', com_nav_theme_system: 'Domyślny',
com_nav_theme_dark: 'Ciemny', 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', 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', 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: { com_ui_rename: {
english: 'Rename', english: 'Rename',
translated: 'Zmień nazwę', translated: 'Zmień nazwę',
@ -898,6 +951,26 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'Eksportuj konwersację', 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: { com_nav_theme: {
english: 'Theme', english: 'Theme',
translated: 'Motyw', translated: 'Motyw',

View file

@ -66,6 +66,17 @@ export default {
com_ui_preview: 'Предпросмотр', com_ui_preview: 'Предпросмотр',
com_ui_upload: 'Загрузить', com_ui_upload: 'Загрузить',
com_ui_connect: 'Подключить', 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: 'Удалить чат?',
com_ui_delete_conversation_confirm: 'Будет удален следующий чат: ', com_ui_delete_conversation_confirm: 'Будет удален следующий чат: ',
com_ui_rename: 'Переименовать', com_ui_rename: 'Переименовать',
@ -302,6 +313,12 @@ export default {
com_nav_export_recursive_or_sequential: 'Рекурсивно или последовательно?', com_nav_export_recursive_or_sequential: 'Рекурсивно или последовательно?',
com_nav_export_recursive: 'Рекурсивно', com_nav_export_recursive: 'Рекурсивно',
com_nav_export_conversation: 'Экспортировать разговор', 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_my_files: 'Мои файлы',
com_nav_theme: 'Тема', com_nav_theme: 'Тема',
com_nav_theme_system: 'Системная', com_nav_theme_system: 'Системная',
@ -364,6 +381,12 @@ export default {
com_ui_upload_error: 'Произошла ошибка при загрузке вашего файла', com_ui_upload_error: 'Произошла ошибка при загрузке вашего файла',
com_user_message: 'Вы', com_user_message: 'Вы',
/* The following are AI Translated */ /* 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_today: 'Сегодня',
com_ui_date_yesterday: 'Вчера', com_ui_date_yesterday: 'Вчера',
com_ui_date_previous_7_days: 'Предыдущие 7 дней', com_ui_date_previous_7_days: 'Предыдущие 7 дней',
@ -800,6 +823,43 @@ export const comparisons = {
english: 'Connect', english: 'Connect',
translated: 'Подключить', 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: { com_ui_delete_conversation: {
english: 'Delete chat?', english: 'Delete chat?',
translated: 'Удалить чат?', translated: 'Удалить чат?',
@ -1635,6 +1695,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'Экспортировать разговор', 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: { com_nav_my_files: {
english: 'My Files', english: 'My Files',
translated: 'Мои файлы', translated: 'Мои файлы',
@ -1864,6 +1948,30 @@ export const comparisons = {
english: 'You', english: 'You',
translated: 'Вы', 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: { com_ui_date_today: {
english: 'Today', english: 'Today',
translated: 'Сегодня', translated: 'Сегодня',

View file

@ -51,6 +51,17 @@ export default {
com_ui_import_conversation_error: 'Det uppstod ett fel vid import av dina konversationer', com_ui_import_conversation_error: 'Det uppstod ett fel vid import av dina konversationer',
com_ui_confirm_action: 'Bekräfta åtgärd', com_ui_confirm_action: 'Bekräfta åtgärd',
com_ui_chats: 'chattar', 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: 'Radera',
com_ui_delete_conversation: 'Radera chatt?', com_ui_delete_conversation: 'Radera chatt?',
com_ui_delete_conversation_confirm: 'Detta kommer att radera', 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_or_sequential: 'Rekursiv eller sekventiell?',
com_nav_export_recursive: 'Rekursiv', com_nav_export_recursive: 'Rekursiv',
com_nav_export_conversation: 'Exportera konversation', 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: 'Tema',
com_nav_theme_system: 'System', com_nav_theme_system: 'System',
com_nav_theme_dark: 'Mörkt', com_nav_theme_dark: 'Mörkt',
@ -474,6 +491,43 @@ export const comparisons = {
english: 'chats', english: 'chats',
translated: 'chattar', 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: { com_ui_delete: {
english: 'Delete', english: 'Delete',
translated: 'Radera', translated: 'Radera',
@ -1185,6 +1239,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'Exportera konversation', 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: { com_nav_theme: {
english: 'Theme', english: 'Theme',
translated: 'Tema', translated: 'Tema',

View file

@ -53,6 +53,17 @@ export default {
com_ui_import_conversation_error: 'Sohbetlerinizi içe aktarırken bir hata oluştu', com_ui_import_conversation_error: 'Sohbetlerinizi içe aktarırken bir hata oluştu',
com_ui_confirm_action: 'İşlemi Onayla', com_ui_confirm_action: 'İşlemi Onayla',
com_ui_chats: 'sohbetler', 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: 'Sil',
com_ui_delete_conversation: 'Sohbet silinecek?', com_ui_delete_conversation: 'Sohbet silinecek?',
com_ui_delete_conversation_confirm: 'Bu 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_or_sequential: 'Yinelemeli mi yoksa sıralı mı?',
com_nav_export_recursive: 'Yinelemeli', com_nav_export_recursive: 'Yinelemeli',
com_nav_export_conversation: 'Konuşmayı dışa aktar', 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: 'Tema',
com_nav_theme_system: 'Sistem', com_nav_theme_system: 'Sistem',
com_nav_theme_dark: 'Koyu', com_nav_theme_dark: 'Koyu',
@ -510,6 +527,43 @@ export const comparisons = {
english: 'chats', english: 'chats',
translated: 'sohbetler', 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: { com_ui_delete: {
english: 'Delete', english: 'Delete',
translated: 'Sil', translated: 'Sil',
@ -1302,6 +1356,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'Konuşmayı dışa aktar', 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: { com_nav_theme: {
english: 'Theme', english: 'Theme',
translated: 'Tema', translated: 'Tema',

View file

@ -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_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_confirm_action: 'Xác nhận hành động',
com_ui_chats: 'cuộc trò chuyện', 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: 'Xóa',
com_ui_delete_conversation: 'Xóa cuộc trò chuyện?', com_ui_delete_conversation: 'Xóa cuộc trò chuyện?',
com_ui_delete_conversation_confirm: 'Điều này sẽ xóa', 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_or_sequential: 'Đệ quy hay tuần tự?',
com_nav_export_recursive: 'Đệ quy', com_nav_export_recursive: 'Đệ quy',
com_nav_export_conversation: 'Xuất cuộc trò chuyện', 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: 'Chủ đề',
com_nav_theme_system: 'Hệ thống', com_nav_theme_system: 'Hệ thống',
com_nav_theme_dark: 'Tối', com_nav_theme_dark: 'Tối',
@ -473,6 +490,43 @@ export const comparisons = {
english: 'chats', english: 'chats',
translated: 'cuộc trò chuyện', 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: { com_ui_delete: {
english: 'Delete', english: 'Delete',
translated: 'Xóa', translated: 'Xóa',
@ -1193,6 +1247,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: 'Xuất cuộc trò chuyện', 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: { com_nav_theme: {
english: 'Theme', english: 'Theme',
translated: 'Chủ đề', translated: 'Chủ đề',

View file

@ -132,6 +132,17 @@ export default {
com_ui_assistants_output: '助手输出', com_ui_assistants_output: '助手输出',
com_ui_delete: '删除', com_ui_delete: '删除',
com_ui_create: '创建', 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: '删除对话?',
com_ui_delete_conversation_confirm: '这将删除', com_ui_delete_conversation_confirm: '这将删除',
com_ui_delete_assistant_confirm: '确定要删除此助手吗?该操作无法撤销。', com_ui_delete_assistant_confirm: '确定要删除此助手吗?该操作无法撤销。',
@ -386,6 +397,12 @@ export default {
com_nav_export_recursive_or_sequential: '递归或顺序?', com_nav_export_recursive_or_sequential: '递归或顺序?',
com_nav_export_recursive: '递归', com_nav_export_recursive: '递归',
com_nav_export_conversation: '导出对话', 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_my_files: '我的文件',
com_nav_theme: '主题', com_nav_theme: '主题',
com_nav_theme_system: '跟随系统设置', com_nav_theme_system: '跟随系统设置',
@ -417,6 +434,15 @@ export default {
com_nav_setting_data: '数据管理', com_nav_setting_data: '数据管理',
com_nav_setting_account: '账户', com_nav_setting_account: '账户',
/* The following are AI Translated */ /* 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_today: '今天',
com_ui_date_yesterday: '昨天', com_ui_date_yesterday: '昨天',
com_ui_date_previous_7_days: '过去7天', com_ui_date_previous_7_days: '过去7天',
@ -1022,6 +1048,43 @@ export const comparisons = {
english: 'Create', english: 'Create',
translated: '创建', 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: { com_ui_delete_conversation: {
english: 'Delete chat?', english: 'Delete chat?',
translated: '删除对话?', translated: '删除对话?',
@ -1991,6 +2054,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: '导出对话', 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: { com_nav_my_files: {
english: 'My Files', english: 'My Files',
translated: '我的文件', translated: '我的文件',
@ -2111,6 +2198,42 @@ export const comparisons = {
english: 'Account', english: 'Account',
translated: '账户', 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: { com_ui_date_today: {
english: 'Today', english: 'Today',
translated: '今天', translated: '今天',

View file

@ -50,6 +50,17 @@ export default {
com_ui_import_conversation_error: '導入對話時發生錯誤', com_ui_import_conversation_error: '導入對話時發生錯誤',
com_ui_confirm_action: '確認操作', com_ui_confirm_action: '確認操作',
com_ui_chats: '對話', 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: '刪除',
com_ui_delete_conversation: '刪除對話?', com_ui_delete_conversation: '刪除對話?',
com_ui_delete_conversation_confirm: '這將刪除', com_ui_delete_conversation_confirm: '這將刪除',
@ -240,6 +251,12 @@ export default {
com_nav_export_recursive_or_sequential: '遞迴還是序列?', com_nav_export_recursive_or_sequential: '遞迴還是序列?',
com_nav_export_recursive: '遞迴', com_nav_export_recursive: '遞迴',
com_nav_export_conversation: '匯出對話', 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: '主題',
com_nav_theme_system: '跟隨系統設定', com_nav_theme_system: '跟隨系統設定',
com_nav_theme_dark: '深色', com_nav_theme_dark: '深色',
@ -266,6 +283,12 @@ export default {
com_nav_setting_general: '一般', com_nav_setting_general: '一般',
com_nav_setting_data: '資料控制', com_nav_setting_data: '資料控制',
/* The following are AI translated */ /* 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_today: '今天',
com_ui_date_yesterday: '昨天', com_ui_date_yesterday: '昨天',
com_ui_date_previous_7_days: '前 7 天', com_ui_date_previous_7_days: '前 7 天',
@ -711,6 +734,43 @@ export const comparisons = {
english: 'chats', english: 'chats',
translated: '對話', 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: { com_ui_delete: {
english: 'Delete', english: 'Delete',
translated: '刪除', translated: '刪除',
@ -1427,6 +1487,30 @@ export const comparisons = {
english: 'Export conversation', english: 'Export conversation',
translated: '匯出對話', 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: { com_nav_theme: {
english: 'Theme', english: 'Theme',
translated: '主題', translated: '主題',
@ -1527,6 +1611,30 @@ export const comparisons = {
english: 'Data controls', english: 'Data controls',
translated: '資料控制', 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: { com_ui_date_today: {
english: 'Today', english: 'Today',
translated: '今天', translated: '今天',

View file

@ -30,13 +30,13 @@ export default function Search() {
return ( return (
<MinimalMessagesWrapper ref={containerRef} className="pt-4"> <MinimalMessagesWrapper ref={containerRef} className="pt-4">
{(messages && messages?.length == 0) || messages === null ? ( {(messages && messages?.length == 0) || messages === null ? (
<div className="my-auto flex h-full w-full items-center justify-center gap-1 bg-gray-50 p-3 text-lg text-gray-500 dark:border-gray-800/50 dark:bg-gray-800 dark:text-gray-300"> <div className="my-auto flex h-full w-full items-center justify-center gap-1 bg-white p-3 text-lg text-gray-500 dark:border-gray-800/50 dark:bg-gray-800 dark:text-gray-300">
{localize('com_ui_nothing_found')} {localize('com_ui_nothing_found')}
</div> </div>
) : ( ) : (
messages?.map((message) => <SearchMessage key={message.messageId} message={message} />) messages?.map((message) => <SearchMessage key={message.messageId} message={message} />)
)} )}
<div className="absolute bottom-0 left-0 right-0 h-[5%] bg-gradient-to-t from-gray-800 to-transparent" /> <div className="absolute bottom-0 left-0 right-0 h-[5%] bg-gradient-to-t from-gray-50 to-transparent dark:from-gray-800" />
</MinimalMessagesWrapper> </MinimalMessagesWrapper>
); );
} }

View file

@ -0,0 +1,5 @@
import ShareView from '~/components/Share/ShareView';
export default function ShareRoute() {
return <ShareView />;
}

View file

@ -7,6 +7,7 @@ import {
ApiErrorWatcher, ApiErrorWatcher,
} from '~/components/Auth'; } from '~/components/Auth';
import { AuthContextProvider } from '~/hooks/AuthContext'; import { AuthContextProvider } from '~/hooks/AuthContext';
import ShareRoute from './ShareRoute';
import ChatRoute from './ChatRoute'; import ChatRoute from './ChatRoute';
import Search from './Search'; import Search from './Search';
import Root from './Root'; import Root from './Root';
@ -31,6 +32,10 @@ export const router = createBrowserRouter([
path: 'reset-password', path: 'reset-password',
element: <ResetPassword />, element: <ResetPassword />,
}, },
{
path: 'share/:shareId',
element: <ShareRoute />,
},
{ {
element: <AuthLayout />, element: <AuthLayout />,
children: [ children: [

View file

@ -0,0 +1,70 @@
import { InfiniteData } from '@tanstack/react-query';
export const addData = <TCollection, TData>(
data: InfiniteData<TCollection>,
collectionName: string,
newData: TData,
findIndex: (page: TCollection) => number,
) => {
const dataJson = JSON.parse(JSON.stringify(data)) as InfiniteData<TCollection>;
const { pageIndex, index } = findPage<TCollection>(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<TData>(data: InfiniteData<TData>, 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 = <TCollection, TData>(
data: InfiniteData<TCollection>,
collectionName: string,
updatedData: TData,
findIndex: (page: TCollection) => number,
) => {
const newData = JSON.parse(JSON.stringify(data)) as InfiniteData<TCollection>;
const { pageIndex, index } = findPage<TCollection>(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 = <TCollection, TData>(
data: TData,
collectionName: string,
findIndex: (page: TCollection) => number,
): TData => {
const newData = JSON.parse(JSON.stringify(data));
const { pageIndex, index } = findPage<TCollection>(newData, findIndex);
if (pageIndex !== -1 && index !== -1) {
// Delete the data from its current page
newData.pages[pageIndex][collectionName].splice(index, 1);
}
return newData;
};

View file

@ -146,11 +146,11 @@ describe('Conversation Utilities', () => {
}, },
], ],
}; };
const { pageIndex, convIndex } = findPageForConversation(data as ConversationData, { const { pageIndex, index } = findPageForConversation(data as ConversationData, {
conversationId: '2', conversationId: '2',
}); });
expect(pageIndex).toBe(0); expect(pageIndex).toBe(0);
expect(convIndex).toBe(1); expect(index).toBe(1);
}); });
}); });
}); });
@ -219,11 +219,11 @@ describe('Conversation Utilities with Fake Data', () => {
describe('findPageForConversation', () => { describe('findPageForConversation', () => {
it('finds the correct page and index for a given conversation in fake data', () => { it('finds the correct page and index for a given conversation in fake data', () => {
const targetConversation = convoData.pages[0].conversations[0]; const targetConversation = convoData.pages[0].conversations[0];
const { pageIndex, convIndex } = findPageForConversation(convoData, { const { pageIndex, index } = findPageForConversation(convoData, {
conversationId: targetConversation.conversationId as string, conversationId: targetConversation.conversationId as string,
}); });
expect(pageIndex).toBeGreaterThanOrEqual(0); expect(pageIndex).toBeGreaterThanOrEqual(0);
expect(convIndex).toBeGreaterThanOrEqual(0); expect(index).toBeGreaterThanOrEqual(0);
}); });
}); });
}); });

View file

@ -14,8 +14,12 @@ import type {
ConversationData, ConversationData,
ConversationUpdater, ConversationUpdater,
GroupedConversations, GroupedConversations,
ConversationListResponse,
} from 'librechat-data-provider'; } from 'librechat-data-provider';
import { addData, deleteData, updateData, findPage } from './collection';
import { InfiniteData } from '@tanstack/react-query';
export const dateKeys = { export const dateKeys = {
today: 'com_ui_date_today', today: 'com_ui_date_today',
yesterday: 'com_ui_date_yesterday', yesterday: 'com_ui_date_yesterday',
@ -105,52 +109,39 @@ export const groupConversationsByDate = (conversations: TConversation[]): Groupe
return Object.entries(sortedGroups); return Object.entries(sortedGroups);
}; };
export const addConversation: ConversationUpdater = (data, newConversation) => { export const addConversation = (
const newData = JSON.parse(JSON.stringify(data)) as ConversationData; data: InfiniteData<ConversationListResponse>,
const { pageIndex, convIndex } = findPageForConversation(newData, newConversation); newConversation: TConversation,
): ConversationData => {
if (pageIndex !== -1 && convIndex !== -1) { return addData<ConversationListResponse, TConversation>(
return updateConversation(data, newConversation); data,
} 'conversations',
newData.pages[0].conversations.unshift({ newConversation,
...newConversation, (page) =>
updatedAt: new Date().toISOString(), page.conversations.findIndex((c) => c.conversationId === newConversation.conversationId),
}); );
return newData;
}; };
export function findPageForConversation( export function findPageForConversation(
data: ConversationData, data: ConversationData,
conversation: TConversation | { conversationId: string }, conversation: TConversation | { conversationId: string },
) { ) {
for (let pageIndex = 0; pageIndex < data.pages.length; pageIndex++) { return findPage<ConversationListResponse>(data, (page) =>
const page = data.pages[pageIndex]; page.conversations.findIndex((c) => c.conversationId === conversation.conversationId),
const convIndex = page.conversations.findIndex(
(c) => c.conversationId === conversation.conversationId,
); );
if (convIndex !== -1) {
return { pageIndex, convIndex };
}
}
return { pageIndex: -1, convIndex: -1 }; // Not found
} }
export const updateConversation: ConversationUpdater = (data, updatedConversation) => { export const updateConversation = (
const newData = JSON.parse(JSON.stringify(data)); data: InfiniteData<ConversationListResponse>,
const { pageIndex, convIndex } = findPageForConversation(newData, updatedConversation); newConversation: TConversation,
): ConversationData => {
if (pageIndex !== -1 && convIndex !== -1) { return updateData<ConversationListResponse, TConversation>(
// Remove the conversation from its current position data,
newData.pages[pageIndex].conversations.splice(convIndex, 1); 'conversations',
// Add the updated conversation to the top of the first page newConversation,
newData.pages[0].conversations.unshift({ (page) =>
...updatedConversation, page.conversations.findIndex((c) => c.conversationId === newConversation.conversationId),
updatedAt: new Date().toISOString(), );
});
}
return newData;
}; };
export const updateConvoFields: ConversationUpdater = ( export const updateConvoFields: ConversationUpdater = (
@ -158,13 +149,13 @@ export const updateConvoFields: ConversationUpdater = (
updatedConversation: Partial<TConversation> & Pick<TConversation, 'conversationId'>, updatedConversation: Partial<TConversation> & Pick<TConversation, 'conversationId'>,
): ConversationData => { ): ConversationData => {
const newData = JSON.parse(JSON.stringify(data)); const newData = JSON.parse(JSON.stringify(data));
const { pageIndex, convIndex } = findPageForConversation( const { pageIndex, index } = findPageForConversation(
newData, newData,
updatedConversation as { conversationId: string }, updatedConversation as { conversationId: string },
); );
if (pageIndex !== -1 && convIndex !== -1) { if (pageIndex !== -1 && index !== -1) {
const deleted = newData.pages[pageIndex].conversations.splice(convIndex, 1); const deleted = newData.pages[pageIndex].conversations.splice(index, 1);
const oldConversation = deleted[0] as TConversation; const oldConversation = deleted[0] as TConversation;
newData.pages[0].conversations.unshift({ newData.pages[0].conversations.unshift({
@ -181,15 +172,9 @@ export const deleteConversation = (
data: ConversationData, data: ConversationData,
conversationId: string, conversationId: string,
): ConversationData => { ): ConversationData => {
const newData = JSON.parse(JSON.stringify(data)); return deleteData<ConversationListResponse, ConversationData>(data, 'conversations', (page) =>
const { pageIndex, convIndex } = findPageForConversation(newData, { conversationId }); page.conversations.findIndex((c) => c.conversationId === conversationId),
);
if (pageIndex !== -1 && convIndex !== -1) {
// Delete the conversation from its current page
newData.pages[pageIndex].conversations.splice(convIndex, 1);
}
return newData;
}; };
export const getConversationById = ( export const getConversationById = (

View file

@ -7,6 +7,7 @@ export * from './presets';
export * from './textarea'; export * from './textarea';
export * from './languages'; export * from './languages';
export * from './endpoints'; export * from './endpoints';
export * from './sharedLink';
export { default as cn } from './cn'; export { default as cn } from './cn';
export { default as buildTree } from './buildTree'; export { default as buildTree } from './buildTree';
export { default as getLoginError } from './getLoginError'; export { default as getLoginError } from './getLoginError';

View file

@ -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],
};

View file

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

View file

@ -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<SharedLinkListResponse>,
newSharedLink: TSharedLink,
): SharedLinkListData => {
return addData<SharedLinkListResponse, TSharedLink>(data, 'sharedLinks', newSharedLink, (page) =>
page.sharedLinks.findIndex((c) => c.shareId === newSharedLink.shareId),
);
};
export const updateSharedLink = (
data: InfiniteData<SharedLinkListResponse>,
newSharedLink: TSharedLink,
): SharedLinkListData => {
return updateData<SharedLinkListResponse, TSharedLink>(
data,
'sharedLinks',
newSharedLink,
(page) => page.sharedLinks.findIndex((c) => c.shareId === newSharedLink.shareId),
);
};
export const deleteSharedLink = (data: SharedLinkListData, shareId: string): SharedLinkListData => {
return deleteData<SharedLinkListResponse, SharedLinkListData>(data, 'sharedLinks', (page) =>
page.sharedLinks.findIndex((c) => c.shareId === shareId),
);
};

View file

@ -7,6 +7,13 @@ export const userPlugins = () => '/api/user/plugins';
export const messages = (conversationId: string, messageId?: string) => export const messages = (conversationId: string, messageId?: string) =>
`/api/messages/${conversationId}${messageId ? `/${messageId}` : ''}`; `/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'; const keysEndpoint = '/api/keys';
export const keys = () => keysEndpoint; export const keys = () => keysEndpoint;

View file

@ -31,6 +31,34 @@ export function getMessagesByConvoId(conversationId: string): Promise<s.TMessage
return request.get(endpoints.messages(conversationId)); return request.get(endpoints.messages(conversationId));
} }
export function getSharedMessages(shareId: string): Promise<t.TSharedMessagesResponse> {
return request.get(endpoints.shareMessages(shareId));
}
export const listSharedLinks = (
params?: q.SharedLinkListParams,
): Promise<q.SharedLinksResponse> => {
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<t.TSharedLinkResponse> {
return request.get(endpoints.shareMessages(shareId));
}
export function createSharedLink(payload: t.TSharedLinkRequest): Promise<t.TSharedLinkResponse> {
return request.post(endpoints.createSharedLink, payload);
}
export function updateSharedLink(payload: t.TSharedLinkRequest): Promise<t.TSharedLinkResponse> {
return request.patch(endpoints.updateSharedLink, payload);
}
export function deleteSharedLink(shareId: string): Promise<t.TDeleteSharedLinkResponse> {
return request.delete(endpoints.shareMessages(shareId));
}
export function updateMessage(payload: t.TUpdateMessageRequest): Promise<unknown> { export function updateMessage(payload: t.TUpdateMessageRequest): Promise<unknown> {
const { conversationId, messageId, text } = payload; const { conversationId, messageId, text } = payload;
if (!conversationId) { if (!conversationId) {

View file

@ -1,5 +1,7 @@
export enum QueryKeys { export enum QueryKeys {
messages = 'messages', messages = 'messages',
sharedMessages = 'sharedMessages',
sharedLinks = 'sharedLinks',
allConversations = 'allConversations', allConversations = 'allConversations',
archivedConversations = 'archivedConversations', archivedConversations = 'archivedConversations',
searchConversations = 'searchConversations', searchConversations = 'searchConversations',

View file

@ -60,6 +60,22 @@ export const useGetMessagesByConvoId = <TData = s.TMessage[]>(
); );
}; };
export const useGetSharedMessages = (
shareId: string,
config?: UseQueryOptions<t.TSharedMessagesResponse>,
): QueryObserverResult<t.TSharedMessagesResponse> => {
return useQuery<t.TSharedMessagesResponse>(
[QueryKeys.sharedMessages, shareId],
() => dataService.getSharedMessages(shareId),
{
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
...config,
},
);
};
export const useGetUserBalance = ( export const useGetUserBalance = (
config?: UseQueryOptions<string>, config?: UseQueryOptions<string>,
): QueryObserverResult<string> => { ): QueryObserverResult<string> => {
@ -398,7 +414,9 @@ export const useUpdateUserPluginsMutation = (): UseMutationResult<
}); });
}; };
export const useGetStartupConfig = (): QueryObserverResult<t.TStartupConfig> => { export const useGetStartupConfig = (
config?: UseQueryOptions<t.TStartupConfig>,
): QueryObserverResult<t.TStartupConfig> => {
return useQuery<t.TStartupConfig>( return useQuery<t.TStartupConfig>(
[QueryKeys.startupConfig], [QueryKeys.startupConfig],
() => dataService.getStartupConfig(), () => dataService.getStartupConfig(),
@ -406,6 +424,7 @@ export const useGetStartupConfig = (): QueryObserverResult<t.TStartupConfig> =>
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
refetchOnReconnect: false, refetchOnReconnect: false,
refetchOnMount: false, refetchOnMount: false,
...config,
}, },
); );
}; };

View file

@ -381,6 +381,19 @@ export type TConversation = z.infer<typeof tConversationSchema> & {
presetOverride?: Partial<TPreset>; presetOverride?: Partial<TPreset>;
}; };
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<typeof tSharedLinkSchema>;
export const openAISchema = tConversationSchema export const openAISchema = tConversationSchema
.pick({ .pick({
model: true, model: true,

View file

@ -1,5 +1,12 @@
import OpenAI from 'openai'; 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'; import type { TSpecsConfig } from './models';
export type TOpenAIMessage = OpenAI.Chat.ChatCompletionMessageParam; export type TOpenAIMessage = OpenAI.Chat.ChatCompletionMessageParam;
export type TOpenAIFunction = OpenAI.Chat.ChatCompletionCreateParams.Function; export type TOpenAIFunction = OpenAI.Chat.ChatCompletionCreateParams.Function;
@ -133,6 +140,19 @@ export type TArchiveConversationRequest = {
export type TArchiveConversationResponse = TConversation; export type TArchiveConversationResponse = TConversation;
export type TSharedMessagesResponse = Omit<TSharedLink, 'messages'> & {
messages: TMessage[];
};
export type TSharedLinkRequest = Partial<
Omit<TSharedLink, 'messages' | 'createdAt' | 'updatedAt'>
> & {
conversationId: string;
};
export type TSharedLinkResponse = TSharedLink;
export type TSharedLinksResponse = TSharedLink[];
export type TDeleteSharedLinkResponse = TSharedLink;
export type TForkConvoRequest = { export type TForkConvoRequest = {
messageId: string; messageId: string;
conversationId: string; conversationId: string;

View file

@ -90,3 +90,13 @@ export type DeleteConversationOptions = MutationOptions<
>; >;
export type ForkConvoOptions = MutationOptions<types.TForkConvoResponse, types.TForkConvoRequest>; export type ForkConvoOptions = MutationOptions<types.TForkConvoResponse, types.TForkConvoRequest>;
export type CreateSharedLinkOptions = MutationOptions<
types.TSharedLink,
Partial<types.TSharedLink>
>;
export type UpdateSharedLinkOptions = MutationOptions<
types.TSharedLink,
Partial<types.TSharedLink>
>;
export type DeleteSharedLinkOptions = MutationOptions<types.TSharedLink, { shareId: string }>;

View file

@ -1,5 +1,5 @@
import type { InfiniteData } from '@tanstack/react-query'; import type { InfiniteData } from '@tanstack/react-query';
import type { TMessage, TConversation } from '../schemas'; import type { TMessage, TConversation, TSharedLink } from '../schemas';
export type Conversation = { export type Conversation = {
id: string; id: string;
createdAt: number; createdAt: number;
@ -16,7 +16,7 @@ export type ConversationListParams = {
order?: 'asc' | 'desc'; order?: 'asc' | 'desc';
pageNumber: string; // Add this line pageNumber: string; // Add this line
conversationId?: string; conversationId?: string;
isArchived: boolean; isArchived?: boolean;
}; };
// Type for the response from the conversation list API // Type for the response from the conversation list API
@ -33,3 +33,24 @@ export type ConversationUpdater = (
data: ConversationData, data: ConversationData,
conversation: TConversation, conversation: TConversation,
) => ConversationData; ) => ConversationData;
export type SharedMessagesResponse = Omit<TSharedLink, 'messages'> & {
messages: TMessage[];
};
export type SharedLinkListParams = Omit<ConversationListParams, 'isArchived' | 'conversationId'> & {
isPublic?: boolean;
};
export type SharedLinksResponse = Omit<ConversationListResponse, 'conversations' | 'messages'> & {
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<SharedLinkListResponse>;