diff --git a/api/app/clients/BaseClient.js b/api/app/clients/BaseClient.js index 44ccc926d..fd1a05183 100644 --- a/api/app/clients/BaseClient.js +++ b/api/app/clients/BaseClient.js @@ -879,13 +879,14 @@ class BaseClient { : await getConvo(this.options.req?.user?.id, message.conversationId); const unsetFields = {}; + const exceptions = new Set(['spec', 'iconURL']); if (existingConvo != null) { this.fetchedConvo = true; for (const key in existingConvo) { if (!key) { continue; } - if (excludedKeys.has(key)) { + if (excludedKeys.has(key) && !exceptions.has(key)) { continue; } diff --git a/bun.lockb b/bun.lockb index e85113bbc..61118178f 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/client/package.json b/client/package.json index 96b402e74..d4cd010a0 100644 --- a/client/package.json +++ b/client/package.json @@ -52,6 +52,7 @@ "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-tabs": "^1.0.3", "@radix-ui/react-toast": "^1.1.5", + "@react-spring/web": "^9.7.5", "@tanstack/react-query": "^4.28.0", "@tanstack/react-table": "^8.11.7", "class-variance-authority": "^0.6.0", @@ -141,8 +142,8 @@ "ts-jest": "^29.2.5", "typescript": "^5.3.3", "vite": "^6.1.0", - "vite-plugin-node-polyfills": "^0.17.0", "vite-plugin-compression": "^0.5.1", + "vite-plugin-node-polyfills": "^0.17.0", "vite-plugin-pwa": "^0.21.1" } } diff --git a/client/src/common/index.ts b/client/src/common/index.ts index 3452818fc..e1a3ab0a0 100644 --- a/client/src/common/index.ts +++ b/client/src/common/index.ts @@ -3,5 +3,6 @@ export * from './artifacts'; export * from './types'; export * from './menus'; export * from './tools'; +export * from './selector'; export * from './assistants-types'; export * from './agents-types'; diff --git a/client/src/common/selector.ts b/client/src/common/selector.ts new file mode 100644 index 000000000..a6380552c --- /dev/null +++ b/client/src/common/selector.ts @@ -0,0 +1,24 @@ +import React from 'react'; +import { TModelSpec, TInterfaceConfig } from 'librechat-data-provider'; + +export interface Endpoint { + value: string; + label: string; + hasModels: boolean; + models?: string[]; + icon: React.ReactNode; + agentNames?: Record; + assistantNames?: Record; + modelIcons?: Record; +} + +export interface SelectedValues { + endpoint: string | null; + model: string | null; + modelSpec: string | null; +} + +export interface ModelSelectorProps { + interfaceConfig: TInterfaceConfig; + modelSpecs: TModelSpec[]; +} diff --git a/client/src/common/types.ts b/client/src/common/types.ts index 118cefce1..71e2e7a0c 100644 --- a/client/src/common/types.ts +++ b/client/src/common/types.ts @@ -1,10 +1,10 @@ import { RefObject } from 'react'; -import { FileSources } from 'librechat-data-provider'; -import type * as InputNumberPrimitive from 'rc-input-number'; -import type { ColumnDef } from '@tanstack/react-table'; -import type { SetterOrUpdater } from 'recoil'; -import type * as t from 'librechat-data-provider'; +import { FileSources, EModelEndpoint } from 'librechat-data-provider'; import type { UseMutationResult } from '@tanstack/react-query'; +import type * as InputNumberPrimitive from 'rc-input-number'; +import type { SetterOrUpdater, RecoilState } from 'recoil'; +import type { ColumnDef } from '@tanstack/react-table'; +import type * as t from 'librechat-data-provider'; import type { LucideIcon } from 'lucide-react'; import type { TranslationKeys } from '~/hooks'; @@ -48,6 +48,14 @@ export type AudioChunk = { }; }; +export type BadgeItem = { + id: string; + icon: React.ComponentType; + label: string; + atom: RecoilState; + isAvailable: boolean; +}; + export type AssistantListItem = { id: string; name: string; @@ -488,6 +496,27 @@ export interface ExtendedFile { metadata?: t.TFile['metadata']; } +export interface ExtendedEndpoint { + value: EModelEndpoint; + label: string; + hasModels: boolean; + icon: JSX.Element | null; + models?: string[]; + agentNames?: Record; + assistantNames?: Record; + modelIcons?: Record; +} + +export interface ModelItemProps { + modelName: string; + endpoint: EModelEndpoint; + isSelected: boolean; + onSelect: () => void; + onNavigateBack: () => void; + icon?: JSX.Element; + className?: string; +} + export type ContextType = { navVisible: boolean; setNavVisible: (visible: boolean) => void }; export interface SwitcherProps { diff --git a/client/src/components/Chat/AddMultiConvo.tsx b/client/src/components/Chat/AddMultiConvo.tsx index 6cfeb04b9..24c1d7cb1 100644 --- a/client/src/components/Chat/AddMultiConvo.tsx +++ b/client/src/components/Chat/AddMultiConvo.tsx @@ -12,7 +12,7 @@ function AddMultiConvo() { const localize = useLocalize(); const clickHandler = () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { title: _t, ...convo } = conversation ?? ({} as TConversation); setAddedConvo({ ...convo, @@ -42,7 +42,7 @@ function AddMultiConvo() { role="button" onClick={clickHandler} data-testid="parameters-button" - className="inline-flex size-10 items-center justify-center rounded-lg border border-border-light bg-transparent text-text-primary transition-all ease-in-out hover:bg-surface-tertiary disabled:pointer-events-none disabled:opacity-50 radix-state-open:bg-surface-tertiary" + className="inline-flex size-10 flex-shrink-0 items-center justify-center rounded-lg border border-border-light bg-transparent text-text-primary transition-all ease-in-out hover:bg-surface-tertiary disabled:pointer-events-none disabled:opacity-50 radix-state-open:bg-surface-tertiary" > diff --git a/client/src/components/Chat/ChatView.tsx b/client/src/components/Chat/ChatView.tsx index dbf39ee84..1fbfdef73 100644 --- a/client/src/components/Chat/ChatView.tsx +++ b/client/src/components/Chat/ChatView.tsx @@ -7,6 +7,7 @@ import type { TMessage } from 'librechat-data-provider'; import type { ChatFormValues } from '~/common'; import { ChatContext, AddedChatContext, useFileMapContext, ChatFormProvider } from '~/Providers'; import { useChatHelpers, useAddedResponse, useSSE } from '~/hooks'; +import ConversationStarters from './Input/ConversationStarters'; import MessagesView from './Messages/MessagesView'; import { Spinner } from '~/components/svg'; import Presentation from './Presentation'; @@ -21,6 +22,7 @@ function ChatView({ index = 0 }: { index?: number }) { const { conversationId } = useParams(); const rootSubmission = useRecoilValue(store.submissionByIndex(index)); const addedSubmission = useRecoilValue(store.submissionByIndex(index + 1)); + const centerFormOnLanding = useRecoilValue(store.centerFormOnLanding); const fileMap = useFileMapContext(); @@ -46,16 +48,18 @@ function ChatView({ index = 0 }: { index?: number }) { }); let content: JSX.Element | null | undefined; + const isLandingPage = !messagesTree || messagesTree.length === 0; + if (isLoading && conversationId !== 'new') { content = (
- +
); - } else if (messagesTree && messagesTree.length !== 0) { - content = } />; + } else if (!isLandingPage) { + content = ; } else { - content = } />; + content = ; } return ( @@ -63,10 +67,27 @@ function ChatView({ index = 0 }: { index?: number }) { - {content} -
- -