diff --git a/.editorconfig b/.editorconfig index 374da0b5d2..8788a6816d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index aee36b3986..23e94ead45 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -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:-} diff --git a/web/app/(commonLayout)/apps/assets/link.svg b/web/app/(commonLayout)/apps/assets/link.svg index 2926c28b16..1301d343f6 100644 --- a/web/app/(commonLayout)/apps/assets/link.svg +++ b/web/app/(commonLayout)/apps/assets/link.svg @@ -1,3 +1,3 @@ - + diff --git a/web/app/(commonLayout)/apps/assets/right-arrow.svg b/web/app/(commonLayout)/apps/assets/right-arrow.svg index a2c1cedf95..7f9dee237a 100644 --- a/web/app/(commonLayout)/apps/assets/right-arrow.svg +++ b/web/app/(commonLayout)/apps/assets/right-arrow.svg @@ -1,3 +1,3 @@ - + diff --git a/web/app/components/app/overview/embedded/index.tsx b/web/app/components/app/overview/embedded/index.tsx index 691b727b8e..3f66e04f31 100644 --- a/web/app/components/app/overview/embedded/index.tsx +++ b/web/app/components/app/overview/embedded/index.tsx @@ -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
-
{OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeBuilder.theme?.primaryColor ?? '#1C64F2', isTestEnv)}
+
{OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeBuilder.theme?.primaryColor ?? '#298df0', isTestEnv)}
diff --git a/web/app/components/base/chat/chat-with-history/sidebar/index.tsx b/web/app/components/base/chat/chat-with-history/sidebar/index.tsx index c563b5dec3..a08a222f60 100644 --- a/web/app/components/base/chat/chat-with-history/sidebar/index.tsx +++ b/web/app/components/base/chat/chat-with-history/sidebar/index.tsx @@ -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) => {
- {/* powered by */} -
- {!appData?.custom_config?.remove_webapp_brand && ( -
-
{t('share.chat.poweredBy')}
- {appData?.custom_config?.replace_webapp_logo && ( - logo - )} - {!appData?.custom_config?.replace_webapp_logo && ( - - )} -
- )} -
{!!showConfirm && ( { if (isResponding) { notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') }) @@ -169,12 +173,12 @@ const ChatInputArea = ({ <>
-
+
{ - !isMultipleLine && operation + !isMultipleLine && !isMobile && operation }
{ @@ -225,7 +229,7 @@ const ChatInputArea = ({ }
{ - isMultipleLine && ( + (isMultipleLine || isMobile) && (
{operation}
) } diff --git a/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx b/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx index a06930c48f..a2947f2c5a 100644 --- a/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx +++ b/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx @@ -257,6 +257,7 @@ const ChatWrapper = () => { switchSibling={siblingMessageId => setTargetMessageId(siblingMessageId)} inputDisabled={inputDisabled} isMobile={isMobile} + chatContainerClassName="scrollbar-small" /> ) } diff --git a/web/app/components/base/chat/embedded-chatbot/header/index.tsx b/web/app/components/base/chat/embedded-chatbot/header/index.tsx index 49444d2d73..a4e80a4b96 100644 --- a/web/app/components/base/chat/embedded-chatbot/header/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/header/index.tsx @@ -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 = ({ }) => { const { t } = useTranslation() const { - appData, currentConversationId, inputsForms, } = useEmbeddedChatbotContext() @@ -74,29 +72,17 @@ const Header: FC = ({ }, 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 (
- {/* powered by */} -
- {!appData?.custom_config?.remove_webapp_brand && ( -
-
{t('share.chat.poweredBy')}
- {appData?.custom_config?.replace_webapp_logo && ( - logo - )} - {!appData?.custom_config?.replace_webapp_logo && ( - - )} -
- )} -
- {currentConversationId && ( - - )} { showToggleExpandButton && ( = ({ {currentConversationId && inputsForms.length > 0 && ( )} + {currentConversationId && isIframe && ( + + + + + + )}
) @@ -131,7 +126,7 @@ const Header: FC = ({ return (
diff --git a/web/app/components/base/chat/embedded-chatbot/header/style.scss b/web/app/components/base/chat/embedded-chatbot/header/style.scss new file mode 100644 index 0000000000..0c89e9bc0e --- /dev/null +++ b/web/app/components/base/chat/embedded-chatbot/header/style.scss @@ -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%; + } +} diff --git a/web/app/components/base/chat/embedded-chatbot/index.tsx b/web/app/components/base/chat/embedded-chatbot/index.tsx index 96dff67bf4..aa5a9ec0a2 100644 --- a/web/app/components/base/chat/embedded-chatbot/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/index.tsx @@ -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 = () => {
@@ -97,7 +94,7 @@ const Chatbot = () => { theme={themeBuilder?.theme} onCreateNewChat={handleNewConversation} /> -
+
{appChatListDataLoading && ( )} @@ -106,24 +103,6 @@ const Chatbot = () => { )}
- {/* powered by */} - {isMobile && ( -
- {!appData?.custom_config?.remove_webapp_brand && ( -
-
{t('share.chat.poweredBy')}
- {appData?.custom_config?.replace_webapp_logo && ( - logo - )} - {!appData?.custom_config?.replace_webapp_logo && ( - - )} -
- )} -
- )}
) } diff --git a/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts b/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts index d4d617d4b7..630867471a 100644 --- a/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts +++ b/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts @@ -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)}` diff --git a/web/app/components/base/icons/src/public/llm/Anthropic.tsx b/web/app/components/base/icons/src/public/llm/Anthropic.tsx index f5de0f5916..123b2807e8 100644 --- a/web/app/components/base/icons/src/public/llm/Anthropic.tsx +++ b/web/app/components/base/icons/src/public/llm/Anthropic.tsx @@ -13,7 +13,7 @@ const Icon = ( }: React.SVGProps & { ref?: React.RefObject>; }, -) => +) => Icon.displayName = 'Anthropic' diff --git a/web/app/components/base/loading/index.tsx b/web/app/components/base/loading/index.tsx index 2ae33108df..6883b211cb 100644 --- a/web/app/components/base/loading/index.tsx +++ b/web/app/components/base/loading/index.tsx @@ -11,10 +11,10 @@ const Loading = (
- - - - + + + + diff --git a/web/app/components/browser-initor.tsx b/web/app/components/browser-initor.tsx index f2f4b02dc0..d6a0328f08 100644 --- a/web/app/components/browser-initor.tsx +++ b/web/app/components/browser-initor.tsx @@ -46,7 +46,7 @@ Object.defineProperty(globalThis, 'sessionStorage', { const BrowserInitor = ({ children, }: { children: React.ReactNode }) => { - return children + return <>{children} } export default BrowserInitor diff --git a/web/app/components/header/app-back/index.tsx b/web/app/components/header/app-back/index.tsx index bfc1736ce8..e537f3c134 100644 --- a/web/app/components/header/app-back/index.tsx +++ b/web/app/components/header/app-back/index.tsx @@ -18,7 +18,7 @@ export default function AppBack({ curApp }: IAppBackProps) {
{curApp?.name} diff --git a/web/app/components/sentry-initor.tsx b/web/app/components/sentry-initor.tsx index 457a1cf7c7..d37b3cad31 100644 --- a/web/app/components/sentry-initor.tsx +++ b/web/app/components/sentry-initor.tsx @@ -23,7 +23,7 @@ const SentryInit = ({ }) } }, []) - return children + return <>{children} } export default SentryInit diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx index 75a1937b7f..196821136a 100644 --- a/web/app/components/share/text-generation/index.tsx +++ b/web/app/components/share/text-generation/index.tsx @@ -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 = ({ /> )}
- {/* powered by */} - {!customConfig?.remove_webapp_brand && ( -
-
{t('share.chat.poweredBy')}
- {customConfig?.replace_webapp_logo && ( - logo - )} - {!customConfig?.replace_webapp_logo && ( - - )} -
- )}
{/* Result */}
+ {/* eslint-disable-next-line ts/ban-ts-comment */} + {/* @ts-expect-error */} {children} diff --git a/web/app/styles/globals.css b/web/app/styles/globals.css index 52e36a2767..2027a945ac 100644 --- a/web/app/styles/globals.css +++ b/web/app/styles/globals.css @@ -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; + } + +} diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index d40d96356b..72f34d82bb 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -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', diff --git a/web/i18n/de-DE/common.ts b/web/i18n/de-DE/common.ts index 509f44708d..902983b588 100644 --- a/web/i18n/de-DE/common.ts +++ b/web/i18n/de-DE/common.ts @@ -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...', }, diff --git a/web/i18n/en-US/common.ts b/web/i18n/en-US/common.ts index 8756095075..51ae786d98 100644 --- a/web/i18n/en-US/common.ts +++ b/web/i18n/en-US/common.ts @@ -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', diff --git a/web/i18n/es-ES/common.ts b/web/i18n/es-ES/common.ts index 483949553e..d74b4d8c04 100644 --- a/web/i18n/es-ES/common.ts +++ b/web/i18n/es-ES/common.ts @@ -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', }, diff --git a/web/i18n/fa-IR/common.ts b/web/i18n/fa-IR/common.ts index 5ca55da25b..73487e344c 100644 --- a/web/i18n/fa-IR/common.ts +++ b/web/i18n/fa-IR/common.ts @@ -544,7 +544,7 @@ const translation = { vectorHash: 'هش بردار:', hitScore: 'امتیاز بازیابی:', }, - inputPlaceholder: 'با ربات صحبت کنید', + inputPlaceholder: 'لطفاً مشکل خود را به من بگویید، از Shift + Enter برای خط جدید استفاده کنید', thought: 'فکر', thinking: 'تفکر...', }, diff --git a/web/i18n/fr-FR/common.ts b/web/i18n/fr-FR/common.ts index a7fc9c671d..61cdbb0c0b 100644 --- a/web/i18n/fr-FR/common.ts +++ b/web/i18n/fr-FR/common.ts @@ -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', }, diff --git a/web/i18n/hi-IN/common.ts b/web/i18n/hi-IN/common.ts index dab3229fe2..7cc446a772 100644 --- a/web/i18n/hi-IN/common.ts +++ b/web/i18n/hi-IN/common.ts @@ -562,7 +562,7 @@ const translation = { vectorHash: 'वेक्टर हैश:', hitScore: 'पुनः प्राप्ति स्कोर:', }, - inputPlaceholder: 'बॉट से बात करें', + inputPlaceholder: 'कृपया अपनी समस्या के बारे में मुझे बताएं, नई पंक्ति के लिए Shift + Enter का उपयोग करें', thought: 'विचार', thinking: 'सोचते हुए...', }, diff --git a/web/i18n/it-IT/common.ts b/web/i18n/it-IT/common.ts index a413a9ef68..ff385552ba 100644 --- a/web/i18n/it-IT/common.ts +++ b/web/i18n/it-IT/common.ts @@ -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', }, diff --git a/web/i18n/ja-JP/common.ts b/web/i18n/ja-JP/common.ts index f2d7e0b4f0..a657606288 100644 --- a/web/i18n/ja-JP/common.ts +++ b/web/i18n/ja-JP/common.ts @@ -561,7 +561,7 @@ const translation = { vectorHash: 'ベクトルハッシュ:', hitScore: '検索スコア:', }, - inputPlaceholder: 'ボットと話す', + inputPlaceholder: 'お困りの問題について教えてください。新しい行では Shift + Enter を使用します', thought: '思考', thinking: '考え中...', }, diff --git a/web/i18n/ko-KR/common.ts b/web/i18n/ko-KR/common.ts index aff00be97a..778a268d99 100644 --- a/web/i18n/ko-KR/common.ts +++ b/web/i18n/ko-KR/common.ts @@ -536,7 +536,7 @@ const translation = { vectorHash: '벡터 해시:', hitScore: '검색 점수:', }, - inputPlaceholder: '봇과 대화', + inputPlaceholder: '문제에 대해 말씀해 주세요, 줄 바꿈을 위해 Shift + Enter 를 사용하세요', thought: '생각', thinking: '생각...', }, diff --git a/web/i18n/pl-PL/common.ts b/web/i18n/pl-PL/common.ts index b0ef026f5e..ee0ff6cce4 100644 --- a/web/i18n/pl-PL/common.ts +++ b/web/i18n/pl-PL/common.ts @@ -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...', }, diff --git a/web/i18n/pt-BR/common.ts b/web/i18n/pt-BR/common.ts index eb92d9ab1d..f6baa1c952 100644 --- a/web/i18n/pt-BR/common.ts +++ b/web/i18n/pt-BR/common.ts @@ -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', }, diff --git a/web/i18n/ro-RO/common.ts b/web/i18n/ro-RO/common.ts index e755d59354..cec00d1322 100644 --- a/web/i18n/ro-RO/common.ts +++ b/web/i18n/ro-RO/common.ts @@ -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', }, diff --git a/web/i18n/ru-RU/common.ts b/web/i18n/ru-RU/common.ts index de925d1742..7cc005bdc5 100644 --- a/web/i18n/ru-RU/common.ts +++ b/web/i18n/ru-RU/common.ts @@ -544,7 +544,7 @@ const translation = { vectorHash: 'Векторный хэш:', hitScore: 'Оценка совпадения:', }, - inputPlaceholder: 'Поговорить с ботом', + inputPlaceholder: 'Пожалуйста, расскажите мне о своей проблеме, используйте Shift + Enter для новой строки', thinking: 'Мыслящий...', thought: 'Мысль', }, diff --git a/web/i18n/sl-SI/app-debug.ts b/web/i18n/sl-SI/app-debug.ts index 8672fa5c58..fcd6a84232 100644 --- a/web/i18n/sl-SI/app-debug.ts +++ b/web/i18n/sl-SI/app-debug.ts @@ -1,3 +1,5 @@ +// eslint-disable-next-line ts/ban-ts-comment +// @ts-ignore const translation = { pageTitle: { line1: 'PROMPT', diff --git a/web/i18n/sl-SI/common.ts b/web/i18n/sl-SI/common.ts index ce80c8a086..bdf724a834 100644 --- a/web/i18n/sl-SI/common.ts +++ b/web/i18n/sl-SI/common.ts @@ -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', diff --git a/web/i18n/th-TH/common.ts b/web/i18n/th-TH/common.ts index fca1dc428c..daa059b289 100644 --- a/web/i18n/th-TH/common.ts +++ b/web/i18n/th-TH/common.ts @@ -539,7 +539,7 @@ const translation = { vectorHash: 'แฮชเวกเตอร์:', hitScore: 'คะแนนการดึงข้อมูล:', }, - inputPlaceholder: 'พูดคุยกับบอท', + inputPlaceholder: 'กรุณาบอกฉันเกี่ยวกับปัญหาของคุณ ใช้ Shift + Enter เพื่อขึ้นบรรทัดใหม่', thought: 'ความคิด', thinking: 'ความคิด ', }, diff --git a/web/i18n/tr-TR/common.ts b/web/i18n/tr-TR/common.ts index d66c226aa6..9fd25a6175 100644 --- a/web/i18n/tr-TR/common.ts +++ b/web/i18n/tr-TR/common.ts @@ -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ü...', }, diff --git a/web/i18n/uk-UA/common.ts b/web/i18n/uk-UA/common.ts index 7416e30594..34f788b719 100644 --- a/web/i18n/uk-UA/common.ts +++ b/web/i18n/uk-UA/common.ts @@ -541,7 +541,7 @@ const translation = { vectorHash: 'Хеш вектора:', hitScore: 'Оцінка звернення:', }, - inputPlaceholder: 'Поговоріть з ботом', + inputPlaceholder: 'Будь ласка, розкажіть мені про вашу проблему, використовуйте Shift + Enter для нового рядка', thought: 'Думка', thinking: 'Мислення...', }, diff --git a/web/i18n/vi-VN/common.ts b/web/i18n/vi-VN/common.ts index 2ce9c3c41a..de07925633 100644 --- a/web/i18n/vi-VN/common.ts +++ b/web/i18n/vi-VN/common.ts @@ -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ĩ...', }, diff --git a/web/i18n/zh-Hans/common.ts b/web/i18n/zh-Hans/common.ts index 22211a92aa..93599efc05 100644 --- a/web/i18n/zh-Hans/common.ts +++ b/web/i18n/zh-Hans/common.ts @@ -561,7 +561,7 @@ const translation = { vectorHash: '向量哈希:', hitScore: '召回得分:', }, - inputPlaceholder: '和机器人聊天', + inputPlaceholder: '请将您遇到的问题告诉我,使用 Shift + Enter 换行', thinking: '深度思考中...', thought: '已深度思考', resend: '重新发送', diff --git a/web/i18n/zh-Hant/common.ts b/web/i18n/zh-Hant/common.ts index cf15a00d61..d7ff8f96b3 100644 --- a/web/i18n/zh-Hant/common.ts +++ b/web/i18n/zh-Hant/common.ts @@ -540,7 +540,7 @@ const translation = { vectorHash: '向量雜湊:', hitScore: '召回得分:', }, - inputPlaceholder: '與 Bot 對話', + inputPlaceholder: '請將您遇到的問題告訴我,使用 Shift + Enter 換行', thinking: '思維。。。', thought: '思想', }, diff --git a/web/public/embed.js b/web/public/embed.js index 1efa541a88..59db8c9a75 100644 --- a/web/public/embed.js +++ b/web/public/embed.js @@ -6,13 +6,13 @@ // attention: This JavaScript script must be placed after the 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 = ` @@ -21,7 +21,7 @@ - `; + ` 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 } -})(); +})() diff --git a/web/public/embed.min.js b/web/public/embed.min.js index b2781ee47d..3d86a67bcd 100644 --- a/web/public/embed.min.js +++ b/web/public/embed.min.js @@ -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{"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{"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=` + `),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=` - `,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{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{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})(); \ No newline at end of file + `,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{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{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})(); \ No newline at end of file