mirror of
https://github.com/langgenius/dify.git
synced 2026-03-24 07:47:56 +08:00
This commit is contained in:
@ -45,6 +45,13 @@ type ChatInputAreaProps = {
|
||||
theme?: Theme | null
|
||||
isResponding?: boolean
|
||||
disabled?: boolean
|
||||
/**
|
||||
* Controls whether pressing Enter sends the message.
|
||||
* - true (default): Enter sends, Shift+Enter inserts newline
|
||||
* - false: Enter inserts newline, Shift+Enter sends
|
||||
* Useful for CJK (Japanese/Korean/Chinese) IME users who expect Enter to insert newlines.
|
||||
*/
|
||||
sendOnEnter?: boolean
|
||||
}
|
||||
const ChatInputArea = ({
|
||||
readonly,
|
||||
@ -61,6 +68,7 @@ const ChatInputArea = ({
|
||||
theme,
|
||||
isResponding,
|
||||
disabled,
|
||||
sendOnEnter = true,
|
||||
}: ChatInputAreaProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useToastContext()
|
||||
@ -131,7 +139,14 @@ const ChatInputArea = ({
|
||||
}, 50)
|
||||
}
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) {
|
||||
// Determine if this key combo should trigger send:
|
||||
// sendOnEnter=true (default): Enter sends, Shift+Enter inserts newline
|
||||
// sendOnEnter=false: Shift+Enter sends, Enter inserts newline
|
||||
const isSendCombo = sendOnEnter
|
||||
? (e.key === 'Enter' && !e.shiftKey)
|
||||
: (e.key === 'Enter' && e.shiftKey)
|
||||
|
||||
if (isSendCombo && !e.nativeEvent.isComposing) {
|
||||
// if isComposing, exit
|
||||
if (isComposingRef.current)
|
||||
return
|
||||
|
||||
@ -75,6 +75,7 @@ export type ChatProps = {
|
||||
inputDisabled?: boolean
|
||||
sidebarCollapseState?: boolean
|
||||
hideAvatar?: boolean
|
||||
sendOnEnter?: boolean
|
||||
onHumanInputFormSubmit?: (formToken: string, formData: any) => Promise<void>
|
||||
getHumanInputNodeData?: (nodeID: string) => any
|
||||
}
|
||||
@ -119,6 +120,7 @@ const Chat: FC<ChatProps> = ({
|
||||
inputDisabled,
|
||||
sidebarCollapseState,
|
||||
hideAvatar,
|
||||
sendOnEnter,
|
||||
onHumanInputFormSubmit,
|
||||
getHumanInputNodeData,
|
||||
}) => {
|
||||
@ -363,6 +365,7 @@ const Chat: FC<ChatProps> = ({
|
||||
theme={themeBuilder?.theme}
|
||||
isResponding={isResponding}
|
||||
readonly={readonly}
|
||||
sendOnEnter={sendOnEnter}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@ -58,6 +58,15 @@ const ChatWrapper = () => {
|
||||
appSourceType,
|
||||
} = useEmbeddedChatbotContext()
|
||||
|
||||
// Read sendOnEnter from URL params (e.g., ?sendOnEnter=false)
|
||||
const sendOnEnter = useMemo(() => {
|
||||
if (typeof window === 'undefined')
|
||||
return true
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const param = urlParams.get('sendOnEnter')
|
||||
return param !== 'false'
|
||||
}, [])
|
||||
|
||||
const appConfig = useMemo(() => {
|
||||
const config = appParams || {}
|
||||
|
||||
@ -321,6 +330,7 @@ const ChatWrapper = () => {
|
||||
themeBuilder={themeBuilder}
|
||||
switchSibling={doSwitchSibling}
|
||||
inputDisabled={inputDisabled}
|
||||
sendOnEnter={sendOnEnter}
|
||||
questionIcon={
|
||||
initUserVariables?.avatar_url
|
||||
? (
|
||||
|
||||
@ -135,6 +135,11 @@
|
||||
config.baseUrl || `https://${config.isDev ? "dev." : ""}udify.app`;
|
||||
const targetOrigin = new URL(baseUrl).origin;
|
||||
|
||||
// Pass sendOnEnter config as URL parameter
|
||||
if (config.sendOnEnter === false) {
|
||||
params.set('sendOnEnter', 'false');
|
||||
}
|
||||
|
||||
// pre-check the length of the URL
|
||||
const iframeUrl = `${baseUrl}/chatbot/${config.token}?${params}`;
|
||||
// 1) CREATE the iframe immediately, so it can load in the background:
|
||||
|
||||
2
web/public/embed.min.js
vendored
2
web/public/embed.min.js
vendored
@ -48,7 +48,7 @@
|
||||
transition-property: width, height;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
`;async function embedChatbot(){let isDragging=false;if(!config||!config.token){console.error(`${configKey} is empty or token is not provided`);return}async function compressAndEncodeBase64(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))}async function getCompressedInputsFromConfig(){const inputs=config?.inputs||{};const compressedInputs={};await Promise.all(Object.entries(inputs).map(async([key,value])=>{compressedInputs[key]=await compressAndEncodeBase64(value)}));return compressedInputs}async function getCompressedSystemVariablesFromConfig(){const systemVariables=config?.systemVariables||{};const compressedSystemVariables={};await Promise.all(Object.entries(systemVariables).map(async([key,value])=>{compressedSystemVariables[`sys.${key}`]=await compressAndEncodeBase64(value)}));return compressedSystemVariables}async function getCompressedUserVariablesFromConfig(){const userVariables=config?.userVariables||{};const compressedUserVariables={};await Promise.all(Object.entries(userVariables).map(async([key,value])=>{compressedUserVariables[`user.${key}`]=await compressAndEncodeBase64(value)}));return compressedUserVariables}const params=new URLSearchParams({...await getCompressedInputsFromConfig(),...await getCompressedSystemVariablesFromConfig(),...await getCompressedUserVariablesFromConfig()});const baseUrl=config.baseUrl||`https://${config.isDev?"dev.":""}udify.app`;const targetOrigin=new URL(baseUrl).origin;const iframeUrl=`${baseUrl}/chatbot/${config.token}?${params}`;const preloadedIframe=createIframe();preloadedIframe.style.display="none";document.body.appendChild(preloadedIframe);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")}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}function resetIframePosition(){if(window.innerWidth<=640)return;const targetIframe=document.getElementById(iframeId);const targetButton=document.getElementById(buttonId);if(targetIframe&&targetButton){const buttonRect=targetButton.getBoundingClientRect();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"}else{targetIframe.style.bottom=`var(--${buttonId}-bottom, 1rem)`;targetIframe.style.top="unset"}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"}else{targetIframe.style.right=`var(--${buttonId}-right, 1rem)`;targetIframe.style.left="unset"}}}function toggleExpand(){isExpanded=!isExpanded;const targetIframe=document.getElementById(iframeId);if(!targetIframe)return;if(isExpanded){targetIframe.style.cssText=expandedIframeStyleText}else{targetIframe.style.cssText=originalIframeStyleText}resetIframePosition()}window.addEventListener("message",event=>{if(event.origin!==targetOrigin)return;const targetIframe=document.getElementById(iframeId);if(!targetIframe||event.source!==targetIframe.contentWindow)return;if(event.data.type==="dify-chatbot-iframe-ready"){targetIframe.contentWindow?.postMessage({type:"dify-chatbot-config",payload:{isToggledByButton:true,isDraggable:!!config.draggable}},targetOrigin)}if(event.data.type==="dify-chatbot-expand-change"){toggleExpand()}});function createButton(){const containerDiv=document.createElement("div");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)}else{containerDiv.style.cssText=value}}else if(typeof value==="function"){containerDiv.addEventListener(key.replace(/^on/,"").toLowerCase(),value)}else{containerDiv[key]=value}});containerDiv.id=buttonId;const styleSheet=document.createElement("style");document.head.appendChild(styleSheet);styleSheet.sheet.insertRule(`
|
||||
`;async function embedChatbot(){let isDragging=false;if(!config||!config.token){console.error(`${configKey} is empty or token is not provided`);return}async function compressAndEncodeBase64(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))}async function getCompressedInputsFromConfig(){const inputs=config?.inputs||{};const compressedInputs={};await Promise.all(Object.entries(inputs).map(async([key,value])=>{compressedInputs[key]=await compressAndEncodeBase64(value)}));return compressedInputs}async function getCompressedSystemVariablesFromConfig(){const systemVariables=config?.systemVariables||{};const compressedSystemVariables={};await Promise.all(Object.entries(systemVariables).map(async([key,value])=>{compressedSystemVariables[`sys.${key}`]=await compressAndEncodeBase64(value)}));return compressedSystemVariables}async function getCompressedUserVariablesFromConfig(){const userVariables=config?.userVariables||{};const compressedUserVariables={};await Promise.all(Object.entries(userVariables).map(async([key,value])=>{compressedUserVariables[`user.${key}`]=await compressAndEncodeBase64(value)}));return compressedUserVariables}const params=new URLSearchParams({...await getCompressedInputsFromConfig(),...await getCompressedSystemVariablesFromConfig(),...await getCompressedUserVariablesFromConfig()});const baseUrl=config.baseUrl||`https://${config.isDev?"dev.":""}udify.app`;const targetOrigin=new URL(baseUrl).origin;if(config.sendOnEnter===false){params.set("sendOnEnter","false")}const iframeUrl=`${baseUrl}/chatbot/${config.token}?${params}`;const preloadedIframe=createIframe();preloadedIframe.style.display="none";document.body.appendChild(preloadedIframe);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")}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}function resetIframePosition(){if(window.innerWidth<=640)return;const targetIframe=document.getElementById(iframeId);const targetButton=document.getElementById(buttonId);if(targetIframe&&targetButton){const buttonRect=targetButton.getBoundingClientRect();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"}else{targetIframe.style.bottom=`var(--${buttonId}-bottom, 1rem)`;targetIframe.style.top="unset"}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"}else{targetIframe.style.right=`var(--${buttonId}-right, 1rem)`;targetIframe.style.left="unset"}}}function toggleExpand(){isExpanded=!isExpanded;const targetIframe=document.getElementById(iframeId);if(!targetIframe)return;if(isExpanded){targetIframe.style.cssText=expandedIframeStyleText}else{targetIframe.style.cssText=originalIframeStyleText}resetIframePosition()}window.addEventListener("message",event=>{if(event.origin!==targetOrigin)return;const targetIframe=document.getElementById(iframeId);if(!targetIframe||event.source!==targetIframe.contentWindow)return;if(event.data.type==="dify-chatbot-iframe-ready"){targetIframe.contentWindow?.postMessage({type:"dify-chatbot-config",payload:{isToggledByButton:true,isDraggable:!!config.draggable}},targetOrigin)}if(event.data.type==="dify-chatbot-expand-change"){toggleExpand()}});function createButton(){const containerDiv=document.createElement("div");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)}else{containerDiv.style.cssText=value}}else if(typeof value==="function"){containerDiv.addEventListener(key.replace(/^on/,"").toLowerCase(),value)}else{containerDiv[key]=value}});containerDiv.id=buttonId;const styleSheet=document.createElement("style");document.head.appendChild(styleSheet);styleSheet.sheet.insertRule(`
|
||||
#${containerDiv.id} {
|
||||
position: fixed;
|
||||
bottom: var(--${containerDiv.id}-bottom, 1rem);
|
||||
|
||||
Reference in New Issue
Block a user