diff --git a/app/api/conversations/[conversationId]/name/route.ts b/app/api/conversations/[conversationId]/name/route.ts index 1411123..09db40c 100644 --- a/app/api/conversations/[conversationId]/name/route.ts +++ b/app/api/conversations/[conversationId]/name/route.ts @@ -3,14 +3,14 @@ import { NextResponse } from 'next/server' import { client, getInfo } from '@/app/api/utils/common' export async function POST(request: NextRequest, { params }: { - params: { conversationId: string } + params: Promise<{ conversationId: string }> }) { const body = await request.json() const { auto_generate, name, } = body - const { conversationId } = params + const { conversationId } = await params const { user } = getInfo(request) // auto generate name diff --git a/app/api/messages/[messageId]/feedbacks/route.ts b/app/api/messages/[messageId]/feedbacks/route.ts index 4d90fe6..d742f7c 100644 --- a/app/api/messages/[messageId]/feedbacks/route.ts +++ b/app/api/messages/[messageId]/feedbacks/route.ts @@ -3,13 +3,13 @@ import { NextResponse } from 'next/server' import { client, getInfo } from '@/app/api/utils/common' export async function POST(request: NextRequest, { params }: { - params: { messageId: string } + params: Promise<{ messageId: string }> }) { const body = await request.json() const { rating, } = body - const { messageId } = params + const { messageId } = await params const { user } = getInfo(request) const { data } = await client.messageFeedback(messageId, rating, user) return NextResponse.json(data) diff --git a/app/api/utils/common.ts b/app/api/utils/common.ts index cc0f6b4..8a13a24 100644 --- a/app/api/utils/common.ts +++ b/app/api/utils/common.ts @@ -15,10 +15,10 @@ export const getInfo = (request: NextRequest) => { } export const setSession = (sessionId: string) => { - if (APP_INFO.disable_session_same_site) - return { 'Set-Cookie': `session_id=${sessionId}; SameSite=None; Secure` } + if (APP_INFO.disable_session_same_site) + { return { 'Set-Cookie': `session_id=${sessionId}; SameSite=None; Secure` } } - return { 'Set-Cookie': `session_id=${sessionId}` } + return { 'Set-Cookie': `session_id=${sessionId}` } } export const client = new ChatClient(API_KEY, API_URL || undefined) diff --git a/app/components/base/image-gallery/index.tsx b/app/components/base/image-gallery/index.tsx index c8d32f4..9a0c13d 100644 --- a/app/components/base/image-gallery/index.tsx +++ b/app/components/base/image-gallery/index.tsx @@ -32,12 +32,15 @@ const ImageGallery: FC = ({ }) => { const [imagePreviewUrl, setImagePreviewUrl] = useState('') - const imgNum = srcs.length + const validSrcs = srcs.filter(src => src && src.trim() !== '') + const imgNum = validSrcs.length const imgStyle = getWidthStyle(imgNum) + + if (imgNum === 0) { return null } + return (
- {/* TODO: support preview */} - {srcs.map((src, index) => ( + {validSrcs.map((src, index) => ( +
{content}
) diff --git a/app/components/base/tooltip/index.tsx b/app/components/base/tooltip/index.tsx index 54fc51d..bf72c0d 100644 --- a/app/components/base/tooltip/index.tsx +++ b/app/components/base/tooltip/index.tsx @@ -1,9 +1,8 @@ 'use client' import classNames from 'classnames' import type { FC } from 'react' -import React from 'react' -import { Tooltip as ReactTooltip } from 'react-tooltip' // fixed version to 5.8.3 https://github.com/ReactTooltip/react-tooltip/issues/972 -import 'react-tooltip/dist/react-tooltip.css' +import React, { useState } from 'react' +import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' interface TooltipProps { selector: string @@ -15,6 +14,10 @@ interface TooltipProps { children: React.ReactNode } +const arrow = ( + +) + const Tooltip: FC = ({ selector, content, @@ -24,22 +27,31 @@ const Tooltip: FC = ({ className, clickable, }) => { + const [open, setOpen] = useState(false) + const triggerMethod = clickable ? 'click' : 'hover' + return ( -
- {React.cloneElement(children as React.ReactElement, { - 'data-tooltip-id': selector, - }) - } - + triggerMethod === 'click' && setOpen(v => !v)} + onMouseEnter={() => triggerMethod === 'hover' && setOpen(true)} + onMouseLeave={() => triggerMethod === 'hover' && setOpen(false)} > - {htmlContent && htmlContent} - -
+ {children} + + +
+ {htmlContent ?? content} + {arrow} +
+
+ ) } diff --git a/app/components/chat/answer/index.tsx b/app/components/chat/answer/index.tsx index 56733a4..8bb8e2c 100644 --- a/app/components/chat/answer/index.tsx +++ b/app/components/chat/answer/index.tsx @@ -187,7 +187,7 @@ const Answer: FC = ({
)} -
+
{workflowProcess && ( diff --git a/app/components/chat/index.tsx b/app/components/chat/index.tsx index ee4fabc..b5df141 100644 --- a/app/components/chat/index.tsx +++ b/app/components/chat/index.tsx @@ -170,7 +170,7 @@ const Chat: FC = ({
{ !isHideSendInput && ( -
+
{ visionConfig?.enabled && ( @@ -217,8 +217,8 @@ const Chat: FC = ({ onKeyDown={handleKeyDown} autoSize /> -
-
{query.trim().length}
+
+
{query.trim().length}
& { @@ -23,7 +23,7 @@ const Question: FC = ({ id, content, useCurrentUserAvatar, imgSr {imgSrcs && imgSrcs.length > 0 && ( )} - +
diff --git a/app/components/index.tsx b/app/components/index.tsx index 76b7700..5f932bf 100644 --- a/app/components/index.tsx +++ b/app/components/index.tsx @@ -174,8 +174,15 @@ const Main: FC = () => { const [chatList, setChatList, getChatList] = useGetState([]) const chatListDomRef = useRef(null) useEffect(() => { - // scroll to bottom - if (chatListDomRef.current) { chatListDomRef.current.scrollTop = chatListDomRef.current.scrollHeight } + // scroll to bottom with page-level scrolling + if (chatListDomRef.current) { + setTimeout(() => { + chatListDomRef.current?.scrollIntoView({ + behavior: 'auto', + block: 'end', + }) + }, 50) + } }, [chatList, currConversationId]) // user can not edit inputs if user had send message const canEditInputs = !chatList.some(item => item.isAnswer === false) && isNewConversation @@ -677,18 +684,16 @@ const Main: FC = () => { { hasSetInputs && ( -
-
- -
+
+
) }
diff --git a/app/components/welcome/index.tsx b/app/components/welcome/index.tsx index f55d22e..59b7376 100644 --- a/app/components/welcome/index.tsx +++ b/app/components/welcome/index.tsx @@ -37,7 +37,6 @@ const Welcome: FC = ({ savedInputs, onInputsChange, }) => { - console.log(promptConfig) const { t } = useTranslation() const hasVar = promptConfig.prompt_variables.length > 0 const [isFold, setIsFold] = useState(true) diff --git a/app/layout.tsx b/app/layout.tsx index 7092434..c5bc12e 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -3,12 +3,12 @@ import { getLocaleOnServer } from '@/i18n/server' import './styles/globals.css' import './styles/markdown.scss' -const LocaleLayout = ({ +const LocaleLayout = async ({ children, }: { children: React.ReactNode }) => { - const locale = getLocaleOnServer() + const locale = await getLocaleOnServer() return ( diff --git a/config/index.ts b/config/index.ts index 74c9758..d803bb6 100644 --- a/config/index.ts +++ b/config/index.ts @@ -8,7 +8,7 @@ export const APP_INFO: AppInfo = { copyright: '', privacy_policy: '', default_language: 'en', - disable_session_same_site: false, // set it to true if you want to embed the chatbot in an iframe + disable_session_same_site: false, // set it to true if you want to embed the chatbot in an iframe } export const isShowPrompt = false diff --git a/i18n/lang/app.fr.ts b/i18n/lang/app.fr.ts index 6850077..b69fd03 100644 --- a/i18n/lang/app.fr.ts +++ b/i18n/lang/app.fr.ts @@ -34,4 +34,3 @@ const translation = { } export default translation - diff --git a/i18n/lang/common.fr.ts b/i18n/lang/common.fr.ts index 9389d85..a0e7380 100644 --- a/i18n/lang/common.fr.ts +++ b/i18n/lang/common.fr.ts @@ -31,4 +31,3 @@ const translation = { } export default translation - diff --git a/i18n/lang/tools.fr.ts b/i18n/lang/tools.fr.ts index d6aeb25..1a0bbd6 100644 --- a/i18n/lang/tools.fr.ts +++ b/i18n/lang/tools.fr.ts @@ -101,4 +101,3 @@ const translation = { } export default translation - diff --git a/i18n/server.ts b/i18n/server.ts index 5b97004..f043b98 100644 --- a/i18n/server.ts +++ b/i18n/server.ts @@ -6,19 +6,20 @@ import { match } from '@formatjs/intl-localematcher' import type { Locale } from '.' import { i18n } from '.' -export const getLocaleOnServer = (): Locale => { +export const getLocaleOnServer = async (): Promise => { // @ts-expect-error locales are readonly const locales: string[] = i18n.locales let languages: string[] | undefined // get locale from cookie - const localeCookie = cookies().get('locale') + const localeCookie = (await cookies()).get('locale') languages = localeCookie?.value ? [localeCookie.value] : [] if (!languages.length) { // Negotiator expects plain object so we need to transform headers const negotiatorHeaders: Record = {} - headers().forEach((value, key) => (negotiatorHeaders[key] = value)) + const headersList = await headers() + headersList.forEach((value, key) => (negotiatorHeaders[key] = value)) // Use negotiator and intl-localematcher to get best locale languages = new Negotiator({ headers: negotiatorHeaders }).languages() } diff --git a/package.json b/package.json index 07276b1..c9bf7a3 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "react-i18next": "^12.2.0", "react-markdown": "^8.0.6", "react-syntax-highlighter": "^15.5.0", - "react-tooltip": "~5.8.3", + "react-tooltip": "~5.29.1", "rehype-katex": "^7.0.1", "remark-breaks": "^4.0.0", "remark-gfm": "^4.0.0", diff --git a/types/app.ts b/types/app.ts index 861ad9c..7d0d50f 100644 --- a/types/app.ts +++ b/types/app.ts @@ -112,7 +112,7 @@ export interface AppInfo { default_language: Locale copyright?: string privacy_policy?: string - disable_session_same_site?: boolean + disable_session_same_site?: boolean } export enum Resolution {