mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 18:08:07 +08:00
Merge remote-tracking branch 'origin/main' into feat/model-plugins-implementing
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
|
||||
? (
|
||||
|
||||
@ -6,13 +6,6 @@ import * as React from 'react'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
// z-index strategy (relies on root `isolation: isolate` in layout.tsx):
|
||||
// All overlay primitives (Tooltip / Popover / Dropdown / Select / Dialog / AlertDialog) — z-50
|
||||
// Overlays share the same z-index; DOM order handles stacking when multiple are open.
|
||||
// This ensures overlays inside an AlertDialog (e.g. a Tooltip on a dialog button) render
|
||||
// above the dialog backdrop instead of being clipped by it.
|
||||
// Toast — z-[99], always on top (defined in toast component)
|
||||
|
||||
export const AlertDialog = BaseAlertDialog.Root
|
||||
export const AlertDialogTrigger = BaseAlertDialog.Trigger
|
||||
export const AlertDialogTitle = BaseAlertDialog.Title
|
||||
@ -39,7 +32,7 @@ export function AlertDialogContent({
|
||||
<BaseAlertDialog.Backdrop
|
||||
{...backdropProps}
|
||||
className={cn(
|
||||
'fixed inset-0 z-50 bg-background-overlay',
|
||||
'fixed inset-0 z-[1002] bg-background-overlay',
|
||||
'transition-opacity duration-150 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0 motion-reduce:transition-none',
|
||||
overlayClassName,
|
||||
)}
|
||||
@ -47,7 +40,7 @@ export function AlertDialogContent({
|
||||
<BaseAlertDialog.Popup
|
||||
{...popupProps}
|
||||
className={cn(
|
||||
'fixed left-1/2 top-1/2 z-50 max-h-[calc(100vh-2rem)] w-[480px] max-w-[calc(100vw-2rem)] -translate-x-1/2 -translate-y-1/2 overflow-y-auto overscroll-contain rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg',
|
||||
'fixed left-1/2 top-1/2 z-[1002] max-h-[calc(100vh-2rem)] w-[480px] max-w-[calc(100vw-2rem)] -translate-x-1/2 -translate-y-1/2 overflow-y-auto overscroll-contain rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg',
|
||||
'transition-[transform,scale,opacity] duration-150 data-[ending-style]:scale-95 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0 motion-reduce:transition-none',
|
||||
className,
|
||||
)}
|
||||
|
||||
@ -74,7 +74,7 @@ function renderContextMenuPopup({
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
alignOffset={alignOffset}
|
||||
className={cn('z-50 outline-none', className)}
|
||||
className={cn('z-[1002] outline-none', className)}
|
||||
{...positionerProps}
|
||||
>
|
||||
<BaseContextMenu.Popup
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
'use client'
|
||||
|
||||
// z-index strategy (relies on root `isolation: isolate` in layout.tsx):
|
||||
// All overlay primitives (Tooltip / Popover / Dropdown / Select / Dialog) — z-50
|
||||
// z-index strategy (relies on root `isolation: isolate` in layout.tsx):
|
||||
// All base/ui/* overlay primitives — z-[1002]
|
||||
// Overlays share the same z-index; DOM order handles stacking when multiple are open.
|
||||
// This ensures overlays inside a Dialog (e.g. a Tooltip on a dialog button) render
|
||||
// above the dialog backdrop instead of being clipped by it.
|
||||
// Toast — z-[99], always on top (defined in toast component)
|
||||
// During migration, z-[1002] is chosen to sit above all legacy overlays
|
||||
// (Modal z-[60], PortalToFollowElem callers up to z-[1001]).
|
||||
// Once all legacy overlays are migrated, this can be reduced back to z-50.
|
||||
// Toast — z-[9999], always on top (defined in toast component)
|
||||
|
||||
import { Dialog as BaseDialog } from '@base-ui/react/dialog'
|
||||
import * as React from 'react'
|
||||
@ -57,7 +60,7 @@ export function DialogContent({
|
||||
<BaseDialog.Backdrop
|
||||
{...backdropProps}
|
||||
className={cn(
|
||||
'fixed inset-0 z-50 bg-background-overlay',
|
||||
'fixed inset-0 z-[1002] bg-background-overlay',
|
||||
'transition-opacity duration-150 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0 motion-reduce:transition-none',
|
||||
overlayClassName,
|
||||
backdropProps?.className,
|
||||
@ -65,7 +68,7 @@ export function DialogContent({
|
||||
/>
|
||||
<BaseDialog.Popup
|
||||
className={cn(
|
||||
'fixed left-1/2 top-1/2 z-50 max-h-[80dvh] w-[480px] max-w-[calc(100vw-2rem)] -translate-x-1/2 -translate-y-1/2 overflow-y-auto overscroll-contain rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-6 shadow-xl',
|
||||
'fixed left-1/2 top-1/2 z-[1002] max-h-[80dvh] w-[480px] max-w-[calc(100vw-2rem)] -translate-x-1/2 -translate-y-1/2 overflow-y-auto overscroll-contain rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-6 shadow-xl',
|
||||
'transition-[transform,scale,opacity] duration-150 data-[ending-style]:scale-95 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0 motion-reduce:transition-none',
|
||||
className,
|
||||
)}
|
||||
|
||||
@ -131,7 +131,7 @@ function renderDropdownMenuPopup({
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
alignOffset={alignOffset}
|
||||
className={cn('z-50 outline-none', className)}
|
||||
className={cn('z-[1002] outline-none', className)}
|
||||
{...positionerProps}
|
||||
>
|
||||
<Menu.Popup
|
||||
|
||||
@ -4,4 +4,4 @@ export const menuGroupLabelClassName = 'px-3 pb-0.5 pt-1 text-text-tertiary syst
|
||||
export const menuSeparatorClassName = 'my-1 h-px bg-divider-subtle'
|
||||
export const menuPopupBaseClassName = 'max-h-[var(--available-height)] overflow-y-auto overflow-x-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur py-1 text-sm text-text-secondary shadow-lg outline-none focus:outline-none focus-visible:outline-none backdrop-blur-[5px]'
|
||||
export const menuPopupAnimationClassName = 'origin-[var(--transform-origin)] transition-[transform,scale,opacity] data-[ending-style]:scale-95 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0 motion-reduce:transition-none'
|
||||
export const menuBackdropClassName = 'fixed inset-0 z-50 bg-transparent transition-opacity duration-150 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0 motion-reduce:transition-none'
|
||||
export const menuBackdropClassName = 'fixed inset-0 z-[1002] bg-transparent transition-opacity duration-150 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0 motion-reduce:transition-none'
|
||||
|
||||
@ -48,7 +48,7 @@ export function PopoverContent({
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
alignOffset={alignOffset}
|
||||
className={cn('z-50 outline-none', className)}
|
||||
className={cn('z-[1002] outline-none', className)}
|
||||
{...positionerProps}
|
||||
>
|
||||
<BasePopover.Popup
|
||||
|
||||
@ -115,7 +115,7 @@ export function SelectContent({
|
||||
sideOffset={sideOffset}
|
||||
alignOffset={alignOffset}
|
||||
alignItemWithTrigger={false}
|
||||
className={cn('z-50 outline-none', className)}
|
||||
className={cn('z-[1002] outline-none', className)}
|
||||
{...positionerProps}
|
||||
>
|
||||
<BaseSelect.Popup
|
||||
|
||||
@ -37,7 +37,7 @@ export function TooltipContent({
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
alignOffset={alignOffset}
|
||||
className={cn('z-50 outline-none', className)}
|
||||
className={cn('z-[1002] outline-none', className)}
|
||||
>
|
||||
<BaseTooltip.Popup
|
||||
className={cn(
|
||||
|
||||
@ -551,6 +551,7 @@ const Result: FC<IResultProps> = ({
|
||||
}))
|
||||
},
|
||||
onWorkflowPaused: ({ data: workflowPausedData }) => {
|
||||
tempMessageId = workflowPausedData.workflow_run_id
|
||||
const url = `/workflow/${workflowPausedData.workflow_run_id}/events`
|
||||
sseGet(
|
||||
url,
|
||||
|
||||
@ -45,8 +45,9 @@ const WorkflowOnboardingModal: FC<WorkflowOnboardingModalProps> = ({
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
||||
{/* TODO: reduce z-[1002] to match base/ui primitives after legacy overlay migration completes */}
|
||||
<DialogPortal>
|
||||
<div className="pointer-events-none fixed left-1/2 top-1/2 z-50 flex -translate-x-1/2 translate-y-[165px] items-center gap-1 text-text-quaternary body-xs-regular">
|
||||
<div className="pointer-events-none fixed left-1/2 top-1/2 z-[1002] flex -translate-x-1/2 translate-y-[165px] items-center gap-1 text-text-quaternary body-xs-regular">
|
||||
<span>{t('onboarding.escTip.press', { ns: 'workflow' })}</span>
|
||||
<ShortcutsName keys={[t('onboarding.escTip.key', { ns: 'workflow' })]} textColor="secondary" />
|
||||
<span>{t('onboarding.escTip.toDismiss', { ns: 'workflow' })}</span>
|
||||
|
||||
Reference in New Issue
Block a user