2026-01-16 09:15:04 +08:00
"use client" ;
2026-03-03 21:32:01 +08:00
import { useCallback } from "react" ;
2026-01-16 09:15:04 +08:00
2026-03-03 21:32:01 +08:00
import { type PromptInputMessage } from "@/components/ai-elements/prompt-input" ;
import { ArtifactTrigger } from "@/components/workspace/artifacts" ;
2026-01-17 00:02:03 +08:00
import {
2026-03-03 21:32:01 +08:00
ChatBox ,
useSpecificChatMode ,
useThreadChat ,
} from "@/components/workspace/chats" ;
2026-01-16 09:15:04 +08:00
import { InputBox } from "@/components/workspace/input-box" ;
2026-01-17 00:05:19 +08:00
import { MessageList } from "@/components/workspace/messages" ;
2026-01-18 17:13:15 +08:00
import { ThreadContext } from "@/components/workspace/messages/context" ;
2026-01-17 17:37:12 +08:00
import { ThreadTitle } from "@/components/workspace/thread-title" ;
2026-01-22 00:26:11 +08:00
import { TodoList } from "@/components/workspace/todo-list" ;
2026-01-17 19:46:02 +08:00
import { Welcome } from "@/components/workspace/welcome" ;
2026-01-20 14:06:47 +08:00
import { useI18n } from "@/core/i18n/hooks" ;
2026-01-31 11:08:27 +08:00
import { useNotification } from "@/core/notification/hooks" ;
2026-01-16 09:55:02 +08:00
import { useLocalSettings } from "@/core/settings" ;
2026-03-03 21:32:01 +08:00
import { useThreadStream } from "@/core/threads/hooks" ;
import { textOfMessage } from "@/core/threads/utils" ;
2026-01-24 18:01:27 +08:00
import { env } from "@/env" ;
2026-01-17 11:02:33 +08:00
import { cn } from "@/lib/utils" ;
2026-01-16 09:15:04 +08:00
export default function ChatPage() {
2026-01-20 14:06:47 +08:00
const { t } = useI18n ( ) ;
2026-01-17 11:02:33 +08:00
const [ settings , setSettings ] = useLocalSettings ( ) ;
2026-03-03 21:32:01 +08:00
const { threadId , isNewThread , setIsNewThread , isMock } = useThreadChat ( ) ;
useSpecificChatMode ( ) ;
2026-01-17 11:02:33 +08:00
2026-01-31 11:08:27 +08:00
const { showNotification } = useNotification ( ) ;
2026-03-03 21:32:01 +08:00
2026-03-18 15:10:27 +08:00
const [ thread , sendMessage , isUploading ] = useThreadStream ( {
2026-03-03 21:32:01 +08:00
threadId : isNewThread ? undefined : threadId ,
context : settings.context ,
isMock ,
onStart : ( ) = > {
setIsNewThread ( false ) ;
2026-03-12 14:57:17 +08:00
// ! Important: Never use next.js router for navigation in this case, otherwise it will cause the thread to re-mount and lose all states. Use native history API instead.
history . replaceState ( null , "" , ` /workspace/chats/ ${ threadId } ` ) ;
2026-03-03 21:32:01 +08:00
} ,
2026-01-31 11:08:27 +08:00
onFinish : ( state ) = > {
if ( document . hidden || ! document . hasFocus ( ) ) {
2026-01-31 20:22:15 +08:00
let body = "Conversation finished" ;
2026-03-02 20:35:46 +08:00
const lastMessage = state . messages . at ( - 1 ) ;
2026-01-31 20:22:15 +08:00
if ( lastMessage ) {
const textContent = textOfMessage ( lastMessage ) ;
if ( textContent ) {
2026-03-03 21:32:01 +08:00
body =
textContent . length > 200
? textContent . substring ( 0 , 200 ) + "..."
: textContent ;
2026-01-31 20:22:15 +08:00
}
}
2026-03-03 21:32:01 +08:00
showNotification ( state . title , { body } ) ;
2026-01-31 11:08:27 +08:00
}
} ,
2026-03-03 21:32:01 +08:00
} ) ;
2026-01-22 00:26:11 +08:00
2026-03-03 21:32:01 +08:00
const handleSubmit = useCallback (
( message : PromptInputMessage ) = > {
void sendMessage ( threadId , message ) ;
2026-01-16 09:15:04 +08:00
} ,
2026-03-03 21:32:01 +08:00
[ sendMessage , threadId ] ,
) ;
2026-01-16 09:15:04 +08:00
const handleStop = useCallback ( async ( ) = > {
await thread . stop ( ) ;
} , [ thread ] ) ;
2026-01-17 11:02:33 +08:00
2026-01-16 09:15:04 +08:00
return (
2026-03-03 21:32:01 +08:00
< ThreadContext.Provider value = { { thread , isMock } } >
< ChatBox threadId = { threadId } >
< div className = "relative flex size-full min-h-0 justify-between" >
< header
2026-01-18 17:13:15 +08:00
className = { cn (
2026-03-03 21:32:01 +08:00
"absolute top-0 right-0 left-0 z-30 flex h-12 shrink-0 items-center px-4" ,
isNewThread
? "bg-background/0 backdrop-blur-none"
: "bg-background/80 shadow-xs backdrop-blur" ,
2026-01-18 17:13:15 +08:00
) }
>
2026-03-03 21:32:01 +08:00
< div className = "flex w-full items-center text-sm font-medium" >
< ThreadTitle threadId = { threadId } thread = { thread } / >
< / div >
< div >
< ArtifactTrigger / >
< / div >
< / header >
< main className = "flex min-h-0 max-w-full grow flex-col" >
< div className = "flex size-full justify-center" >
< MessageList
className = { cn ( "size-full" , ! isNewThread && "pt-10" ) }
2026-01-18 17:13:15 +08:00
threadId = { threadId }
2026-03-03 21:32:01 +08:00
thread = { thread }
2026-01-18 17:13:15 +08:00
/ >
2026-03-03 21:32:01 +08:00
< / div >
< div className = "absolute right-0 bottom-0 left-0 z-30 flex justify-center px-4" >
< div
className = { cn (
"relative w-full" ,
isNewThread && "-translate-y-[calc(50vh-96px)]" ,
isNewThread
? "max-w-(--container-width-sm)"
: "max-w-(--container-width-md)" ,
) }
>
< div className = "absolute -top-4 right-0 left-0 z-0" >
< div className = "absolute right-0 bottom-0 left-0" >
< TodoList
className = "bg-background/5"
todos = { thread . values . todos ? ? [ ] }
hidden = {
! thread . values . todos || thread . values . todos . length === 0
}
/ >
< / div >
2026-01-17 17:21:37 +08:00
< / div >
2026-03-03 21:32:01 +08:00
< InputBox
className = { cn ( "bg-background/5 w-full -translate-y-4" ) }
isNewThread = { isNewThread }
2026-03-06 22:39:58 +08:00
threadId = { threadId }
2026-03-03 21:32:01 +08:00
autoFocus = { isNewThread }
2026-03-20 17:18:59 +08:00
status = {
thread . error
? "error"
: thread . isLoading
? "streaming"
: "ready"
}
2026-03-03 21:32:01 +08:00
context = { settings . context }
extraHeader = {
isNewThread && < Welcome mode = { settings . context . mode } / >
}
2026-03-18 15:10:27 +08:00
disabled = { env . NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" || isUploading }
2026-03-03 21:32:01 +08:00
onContextChange = { ( context ) = > setSettings ( "context" , context ) }
onSubmit = { handleSubmit }
onStop = { handleStop }
/ >
{ env . NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" && (
< div className = "text-muted-foreground/67 w-full translate-y-12 text-center text-xs" >
{ t . common . notAvailableInDemoMode }
2026-01-18 17:13:15 +08:00
< / div >
) }
< / div >
2026-03-03 21:32:01 +08:00
< / div >
< / main >
< / div >
< / ChatBox >
2026-01-18 17:13:15 +08:00
< / ThreadContext.Provider >
2026-01-16 09:15:04 +08:00
) ;
}