diff --git a/web/src/components/originui/password-input.tsx b/web/src/components/originui/password-input.tsx new file mode 100644 index 000000000..28717c443 --- /dev/null +++ b/web/src/components/originui/password-input.tsx @@ -0,0 +1,46 @@ +// https://originui.com/r/comp-23.json + +'use client'; + +import { EyeIcon, EyeOffIcon } from 'lucide-react'; +import React, { useId, useState } from 'react'; +import { Input, InputProps } from '../ui/input'; + +export default React.forwardRef( + function PasswordInput({ ...props }, ref) { + const id = useId(); + const [isVisible, setIsVisible] = useState(false); + + const toggleVisibility = () => setIsVisible((prevState) => !prevState); + + return ( +
+ {/* */} +
+ + +
+
+ ); + }, +); diff --git a/web/src/components/page-header.tsx b/web/src/components/page-header.tsx index 903509ccd..0fe5daddc 100644 --- a/web/src/components/page-header.tsx +++ b/web/src/components/page-header.tsx @@ -2,7 +2,7 @@ import { PropsWithChildren } from 'react'; export function PageHeader({ children }: PropsWithChildren) { return ( -
+
{children}
); diff --git a/web/src/components/password-input/index.tsx b/web/src/components/password-input/index.tsx deleted file mode 100644 index 515e9d4c3..000000000 --- a/web/src/components/password-input/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Input } from '@/components/originui/input'; -import { EyeIcon, EyeOffIcon } from 'lucide-react'; -import { ChangeEvent, forwardRef, useId, useState } from 'react'; - -type PropType = { - name: string; - value: string; - onBlur: () => void; - onChange: (event: ChangeEvent) => void; -}; - -function PasswordInput(props: PropType) { - const id = useId(); - const [isVisible, setIsVisible] = useState(false); - - const toggleVisibility = () => setIsVisible((prevState) => !prevState); - - return ( -
- {/* */} -
- props.onChange(ev)} - /> - -
-
- ); -} - -export default forwardRef(PasswordInput); diff --git a/web/src/components/tavily-form-field.tsx b/web/src/components/tavily-form-field.tsx new file mode 100644 index 000000000..abccae52b --- /dev/null +++ b/web/src/components/tavily-form-field.tsx @@ -0,0 +1,51 @@ +import { useTranslate } from '@/hooks/common-hooks'; +import { useFormContext } from 'react-hook-form'; +import PasswordInput from './originui/password-input'; +import { + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from './ui/form'; + +interface IProps { + name?: string; +} + +export function TavilyFormField({ + name = 'prompt_config.tavily_api_key', +}: IProps) { + const form = useFormContext(); + const { t } = useTranslate('chat'); + + return ( + ( + + Tavily API Key + + + + + + {t('tavilyApiKeyHelp')} + + + + + )} + /> + ); +} diff --git a/web/src/pages/next-chats/chat/app-settings/chat-basic-settings.tsx b/web/src/pages/next-chats/chat/app-settings/chat-basic-settings.tsx index a67217ed9..3c81c384a 100644 --- a/web/src/pages/next-chats/chat/app-settings/chat-basic-settings.tsx +++ b/web/src/pages/next-chats/chat/app-settings/chat-basic-settings.tsx @@ -3,6 +3,7 @@ import { FileUploader } from '@/components/file-uploader'; import { KnowledgeBaseFormField } from '@/components/knowledge-base-item'; import { SwitchFormField } from '@/components/switch-fom-field'; +import { TavilyFormField } from '@/components/tavily-form-field'; import { FormControl, FormField, @@ -105,6 +106,7 @@ export default function ChatBasicSetting() { name={'prompt_config.tts'} label={t('tts')} > + ); diff --git a/web/src/pages/next-chats/chat/app-settings/chat-settings.tsx b/web/src/pages/next-chats/chat/app-settings/chat-settings.tsx index 18f051b06..c1c81969a 100644 --- a/web/src/pages/next-chats/chat/app-settings/chat-settings.tsx +++ b/web/src/pages/next-chats/chat/app-settings/chat-settings.tsx @@ -68,8 +68,8 @@ export function ChatSettings({ switchSettingVisible }: ChatSettingsProps) { }, [data, form]); return ( -
-
+
+
Chat Settings
diff --git a/web/src/pages/next-chats/chat/app-settings/use-chat-setting-schema.tsx b/web/src/pages/next-chats/chat/app-settings/use-chat-setting-schema.tsx index 0edf3ce47..b68b4ee72 100644 --- a/web/src/pages/next-chats/chat/app-settings/use-chat-setting-schema.tsx +++ b/web/src/pages/next-chats/chat/app-settings/use-chat-setting-schema.tsx @@ -24,6 +24,7 @@ export function useChatSettingSchema() { optional: z.boolean(), }), ), + tavily_api_key: z.string().optional(), }); const formSchema = z.object({ diff --git a/web/src/pages/next-chats/chat/chat-box/multiple-chat-box.tsx b/web/src/pages/next-chats/chat/chat-box/multiple-chat-box.tsx new file mode 100644 index 000000000..d03b64a3b --- /dev/null +++ b/web/src/pages/next-chats/chat/chat-box/multiple-chat-box.tsx @@ -0,0 +1,155 @@ +import { NextMessageInput } from '@/components/message-input/next'; +import MessageItem from '@/components/message-item'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { MessageType } from '@/constants/chat'; +import { + useFetchConversation, + useFetchDialog, + useGetChatSearchParams, +} from '@/hooks/use-chat-request'; +import { useFetchUserInfo } from '@/hooks/user-setting-hooks'; +import { buildMessageUuidWithRole } from '@/utils/chat'; +import { Trash2 } from 'lucide-react'; +import { useCallback } from 'react'; +import { + useGetSendButtonDisabled, + useSendButtonDisabled, +} from '../../hooks/use-button-disabled'; +import { useCreateConversationBeforeUploadDocument } from '../../hooks/use-create-conversation'; +import { useSendMessage } from '../../hooks/use-send-chat-message'; +import { buildMessageItemReference } from '../../utils'; +import { useAddChatBox } from '../use-add-box'; + +type MultipleChatBoxProps = { + controller: AbortController; + chatBoxIds: string[]; +} & Pick, 'removeChatBox'>; + +type ChatCardProps = { id: string } & Pick< + MultipleChatBoxProps, + 'controller' | 'removeChatBox' +>; + +function ChatCard({ controller, removeChatBox, id }: ChatCardProps) { + const { + value, + // scrollRef, + messageContainerRef, + sendLoading, + derivedMessages, + handleInputChange, + handlePressEnter, + regenerateMessage, + removeMessageById, + stopOutputMessage, + } = useSendMessage(controller); + + const { data: userInfo } = useFetchUserInfo(); + const { data: currentDialog } = useFetchDialog(); + const { data: conversation } = useFetchConversation(); + + const handleRemoveChatBox = useCallback(() => { + removeChatBox(id); + }, [id, removeChatBox]); + + return ( + + + +
+ Card Title + +
+ +
+
+ +
+
+ {derivedMessages?.map((message, i) => { + return ( + + ); + })} +
+ {/*
*/} +
+ + + ); +} + +export function MultipleChatBox({ + controller, + chatBoxIds, + removeChatBox, +}: MultipleChatBoxProps) { + const { + value, + sendLoading, + handleInputChange, + handlePressEnter, + stopOutputMessage, + } = useSendMessage(controller); + + const { createConversationBeforeUploadDocument } = + useCreateConversationBeforeUploadDocument(); + const { conversationId } = useGetChatSearchParams(); + const disabled = useGetSendButtonDisabled(); + const sendDisabled = useSendButtonDisabled(value); + return ( +
+
+ {chatBoxIds.map((id) => ( + + ))} +
+ +
+ ); +} diff --git a/web/src/pages/next-chats/chat/chat-box.tsx b/web/src/pages/next-chats/chat/chat-box/single-chat-box.tsx similarity index 88% rename from web/src/pages/next-chats/chat/chat-box.tsx rename to web/src/pages/next-chats/chat/chat-box/single-chat-box.tsx index 6495f3fcc..4db3183e5 100644 --- a/web/src/pages/next-chats/chat/chat-box.tsx +++ b/web/src/pages/next-chats/chat/chat-box/single-chat-box.tsx @@ -11,16 +11,16 @@ import { buildMessageUuidWithRole } from '@/utils/chat'; import { useGetSendButtonDisabled, useSendButtonDisabled, -} from '../hooks/use-button-disabled'; -import { useCreateConversationBeforeUploadDocument } from '../hooks/use-create-conversation'; -import { useSendMessage } from '../hooks/use-send-chat-message'; -import { buildMessageItemReference } from '../utils'; +} from '../../hooks/use-button-disabled'; +import { useCreateConversationBeforeUploadDocument } from '../../hooks/use-create-conversation'; +import { useSendMessage } from '../../hooks/use-send-chat-message'; +import { buildMessageItemReference } from '../../utils'; interface IProps { controller: AbortController; } -export function ChatBox({ controller }: IProps) { +export function SingleChatBox({ controller }: IProps) { const { value, // scrollRef, @@ -43,7 +43,7 @@ export function ChatBox({ controller }: IProps) { const sendDisabled = useSendButtonDisabled(value); return ( -
+
{derivedMessages?.map((message, i) => { diff --git a/web/src/pages/next-chats/chat/index.tsx b/web/src/pages/next-chats/chat/index.tsx index b8925e022..8860bb791 100644 --- a/web/src/pages/next-chats/chat/index.tsx +++ b/web/src/pages/next-chats/chat/index.tsx @@ -7,14 +7,20 @@ import { BreadcrumbPage, BreadcrumbSeparator, } from '@/components/ui/breadcrumb'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { useSetModalState } from '@/hooks/common-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useFetchDialog } from '@/hooks/use-chat-request'; +import { cn } from '@/lib/utils'; +import { Plus } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { useHandleClickConversationCard } from '../hooks/use-click-card'; import { ChatSettings } from './app-settings/chat-settings'; -import { ChatBox } from './chat-box'; +import { MultipleChatBox } from './chat-box/multiple-chat-box'; +import { SingleChatBox } from './chat-box/single-chat-box'; import { Sessions } from './sessions'; +import { useAddChatBox } from './use-add-box'; export default function Chat() { const { navigateToChatList } = useNavigatePage(); @@ -24,9 +30,16 @@ export default function Chat() { useHandleClickConversationCard(); const { visible: settingVisible, switchVisible: switchSettingVisible } = useSetModalState(true); + const { + removeChatBox, + addChatBox, + chatBoxIds, + hasSingleChatBox, + hasThreeChatBox, + } = useAddChatBox(); return ( -
+
@@ -43,18 +56,52 @@ export default function Chat() {
-
- - -
- {settingVisible && ( - - )} + + + + + + + +
+ Card Title + +
+ +
+
+ + {hasSingleChatBox ? ( + + ) : ( + + )} + +
+ {settingVisible && ( + + )} +
+
); diff --git a/web/src/pages/next-chats/chat/use-add-box.ts b/web/src/pages/next-chats/chat/use-add-box.ts new file mode 100644 index 000000000..a093afa7d --- /dev/null +++ b/web/src/pages/next-chats/chat/use-add-box.ts @@ -0,0 +1,26 @@ +import { useCallback, useState } from 'react'; +import { v4 as uuid } from 'uuid'; + +export function useAddChatBox() { + const [ids, setIds] = useState([uuid()]); + + const hasSingleChatBox = ids.length === 1; + + const hasThreeChatBox = ids.length === 3; + + const addChatBox = useCallback(() => { + setIds((prev) => [...prev, uuid()]); + }, []); + + const removeChatBox = useCallback((id: string) => { + setIds((prev) => prev.filter((x) => x !== id)); + }, []); + + return { + chatBoxIds: ids, + hasSingleChatBox, + hasThreeChatBox, + addChatBox, + removeChatBox, + }; +} diff --git a/web/src/pages/profile-setting/profile/index.tsx b/web/src/pages/profile-setting/profile/index.tsx index fab4a12d0..d494e9c48 100644 --- a/web/src/pages/profile-setting/profile/index.tsx +++ b/web/src/pages/profile-setting/profile/index.tsx @@ -1,4 +1,4 @@ -import PasswordInput from '@/components/password-input'; +import PasswordInput from '@/components/originui/password-input'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Button } from '@/components/ui/button'; import {