()
| 987 | } |
| 988 | |
| 989 | function _Chat() { |
| 990 | type RenderMessage = ChatMessage & { preview?: boolean }; |
| 991 | |
| 992 | const chatStore = useChatStore(); |
| 993 | const session = chatStore.currentSession(); |
| 994 | const config = useAppConfig(); |
| 995 | const fontSize = config.fontSize; |
| 996 | const fontFamily = config.fontFamily; |
| 997 | |
| 998 | const [showExport, setShowExport] = useState(false); |
| 999 | |
| 1000 | const inputRef = useRef<HTMLTextAreaElement>(null); |
| 1001 | const [userInput, setUserInput] = useState(""); |
| 1002 | const [isLoading, setIsLoading] = useState(false); |
| 1003 | const { submitKey, shouldSubmit } = useSubmitHandler(); |
| 1004 | const scrollRef = useRef<HTMLDivElement>(null); |
| 1005 | const isScrolledToBottom = scrollRef?.current |
| 1006 | ? Math.abs( |
| 1007 | scrollRef.current.scrollHeight - |
| 1008 | (scrollRef.current.scrollTop + scrollRef.current.clientHeight), |
| 1009 | ) <= 1 |
| 1010 | : false; |
| 1011 | const isAttachWithTop = useMemo(() => { |
| 1012 | const lastMessage = scrollRef.current?.lastElementChild as HTMLElement; |
| 1013 | // if scrolllRef is not ready or no message, return false |
| 1014 | if (!scrollRef?.current || !lastMessage) return false; |
| 1015 | const topDistance = |
| 1016 | lastMessage!.getBoundingClientRect().top - |
| 1017 | scrollRef.current.getBoundingClientRect().top; |
| 1018 | // leave some space for user question |
| 1019 | return topDistance < 100; |
| 1020 | }, [scrollRef?.current?.scrollHeight]); |
| 1021 | |
| 1022 | const isTyping = userInput !== ""; |
| 1023 | |
| 1024 | // if user is typing, should auto scroll to bottom |
| 1025 | // if user is not typing, should auto scroll to bottom only if already at bottom |
| 1026 | const { setAutoScroll, scrollDomToBottom } = useScrollToBottom( |
| 1027 | scrollRef, |
| 1028 | (isScrolledToBottom || isAttachWithTop) && !isTyping, |
| 1029 | session.messages, |
| 1030 | ); |
| 1031 | const [hitBottom, setHitBottom] = useState(true); |
| 1032 | const isMobileScreen = useMobileScreen(); |
| 1033 | const navigate = useNavigate(); |
| 1034 | const [attachImages, setAttachImages] = useState<string[]>([]); |
| 1035 | const [uploading, setUploading] = useState(false); |
| 1036 | |
| 1037 | // prompt hints |
| 1038 | const promptStore = usePromptStore(); |
| 1039 | const [promptHints, setPromptHints] = useState<RenderPrompt[]>([]); |
| 1040 | const onSearch = useDebouncedCallback( |
| 1041 | (text: string) => { |
| 1042 | const matchedPrompts = promptStore.search(text); |
| 1043 | setPromptHints(matchedPrompts); |
| 1044 | }, |
| 1045 | 100, |
| 1046 | { leading: true, trailing: true }, |
nothing calls this directly
no test coverage detected