mirror of https://github.com/langgenius/dify.git
Merge ffc19c1298 into a813b9f103
This commit is contained in:
commit
3b30fc4d4d
|
|
@ -20,6 +20,8 @@ import { createContext, useContext } from 'use-context-selector'
|
|||
export type ChatWithHistoryContextValue = {
|
||||
appMeta?: AppMeta | null
|
||||
appData?: AppData | null
|
||||
siteDescription?: string
|
||||
showSiteDescription: boolean
|
||||
appParams?: ChatConfig
|
||||
appChatListDataLoading?: boolean
|
||||
currentConversationId: string
|
||||
|
|
@ -95,5 +97,7 @@ export const ChatWithHistoryContext = createContext<ChatWithHistoryContextValue>
|
|||
setCurrentConversationInputs: noop,
|
||||
allInputsHidden: false,
|
||||
initUserVariables: {},
|
||||
siteDescription: '',
|
||||
showSiteDescription: false,
|
||||
})
|
||||
export const useChatWithHistoryContext = () => useContext(ChatWithHistoryContext)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import AppIcon from '@/app/components/base/app-icon'
|
|||
import InputsFormContent from '@/app/components/base/chat/chat-with-history/inputs-form/content'
|
||||
import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { useChatWithHistoryContext } from './context'
|
||||
import MobileOperationDropdown from './header/mobile-operation-dropdown'
|
||||
import Operation from './header/operation'
|
||||
|
|
@ -14,6 +15,8 @@ import Sidebar from './sidebar'
|
|||
const HeaderInMobile = () => {
|
||||
const {
|
||||
appData,
|
||||
siteDescription,
|
||||
showSiteDescription,
|
||||
currentConversationId,
|
||||
currentConversationItem,
|
||||
pinnedConversationList,
|
||||
|
|
@ -63,43 +66,50 @@ const HeaderInMobile = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="flex shrink-0 items-center gap-1 bg-mask-top2bottom-gray-50-to-transparent px-2 py-3">
|
||||
<ActionButton size="l" className="shrink-0" onClick={() => setShowSidebar(true)}>
|
||||
<div className="i-ri-menu-line h-[18px] w-[18px]" />
|
||||
</ActionButton>
|
||||
<div className="flex grow items-center justify-center">
|
||||
{!currentConversationId && (
|
||||
<>
|
||||
<AppIcon
|
||||
className="mr-2"
|
||||
size="tiny"
|
||||
icon={appData?.site.icon}
|
||||
iconType={appData?.site.icon_type}
|
||||
imageUrl={appData?.site.icon_url}
|
||||
background={appData?.site.icon_background}
|
||||
<div className={cn('shrink-0 bg-mask-top2bottom-gray-50-to-transparent px-2', showSiteDescription ? 'py-2' : 'py-3')}>
|
||||
<div className="flex items-center gap-1">
|
||||
<ActionButton size="l" className="shrink-0" onClick={() => setShowSidebar(true)}>
|
||||
<div className="i-ri-menu-line h-[18px] w-[18px]" />
|
||||
</ActionButton>
|
||||
<div className="flex grow items-center justify-center">
|
||||
{!currentConversationId && (
|
||||
<>
|
||||
<AppIcon
|
||||
className="mr-2"
|
||||
size="tiny"
|
||||
icon={appData?.site.icon}
|
||||
iconType={appData?.site.icon_type}
|
||||
imageUrl={appData?.site.icon_url}
|
||||
background={appData?.site.icon_background}
|
||||
/>
|
||||
<div className="truncate text-text-secondary system-md-semibold">
|
||||
{appData?.site.title}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{currentConversationId && (
|
||||
<Operation
|
||||
title={currentConversationItem?.name || ''}
|
||||
isPinned={!!isPin}
|
||||
togglePin={() => handleOperate(isPin ? 'unpin' : 'pin')}
|
||||
isShowDelete
|
||||
isShowRenameConversation
|
||||
onRenameConversation={() => handleOperate('rename')}
|
||||
onDelete={() => handleOperate('delete')}
|
||||
/>
|
||||
<div className="truncate text-text-secondary system-md-semibold">
|
||||
{appData?.site.title}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{currentConversationId && (
|
||||
<Operation
|
||||
title={currentConversationItem?.name || ''}
|
||||
isPinned={!!isPin}
|
||||
togglePin={() => handleOperate(isPin ? 'unpin' : 'pin')}
|
||||
isShowDelete
|
||||
isShowRenameConversation
|
||||
onRenameConversation={() => handleOperate('rename')}
|
||||
onDelete={() => handleOperate('delete')}
|
||||
/>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
<MobileOperationDropdown
|
||||
handleResetChat={handleNewConversation}
|
||||
handleViewChatSettings={() => setShowChatSettings(true)}
|
||||
hideViewChatSettings={inputsForms.length < 1}
|
||||
/>
|
||||
</div>
|
||||
<MobileOperationDropdown
|
||||
handleResetChat={handleNewConversation}
|
||||
handleViewChatSettings={() => setShowChatSettings(true)}
|
||||
hideViewChatSettings={inputsForms.length < 1}
|
||||
/>
|
||||
{showSiteDescription && (
|
||||
<div className="system-xs-regular mt-1 line-clamp-2 break-words px-1 text-center text-text-tertiary">
|
||||
{siteDescription}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{showSidebar && (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import Operation from './operation'
|
|||
const Header = () => {
|
||||
const {
|
||||
appData,
|
||||
siteDescription,
|
||||
showSiteDescription,
|
||||
currentConversationId,
|
||||
currentConversationItem,
|
||||
inputsForms,
|
||||
|
|
@ -74,72 +76,79 @@ const Header = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-14 shrink-0 items-center justify-between p-3">
|
||||
<div className={cn('flex items-center gap-1 transition-all duration-200 ease-in-out', !isSidebarCollapsed && 'user-select-none opacity-0')}>
|
||||
<ActionButton className={cn(!isSidebarCollapsed && 'cursor-default')} size="l" onClick={() => handleSidebarCollapse(false)}>
|
||||
<RiLayoutRight2Line className="h-[18px] w-[18px]" />
|
||||
</ActionButton>
|
||||
<div className="mr-1 shrink-0">
|
||||
<AppIcon
|
||||
size="large"
|
||||
iconType={appData?.site.icon_type}
|
||||
icon={appData?.site.icon}
|
||||
background={appData?.site.icon_background}
|
||||
imageUrl={appData?.site.icon_url}
|
||||
/>
|
||||
</div>
|
||||
{!currentConversationId && (
|
||||
<div className={cn('grow truncate text-text-secondary system-md-semibold')}>{appData?.site.title}</div>
|
||||
)}
|
||||
{currentConversationId && currentConversationItem && isSidebarCollapsed && (
|
||||
<>
|
||||
<div className="p-1 text-divider-deep">/</div>
|
||||
<Operation
|
||||
title={currentConversationItem?.name || ''}
|
||||
isPinned={!!isPin}
|
||||
togglePin={() => handleOperate(isPin ? 'unpin' : 'pin')}
|
||||
isShowDelete
|
||||
isShowRenameConversation
|
||||
onRenameConversation={() => handleOperate('rename')}
|
||||
onDelete={() => handleOperate('delete')}
|
||||
<div className={cn('shrink-0 p-3', showSiteDescription && 'flex flex-col gap-1')}>
|
||||
<div className="flex h-14 items-center justify-between">
|
||||
<div className={cn('flex items-center gap-1 transition-all duration-200 ease-in-out', !isSidebarCollapsed && 'user-select-none opacity-0')}>
|
||||
<ActionButton className={cn(!isSidebarCollapsed && 'cursor-default')} size="l" onClick={() => handleSidebarCollapse(false)}>
|
||||
<RiLayoutRight2Line className="h-[18px] w-[18px]" />
|
||||
</ActionButton>
|
||||
<div className="mr-1 shrink-0">
|
||||
<AppIcon
|
||||
size="large"
|
||||
iconType={appData?.site.icon_type}
|
||||
icon={appData?.site.icon}
|
||||
background={appData?.site.icon_background}
|
||||
imageUrl={appData?.site.icon_url}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<div className="flex items-center px-1">
|
||||
<div className="h-[14px] w-px bg-divider-regular"></div>
|
||||
</div>
|
||||
{!currentConversationId && (
|
||||
<div className={cn('system-md-semibold grow truncate text-text-secondary')}>{appData?.site.title}</div>
|
||||
)}
|
||||
{currentConversationId && currentConversationItem && isSidebarCollapsed && (
|
||||
<>
|
||||
<div className="p-1 text-divider-deep">/</div>
|
||||
<Operation
|
||||
title={currentConversationItem?.name || ''}
|
||||
isPinned={!!isPin}
|
||||
togglePin={() => handleOperate(isPin ? 'unpin' : 'pin')}
|
||||
isShowDelete
|
||||
isShowRenameConversation
|
||||
onRenameConversation={() => handleOperate('rename')}
|
||||
onDelete={() => handleOperate('delete')}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<div className="flex items-center px-1">
|
||||
<div className="h-[14px] w-px bg-divider-regular"></div>
|
||||
</div>
|
||||
{isSidebarCollapsed && (
|
||||
<Tooltip
|
||||
disabled={!!currentConversationId}
|
||||
popupContent={t('chat.newChatTip', { ns: 'share' })}
|
||||
>
|
||||
<div>
|
||||
<ActionButton
|
||||
size="l"
|
||||
state={(!currentConversationId || isResponding) ? ActionButtonState.Disabled : ActionButtonState.Default}
|
||||
disabled={!currentConversationId || isResponding}
|
||||
onClick={handleNewConversation}
|
||||
>
|
||||
<RiEditBoxLine className="h-[18px] w-[18px]" />
|
||||
</ActionButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
{isSidebarCollapsed && (
|
||||
<Tooltip
|
||||
disabled={!!currentConversationId}
|
||||
popupContent={t('chat.newChatTip', { ns: 'share' })}
|
||||
>
|
||||
<div>
|
||||
<ActionButton
|
||||
size="l"
|
||||
state={(!currentConversationId || isResponding) ? ActionButtonState.Disabled : ActionButtonState.Default}
|
||||
disabled={!currentConversationId || isResponding}
|
||||
onClick={handleNewConversation}
|
||||
>
|
||||
<RiEditBoxLine className="h-[18px] w-[18px]" />
|
||||
<div className="flex items-center gap-1">
|
||||
{currentConversationId && (
|
||||
<Tooltip
|
||||
popupContent={t('chat.resetChat', { ns: 'share' })}
|
||||
>
|
||||
<ActionButton size="l" onClick={handleNewConversation}>
|
||||
<RiResetLeftLine className="h-[18px] w-[18px]" />
|
||||
</ActionButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
{currentConversationId && (
|
||||
<Tooltip
|
||||
popupContent={t('chat.resetChat', { ns: 'share' })}
|
||||
>
|
||||
<ActionButton size="l" onClick={handleNewConversation}>
|
||||
<RiResetLeftLine className="h-[18px] w-[18px]" />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
{currentConversationId && inputsForms.length > 0 && (
|
||||
<ViewFormDropdown />
|
||||
)}
|
||||
</Tooltip>
|
||||
)}
|
||||
{currentConversationId && inputsForms.length > 0 && (
|
||||
<ViewFormDropdown />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{showSiteDescription && (
|
||||
<div className="system-xs-regular max-h-[2.5rem] overflow-y-auto break-words text-text-tertiary">
|
||||
{siteDescription}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!!showConfirm && (
|
||||
<Confirm
|
||||
|
|
|
|||
|
|
@ -193,6 +193,9 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
|||
}
|
||||
}, [appId, conversationIdInfo, setConversationIdInfo, userId])
|
||||
|
||||
const siteDescription = appData?.site?.description || ''
|
||||
const showSiteDescription = !currentConversationId && !!siteDescription
|
||||
|
||||
const [newConversationId, setNewConversationId] = useState('')
|
||||
const chatShouldReloadKey = useMemo(() => {
|
||||
if (currentConversationId === newConversationId)
|
||||
|
|
@ -587,6 +590,8 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
|||
currentConversationItem,
|
||||
handleConversationIdInfoChange,
|
||||
appData,
|
||||
siteDescription,
|
||||
showSiteDescription,
|
||||
appParams: appParams || {} as ChatConfig,
|
||||
appMeta,
|
||||
appPinnedConversationData,
|
||||
|
|
|
|||
|
|
@ -147,11 +147,15 @@ const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({
|
|||
setCurrentConversationInputs,
|
||||
allInputsHidden,
|
||||
initUserVariables,
|
||||
siteDescription,
|
||||
showSiteDescription,
|
||||
} = useChatWithHistory(installedAppInfo)
|
||||
|
||||
return (
|
||||
<ChatWithHistoryContext.Provider value={{
|
||||
appData,
|
||||
siteDescription,
|
||||
showSiteDescription,
|
||||
appParams,
|
||||
appMeta,
|
||||
appChatListDataLoading,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export type IHeaderProps = {
|
|||
allowResetChat?: boolean
|
||||
customerIcon?: React.ReactNode
|
||||
title: string
|
||||
description?: string
|
||||
theme?: Theme
|
||||
onCreateNewChat?: () => void
|
||||
}
|
||||
|
|
@ -29,6 +30,7 @@ const Header: FC<IHeaderProps> = ({
|
|||
allowResetChat,
|
||||
customerIcon,
|
||||
title,
|
||||
description,
|
||||
theme,
|
||||
onCreateNewChat,
|
||||
}) => {
|
||||
|
|
@ -142,47 +144,57 @@ const Header: FC<IHeaderProps> = ({
|
|||
|
||||
return (
|
||||
<div
|
||||
className={cn('flex h-14 shrink-0 items-center justify-between rounded-t-2xl px-3')}
|
||||
className={cn('shrink-0 rounded-t-2xl px-3', description ? 'pb-2' : '')}
|
||||
style={CssTransform(theme?.headerBorderBottomStyle ?? '')}
|
||||
>
|
||||
<div className="flex grow items-center space-x-3">
|
||||
{customerIcon}
|
||||
<div
|
||||
className="truncate system-md-semibold"
|
||||
style={CssTransform(theme?.colorFontOnHeaderStyle ?? '')}
|
||||
>
|
||||
{title}
|
||||
<div className="flex h-14 items-center justify-between">
|
||||
<div className="flex grow items-center space-x-3">
|
||||
{customerIcon}
|
||||
<div
|
||||
className="system-md-semibold truncate"
|
||||
style={CssTransform(theme?.colorFontOnHeaderStyle ?? '')}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
{
|
||||
showToggleExpandButton && (
|
||||
<div className="flex items-center gap-1">
|
||||
{
|
||||
showToggleExpandButton && (
|
||||
<Tooltip
|
||||
popupContent={expanded ? t('chat.collapse', { ns: 'share' }) : t('chat.expand', { ns: 'share' })}
|
||||
>
|
||||
<ActionButton size="l" onClick={handleToggleExpand}>
|
||||
{
|
||||
expanded
|
||||
? <RiCollapseDiagonal2Line className={cn('h-[18px] w-[18px]', theme?.colorPathOnHeader)} />
|
||||
: <RiExpandDiagonal2Line className={cn('h-[18px] w-[18px]', theme?.colorPathOnHeader)} />
|
||||
}
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
{currentConversationId && allowResetChat && (
|
||||
<Tooltip
|
||||
popupContent={expanded ? t('chat.collapse', { ns: 'share' }) : t('chat.expand', { ns: 'share' })}
|
||||
popupContent={t('chat.resetChat', { ns: 'share' })}
|
||||
>
|
||||
<ActionButton size="l" onClick={handleToggleExpand} data-testid="mobile-expand-button">
|
||||
{
|
||||
expanded
|
||||
? <div className={cn('i-ri-collapse-diagonal-2-line h-[18px] w-[18px]', theme?.colorPathOnHeader)} />
|
||||
: <div className={cn('i-ri-expand-diagonal-2-line h-[18px] w-[18px]', theme?.colorPathOnHeader)} />
|
||||
}
|
||||
<ActionButton size="l" onClick={onCreateNewChat}>
|
||||
<RiResetLeftLine className={cn('h-[18px] w-[18px]', theme?.colorPathOnHeader)} />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
{currentConversationId && allowResetChat && (
|
||||
<Tooltip
|
||||
popupContent={t('chat.resetChat', { ns: 'share' })}
|
||||
>
|
||||
<ActionButton size="l" onClick={onCreateNewChat} data-testid="mobile-reset-chat-button">
|
||||
<div className={cn('i-ri-reset-left-line h-[18px] w-[18px]', theme?.colorPathOnHeader)} />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
{currentConversationId && inputsForms.length > 0 && !allInputsHidden && (
|
||||
<ViewFormDropdown iconColor={theme?.colorPathOnHeader} />
|
||||
)}
|
||||
)}
|
||||
{currentConversationId && inputsForms.length > 0 && !allInputsHidden && (
|
||||
<ViewFormDropdown iconColor={theme?.colorPathOnHeader} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{description && (
|
||||
<div
|
||||
className="system-xs-regular line-clamp-2 break-words text-text-tertiary"
|
||||
style={CssTransform(theme?.colorFontOnHeaderStyle ?? '')}
|
||||
>
|
||||
{description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,10 +60,16 @@ const Chatbot = () => {
|
|||
isMobile={isMobile}
|
||||
allowResetChat={allowResetChat}
|
||||
title={site?.title || ''}
|
||||
description={site?.description}
|
||||
customerIcon={isDify() ? difyIcon : ''}
|
||||
theme={themeBuilder?.theme}
|
||||
onCreateNewChat={handleNewConversation}
|
||||
/>
|
||||
{!isMobile && site?.description && (
|
||||
<div className="system-xs-regular shrink-0 break-words px-3 pb-2 text-text-tertiary">
|
||||
{site.description}
|
||||
</div>
|
||||
)}
|
||||
<div className={cn('flex grow flex-col overflow-y-auto', isMobile && 'm-[0.5px] !h-[calc(100vh_-_3rem)] rounded-2xl bg-chatbot-bg')}>
|
||||
{appChatListDataLoading && (
|
||||
<Loading type="app" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue