pull/19680/head
Yooco_彭栋良 1 year ago
parent 1119790b02
commit 79ac985602

@ -6,7 +6,7 @@ root = true
# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
end_of_line = lf
end_of_line = crlf
insert_final_newline = true
trim_trailing_whitespace = true

@ -546,8 +546,9 @@ services:
# Frontend web application.
web:
image: langgenius/dify-web:1.3.1
image: langgenius/dify-web:latest
restart: always
build: ../web
environment:
CONSOLE_API_URL: ${CONSOLE_API_URL:-}
APP_API_URL: ${APP_API_URL:-}

@ -1,3 +1,3 @@
<svg width="13" height="14" viewBox="0 0 13 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.41663 3.75008H3.24996C2.96264 3.75008 2.68709 3.86422 2.48393 4.06738C2.28076 4.27055 2.16663 4.5461 2.16663 4.83341V10.2501C2.16663 10.5374 2.28076 10.8129 2.48393 11.0161C2.68709 11.2193 2.96264 11.3334 3.24996 11.3334H8.66663C8.95394 11.3334 9.22949 11.2193 9.43266 11.0161C9.63582 10.8129 9.74996 10.5374 9.74996 10.2501V8.08341M7.58329 2.66675H10.8333M10.8333 2.66675V5.91675M10.8333 2.66675L5.41663 8.08341" stroke="#1C64F2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.41663 3.75008H3.24996C2.96264 3.75008 2.68709 3.86422 2.48393 4.06738C2.28076 4.27055 2.16663 4.5461 2.16663 4.83341V10.2501C2.16663 10.5374 2.28076 10.8129 2.48393 11.0161C2.68709 11.2193 2.96264 11.3334 3.24996 11.3334H8.66663C8.95394 11.3334 9.22949 11.2193 9.43266 11.0161C9.63582 10.8129 9.74996 10.5374 9.74996 10.2501V8.08341M7.58329 2.66675H10.8333M10.8333 2.66675V5.91675M10.8333 2.66675L5.41663 8.08341" stroke="#298df0" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 595 B

After

Width:  |  Height:  |  Size: 595 B

@ -1,3 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 2.5L10.5 6M10.5 6L7 9.5M10.5 6H1.5" stroke="#1C64F2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 2.5L10.5 6M10.5 6L7 9.5M10.5 6H1.5" stroke="#298df0" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 217 B

After

Width:  |  Height:  |  Size: 217 B

@ -97,7 +97,7 @@ const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, classNam
copy(splitUrl[1])
}
else {
copy(OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeBuilder.theme?.primaryColor ?? '#1C64F2', isTestEnv))
copy(OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeBuilder.theme?.primaryColor ?? '#298df0', isTestEnv))
}
setIsCopied({ ...isCopied, [option]: true })
}
@ -183,7 +183,7 @@ const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, classNam
</div>
<div className="flex w-full items-start justify-start gap-2 overflow-x-auto p-3">
<div className="shrink grow basis-0 font-mono text-[13px] leading-tight text-text-secondary">
<pre className='select-text'>{OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeBuilder.theme?.primaryColor ?? '#1C64F2', isTestEnv)}</pre>
<pre className='select-text'>{OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeBuilder.theme?.primaryColor ?? '#298df0', isTestEnv)}</pre>
</div>
</div>
</div>

@ -16,7 +16,6 @@ import List from '@/app/components/base/chat/chat-with-history/sidebar/list'
import MenuDropdown from '@/app/components/share/text-generation/menu-dropdown'
import Confirm from '@/app/components/base/confirm'
import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
import LogoSite from '@/app/components/base/logo/logo-site'
import type { ConversationItem } from '@/models/share'
import cn from '@/utils/classnames'
@ -137,22 +136,6 @@ const Sidebar = ({ isPanel }: Props) => {
</div>
<div className='flex shrink-0 items-center justify-between p-3'>
<MenuDropdown placement='top-start' data={appData?.site} />
{/* powered by */}
<div className='shrink-0'>
{!appData?.custom_config?.remove_webapp_brand && (
<div className={cn(
'flex shrink-0 items-center gap-1.5 px-2',
)}>
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
{appData?.custom_config?.replace_webapp_logo && (
<img src={appData?.custom_config?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' />
)}
{!appData?.custom_config?.replace_webapp_logo && (
<LogoSite className='!h-5' />
)}
</div>
)}
</div>
</div>
{!!showConfirm && (
<Confirm

@ -27,6 +27,7 @@ import { useToastContext } from '@/app/components/base/toast'
import FeatureBar from '@/app/components/base/features/new-feature-panel/feature-bar'
import type { FileUpload } from '@/app/components/base/features/types'
import { TransferMethod } from '@/types/app'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
type ChatInputAreaProps = {
showFeatureBar?: boolean
@ -81,6 +82,9 @@ const ChatInputArea = ({
const historyRef = useRef([''])
const [currentIndex, setCurrentIndex] = useState(-1)
const isComposingRef = useRef(false)
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
const handleSend = () => {
if (isResponding) {
notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') })
@ -169,12 +173,12 @@ const ChatInputArea = ({
<>
<div
className={cn(
'relative z-10 rounded-xl border border-components-chat-input-border bg-components-panel-bg-blur pb-[9px] shadow-md',
'relative z-10 rounded-md border border-components-chat-input-border bg-components-panel-bg-blur pb-[9px] shadow-md',
isDragActive && 'border border-dashed border-components-option-card-option-selected-border',
disabled && 'pointer-events-none border-components-panel-border opacity-50 shadow-none',
)}
>
<div className='relative max-h-[158px] overflow-y-auto overflow-x-hidden px-[9px] pt-[9px]'>
<div className='scrollbar-small relative max-h-[158px] overflow-y-auto overflow-x-hidden px-[9px] pt-[9px]'>
<FileListInChatInput fileConfig={visionConfig!} />
<div
ref={wrapperRef}
@ -212,7 +216,7 @@ const ChatInputArea = ({
/>
</div>
{
!isMultipleLine && operation
!isMultipleLine && !isMobile && operation
}
</div>
{
@ -225,7 +229,7 @@ const ChatInputArea = ({
}
</div>
{
isMultipleLine && (
(isMultipleLine || isMobile) && (
<div className='px-[9px]'>{operation}</div>
)
}

@ -257,6 +257,7 @@ const ChatWrapper = () => {
switchSibling={siblingMessageId => setTargetMessageId(siblingMessageId)}
inputDisabled={inputDisabled}
isMobile={isMobile}
chatContainerClassName="scrollbar-small"
/>
)
}

@ -1,6 +1,6 @@
import type { FC } from 'react'
import React, { useCallback, useEffect, useState } from 'react'
import { RiCollapseDiagonal2Line, RiExpandDiagonal2Line, RiResetLeftLine } from '@remixicon/react'
import { RiCloseLine, RiCollapseDiagonal2Line, RiExpandDiagonal2Line, RiResetLeftLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import type { Theme } from '../theme/theme-context'
import { CssTransform } from '../theme/utils'
@ -9,10 +9,9 @@ import {
} from '../context'
import Tooltip from '@/app/components/base/tooltip'
import ActionButton from '@/app/components/base/action-button'
import Divider from '@/app/components/base/divider'
import ViewFormDropdown from '@/app/components/base/chat/embedded-chatbot/inputs-form/view-form-dropdown'
import LogoSite from '@/app/components/base/logo/logo-site'
import cn from '@/utils/classnames'
import './style.scss'
export type IHeaderProps = {
isMobile?: boolean
@ -32,7 +31,6 @@ const Header: FC<IHeaderProps> = ({
}) => {
const { t } = useTranslation()
const {
appData,
currentConversationId,
inputsForms,
} = useEmbeddedChatbotContext()
@ -74,29 +72,17 @@ const Header: FC<IHeaderProps> = ({
}, parentOrigin)
}, [isIframe, parentOrigin, showToggleExpandButton, expanded])
const handleCloseIframe = useCallback(() => {
if (!isIframe) return
window.parent.postMessage({
type: 'dify-chatbot-iframe-close',
}, parentOrigin)
}, [isIframe, parentOrigin])
if (!isMobile) {
return (
<div className='flex h-14 shrink-0 items-center justify-end p-3'>
<div className='flex items-center gap-1'>
{/* powered by */}
<div className='shrink-0'>
{!appData?.custom_config?.remove_webapp_brand && (
<div className={cn(
'flex shrink-0 items-center gap-1.5 px-2',
)}>
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
{appData?.custom_config?.replace_webapp_logo && (
<img src={appData?.custom_config?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' />
)}
{!appData?.custom_config?.replace_webapp_logo && (
<LogoSite className='!h-5' />
)}
</div>
)}
</div>
{currentConversationId && (
<Divider type='vertical' className='h-3.5' />
)}
{
showToggleExpandButton && (
<Tooltip
@ -124,6 +110,15 @@ const Header: FC<IHeaderProps> = ({
{currentConversationId && inputsForms.length > 0 && (
<ViewFormDropdown />
)}
{currentConversationId && isIframe && (
<Tooltip
popupContent={t('关闭')}
>
<ActionButton size='l' onClick={handleCloseIframe}>
<RiCloseLine className='h-[18px] w-[18px]' />
</ActionButton>
</Tooltip>
)}
</div>
</div>
)
@ -131,7 +126,7 @@ const Header: FC<IHeaderProps> = ({
return (
<div
className={cn('flex h-14 shrink-0 items-center justify-between rounded-t-2xl px-3')}
className={cn('embedded-header-wrapper flex h-14 shrink-0 items-center justify-between px-3')}
style={Object.assign({}, CssTransform(theme?.backgroundHeaderColorStyle ?? ''), CssTransform(theme?.headerBorderBottomStyle ?? '')) }
>
<div className="flex grow items-center space-x-3">

@ -0,0 +1,16 @@
.embedded-header-wrapper{
position: relative;
top: 0;
right: 0;
&:before {
content: "";
border-style: solid;
border-width: 5px 0px 0px;
border-image: linear-gradient(270deg, rgb(235, 134, 152) 0%, rgb(19, 102, 236) 100%) 1 / 1 / 0 stretch;
position: absolute;
top: 0px;
left: 0px;
width: 100%;
}
}

@ -3,7 +3,6 @@ import {
useState,
} from 'react'
import { useAsyncEffect } from 'ahooks'
import { useTranslation } from 'react-i18next'
import {
EmbeddedChatbotContext,
useEmbeddedChatbotContext,
@ -19,7 +18,6 @@ import Loading from '@/app/components/base/loading'
import LogoHeader from '@/app/components/base/logo/logo-embedded-chat-header'
import Header from '@/app/components/base/chat/embedded-chatbot/header'
import ChatWrapper from '@/app/components/base/chat/embedded-chatbot/chat-wrapper'
import LogoSite from '@/app/components/base/logo/logo-site'
import cn from '@/utils/classnames'
const Chatbot = () => {
@ -34,7 +32,6 @@ const Chatbot = () => {
handleNewConversation,
themeBuilder,
} = useEmbeddedChatbotContext()
const { t } = useTranslation()
const customConfig = appData?.custom_config
const site = appData?.site
@ -84,8 +81,8 @@ const Chatbot = () => {
<div className='relative'>
<div
className={cn(
'flex flex-col rounded-2xl border border-components-panel-border-subtle',
isMobile ? 'h-[calc(100vh_-_60px)] border-[0.5px] border-components-panel-border shadow-xs' : 'h-[100vh] bg-chatbot-bg',
'flex flex-col border-components-panel-border-subtle',
isMobile ? 'h-[calc(100vh_-_60px)] border-components-panel-border shadow-xs' : 'h-[100vh] bg-chatbot-bg',
)}
style={isMobile ? Object.assign({}, CssTransform(themeBuilder?.theme?.backgroundHeaderColorStyle ?? '')) : {}}
>
@ -97,7 +94,7 @@ const Chatbot = () => {
theme={themeBuilder?.theme}
onCreateNewChat={handleNewConversation}
/>
<div className={cn('flex grow flex-col overflow-y-auto', isMobile && '!h-[calc(100vh_-_3rem)] rounded-2xl bg-chatbot-bg')}>
<div className={cn('flex grow flex-col overflow-y-auto', isMobile && '!h-[calc(100vh_-_3rem)] bg-chatbot-bg')}>
{appChatListDataLoading && (
<Loading type='app' />
)}
@ -106,24 +103,6 @@ const Chatbot = () => {
)}
</div>
</div>
{/* powered by */}
{isMobile && (
<div className='flex h-[60px] shrink-0 items-center pl-2'>
{!appData?.custom_config?.remove_webapp_brand && (
<div className={cn(
'flex shrink-0 items-center gap-1.5 px-2',
)}>
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
{appData?.custom_config?.replace_webapp_logo && (
<img src={appData?.custom_config?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' />
)}
{!appData?.custom_config?.replace_webapp_logo && (
<LogoSite className='!h-5' />
)}
</div>
)}
</div>
)}
</div>
)
}

@ -5,12 +5,12 @@ export class Theme {
public chatColorTheme: string | null
public chatColorThemeInverted: boolean
public primaryColor = '#1C64F2'
public backgroundHeaderColorStyle = 'backgroundImage: linear-gradient(to right, #2563eb, #0ea5e9)'
public primaryColor = '#298df0'
public backgroundHeaderColorStyle = 'background: var(--color-chatbot-bg)'
public headerBorderBottomStyle = ''
public colorFontOnHeaderStyle = 'color: white'
public colorPathOnHeader = 'text-text-primary-on-surface'
public backgroundButtonDefaultColorStyle = 'backgroundColor: #1C64F2'
public colorFontOnHeaderStyle = 'color: var(--color-text-primary)'
public colorPathOnHeader = 'text-quaternary'
public backgroundButtonDefaultColorStyle = 'backgroundColor: #298df0'
public roundedBackgroundColorStyle = 'backgroundColor: rgb(245 248 255)'
public chatBubbleColorStyle = 'backgroundColor: rgb(225 239 254)'
public chatBubbleColor = 'rgb(225 239 254)'
@ -24,7 +24,7 @@ export class Theme {
private configCustomColor() {
if (this.chatColorTheme !== null && this.chatColorTheme !== '') {
this.primaryColor = this.chatColorTheme ?? '#1C64F2'
this.primaryColor = this.chatColorTheme ?? '#298df0'
this.backgroundHeaderColorStyle = `backgroundColor: ${this.primaryColor}`
this.backgroundButtonDefaultColorStyle = `backgroundColor: ${this.primaryColor}; color: ${this.colorFontOnHeaderStyle};`
this.roundedBackgroundColorStyle = `backgroundColor: ${hexToRGBA(this.primaryColor, 0.05)}`

@ -13,7 +13,7 @@ const Icon = (
}: React.SVGProps<SVGSVGElement> & {
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
},
) => <IconBase {...props} ref={ref} data={data as IconData} />
) => <IconBase {...props} ref={ref} data={data as unknown as IconData} />
Icon.displayName = 'Anthropic'

@ -11,10 +11,10 @@ const Loading = (
<div className={`flex w-full items-center justify-center ${type === 'app' ? 'h-full' : ''}`}>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className='spin-animation'>
<g clipPath="url(#clip0_324_2488)">
<path d="M15 0H10C9.44772 0 9 0.447715 9 1V6C9 6.55228 9.44772 7 10 7H15C15.5523 7 16 6.55228 16 6V1C16 0.447715 15.5523 0 15 0Z" fill="#1C64F2" />
<path opacity="0.5" d="M15 9H10C9.44772 9 9 9.44772 9 10V15C9 15.5523 9.44772 16 10 16H15C15.5523 16 16 15.5523 16 15V10C16 9.44772 15.5523 9 15 9Z" fill="#1C64F2" />
<path opacity="0.1" d="M6 9H1C0.447715 9 0 9.44772 0 10V15C0 15.5523 0.447715 16 1 16H6C6.55228 16 7 15.5523 7 15V10C7 9.44772 6.55228 9 6 9Z" fill="#1C64F2" />
<path opacity="0.2" d="M6 0H1C0.447715 0 0 0.447715 0 1V6C0 6.55228 0.447715 7 1 7H6C6.55228 7 7 6.55228 7 6V1C7 0.447715 6.55228 0 6 0Z" fill="#1C64F2" />
<path d="M15 0H10C9.44772 0 9 0.447715 9 1V6C9 6.55228 9.44772 7 10 7H15C15.5523 7 16 6.55228 16 6V1C16 0.447715 15.5523 0 15 0Z" fill="#298df0" />
<path opacity="0.5" d="M15 9H10C9.44772 9 9 9.44772 9 10V15C9 15.5523 9.44772 16 10 16H15C15.5523 16 16 15.5523 16 15V10C16 9.44772 15.5523 9 15 9Z" fill="#298df0" />
<path opacity="0.1" d="M6 9H1C0.447715 9 0 9.44772 0 10V15C0 15.5523 0.447715 16 1 16H6C6.55228 16 7 15.5523 7 15V10C7 9.44772 6.55228 9 6 9Z" fill="#298df0" />
<path opacity="0.2" d="M6 0H1C0.447715 0 0 0.447715 0 1V6C0 6.55228 0.447715 7 1 7H6C6.55228 7 7 6.55228 7 6V1C7 0.447715 6.55228 0 6 0Z" fill="#298df0" />
</g>
<defs>
<clipPath id="clip0_324_2488">

@ -46,7 +46,7 @@ Object.defineProperty(globalThis, 'sessionStorage', {
const BrowserInitor = ({
children,
}: { children: React.ReactNode }) => {
return children
return <>{children}</>
}
export default BrowserInitor

@ -18,7 +18,7 @@ export default function AppBack({ curApp }: IAppBackProps) {
<div
className={classNames(`
flex items-center h-7 pl-2.5 pr-2
text-[#1C64F2] font-semibold cursor-pointer
text-[#298df0] font-semibold cursor-pointer
rounded-[10px]
${curApp && 'hover:bg-[#EBF5FF]'}
`)}

@ -35,7 +35,7 @@ export default function AppSelector({ appItems, curApp }: IAppSelectorProps) {
className="
inline-flex h-7 w-full items-center justify-center
rounded-[10px] pl-2 pr-2.5 text-[14px] font-semibold
text-[#1C64F2] hover:bg-[#EBF5FF]
text-[#298df0] hover:bg-[#EBF5FF]
"
>
{curApp?.name}

@ -23,7 +23,7 @@ const SentryInit = ({
})
}
}, [])
return children
return <>{children}</>
}
export default SentryInit

@ -36,7 +36,6 @@ import Toast from '@/app/components/base/toast'
import type { VisionFile, VisionSettings } from '@/types/app'
import { Resolution, TransferMethod } from '@/types/app'
import { useAppFavicon } from '@/hooks/use-app-favicon'
import LogoSite from '@/app/components/base/logo/logo-site'
import cn from '@/utils/classnames'
const GROUP_SIZE = 5 // to avoid RPM(Request per minute) limit. The group task finished then the next group.
@ -623,22 +622,6 @@ const TextGeneration: FC<IMainProps> = ({
/>
)}
</div>
{/* powered by */}
{!customConfig?.remove_webapp_brand && (
<div className={cn(
'flex shrink-0 items-center gap-1.5 bg-components-panel-bg py-3',
isPC ? 'px-8' : 'px-4',
!isPC && resultExisted && 'rounded-b-2xl border-b-[0.5px] border-divider-regular',
)}>
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
{customConfig?.replace_webapp_logo && (
<img src={customConfig?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' />
)}
{!customConfig?.replace_webapp_logo && (
<LogoSite className='!h-5' />
)}
</div>
)}
</div>
{/* Result */}
<div className={cn(

@ -68,6 +68,8 @@ const LocaleLayout = async ({
enableSystem
disableTransitionOnChange
>
{/* eslint-disable-next-line ts/ban-ts-comment */}
{/* @ts-expect-error */}
<I18nServer>
{children}
</I18nServer>

@ -698,3 +698,26 @@ button:focus-within {
scrollbar-width: none;
}
}
/** small size scroll bar */
.scrollbar-small {
&::-webkit-scrollbar {
width: 6px;
height: 6px;
}
&::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: #b0b0b0;
}
&::-webkit-scrollbar-track {
background-color: #f0f0f0;
border-radius: 4px;
}
&::-webkit-scrollbar-thumb:hover {
background-color: #888888;
}
}

@ -167,7 +167,6 @@ export default combine(
'sonarjs/max-lines': 'warn', // max 1000 lines
'sonarjs/no-variable-usage-before-declaration': 'error',
// security
// eslint-disable-next-line sonarjs/no-hardcoded-passwords
'sonarjs/no-hardcoded-passwords': 'off', // detect the wrong code that is not password.
'sonarjs/no-hardcoded-secrets': 'off',
'sonarjs/pseudo-random': 'off',

@ -540,7 +540,7 @@ const translation = {
vectorHash: 'Vektorhash:',
hitScore: 'Abrufwertung:',
},
inputPlaceholder: 'Sprechen Sie mit dem Bot',
inputPlaceholder: 'Bitte erzählen Sie mir von Ihrem Anliegen, verwenden Sie Umschalt + Eingabetaste für einen Zeilenumbruch',
thought: 'Gedanke',
thinking: 'Denken...',
},

@ -561,7 +561,7 @@ const translation = {
vectorHash: 'Vector hash:',
hitScore: 'Retrieval Score:',
},
inputPlaceholder: 'Talk to Bot',
inputPlaceholder: 'Please tell me about your issue, use Shift + Enter for a new line',
thinking: 'Thinking...',
thought: 'Thought',
resend: 'Resend',

@ -544,7 +544,7 @@ const translation = {
vectorHash: 'Hash de vector:',
hitScore: 'Puntuación de recuperación:',
},
inputPlaceholder: 'Hablar con el bot',
inputPlaceholder: 'Por favor, cuéntame sobre tu problema, utiliza Mayús + Intro para una nueva línea',
thinking: 'Pensamiento...',
thought: 'Pensamiento',
},

@ -544,7 +544,7 @@ const translation = {
vectorHash: 'هش بردار:',
hitScore: 'امتیاز بازیابی:',
},
inputPlaceholder: 'با ربات صحبت کنید',
inputPlaceholder: 'لطفاً مشکل خود را به من بگویید، از Shift + Enter برای خط جدید استفاده کنید',
thought: 'فکر',
thinking: 'تفکر...',
},

@ -540,7 +540,7 @@ const translation = {
vectorHash: 'Hachage vectoriel:',
hitScore: 'Score de Récupération:',
},
inputPlaceholder: 'Parler au bot',
inputPlaceholder: 'Veuillez me parler de votre problème, utilisez Maj + Entrée pour une nouvelle ligne',
thinking: 'Pensée...',
thought: 'Pensée',
},

@ -562,7 +562,7 @@ const translation = {
vectorHash: 'वेक्टर हैश:',
hitScore: 'पुनः प्राप्ति स्कोर:',
},
inputPlaceholder: 'बॉट से बात करें',
inputPlaceholder: 'कृपया अपनी समस्या के बारे में मुझे बताएं, नई पंक्ति के लिए Shift + Enter का उपयोग करें',
thought: 'विचार',
thinking: 'सोचते हुए...',
},

@ -571,7 +571,7 @@ const translation = {
vectorHash: 'Hash del vettore:',
hitScore: 'Punteggio di recupero:',
},
inputPlaceholder: 'Parla con il bot',
inputPlaceholder: 'Per favore, parlami del tuo problema, usa Maiusc + Invio per una nuova riga',
thinking: 'Pensante...',
thought: 'Pensiero',
},

@ -561,7 +561,7 @@ const translation = {
vectorHash: 'ベクトルハッシュ:',
hitScore: '検索スコア:',
},
inputPlaceholder: 'ボットと話す',
inputPlaceholder: 'お困りの問題について教えてください。新しい行では Shift + Enter を使用します',
thought: '思考',
thinking: '考え中...',
},

@ -536,7 +536,7 @@ const translation = {
vectorHash: '벡터 해시:',
hitScore: '검색 점수:',
},
inputPlaceholder: '봇과 대화',
inputPlaceholder: '문제에 대해 말씀해 주세요, 줄 바꿈을 위해 Shift + Enter 를 사용하세요',
thought: '생각',
thinking: '생각...',
},

@ -555,7 +555,7 @@ const translation = {
vectorHash: 'Wektor hash:',
hitScore: 'Wynik trafień:',
},
inputPlaceholder: 'Porozmawiaj z botem',
inputPlaceholder: 'Proszę opowiedz mi o swoim problemie, użyj Shift + Enter, aby dodać nową linię',
thought: 'Myśl',
thinking: 'Myślenie...',
},

@ -540,7 +540,7 @@ const translation = {
vectorHash: 'Hash de vetor:',
hitScore: 'Pontuação de recuperação:',
},
inputPlaceholder: 'Fale com o bot',
inputPlaceholder: 'Por favor, conte-me sobre o seu problema, use Shift + Enter para uma nova linha',
thinking: 'Pensante...',
thought: 'Pensamento',
},

@ -540,7 +540,7 @@ const translation = {
vectorHash: 'Hash vector:',
hitScore: 'Scor de recuperare:',
},
inputPlaceholder: 'Vorbește cu Bot',
inputPlaceholder: 'Vă rog să-mi spuneți despre problema dumneavoastră, folosiți Shift + Enter pentru o nouă linie',
thinking: 'Gândire...',
thought: 'Gând',
},

@ -544,7 +544,7 @@ const translation = {
vectorHash: 'Векторный хэш:',
hitScore: 'Оценка совпадения:',
},
inputPlaceholder: 'Поговорить с ботом',
inputPlaceholder: 'Пожалуйста, расскажите мне о своей проблеме, используйте Shift + Enter для новой строки',
thinking: 'Мыслящий...',
thought: 'Мысль',
},

@ -1,3 +1,5 @@
// eslint-disable-next-line ts/ban-ts-comment
// @ts-ignore
const translation = {
pageTitle: {
line1: 'PROMPT',

@ -740,7 +740,7 @@ const translation = {
title: 'CITATI',
},
conversationNameCanNotEmpty: 'Zahtevano ime pogovora',
inputPlaceholder: 'Pogovorite se z botom',
inputPlaceholder: 'Prosimo, povejte mi o svoji težavi, uporabite Shift + Enter za novo vrstico',
renameConversation: 'Preimenovanje pogovora',
conversationName: 'Ime pogovora',
conversationNamePlaceholder: 'Prosimo, vnesite ime pogovora',

@ -539,7 +539,7 @@ const translation = {
vectorHash: 'แฮชเวกเตอร์:',
hitScore: 'คะแนนการดึงข้อมูล:',
},
inputPlaceholder: 'พูดคุยกับบอท',
inputPlaceholder: 'กรุณาบอกฉันเกี่ยวกับปัญหาของคุณ ใช้ Shift + Enter เพื่อขึ้นบรรทัดใหม่',
thought: 'ความคิด',
thinking: 'ความคิด ',
},

@ -544,7 +544,7 @@ const translation = {
vectorHash: 'Vektör Hash:',
hitScore: 'Geri Alım Skoru:',
},
inputPlaceholder: 'Bot ile konuş',
inputPlaceholder: 'Lütfen karşılaştığınız sorunu bana anlatın, yeni satır için Shift + Enter kullanın',
thought: 'Düşünce',
thinking: 'Düşünü...',
},

@ -541,7 +541,7 @@ const translation = {
vectorHash: 'Хеш вектора:',
hitScore: 'Оцінка звернення:',
},
inputPlaceholder: 'Поговоріть з ботом',
inputPlaceholder: 'Будь ласка, розкажіть мені про вашу проблему, використовуйте Shift + Enter для нового рядка',
thought: 'Думка',
thinking: 'Мислення...',
},

@ -540,7 +540,7 @@ const translation = {
vectorHash: 'Vector hash:',
hitScore: 'Điểm truy xuất:',
},
inputPlaceholder: 'Nói chuyện với Bot',
inputPlaceholder: 'Vui lòng cho tôi biết vấn đề bạn gặp phải, sử dụng Shift + Enter để xuống dòng',
thought: 'Tư duy',
thinking: 'Suy nghĩ...',
},

@ -561,7 +561,7 @@ const translation = {
vectorHash: '向量哈希:',
hitScore: '召回得分:',
},
inputPlaceholder: '和机器人聊天',
inputPlaceholder: '请将您遇到的问题告诉我,使用 Shift + Enter 换行',
thinking: '深度思考中...',
thought: '已深度思考',
resend: '重新发送',

@ -540,7 +540,7 @@ const translation = {
vectorHash: '向量雜湊:',
hitScore: '召回得分:',
},
inputPlaceholder: '與 Bot 對話',
inputPlaceholder: '請將您遇到的問題告訴我,使用 Shift + Enter 換行',
thinking: '思維。。。',
thought: '思想',
},

@ -6,13 +6,13 @@
// attention: This JavaScript script must be placed after the <body> element. Otherwise, the script will not work.
(function () {
(function() {
// Constants for DOM element IDs and configuration key
const configKey = "difyChatbotConfig";
const buttonId = "dify-chatbot-bubble-button";
const iframeId = "dify-chatbot-bubble-window";
const config = window[configKey];
let isExpanded = false;
const configKey = 'difyChatbotConfig'
const buttonId = 'dify-chatbot-bubble-button'
const iframeId = 'dify-chatbot-bubble-window'
const config = window[configKey]
let isExpanded = false
// SVG icons for open and close states
const svgIcons = `<svg id="openIcon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -21,7 +21,7 @@
<svg id="closeIcon" style="display:none" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 18L6 6M6 18L18 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
`;
`
const originalIframeStyleText = `
@ -33,9 +33,9 @@
right: var(--${buttonId}-right, 1rem); /* Align with dify-chatbot-bubble-button. */
bottom: var(--${buttonId}-bottom, 1rem); /* Align with dify-chatbot-bubble-button. */
left: unset;
width: 24rem;
width: 30rem;
max-width: calc(100vw - 2rem);
height: 43.75rem;
height: 44rem;
max-height: calc(100vh - 6rem);
border: none;
z-index: 2147483640;
@ -51,16 +51,10 @@
display: flex;
flex-direction: column;
justify-content: space-between;
top: unset;
right: var(--${buttonId}-right, 1rem); /* Align with dify-chatbot-bubble-button. */
bottom: var(--${buttonId}-bottom, 1rem); /* Align with dify-chatbot-bubble-button. */
left: unset;
min-width: 24rem;
width: 48%;
max-width: 40rem; /* Match mobile breakpoint*/
min-height: 43.75rem;
height: 88%;
max-height: calc(100vh - 6rem);
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
z-index: 2147483640;
overflow: hidden;
@ -75,130 +69,134 @@
let isDragging = false
if (!config || !config.token) {
console.error(`${configKey} is empty or token is not provided`);
return;
console.error(`${configKey} is empty or token is not provided`)
return
}
async function compressAndEncodeBase64(input) {
const uint8Array = new TextEncoder().encode(input);
const uint8Array = new TextEncoder().encode(input)
const compressedStream = new Response(
new Blob([uint8Array])
.stream()
.pipeThrough(new CompressionStream("gzip"))
).arrayBuffer();
const compressedUint8Array = new Uint8Array(await compressedStream);
return btoa(String.fromCharCode(...compressedUint8Array));
.pipeThrough(new CompressionStream('gzip')),
).arrayBuffer()
const compressedUint8Array = new Uint8Array(await compressedStream)
return btoa(String.fromCharCode(...compressedUint8Array))
}
async function getCompressedInputsFromConfig() {
const inputs = config?.inputs || {};
const compressedInputs = {};
const inputs = config?.inputs || {}
const compressedInputs = {}
await Promise.all(
Object.entries(inputs).map(async ([key, value]) => {
compressedInputs[key] = await compressAndEncodeBase64(value);
})
);
return compressedInputs;
compressedInputs[key] = await compressAndEncodeBase64(value)
}),
)
return compressedInputs
}
async function getCompressedSystemVariablesFromConfig() {
const systemVariables = config?.systemVariables || {};
const compressedSystemVariables = {};
const systemVariables = config?.systemVariables || {}
const compressedSystemVariables = {}
await Promise.all(
Object.entries(systemVariables).map(async ([key, value]) => {
compressedSystemVariables[`sys.${key}`] = await compressAndEncodeBase64(value);
})
);
return compressedSystemVariables;
compressedSystemVariables[`sys.${key}`] = await compressAndEncodeBase64(value)
}),
)
return compressedSystemVariables
}
const params = new URLSearchParams({
...await getCompressedInputsFromConfig(),
...await getCompressedSystemVariablesFromConfig()
});
...await getCompressedSystemVariablesFromConfig(),
})
const baseUrl =
config.baseUrl || `https://${config.isDev ? "dev." : ""}udify.app`;
const targetOrigin = new URL(baseUrl).origin;
config.baseUrl || `https://${config.isDev ? 'dev.' : ''}udify.app`
const targetOrigin = new URL(baseUrl).origin
// pre-check the length of the URL
const iframeUrl = `${baseUrl}/chatbot/${config.token}?${params}`;
const iframeUrl = `${baseUrl}/chatbot/${config.token}?${params}`
// 1) CREATE the iframe immediately, so it can load in the background:
const preloadedIframe = createIframe();
const preloadedIframe = createIframe()
// 2) HIDE it by default:
preloadedIframe.style.display = "none";
preloadedIframe.style.display = 'none'
// 3) APPEND it to the document body right away:
document.body.appendChild(preloadedIframe);
document.body.appendChild(preloadedIframe)
// ─── End Fix Snippet
if (iframeUrl.length > 2048) {
console.error("The URL is too long, please reduce the number of inputs to prevent the bot from failing to load");
console.error('The URL is too long, please reduce the number of inputs to prevent the bot from failing to load')
}
// Function to create the iframe for the chatbot
function createIframe() {
const iframe = document.createElement("iframe");
iframe.allow = "fullscreen;microphone";
iframe.title = "dify chatbot bubble window";
iframe.id = iframeId;
iframe.src = iframeUrl;
iframe.style.cssText = originalIframeStyleText;
return iframe;
const iframe = document.createElement('iframe')
iframe.allow = 'fullscreen;microphone'
iframe.title = 'dify chatbot bubble window'
iframe.id = iframeId
iframe.src = iframeUrl
iframe.style.cssText = originalIframeStyleText
return iframe
}
// Function to reset the iframe position
function resetIframePosition() {
if (window.innerWidth <= 640) return;
if (window.innerWidth <= 640) return
const targetIframe = document.getElementById(iframeId);
const targetButton = document.getElementById(buttonId);
const targetIframe = document.getElementById(iframeId)
const targetButton = document.getElementById(buttonId)
if (targetIframe && targetButton) {
const buttonRect = targetButton.getBoundingClientRect();
const buttonRect = targetButton.getBoundingClientRect()
// We don't necessarily need iframeRect anymore with the center logic
const viewportCenterY = window.innerHeight / 2;
const buttonCenterY = buttonRect.top + buttonRect.height / 2;
const viewportCenterY = window.innerHeight / 2
const buttonCenterY = buttonRect.top + buttonRect.height / 2
if (buttonCenterY < viewportCenterY) {
targetIframe.style.top = `var(--${buttonId}-bottom, 1rem)`;
targetIframe.style.bottom = 'unset';
targetIframe.style.top = `var(--${buttonId}-bottom, 1rem)`
targetIframe.style.bottom = 'unset'
} else {
targetIframe.style.bottom = `var(--${buttonId}-bottom, 1rem)`;
targetIframe.style.top = 'unset';
targetIframe.style.bottom = `var(--${buttonId}-bottom, 1rem)`
targetIframe.style.top = 'unset'
}
const viewportCenterX = window.innerWidth / 2;
const buttonCenterX = buttonRect.left + buttonRect.width / 2;
const viewportCenterX = window.innerWidth / 2
const buttonCenterX = buttonRect.left + buttonRect.width / 2
if (buttonCenterX < viewportCenterX) {
targetIframe.style.left = `var(--${buttonId}-right, 1rem)`;
targetIframe.style.right = 'unset';
targetIframe.style.left = `var(--${buttonId}-right, 1rem)`
targetIframe.style.right = 'unset'
} else {
targetIframe.style.right = `var(--${buttonId}-right, 1rem)`;
targetIframe.style.left = 'unset';
targetIframe.style.right = `var(--${buttonId}-right, 1rem)`
targetIframe.style.left = 'unset'
}
}
}
function toggleExpand() {
isExpanded = !isExpanded;
isExpanded = !isExpanded
const targetIframe = document.getElementById(iframeId)
if (!targetIframe) return
const targetIframe = document.getElementById(iframeId);
if (!targetIframe) return;
const targetButton = document.getElementById(buttonId)
if (isExpanded) {
targetIframe.style.cssText = expandedIframeStyleText;
targetIframe.style.cssText = expandedIframeStyleText
targetButton.style.display = 'none'
} else {
targetIframe.style.cssText = originalIframeStyleText;
targetIframe.style.cssText = originalIframeStyleText
targetButton.style.display = 'bkock'
}
resetIframePosition();
resetIframePosition()
}
window.addEventListener('message', (event) => {
if (event.origin !== targetOrigin) return;
if (event.origin !== targetOrigin) return
const targetIframe = document.getElementById(iframeId);
if (!targetIframe || event.source !== targetIframe.contentWindow) return;
const targetIframe = document.getElementById(iframeId)
if (!targetIframe || event.source !== targetIframe.contentWindow) return
if (event.data.type === 'dify-chatbot-iframe-ready') {
targetIframe.contentWindow?.postMessage(
@ -209,43 +207,48 @@
isDraggable: !!config.draggable,
},
},
targetOrigin
);
}
if (event.data.type === 'dify-chatbot-expand-change') {
toggleExpand();
targetOrigin,
)
} else if (event.data.type === 'dify-chatbot-expand-change') {
toggleExpand()
} else if (event.data.type === 'dify-chatbot-iframe-close') {
const targetIframe = document.getElementById(iframeId)
if (targetIframe) {
targetIframe.style.display = 'none'
setSvgIcon('open')
isExpanded = false
}
}
});
})
// Function to create the chat button
function createButton() {
const containerDiv = document.createElement("div");
const containerDiv = document.createElement('div')
// Apply custom properties from config
Object.entries(config.containerProps || {}).forEach(([key, value]) => {
if (key === "className") {
containerDiv.classList.add(...value.split(" "));
} else if (key === "style") {
if (typeof value === "object") {
Object.assign(containerDiv.style, value);
if (key === 'className') {
containerDiv.classList.add(...value.split(' '))
} else if (key === 'style') {
if (typeof value === 'object') {
Object.assign(containerDiv.style, value)
} else {
containerDiv.style.cssText = value;
containerDiv.style.cssText = value
}
} else if (typeof value === "function") {
} else if (typeof value === 'function') {
containerDiv.addEventListener(
key.replace(/^on/, "").toLowerCase(),
value
);
key.replace(/^on/, '').toLowerCase(),
value,
)
} else {
containerDiv[key] = value;
containerDiv[key] = value
}
});
})
containerDiv.id = buttonId;
containerDiv.id = buttonId
// Add styles for the button
const styleSheet = document.createElement("style");
document.head.appendChild(styleSheet);
const styleSheet = document.createElement('style')
document.head.appendChild(styleSheet)
styleSheet.sheet.insertRule(`
#${containerDiv.id} {
position: fixed;
@ -261,181 +264,185 @@
cursor: pointer;
z-index: 2147483647;
}
`);
`)
// Create display div for the button icon
const displayDiv = document.createElement("div");
const displayDiv = document.createElement('div')
displayDiv.style.cssText =
"position: relative; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; z-index: 2147483647;";
displayDiv.innerHTML = svgIcons;
containerDiv.appendChild(displayDiv);
document.body.appendChild(containerDiv);
'position: relative; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; z-index: 2147483647;'
displayDiv.innerHTML = svgIcons
containerDiv.appendChild(displayDiv)
document.body.appendChild(containerDiv)
// Add click event listener to toggle chatbot
containerDiv.addEventListener("click", handleClick);
containerDiv.addEventListener('click', handleClick)
// Add touch event listener
containerDiv.addEventListener("touchend", (event) => {
event.preventDefault();
handleClick();
}, { passive: false });
containerDiv.addEventListener('touchend', (event) => {
event.preventDefault()
handleClick()
}, { passive: false })
function handleClick() {
if (isDragging) return;
if (isDragging) return
const targetIframe = document.getElementById(iframeId);
const targetIframe = document.getElementById(iframeId)
if (!targetIframe) {
containerDiv.appendChild(createIframe());
resetIframePosition();
this.title = "Exit (ESC)";
setSvgIcon("close");
document.addEventListener("keydown", handleEscKey);
return;
containerDiv.appendChild(createIframe())
resetIframePosition()
this.title = 'Exit (ESC)'
setSvgIcon('close')
document.addEventListener('keydown', handleEscKey)
return
}
targetIframe.style.display =
targetIframe.style.display === "none" ? "block" : "none";
targetIframe.style.display === "none"
? setSvgIcon("open")
: setSvgIcon("close");
targetIframe.style.display === 'none' ? 'block' : 'none'
targetIframe.style.display === 'none'
? setSvgIcon('open')
: setSvgIcon('close')
if (targetIframe.style.display === "none") {
document.removeEventListener("keydown", handleEscKey);
if (targetIframe.style.display === 'none') {
document.removeEventListener('keydown', handleEscKey)
} else {
document.addEventListener("keydown", handleEscKey);
document.addEventListener('keydown', handleEscKey)
}
resetIframePosition();
resetIframePosition()
}
// Enable dragging if specified in config
if (config.draggable) {
enableDragging(containerDiv, config.dragAxis || "both");
enableDragging(containerDiv, config.dragAxis || 'both')
}
}
// Function to enable dragging of the chat button
function enableDragging(element, axis) {
let startX, startY, startClientX, startClientY;
let startX, startY, startClientX, startClientY
element.addEventListener("mousedown", startDragging);
element.addEventListener("touchstart", startDragging);
element.addEventListener('mousedown', startDragging)
element.addEventListener('touchstart', startDragging)
function startDragging(e) {
isDragging = false;
if (e.type === "touchstart") {
startX = e.touches[0].clientX - element.offsetLeft;
startY = e.touches[0].clientY - element.offsetTop;
startClientX = e.touches[0].clientX;
startClientY = e.touches[0].clientY;
isDragging = false
if (e.type === 'touchstart') {
startX = e.touches[0].clientX - element.offsetLeft
startY = e.touches[0].clientY - element.offsetTop
startClientX = e.touches[0].clientX
startClientY = e.touches[0].clientY
} else {
startX = e.clientX - element.offsetLeft;
startY = e.clientY - element.offsetTop;
startClientX = e.clientX;
startClientY = e.clientY;
startX = e.clientX - element.offsetLeft
startY = e.clientY - element.offsetTop
startClientX = e.clientX
startClientY = e.clientY
}
document.addEventListener("mousemove", drag);
document.addEventListener("touchmove", drag, { passive: false });
document.addEventListener("mouseup", stopDragging);
document.addEventListener("touchend", stopDragging);
e.preventDefault();
document.addEventListener('mousemove', drag)
document.addEventListener('touchmove', drag, { passive: false })
document.addEventListener('mouseup', stopDragging)
document.addEventListener('touchend', stopDragging)
e.preventDefault()
}
function drag(e) {
const touch = e.type === "touchmove" ? e.touches[0] : e;
const deltaX = touch.clientX - startClientX;
const deltaY = touch.clientY - startClientY;
const touch = e.type === 'touchmove' ? e.touches[0] : e
const deltaX = touch.clientX - startClientX
const deltaY = touch.clientY - startClientY
// Determine whether it is a drag operation
if (Math.abs(deltaX) > 8 || Math.abs(deltaY) > 8) {
isDragging = true;
isDragging = true
}
if (!isDragging) return;
if (!isDragging) return
element.style.transition = "none";
element.style.cursor = "grabbing";
element.style.transition = 'none'
element.style.cursor = 'grabbing'
// Hide iframe while dragging
const targetIframe = document.getElementById(iframeId);
const targetIframe = document.getElementById(iframeId)
if (targetIframe) {
targetIframe.style.display = "none";
setSvgIcon("open");
targetIframe.style.display = 'none'
setSvgIcon('open')
}
let newLeft, newBottom;
if (e.type === "touchmove") {
newLeft = e.touches[0].clientX - startX;
newBottom = window.innerHeight - e.touches[0].clientY - startY;
let newLeft, newBottom
if (e.type === 'touchmove') {
newLeft = e.touches[0].clientX - startX
newBottom = window.innerHeight - e.touches[0].clientY - startY
} else {
newLeft = e.clientX - startX;
newBottom = window.innerHeight - e.clientY - startY;
newLeft = e.clientX - startX
newBottom = window.innerHeight - e.clientY - startY
}
const elementRect = element.getBoundingClientRect();
const maxX = window.innerWidth - elementRect.width;
const maxY = window.innerHeight - elementRect.height;
const elementRect = element.getBoundingClientRect()
const maxX = window.innerWidth - elementRect.width
const maxY = window.innerHeight - elementRect.height
// Update position based on drag axis
if (axis === "x" || axis === "both") {
if (axis === 'x' || axis === 'both') {
element.style.setProperty(
`--${buttonId}-left`,
`${Math.max(0, Math.min(newLeft, maxX))}px`
);
`${Math.max(0, Math.min(newLeft, maxX))}px`,
)
}
if (axis === "y" || axis === "both") {
if (axis === 'y' || axis === 'both') {
element.style.setProperty(
`--${buttonId}-bottom`,
`${Math.max(0, Math.min(newBottom, maxY))}px`
);
`${Math.max(0, Math.min(newBottom, maxY))}px`,
)
}
}
function stopDragging() {
setTimeout(() => {
isDragging = false;
}, 0);
element.style.transition = "";
element.style.cursor = "pointer";
document.removeEventListener("mousemove", drag);
document.removeEventListener("touchmove", drag);
document.removeEventListener("mouseup", stopDragging);
document.removeEventListener("touchend", stopDragging);
isDragging = false
}, 0)
element.style.transition = ''
element.style.cursor = 'pointer'
document.removeEventListener('mousemove', drag)
document.removeEventListener('touchmove', drag)
document.removeEventListener('mouseup', stopDragging)
document.removeEventListener('touchend', stopDragging)
}
}
// Create the chat button if it doesn't exist
if (!document.getElementById(buttonId)) {
createButton();
createButton()
}
}
function setSvgIcon(type = "open") {
if (type === "open") {
document.getElementById("openIcon").style.display = "block";
document.getElementById("closeIcon").style.display = "none";
function setSvgIcon(type = 'open') {
if (type === 'open') {
document.getElementById('openIcon').style.display = 'block'
document.getElementById('closeIcon').style.display = 'none'
} else if (type === 'hidden') {
document.getElementById('openIcon').style.display = 'none'
document.getElementById('closeIcon').style.display = 'none'
} else {
document.getElementById("openIcon").style.display = "none";
document.getElementById("closeIcon").style.display = "block";
document.getElementById('openIcon').style.display = 'none'
document.getElementById('closeIcon').style.display = 'block'
}
}
// Add esc Exit keyboard event triggered
function handleEscKey(event) {
if (event.key === "Escape") {
const targetIframe = document.getElementById(iframeId);
if (targetIframe && targetIframe.style.display !== "none") {
targetIframe.style.display = "none";
setSvgIcon("open");
if (event.key === 'Escape') {
const targetIframe = document.getElementById(iframeId)
if (targetIframe && targetIframe.style.display !== 'none') {
targetIframe.style.display = 'none'
setSvgIcon('open')
}
}
}
document.addEventListener("keydown", handleEscKey);
document.addEventListener('keydown', handleEscKey)
// Set the embedChatbot function to run when the body is loaded,Avoid infinite nesting
if (config?.dynamicScript) {
embedChatbot();
embedChatbot()
} else {
document.body.onload = embedChatbot;
document.body.onload = embedChatbot
}
})();
})()

@ -1,15 +1,15 @@
(()=>{let t="difyChatbotConfig",h="dify-chatbot-bubble-button",m="dify-chatbot-bubble-window",y=window[t],a=!1,l=`
(()=>{let t="difyChatbotConfig",m="dify-chatbot-bubble-button",y="dify-chatbot-bubble-window",h=window[t],l=!1,a=`
position: absolute;
display: flex;
flex-direction: column;
justify-content: space-between;
top: unset;
right: var(--${h}-right, 1rem); /* Align with dify-chatbot-bubble-button. */
bottom: var(--${h}-bottom, 1rem); /* Align with dify-chatbot-bubble-button. */
right: var(--${m}-right, 1rem); /* Align with dify-chatbot-bubble-button. */
bottom: var(--${m}-bottom, 1rem); /* Align with dify-chatbot-bubble-button. */
left: unset;
width: 24rem;
width: 30rem;
max-width: calc(100vw - 2rem);
height: 43.75rem;
height: 44rem;
max-height: calc(100vh - 6rem);
border: none;
z-index: 2147483640;
@ -18,7 +18,7 @@
transition-property: width, height;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
`;async function e(){let u=!1;if(y&&y.token){var e=new URLSearchParams({...await(async()=>{var e=y?.inputs||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n[e]=await i(t)})),n})(),...await(async()=>{var e=y?.systemVariables||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n["sys."+e]=await i(t)})),n})()}),n=y.baseUrl||`https://${y.isDev?"dev.":""}udify.app`;let o=new URL(n).origin,t=`${n}/chatbot/${y.token}?`+e;n=s();async function i(e){e=(new TextEncoder).encode(e),e=new Response(new Blob([e]).stream().pipeThrough(new CompressionStream("gzip"))).arrayBuffer(),e=new Uint8Array(await e);return btoa(String.fromCharCode(...e))}function s(){var e=document.createElement("iframe");return e.allow="fullscreen;microphone",e.title="dify chatbot bubble window",e.id=m,e.src=t,e.style.cssText=l,e}function d(){var e,t,n;window.innerWidth<=640||(e=document.getElementById(m),t=document.getElementById(h),e&&t&&(t=t.getBoundingClientRect(),n=window.innerHeight/2,t.top+t.height/2<n?(e.style.top=`var(--${h}-bottom, 1rem)`,e.style.bottom="unset"):(e.style.bottom=`var(--${h}-bottom, 1rem)`,e.style.top="unset"),t.left+t.width/2<window.innerWidth/2?(e.style.left=`var(--${h}-right, 1rem)`,e.style.right="unset"):(e.style.right=`var(--${h}-right, 1rem)`,e.style.left="unset")))}function r(){let n=document.createElement("div");Object.entries(y.containerProps||{}).forEach(([e,t])=>{"className"===e?n.classList.add(...t.split(" ")):"style"===e?"object"==typeof t?Object.assign(n.style,t):n.style.cssText=t:"function"==typeof t?n.addEventListener(e.replace(/^on/,"").toLowerCase(),t):n[e]=t}),n.id=h;var e=document.createElement("style"),e=(document.head.appendChild(e),e.sheet.insertRule(`
`;async function e(){let u=!1;if(h&&h.token){var e=new URLSearchParams({...await(async()=>{var e=h?.inputs||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n[e]=await o(t)})),n})(),...await(async()=>{var e=h?.systemVariables||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n["sys."+e]=await o(t)})),n})()}),n=h.baseUrl||`https://${h.isDev?"dev.":""}udify.app`;let i=new URL(n).origin,t=`${n}/chatbot/${h.token}?`+e;n=s();async function o(e){e=(new TextEncoder).encode(e),e=new Response(new Blob([e]).stream().pipeThrough(new CompressionStream("gzip"))).arrayBuffer(),e=new Uint8Array(await e);return btoa(String.fromCharCode(...e))}function s(){var e=document.createElement("iframe");return e.allow="fullscreen;microphone",e.title="dify chatbot bubble window",e.id=y,e.src=t,e.style.cssText=a,e}function d(){var e,t,n;window.innerWidth<=640||(e=document.getElementById(y),t=document.getElementById(m),e&&t&&(t=t.getBoundingClientRect(),n=window.innerHeight/2,t.top+t.height/2<n?(e.style.top=`var(--${m}-bottom, 1rem)`,e.style.bottom="unset"):(e.style.bottom=`var(--${m}-bottom, 1rem)`,e.style.top="unset"),t.left+t.width/2<window.innerWidth/2?(e.style.left=`var(--${m}-right, 1rem)`,e.style.right="unset"):(e.style.right=`var(--${m}-right, 1rem)`,e.style.left="unset")))}function r(){let n=document.createElement("div");Object.entries(h.containerProps||{}).forEach(([e,t])=>{"className"===e?n.classList.add(...t.split(" ")):"style"===e?"object"==typeof t?Object.assign(n.style,t):n.style.cssText=t:"function"==typeof t?n.addEventListener(e.replace(/^on/,"").toLowerCase(),t):n[e]=t}),n.id=m;var e=document.createElement("style"),e=(document.head.appendChild(e),e.sheet.insertRule(`
#${n.id} {
position: fixed;
bottom: var(--${n.id}-bottom, 1rem);
@ -33,10 +33,26 @@
cursor: pointer;
z-index: 2147483647;
}
`),document.createElement("div"));function t(){var e;u||((e=document.getElementById(m))?(e.style.display="none"===e.style.display?"block":"none","none"===e.style.display?p("open"):p("close"),"none"===e.style.display?document.removeEventListener("keydown",b):document.addEventListener("keydown",b),d()):(n.appendChild(s()),d(),this.title="Exit (ESC)",p("close"),document.addEventListener("keydown",b)))}if(e.style.cssText="position: relative; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; z-index: 2147483647;",e.innerHTML=`<svg id="openIcon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
`),document.createElement("div"));function t(){var e;u||((e=document.getElementById(y))?(e.style.display="none"===e.style.display?"block":"none","none"===e.style.display?p("open"):p("close"),"none"===e.style.display?document.removeEventListener("keydown",g):document.addEventListener("keydown",g),d()):(n.appendChild(s()),d(),this.title="Exit (ESC)",p("close"),document.addEventListener("keydown",g)))}if(e.style.cssText="position: relative; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; z-index: 2147483647;",e.innerHTML=`<svg id="openIcon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.7586 2L16.2412 2C17.0462 1.99999 17.7105 1.99998 18.2517 2.04419C18.8138 2.09012 19.3305 2.18868 19.8159 2.43598C20.5685 2.81947 21.1804 3.43139 21.5639 4.18404C21.8112 4.66937 21.9098 5.18608 21.9557 5.74818C21.9999 6.28937 21.9999 6.95373 21.9999 7.7587L22 14.1376C22.0004 14.933 22.0007 15.5236 21.8636 16.0353C21.4937 17.4156 20.4155 18.4938 19.0352 18.8637C18.7277 18.9461 18.3917 18.9789 17.9999 18.9918L17.9999 20.371C18 20.6062 18 20.846 17.9822 21.0425C17.9651 21.2305 17.9199 21.5852 17.6722 21.8955C17.3872 22.2525 16.9551 22.4602 16.4983 22.4597C16.1013 22.4593 15.7961 22.273 15.6386 22.1689C15.474 22.06 15.2868 21.9102 15.1031 21.7632L12.69 19.8327C12.1714 19.4178 12.0174 19.3007 11.8575 19.219C11.697 19.137 11.5262 19.0771 11.3496 19.0408C11.1737 19.0047 10.9803 19 10.3162 19H7.75858C6.95362 19 6.28927 19 5.74808 18.9558C5.18598 18.9099 4.66928 18.8113 4.18394 18.564C3.43129 18.1805 2.81937 17.5686 2.43588 16.816C2.18859 16.3306 2.09002 15.8139 2.0441 15.2518C1.99988 14.7106 1.99989 14.0463 1.9999 13.2413V7.75868C1.99989 6.95372 1.99988 6.28936 2.0441 5.74818C2.09002 5.18608 2.18859 4.66937 2.43588 4.18404C2.81937 3.43139 3.43129 2.81947 4.18394 2.43598C4.66928 2.18868 5.18598 2.09012 5.74808 2.04419C6.28927 1.99998 6.95364 1.99999 7.7586 2ZM10.5073 7.5C10.5073 6.67157 9.83575 6 9.00732 6C8.1789 6 7.50732 6.67157 7.50732 7.5C7.50732 8.32843 8.1789 9 9.00732 9C9.83575 9 10.5073 8.32843 10.5073 7.5ZM16.6073 11.7001C16.1669 11.3697 15.5426 11.4577 15.2105 11.8959C15.1488 11.9746 15.081 12.0486 15.0119 12.1207C14.8646 12.2744 14.6432 12.4829 14.3566 12.6913C13.7796 13.111 12.9818 13.5001 12.0073 13.5001C11.0328 13.5001 10.235 13.111 9.65799 12.6913C9.37138 12.4829 9.15004 12.2744 9.00274 12.1207C8.93366 12.0486 8.86581 11.9745 8.80418 11.8959C8.472 11.4577 7.84775 11.3697 7.40732 11.7001C6.96549 12.0314 6.87595 12.6582 7.20732 13.1001C7.20479 13.0968 7.21072 13.1043 7.22094 13.1171C7.24532 13.1478 7.29407 13.2091 7.31068 13.2289C7.36932 13.2987 7.45232 13.3934 7.55877 13.5045C7.77084 13.7258 8.08075 14.0172 8.48165 14.3088C9.27958 14.8891 10.4818 15.5001 12.0073 15.5001C13.5328 15.5001 14.735 14.8891 15.533 14.3088C15.9339 14.0172 16.2438 13.7258 16.4559 13.5045C16.5623 13.3934 16.6453 13.2987 16.704 13.2289C16.7333 13.1939 16.7567 13.165 16.7739 13.1432C17.1193 12.6969 17.0729 12.0493 16.6073 11.7001ZM15.0073 6C15.8358 6 16.5073 6.67157 16.5073 7.5C16.5073 8.32843 15.8358 9 15.0073 9C14.1789 9 13.5073 8.32843 13.5073 7.5C13.5073 6.67157 14.1789 6 15.0073 6Z" fill="white"/>
</svg>
<svg id="closeIcon" style="display:none" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 18L6 6M6 18L18 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
`,n.appendChild(e),document.body.appendChild(n),n.addEventListener("click",t),n.addEventListener("touchend",e=>{e.preventDefault(),t()},{passive:!1}),y.draggable){var a=n;var l=y.dragAxis||"both";let s,d,t,r;function o(e){u=!1,r=("touchstart"===e.type?(s=e.touches[0].clientX-a.offsetLeft,d=e.touches[0].clientY-a.offsetTop,t=e.touches[0].clientX,e.touches[0]):(s=e.clientX-a.offsetLeft,d=e.clientY-a.offsetTop,t=e.clientX,e)).clientY,document.addEventListener("mousemove",i),document.addEventListener("touchmove",i,{passive:!1}),document.addEventListener("mouseup",c),document.addEventListener("touchend",c),e.preventDefault()}function i(n){var o="touchmove"===n.type?n.touches[0]:n,i=o.clientX-t,o=o.clientY-r;if(u=8<Math.abs(i)||8<Math.abs(o)?!0:u){a.style.transition="none",a.style.cursor="grabbing";i=document.getElementById(m);i&&(i.style.display="none",p("open"));let e,t;t="touchmove"===n.type?(e=n.touches[0].clientX-s,window.innerHeight-n.touches[0].clientY-d):(e=n.clientX-s,window.innerHeight-n.clientY-d);o=a.getBoundingClientRect(),i=window.innerWidth-o.width,n=window.innerHeight-o.height;"x"!==l&&"both"!==l||a.style.setProperty(`--${h}-left`,Math.max(0,Math.min(e,i))+"px"),"y"!==l&&"both"!==l||a.style.setProperty(`--${h}-bottom`,Math.max(0,Math.min(t,n))+"px")}}function c(){setTimeout(()=>{u=!1},0),a.style.transition="",a.style.cursor="pointer",document.removeEventListener("mousemove",i),document.removeEventListener("touchmove",i),document.removeEventListener("mouseup",c),document.removeEventListener("touchend",c)}a.addEventListener("mousedown",o),a.addEventListener("touchstart",o)}}n.style.display="none",document.body.appendChild(n),2048<t.length&&console.error("The URL is too long, please reduce the number of inputs to prevent the bot from failing to load"),window.addEventListener("message",e=>{var t,n;e.origin===o&&(t=document.getElementById(m))&&e.source===t.contentWindow&&("dify-chatbot-iframe-ready"===e.data.type&&t.contentWindow?.postMessage({type:"dify-chatbot-config",payload:{isToggledByButton:!0,isDraggable:!!y.draggable}},o),"dify-chatbot-expand-change"===e.data.type)&&(a=!a,n=document.getElementById(m))&&(a?n.style.cssText="\n position: absolute;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n top: unset;\n right: var(--dify-chatbot-bubble-button-right, 1rem); /* Align with dify-chatbot-bubble-button. */\n bottom: var(--dify-chatbot-bubble-button-bottom, 1rem); /* Align with dify-chatbot-bubble-button. */\n left: unset;\n min-width: 24rem;\n width: 48%;\n max-width: 40rem; /* Match mobile breakpoint*/\n min-height: 43.75rem;\n height: 88%;\n max-height: calc(100vh - 6rem);\n border: none;\n z-index: 2147483640;\n overflow: hidden;\n user-select: none;\n transition-property: width, height;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n ":n.style.cssText=l,d())}),document.getElementById(h)||r()}else console.error(t+" is empty or token is not provided")}function p(e="open"){"open"===e?(document.getElementById("openIcon").style.display="block",document.getElementById("closeIcon").style.display="none"):(document.getElementById("openIcon").style.display="none",document.getElementById("closeIcon").style.display="block")}function b(e){"Escape"===e.key&&(e=document.getElementById(m))&&"none"!==e.style.display&&(e.style.display="none",p("open"))}h,h,document.addEventListener("keydown",b),y?.dynamicScript?e():document.body.onload=e})();
`,n.appendChild(e),document.body.appendChild(n),n.addEventListener("click",t),n.addEventListener("touchend",e=>{e.preventDefault(),t()},{passive:!1}),h.draggable){var l=n;var a=h.dragAxis||"both";let s,d,t,r;function o(e){u=!1,r=("touchstart"===e.type?(s=e.touches[0].clientX-l.offsetLeft,d=e.touches[0].clientY-l.offsetTop,t=e.touches[0].clientX,e.touches[0]):(s=e.clientX-l.offsetLeft,d=e.clientY-l.offsetTop,t=e.clientX,e)).clientY,document.addEventListener("mousemove",i),document.addEventListener("touchmove",i,{passive:!1}),document.addEventListener("mouseup",c),document.addEventListener("touchend",c),e.preventDefault()}function i(n){var o="touchmove"===n.type?n.touches[0]:n,i=o.clientX-t,o=o.clientY-r;if(u=8<Math.abs(i)||8<Math.abs(o)?!0:u){l.style.transition="none",l.style.cursor="grabbing";i=document.getElementById(y);i&&(i.style.display="none",p("open"));let e,t;t="touchmove"===n.type?(e=n.touches[0].clientX-s,window.innerHeight-n.touches[0].clientY-d):(e=n.clientX-s,window.innerHeight-n.clientY-d);o=l.getBoundingClientRect(),i=window.innerWidth-o.width,n=window.innerHeight-o.height;"x"!==a&&"both"!==a||l.style.setProperty(`--${m}-left`,Math.max(0,Math.min(e,i))+"px"),"y"!==a&&"both"!==a||l.style.setProperty(`--${m}-bottom`,Math.max(0,Math.min(t,n))+"px")}}function c(){setTimeout(()=>{u=!1},0),l.style.transition="",l.style.cursor="pointer",document.removeEventListener("mousemove",i),document.removeEventListener("touchmove",i),document.removeEventListener("mouseup",c),document.removeEventListener("touchend",c)}l.addEventListener("mousedown",o),l.addEventListener("touchstart",o)}}n.style.display="none",document.body.appendChild(n),2048<t.length&&console.error("The URL is too long, please reduce the number of inputs to prevent the bot from failing to load"),window.addEventListener("message",t=>{if(t.origin===i){let e=document.getElementById(y);var n,o;if(e&&t.source===e.contentWindow)if("dify-chatbot-iframe-ready"===t.data.type)e.contentWindow?.postMessage({type:"dify-chatbot-config",payload:{isToggledByButton:!0,isDraggable:!!h.draggable}},i);else if("dify-chatbot-expand-change"===t.data.type)l=!l,(o=document.getElementById(y))&&(n=document.getElementById(m),l?(o.style.cssText=`
position: absolute;
display: flex;
flex-direction: column;
justify-content: space-between;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
z-index: 2147483640;
overflow: hidden;
user-select: none;
transition-property: width, height;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
`,n.style.display="none"):(o.style.cssText=a,n.style.display="bkock"),d());else if("dify-chatbot-iframe-close"===t.data.type){let e=document.getElementById(y);e&&(e.style.display="none",p("open"),l=!1)}}}),document.getElementById(m)||r()}else console.error(t+" is empty or token is not provided")}function p(e="open"){"open"===e?(document.getElementById("openIcon").style.display="block",document.getElementById("closeIcon").style.display="none"):"hidden"===e?(document.getElementById("openIcon").style.display="none",document.getElementById("closeIcon").style.display="none"):(document.getElementById("openIcon").style.display="none",document.getElementById("closeIcon").style.display="block")}function g(e){"Escape"===e.key&&(e=document.getElementById(y))&&"none"!==e.style.display&&(e.style.display="none",p("open"))}document.addEventListener("keydown",g),h?.dynamicScript?e():document.body.onload=e})();
Loading…
Cancel
Save