Feat: Render chat page #3221 (#9298)

### What problem does this PR solve?

Feat: Render chat page #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-08-07 11:07:15 +08:00
committed by GitHub
parent 7c719f8365
commit f0c34d4454
12 changed files with 613 additions and 53 deletions

View File

@ -0,0 +1,23 @@
import {
Sheet,
SheetContent,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@/components/ui/sheet';
import { PropsWithChildren } from 'react';
import { ChatSettings } from './chat-settings';
export function ChatSettingSheet({ children }: PropsWithChildren) {
return (
<Sheet>
<SheetTrigger asChild>{children}</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Chat Settings</SheetTitle>
</SheetHeader>
<ChatSettings></ChatSettings>
</SheetContent>
</Sheet>
);
}

View File

@ -7,8 +7,9 @@ import { ChatModelSettings } from './chat-model-settings';
import { ChatPromptEngine } from './chat-prompt-engine';
import { useChatSettingSchema } from './use-chat-setting-schema';
export function AppSettings() {
export function ChatSettings() {
const formSchema = useChatSettingSchema();
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
@ -32,27 +33,19 @@ export function AppSettings() {
}
return (
<section className="py-6 w-[500px] max-w-[25%] ">
<div className="text-2xl font-bold mb-4 text-colors-text-neutral-strong px-6">
App settings
</div>
<div className="overflow-auto max-h-[81vh] px-6 ">
<FormProvider {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<ChatBasicSetting></ChatBasicSetting>
<ChatPromptEngine></ChatPromptEngine>
<ChatModelSettings></ChatModelSettings>
</form>
</FormProvider>
</div>
<div className="p-6 text-center">
<p className="text-colors-text-neutral-weak mb-1">
There are unsaved changes
</p>
<Button variant={'tertiary'} className="w-full">
Update
</Button>
</div>
<section className="py-6">
<FormProvider {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-6 overflow-auto max-h-[88vh] pr-4"
>
<ChatBasicSetting></ChatBasicSetting>
<ChatPromptEngine></ChatPromptEngine>
<ChatModelSettings></ChatModelSettings>
</form>
</FormProvider>
<Button className="w-full my-4">Update</Button>
</section>
);
}

View File

@ -1,9 +1,95 @@
import { ChatInput } from '@/components/chat-input';
import { NextMessageInput } from '@/components/message-input/next';
import MessageItem from '@/components/message-item';
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 {
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';
interface IProps {
controller: AbortController;
}
export function ChatBox({ controller }: IProps) {
const {
value,
scrollRef,
messageContainerRef,
sendLoading,
derivedMessages,
handleInputChange,
handlePressEnter,
regenerateMessage,
removeMessageById,
stopOutputMessage,
} = useSendMessage(controller);
const { data: userInfo } = useFetchUserInfo();
const { data: currentDialog } = useFetchDialog();
const { createConversationBeforeUploadDocument } =
useCreateConversationBeforeUploadDocument();
const { conversationId } = useGetChatSearchParams();
const { data: conversation } = useFetchConversation();
const disabled = useGetSendButtonDisabled();
const sendDisabled = useSendButtonDisabled(value);
export function ChatBox() {
return (
<section className="border-x flex-1">
<ChatInput></ChatInput>
<section className="border-x flex flex-col p-5 w-full">
<div ref={messageContainerRef} className="flex-1 overflow-auto">
<div className="w-full">
{derivedMessages?.map((message, i) => {
return (
<MessageItem
loading={
message.role === MessageType.Assistant &&
sendLoading &&
derivedMessages.length - 1 === i
}
key={buildMessageUuidWithRole(message)}
item={message}
nickname={userInfo.nickname}
avatar={userInfo.avatar}
avatarDialog={currentDialog.icon}
reference={buildMessageItemReference(
{
message: derivedMessages,
reference: conversation.reference,
},
message,
)}
// clickDocumentButton={clickDocumentButton}
index={i}
removeMessageById={removeMessageById}
regenerateMessage={regenerateMessage}
sendLoading={sendLoading}
></MessageItem>
);
})}
</div>
<div ref={scrollRef} />
</div>
<NextMessageInput
disabled={disabled}
sendDisabled={sendDisabled}
sendLoading={sendLoading}
value={value}
onInputChange={handleInputChange}
onPressEnter={handlePressEnter}
conversationId={conversationId}
createConversationBeforeUploadDocument={
createConversationBeforeUploadDocument
}
stopOutputMessage={stopOutputMessage}
/>
</section>
);
}

View File

@ -10,7 +10,7 @@ import {
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useFetchDialog } from '@/hooks/use-chat-request';
import { useTranslation } from 'react-i18next';
import { AppSettings } from './app-settings';
import { useHandleClickConversationCard } from '../hooks/use-click-card';
import { ChatBox } from './chat-box';
import { Sessions } from './sessions';
@ -18,6 +18,8 @@ export default function Chat() {
const { navigateToChatList } = useNavigatePage();
const { data } = useFetchDialog();
const { t } = useTranslation();
const { handleConversationCardClick, controller } =
useHandleClickConversationCard();
return (
<section className="h-full flex flex-col">
@ -36,10 +38,11 @@ export default function Chat() {
</BreadcrumbList>
</Breadcrumb>
</PageHeader>
<div className="flex flex-1">
<Sessions></Sessions>
<ChatBox></ChatBox>
<AppSettings></AppSettings>
<div className="flex flex-1 min-h-0">
<Sessions
handleConversationCardClick={handleConversationCardClick}
></Sessions>
<ChatBox controller={controller}></ChatBox>
</div>
</section>
);

View File

@ -1,37 +1,60 @@
import { MoreButton } from '@/components/more-button';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { useFetchConversationList } from '@/hooks/use-chat-request';
import { useGetChatSearchParams } from '@/hooks/use-chat-request';
import { cn } from '@/lib/utils';
import { Plus } from 'lucide-react';
import { useCallback } from 'react';
import { useHandleClickConversationCard } from '../hooks/use-click-card';
import { useSelectDerivedConversationList } from '../hooks/use-select-conversation-list';
import { ChatSettingSheet } from './app-settings/chat-settings-sheet';
function SessionCard() {
return (
<Card>
<CardContent className="px-3 py-2 flex justify-between items-center group">
xxx
<MoreButton></MoreButton>
</CardContent>
</Card>
type SessionProps = Pick<
ReturnType<typeof useHandleClickConversationCard>,
'handleConversationCardClick'
>;
export function Sessions({ handleConversationCardClick }: SessionProps) {
const { list: conversationList, addTemporaryConversation } =
useSelectDerivedConversationList();
const handleCardClick = useCallback(
(conversationId: string, isNew: boolean) => () => {
handleConversationCardClick(conversationId, isNew);
},
[handleConversationCardClick],
);
}
export function Sessions() {
const sessionList = new Array(10).fill(1);
const {} = useFetchConversationList();
const { conversationId } = useGetChatSearchParams();
return (
<section className="p-6 w-[400px] max-w-[20%]">
<section className="p-6 w-[400px] max-w-[20%] flex flex-col">
<div className="flex justify-between items-center mb-4">
<span className="text-xl font-bold">Conversations</span>
<Button variant={'ghost'}>
<Button variant={'ghost'} onClick={addTemporaryConversation}>
<Plus></Plus>
</Button>
</div>
<div className="space-y-4">
{sessionList.map((x) => (
<SessionCard key={x}></SessionCard>
<div className="space-y-4 flex-1 overflow-auto">
{conversationList.map((x) => (
<Card
key={x.id}
onClick={handleCardClick(x.id, x.is_new)}
className={cn('cursor-pointer bg-transparent', {
'bg-background-card': conversationId === x.id,
})}
>
<CardContent className="px-3 py-2 flex justify-between items-center group">
{x.name}
<MoreButton></MoreButton>
</CardContent>
</Card>
))}
</div>
<div className="py-2">
<ChatSettingSheet>
<Button className="w-full">Chat Settings</Button>
</ChatSettingSheet>
</div>
</section>
);
}