feat: modify the embedded UI

pull/19680/head
Yooco 1 year ago
parent f6a4241a6f
commit 3f37b75ead

@ -49,7 +49,7 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({
if (customConfig)
document.title = `${site.title}`
else
document.title = `${site.title} - Powered by Dify`
document.title = `${site.title} - Powered by OCloud`
}
}, [site, customConfig, themeBuilder])

@ -28,6 +28,7 @@ import FeatureBar from '@/app/components/base/features/new-feature-panel/feature
import type { FileUpload } from '@/app/components/base/features/types'
import { TransferMethod } from '@/types/app'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import './style.scss'
type ChatInputAreaProps = {
showFeatureBar?: boolean
@ -69,6 +70,7 @@ const ChatInputArea = ({
} = useTextAreaHeight()
const [query, setQuery] = useState('')
const [showVoiceInput, setShowVoiceInput] = useState(false)
const [isFocus, setIsFocus] = useState(false)
const filesStore = useFileStore()
const {
handleDragFileEnter,
@ -169,70 +171,83 @@ const ChatInputArea = ({
/>
)
const handleFocus = () => {
setIsFocus(true)
}
const handleBlur = () => {
setIsFocus(false)
}
return (
<>
<div
className={cn(
'relative z-10 rounded-md border border-components-chat-input-border bg-components-panel-bg-blur pb-[9px] shadow-md',
'chat-input-box relative z-10 box-border rounded-md border border-components-chat-input-border bg-components-panel-bg 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',
isFocus && 'chat-input-box-focus',
)}
>
<div className='scrollbar-small relative max-h-[158px] overflow-y-auto overflow-x-hidden px-[9px] pt-[9px]'>
<FileListInChatInput fileConfig={visionConfig!} />
<div
ref={wrapperRef}
className='flex items-center justify-between'
>
<div className='relative flex w-full grow items-center'>
<div
ref={textValueRef}
className='body-lg-regular pointer-events-none invisible absolute h-auto w-auto whitespace-pre p-1 leading-6'
>
{query}
<div className="rounded-md bg-components-panel-bg pb-[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}
className='flex items-center justify-between'
>
<div className='relative flex w-full grow items-center'>
<div
ref={textValueRef}
className='body-lg-regular pointer-events-none invisible absolute h-auto w-auto whitespace-pre p-1 leading-6'
>
{query}
</div>
<Textarea
ref={ref => textareaRef.current = ref as any}
className={cn(
'body-lg-regular w-full resize-none bg-transparent p-1 leading-6 text-text-tertiary outline-none',
)}
style={{ scrollbarWidth: 'none' }}
placeholder={t('common.chat.inputPlaceholder') || ''}
autoFocus
minRows={1}
onResize={handleTextareaResize}
value={query}
onChange={(e) => {
setQuery(e.target.value)
setTimeout(handleTextareaResize, 0)
}}
onKeyDown={handleKeyDown}
onCompositionStart={handleCompositionStart}
onCompositionEnd={handleCompositionEnd}
onPaste={handleClipboardPasteFile}
onDragEnter={handleDragFileEnter}
onDragLeave={handleDragFileLeave}
onDragOver={handleDragFileOver}
onDrop={handleDropFile}
onFocus={handleFocus}
onBlur={handleBlur}
/>
</div>
<Textarea
ref={ref => textareaRef.current = ref as any}
className={cn(
'body-lg-regular w-full resize-none bg-transparent p-1 leading-6 text-text-tertiary outline-none',
)}
placeholder={t('common.chat.inputPlaceholder') || ''}
autoFocus
minRows={1}
onResize={handleTextareaResize}
value={query}
onChange={(e) => {
setQuery(e.target.value)
setTimeout(handleTextareaResize, 0)
}}
onKeyDown={handleKeyDown}
onCompositionStart={handleCompositionStart}
onCompositionEnd={handleCompositionEnd}
onPaste={handleClipboardPasteFile}
onDragEnter={handleDragFileEnter}
onDragLeave={handleDragFileLeave}
onDragOver={handleDragFileOver}
onDrop={handleDropFile}
/>
{
!isMultipleLine && !isMobile && operation
}
</div>
{
!isMultipleLine && !isMobile && operation
showVoiceInput && (
<VoiceInput
onCancel={() => setShowVoiceInput(false)}
onConverted={text => setQuery(text)}
/>
)
}
</div>
{
showVoiceInput && (
<VoiceInput
onCancel={() => setShowVoiceInput(false)}
onConverted={text => setQuery(text)}
/>
(isMultipleLine || isMobile) && (
<div className='px-[9px]'>{operation}</div>
)
}
</div>
{
(isMultipleLine || isMobile) && (
<div className='px-[9px]'>{operation}</div>
)
}
</div>
{showFeatureBar && <FeatureBar showFileUpload={showFileUpload} disabled={featureBarDisabled} onFeatureBarClick={onFeatureBarClick} />}
</>

@ -0,0 +1,25 @@
.chat-input-box {
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 0.3s ease-in-out;
background: linear-gradient(252deg, #df7499, #2b67e2);
z-index: -1;
border-radius: 0.375rem;
}
&.chat-input-box-focus {
position: relative;
padding: 1px;
border: 0;
&:before {
opacity: 1;
}
}
}

@ -1,12 +1,12 @@
import { ChevronRight } from '../../icons/src/vender/line/arrows'
export default function ContentSwitch({
count,
currentIndex,
prevDisabled,
nextDisabled,
switchSibling,
}: {
count,
currentIndex,
prevDisabled,
nextDisabled,
switchSibling,
}: {
count?: number
currentIndex?: number
prevDisabled: boolean
@ -14,26 +14,28 @@ export default function ContentSwitch({
switchSibling: (direction: 'prev' | 'next') => void
}) {
return (
count && count > 1 && currentIndex !== undefined && (
<div className="flex items-center justify-center pt-3.5 text-sm">
<button
className={`${prevDisabled ? 'opacity-30' : 'opacity-100'}`}
disabled={prevDisabled}
onClick={() => !prevDisabled && switchSibling('prev')}
>
<ChevronRight className="h-[14px] w-[14px] rotate-180 text-text-primary" />
</button>
<span className="px-2 text-xs text-text-primary">
<>
{count && count > 1 && currentIndex !== undefined && (
<div className="flex items-center justify-center pt-3.5 text-sm">
<button
className={`${prevDisabled ? 'opacity-30' : 'opacity-100'}`}
disabled={prevDisabled}
onClick={() => !prevDisabled && switchSibling('prev')}
>
<ChevronRight className="h-[14px] w-[14px] rotate-180 text-text-primary" />
</button>
<span className="px-2 text-xs text-text-primary">
{currentIndex + 1} / {count}
</span>
<button
className={`${nextDisabled ? 'opacity-30' : 'opacity-100'}`}
disabled={nextDisabled}
onClick={() => !nextDisabled && switchSibling('next')}
>
<ChevronRight className="h-[14px] w-[14px] text-text-primary" />
</button>
</div>
)
<button
className={`${nextDisabled ? 'opacity-30' : 'opacity-100'}`}
disabled={nextDisabled}
onClick={() => !nextDisabled && switchSibling('next')}
>
<ChevronRight className="h-[14px] w-[14px] text-text-primary" />
</button>
</div>
)}
</>
)
}

@ -138,11 +138,11 @@ const Chat: FC<ChatProps> = ({
if (chatContainerRef.current)
setWidth(document.body.clientWidth - (chatContainerRef.current?.clientWidth + 16) - 8)
if (chatContainerRef.current && chatFooterRef.current)
chatFooterRef.current.style.width = `${chatContainerRef.current.clientWidth}px`
// if (chatContainerRef.current && chatFooterRef.current)
// chatFooterRef.current.style.width = `${chatContainerRef.current.clientWidth}px`
if (chatContainerInnerRef.current && chatFooterInnerRef.current)
chatFooterInnerRef.current.style.width = `${chatContainerInnerRef.current.clientWidth}px`
// if (chatContainerInnerRef.current && chatFooterInnerRef.current)
// chatFooterInnerRef.current.style.width = `${chatContainerInnerRef.current.clientWidth}px`
}, [])
useEffect(() => {
@ -170,7 +170,7 @@ const Chat: FC<ChatProps> = ({
const resizeContainerObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const { blockSize } = entry.borderBoxSize[0]
chatContainerRef.current!.style.paddingBottom = `${blockSize}px`
chatContainerRef.current!.style.marginBottom = `${blockSize}px`
handleScrollToBottom()
}
})
@ -227,10 +227,10 @@ const Chat: FC<ChatProps> = ({
onAnnotationRemoved={onAnnotationRemoved}
onFeedback={onFeedback}
>
<div className='relative h-full'>
<div className='relative flex h-full flex-col'>
<div
ref={chatContainerRef}
className={cn('relative h-full overflow-y-auto overflow-x-hidden', chatContainerClassName)}
className={cn('relative h-full flex-1 overflow-y-auto overflow-x-hidden', chatContainerClassName)}
>
{chatNode}
<div
@ -274,12 +274,12 @@ const Chat: FC<ChatProps> = ({
</div>
</div>
<div
className={`absolute bottom-0 flex justify-center bg-chat-input-mask ${(hasTryToAsk || !noChatInput || !noStopResponding) && chatFooterClassName}`}
className={`absolute bottom-0 flex w-full justify-center bg-chat-input-mask ${(hasTryToAsk || !noChatInput || !noStopResponding) && chatFooterClassName}`}
ref={chatFooterRef}
>
<div
ref={chatFooterInnerRef}
className={cn('relative', chatFooterInnerClassName)}
className={cn('relative w-full', chatFooterInnerClassName)}
>
{
!noStopResponding && isResponding && (

@ -26,6 +26,7 @@ import { useChatContext } from './context'
type QuestionProps = {
item: ChatItem
enabelQuerstionIcon?: boolean
questionIcon?: ReactNode
theme: Theme | null | undefined
enableEdit?: boolean
@ -34,6 +35,7 @@ type QuestionProps = {
const Question: FC<QuestionProps> = ({
item,
enabelQuerstionIcon,
questionIcon,
theme,
enableEdit = true,
@ -77,7 +79,7 @@ const Question: FC<QuestionProps> = ({
return (
<div className='mb-2 flex justify-end pl-14 last:mb-0'>
<div className={cn('group relative mr-4 flex max-w-full items-start', isEditing && 'flex-1')}>
<div className={cn('group relative flex max-w-full items-start', isEditing && 'flex-1', !!enabelQuerstionIcon && 'mr-4')}>
<div className={cn('mr-2 gap-1', isEditing ? 'hidden' : 'flex')}>
<div className="
absolutegap-0.5 hidden rounded-[10px] border-[0.5px] border-components-actionbar-border
@ -140,15 +142,15 @@ const Question: FC<QuestionProps> = ({
</div>
<div className='mt-1 h-[18px]' />
</div>
<div className='h-10 w-10 shrink-0'>
{enabelQuerstionIcon ? <div className="h-10 w-10 shrink-0">
{
questionIcon || (
<div className='h-full w-full rounded-full border-[0.5px] border-black/5'>
<User className='h-full w-full' />
<div className="h-full w-full rounded-full border-[0.5px] border-black/5">
<User className="h-full w-full" />
</div>
)
}
</div>
</div> : null}
</div>
)
}

@ -110,9 +110,9 @@ const Header: FC<IHeaderProps> = ({
{currentConversationId && inputsForms.length > 0 && (
<ViewFormDropdown />
)}
{currentConversationId && isIframe && (
{isIframe && (
<Tooltip
popupContent={t('关闭')}
popupContent={t('common.operation.close')}
>
<ActionButton size='l' onClick={handleCloseIframe}>
<RiCloseLine className='h-[18px] w-[18px]' />
@ -166,6 +166,15 @@ const Header: FC<IHeaderProps> = ({
{currentConversationId && inputsForms.length > 0 && (
<ViewFormDropdown iconColor={theme?.colorPathOnHeader} />
)}
{isIframe && (
<Tooltip
popupContent={t('common.operation.close')}
>
<ActionButton size='l' onClick={handleCloseIframe}>
<RiCloseLine className='h-[18px] w-[18px]' />
</ActionButton>
</Tooltip>
)}
</div>
</div>
)

@ -44,7 +44,7 @@ const Chatbot = () => {
if (customConfig)
document.title = `${site.title}`
else
document.title = `${site.title} - Powered by Dify`
document.title = `${site.title} - Powered by OCloud`
}
}, [site, customConfig, themeBuilder])

@ -75,7 +75,7 @@ export type OnSend = {
(message: string, files: FileEntity[] | undefined, isRegenerate: boolean, lastAnswer?: ChatItem | null): void
}
export type OnRegenerate = (chatItem: ChatItem) => void
export type OnRegenerate = (chatItem: ChatItem, options?: { message: string, files?: FileEntity[] }) => void
export type Callback = {
onSuccess: () => void

@ -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 = 'User'

@ -426,7 +426,7 @@ const TextGeneration: FC<IMainProps> = ({
if (canReplaceLogo)
document.title = `${siteInfo.title}`
else
document.title = `${siteInfo.title} - Powered by Dify`
document.title = `${siteInfo.title} - Powered by OCloud`
}
}, [siteInfo?.title, canReplaceLogo])

@ -10,7 +10,7 @@ import './styles/globals.css'
import './styles/markdown.scss'
export const metadata = {
title: 'Dify',
title: 'OCloud AI',
}
export const viewport: Viewport = {

@ -155,7 +155,7 @@ export default combine(
'sonarjs/regex-complexity': 'warn',
// maintainability
'sonarjs/no-ignored-exceptions': 'off',
'sonarjs/no-commented-code': 'warn',
'sonarjs/no-commented-code': 'off',
'sonarjs/no-unused-vars': 'warn',
'sonarjs/prefer-single-boolean-return': 'warn',
'sonarjs/duplicates-in-character-class': 'off',

@ -6,7 +6,7 @@ const translation = {
},
webapp: {
title: 'WebApp Marke anpassen',
removeBrand: 'Entferne Powered by Dify',
removeBrand: 'Entferne Powered by OCloud',
changeLogo: 'Ändere Powered by Markenbild',
changeLogoTip: 'SVG oder PNG Format mit einer Mindestgröße von 40x40px',
},

@ -8,7 +8,7 @@ const translation = {
},
webapp: {
title: 'Customize WebApp brand',
removeBrand: 'Remove Powered by Dify',
removeBrand: 'Remove Powered by OCloud',
changeLogo: 'Change Powered by Brand Image',
changeLogoTip: 'SVG or PNG format with a minimum size of 40x40px',
},

@ -6,7 +6,7 @@ const translation = {
},
webapp: {
title: 'Personalizar marca de WebApp',
removeBrand: 'Eliminar Powered by Dify',
removeBrand: 'Eliminar Powered by OCloud',
changeLogo: 'Cambiar Imagen de Marca Powered by',
changeLogoTip: 'Formato SVG o PNG con un tamaño mínimo de 40x40px',
},

@ -6,7 +6,7 @@ const translation = {
},
webapp: {
title: 'سفارشی سازی برند وب اپ',
removeBrand: 'حذف "Powered by Dify"',
removeBrand: 'حذف "Powered by OCloud"',
changeLogo: 'تغییر تصویر برند "Powered by"',
changeLogoTip: 'فرمت SVG یا PNG با حداقل اندازه 40x40px',
},

@ -6,7 +6,7 @@ const translation = {
},
webapp: {
title: 'Personalizza il marchio WebApp',
removeBrand: 'Rimuovi Powered by Dify',
removeBrand: 'Rimuovi Powered by OCloud',
changeLogo: 'Cambia immagine del marchio Powered by',
changeLogoTip: 'Formato SVG o PNG con una dimensione minima di 40x40px',
},

@ -6,7 +6,7 @@ const translation = {
},
webapp: {
title: 'WebApp 브랜드 사용자 정의',
removeBrand: 'Powered by Dify 삭제',
removeBrand: 'Powered by OCloud 삭제',
changeLogo: 'Powered by 브랜드 이미지 변경',
changeLogoTip: '최소 크기 40x40px의 SVG 또는 PNG 형식',
},

@ -6,7 +6,7 @@ const translation = {
},
webapp: {
title: 'Personalizar marca do WebApp',
removeBrand: 'Remover Powered by Dify',
removeBrand: 'Remover Powered by OCloud',
changeLogo: 'Alterar Imagem da Marca Powered by',
changeLogoTip: 'Formato SVG ou PNG com tamanho mínimo de 40x40px',
},

@ -6,7 +6,7 @@ const translation = {
},
webapp: {
title: 'Personalizați marca WebApp',
removeBrand: 'Eliminați "Powered by Dify"',
removeBrand: 'Eliminați "Powered by OCloud"',
changeLogo: 'Schimbați imaginea mărcii "Powered by"',
changeLogoTip: 'Format SVG sau PNG cu o dimensiune minimă de 40x40px',
},

@ -6,7 +6,7 @@ const translation = {
},
webapp: {
title: 'Настроить бренд веб-приложения',
removeBrand: 'Удалить Powered by Dify',
removeBrand: 'Удалить Powered by OCloud',
changeLogo: 'Изменить изображение бренда Powered by',
changeLogoTip: 'Формат SVG или PNG с минимальным размером 40x40px',
},

@ -6,7 +6,7 @@ const translation = {
},
webapp: {
title: 'Prilagodi blagovno znamko spletne aplikacije',
removeBrand: 'Odstrani Powered by Dify',
removeBrand: 'Odstrani Powered by OCloud',
changeLogo: 'Spremeni sliko Powered by Brand',
changeLogoTip: 'Format SVG ali PNG z minimalno velikostjo 40x40px',
},

@ -6,7 +6,7 @@ const translation = {
},
webapp: {
title: 'WebApp markasını özelleştir',
removeBrand: 'Powered by Dify\'i kaldır',
removeBrand: 'Powered by OCloud\'i kaldır',
changeLogo: 'Powered by Brand Resmini Değiştir',
changeLogoTip: 'SVG veya PNG formatında, en az 40x40px boyutunda',
},

@ -6,7 +6,7 @@ const translation = {
},
webapp: {
title: 'Налаштувати бренд для WebApp',
removeBrand: 'Видалити Powered by Dify',
removeBrand: 'Видалити Powered by OCloud',
changeLogo: 'Змінити зображення бренду "Powered by"',
changeLogoTip: 'Формат SVG або PNG з мінімальним розміром 40x40 пікселів',
},

@ -8,7 +8,7 @@ const translation = {
},
webapp: {
title: '定制 WebApp 品牌',
removeBrand: '移除 Powered by Dify',
removeBrand: '移除 Powered by OCloud',
changeLogo: '更改 Powered by Brand 图片',
changeLogoTip: 'SVG 或 PNG 格式,最小尺寸为 40x40px',
},

@ -6,7 +6,7 @@ const translation = {
},
webapp: {
title: '定製 WebApp 品牌',
removeBrand: '移除 Powered by Dify',
removeBrand: '移除 Powered by OCloud',
changeLogo: '更改 Powered by Brand 圖片',
changeLogoTip: 'SVG 或 PNG 格式,最小尺寸為 40x40px',
},

@ -51,8 +51,8 @@
display: flex;
flex-direction: column;
justify-content: space-between;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
border: none;
@ -62,6 +62,7 @@
transition-property: width, height;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
transform-origin: right bottom;
`
// Main function to embed the chatbot
@ -187,9 +188,10 @@
targetButton.style.display = 'none'
} else {
targetIframe.style.cssText = originalIframeStyleText
targetButton.style.display = 'bkock'
targetButton.style.display = 'block'
resetIframePosition()
}
resetIframePosition()
}
window.addEventListener('message', (event) => {
@ -212,11 +214,13 @@
} 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
if(isExpanded) {
toggleExpand()
}
const targetIframe = document.getElementById(iframeId)
targetIframe.style.display = 'none'
}
}
})
@ -249,22 +253,59 @@
// Add styles for the button
const styleSheet = document.createElement('style')
document.head.appendChild(styleSheet)
styleSheet.sheet.insertRule(`
styleSheet.textContent = `
#${containerDiv.id} {
position: fixed;
bottom: var(--${containerDiv.id}-bottom, 1rem);
right: var(--${containerDiv.id}-right, 1rem);
left: var(--${containerDiv.id}-left, unset);
top: var(--${containerDiv.id}-top, unset);
width: var(--${containerDiv.id}-width, 48px);
height: var(--${containerDiv.id}-height, 48px);
width: var(--${containerDiv.id}-width, 40px);
height: var(--${containerDiv.id}-height, 40px);
border-radius: var(--${containerDiv.id}-border-radius, 25px);
background-color: var(--${containerDiv.id}-bg-color, #155EEF);
box-shadow: var(--${containerDiv.id}-box-shadow, rgba(0, 0, 0, 0.2) 0px 4px 8px 0px);
cursor: pointer;
z-index: 2147483647;
background: linear-gradient(90deg, #eb8698, #1366ec, #eb8698);
background-size: 200% 100%;
animation: chat-bg-flow 8s ease infinite;
}
@keyframes chat-bubble-float {
0%, 100% {
transform: translateY(0) rotateY(0);
opacity: 1;
}
5% { transform: translateY(0) rotateY(0); }
20% { transform: translateY(0) rotateY(90deg); }
25% { transform: translateY(0) rotateY(0); }
40% {
transform: translateY(-5px) scale(1.2) rotateY(0);
opacity: 0.9;
}
50% { transform: translateY(0) rotateY(0); }
100% { transform: translateY(0) rotateY(0); }
}
#openIcon {
display: inline-block;
animation: chat-bubble-float 4s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
transform-style: preserve-3d;
perspective: 1000px;
}
@keyframes chat-bg-flow {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
`)
`
// Create display div for the button icon
const displayDiv = document.createElement('div')

@ -1,11 +1,11 @@
(()=>{let t="difyChatbotConfig",m="dify-chatbot-bubble-button",y="dify-chatbot-bubble-window",h=window[t],l=!1,a=`
(()=>{let t="difyChatbotConfig",u="dify-chatbot-bubble-button",y="dify-chatbot-bubble-window",p=window[t],l=!1,c=`
position: absolute;
display: flex;
flex-direction: column;
justify-content: space-between;
top: unset;
right: var(--${m}-right, 1rem); /* Align with dify-chatbot-bubble-button. */
bottom: var(--${m}-bottom, 1rem); /* Align with dify-chatbot-bubble-button. */
right: var(--${u}-right, 1rem); /* Align with dify-chatbot-bubble-button. */
bottom: var(--${u}-bottom, 1rem); /* Align with dify-chatbot-bubble-button. */
left: unset;
width: 30rem;
max-width: calc(100vw - 2rem);
@ -18,41 +18,79 @@
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(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(`
`;async function e(){let m=!1;if(p&&p.token){var e=new URLSearchParams({...await(async()=>{var e=p?.inputs||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n[e]=await i(t)})),n})(),...await(async()=>{var e=p?.systemVariables||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n["sys."+e]=await i(t)})),n})()}),o=p.baseUrl||`https://${p.isDev?"dev.":""}udify.app`;let n=new URL(o).origin,t=`${o}/chatbot/${p.token}?`+e;o=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=y,e.src=t,e.style.cssText=c,e}function a(){var e,t,n;window.innerWidth<=640||(e=document.getElementById(y),t=document.getElementById(u),e&&t&&(t=t.getBoundingClientRect(),n=window.innerHeight/2,t.top+t.height/2<n?(e.style.top=`var(--${u}-bottom, 1rem)`,e.style.bottom="unset"):(e.style.bottom=`var(--${u}-bottom, 1rem)`,e.style.top="unset"),t.left+t.width/2<window.innerWidth/2?(e.style.left=`var(--${u}-right, 1rem)`,e.style.right="unset"):(e.style.right=`var(--${u}-right, 1rem)`,e.style.left="unset")))}function r(){l=!l;var e,t=document.getElementById(y);t&&(e=document.getElementById(u),l?(t.style.cssText=`
position: absolute;
display: flex;
flex-direction: column;
justify-content: space-between;
right: 0;
bottom: 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;
transform-origin: right bottom;
`,e.style.display="none"):(t.style.cssText=c,e.style.display="block",a()))}function d(){let n=document.createElement("div");Object.entries(p.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=u;var e=document.createElement("style"),e=(document.head.appendChild(e),e.textContent=`
#${n.id} {
position: fixed;
bottom: var(--${n.id}-bottom, 1rem);
right: var(--${n.id}-right, 1rem);
left: var(--${n.id}-left, unset);
top: var(--${n.id}-top, unset);
width: var(--${n.id}-width, 48px);
height: var(--${n.id}-height, 48px);
width: var(--${n.id}-width, 40px);
height: var(--${n.id}-height, 40px);
border-radius: var(--${n.id}-border-radius, 25px);
background-color: var(--${n.id}-bg-color, #155EEF);
box-shadow: var(--${n.id}-box-shadow, rgba(0, 0, 0, 0.2) 0px 4px 8px 0px);
cursor: pointer;
z-index: 2147483647;
background: linear-gradient(90deg, #eb8698, #1366ec, #eb8698);
background-size: 200% 100%;
animation: chat-bg-flow 8s ease infinite;
}
@keyframes chat-bubble-float {
0%, 100% {
transform: translateY(0) rotateY(0);
opacity: 1;
}
5% { transform: translateY(0) rotateY(0); }
20% { transform: translateY(0) rotateY(90deg); }
25% { transform: translateY(0) rotateY(0); }
40% {
transform: translateY(-5px) scale(1.2) rotateY(0);
opacity: 0.9;
}
50% { transform: translateY(0) rotateY(0); }
100% { transform: translateY(0) rotateY(0); }
}
#openIcon {
display: inline-block;
animation: chat-bubble-float 4s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
transform-style: preserve-3d;
perspective: 1000px;
}
`),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">
@keyframes chat-bg-flow {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
`,document.createElement("div"));function t(){var e;m||((e=document.getElementById(y))?(e.style.display="none"===e.style.display?"block":"none","none"===e.style.display?h("open"):h("close"),"none"===e.style.display?document.removeEventListener("keydown",b):document.addEventListener("keydown",b),a()):(n.appendChild(s()),a(),this.title="Exit (ESC)",h("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">
<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}),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})();
`,n.appendChild(e),document.body.appendChild(n),n.addEventListener("click",t),n.addEventListener("touchend",e=>{e.preventDefault(),t()},{passive:!1}),p.draggable){var d=n;var l=p.dragAxis||"both";let s,a,t,r;function o(e){m=!1,r=("touchstart"===e.type?(s=e.touches[0].clientX-d.offsetLeft,a=e.touches[0].clientY-d.offsetTop,t=e.touches[0].clientX,e.touches[0]):(s=e.clientX-d.offsetLeft,a=e.clientY-d.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(m=8<Math.abs(i)||8<Math.abs(o)?!0:m){d.style.transition="none",d.style.cursor="grabbing";i=document.getElementById(y);i&&(i.style.display="none",h("open"));let e,t;t="touchmove"===n.type?(e=n.touches[0].clientX-s,window.innerHeight-n.touches[0].clientY-a):(e=n.clientX-s,window.innerHeight-n.clientY-a);o=d.getBoundingClientRect(),i=window.innerWidth-o.width,n=window.innerHeight-o.height;"x"!==l&&"both"!==l||d.style.setProperty(`--${u}-left`,Math.max(0,Math.min(e,i))+"px"),"y"!==l&&"both"!==l||d.style.setProperty(`--${u}-bottom`,Math.max(0,Math.min(t,n))+"px")}}function c(){setTimeout(()=>{m=!1},0),d.style.transition="",d.style.cursor="pointer",document.removeEventListener("mousemove",i),document.removeEventListener("touchmove",i),document.removeEventListener("mouseup",c),document.removeEventListener("touchend",c)}d.addEventListener("mousedown",o),d.addEventListener("touchstart",o)}}o.style.display="none",document.body.appendChild(o),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===n){let e=document.getElementById(y);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:!!p.draggable}},n);else if("dify-chatbot-expand-change"===t.data.type)r();else if("dify-chatbot-iframe-close"===t.data.type&&e){h("open"),l&&r();let e=document.getElementById(y);e.style.display="none"}}}),document.getElementById(u)||d()}else console.error(t+" is empty or token is not provided")}function h(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 b(e){"Escape"===e.key&&(e=document.getElementById(y))&&"none"!==e.style.display&&(e.style.display="none",h("open"))}document.addEventListener("keydown",b),p?.dynamicScript?e():document.body.onload=e})();
Loading…
Cancel
Save