feat: configurable Enter/Shift+Enter send behavior in embedded chat (#32295) (#32300)

This commit is contained in:
agent-steven
2026-03-10 12:44:11 +09:00
committed by GitHub
parent 504138bb23
commit 2a3cc2951b
5 changed files with 35 additions and 2 deletions

View File

@ -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

View File

@ -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}
/>
)
}

View File

@ -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
? (