mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 10:28:10 +08:00
Merge commit 'fb41b215' into sandboxed-agent-rebase
Made-with: Cursor # Conflicts: # .devcontainer/post_create_command.sh # api/commands.py # api/core/agent/cot_agent_runner.py # api/core/agent/fc_agent_runner.py # api/core/app/apps/workflow_app_runner.py # api/core/app/entities/queue_entities.py # api/core/app/entities/task_entities.py # api/core/workflow/workflow_entry.py # api/dify_graph/enums.py # api/dify_graph/graph/graph.py # api/dify_graph/graph_events/node.py # api/dify_graph/model_runtime/entities/message_entities.py # api/dify_graph/node_events/node.py # api/dify_graph/nodes/agent/agent_node.py # api/dify_graph/nodes/base/__init__.py # api/dify_graph/nodes/base/entities.py # api/dify_graph/nodes/base/node.py # api/dify_graph/nodes/llm/entities.py # api/dify_graph/nodes/llm/node.py # api/dify_graph/nodes/tool/tool_node.py # api/pyproject.toml # api/uv.lock # web/app/components/base/avatar/__tests__/index.spec.tsx # web/app/components/base/avatar/index.tsx # web/app/components/base/date-and-time-picker/time-picker/__tests__/index.spec.tsx # web/app/components/base/file-uploader/file-from-link-or-local/index.tsx # web/app/components/base/prompt-editor/index.tsx # web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx # web/app/components/header/account-dropdown/index.spec.tsx # web/app/components/share/text-generation/index.tsx # web/app/components/workflow/block-selector/tool/action-item.tsx # web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx # web/app/components/workflow/hooks/use-edges-interactions.ts # web/app/components/workflow/hooks/use-nodes-interactions.ts # web/app/components/workflow/index.tsx # web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx # web/app/components/workflow/nodes/http/components/key-value/key-value-edit/index.tsx # web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/email-item.tsx # web/app/components/workflow/nodes/loop/use-interactions.ts # web/contract/router.ts # web/env.ts # web/eslint-suppressions.json # web/package.json # web/pnpm-lock.yaml
This commit is contained in:
@ -1,24 +1,18 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import Editor, { loader } from '@monaco-editor/react'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import * as React from 'react'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
getFilesInLogs,
|
||||
} from '@/app/components/base/file-uploader/utils'
|
||||
import { ModernMonacoEditor } from '@/app/components/base/modern-monaco/modern-monaco-editor'
|
||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import { Theme } from '@/types/app'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { basePath } from '@/utils/var'
|
||||
import Base from '../base'
|
||||
import './style.css'
|
||||
|
||||
// load file from local instead of cdn https://github.com/suren-atoyan/monaco-react/issues/482
|
||||
if (typeof window !== 'undefined')
|
||||
loader.config({ paths: { vs: `${window.location.origin}${basePath}/vs` } })
|
||||
|
||||
const CODE_EDITOR_LINE_HEIGHT = 18
|
||||
|
||||
export type Props = {
|
||||
@ -76,15 +70,10 @@ const CodeEditor: FC<Props> = ({
|
||||
tip,
|
||||
footer,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [isFocus, setIsFocus] = React.useState(false)
|
||||
const [isMounted, setIsMounted] = React.useState(false)
|
||||
const minHeight = height || 200
|
||||
const [editorContentHeight, setEditorContentHeight] = useState(56)
|
||||
const { theme: appTheme } = useTheme()
|
||||
const valueRef = useRef(value)
|
||||
useEffect(() => {
|
||||
valueRef.current = value
|
||||
}, [value])
|
||||
|
||||
const fileList = useMemo(() => {
|
||||
if (typeof value === 'object')
|
||||
@ -111,19 +100,16 @@ const CodeEditor: FC<Props> = ({
|
||||
editorRef.current = editor
|
||||
resizeEditorToContent()
|
||||
|
||||
editor.onDidFocusEditorText(() => {
|
||||
setIsFocus(true)
|
||||
onFocus?.()
|
||||
})
|
||||
editor.onDidBlurEditorText(() => {
|
||||
setIsFocus(false)
|
||||
onBlur?.()
|
||||
})
|
||||
|
||||
monaco.editor.setTheme(appTheme === Theme.light ? 'light' : 'vs-dark') // Fix: sometimes not load the default theme
|
||||
|
||||
onMount?.(editor, monaco)
|
||||
setIsMounted(true)
|
||||
}
|
||||
|
||||
const handleEditorFocus = () => {
|
||||
setIsFocus(true)
|
||||
}
|
||||
|
||||
const handleEditorBlur = () => {
|
||||
setIsFocus(false)
|
||||
}
|
||||
|
||||
const outPutValue = (() => {
|
||||
@ -137,31 +123,23 @@ const CodeEditor: FC<Props> = ({
|
||||
}
|
||||
})()
|
||||
|
||||
const theme = useMemo(() => {
|
||||
if (appTheme === Theme.light)
|
||||
return 'light'
|
||||
return 'vs-dark'
|
||||
}, [appTheme])
|
||||
|
||||
const main = (
|
||||
<>
|
||||
{/* https://www.npmjs.com/package/@monaco-editor/react */}
|
||||
<Editor
|
||||
<ModernMonacoEditor
|
||||
// className='min-h-[100%]' // h-full
|
||||
// language={language === CodeLanguage.javascript ? 'javascript' : 'python'}
|
||||
language={languageMap[language] || 'javascript'}
|
||||
theme={isMounted ? theme : 'default-theme'} // sometimes not load the default theme
|
||||
value={outPutValue}
|
||||
loading={<span className="text-text-primary">Loading...</span>}
|
||||
readOnly={readOnly}
|
||||
onChange={handleEditorChange}
|
||||
onFocus={handleEditorFocus}
|
||||
onBlur={handleEditorBlur}
|
||||
onReady={handleEditorDidMount}
|
||||
loading={<span className="text-text-primary">{t('loading', { ns: 'common' })}</span>}
|
||||
// https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IEditorOptions.html
|
||||
options={{
|
||||
readOnly,
|
||||
domReadOnly: true,
|
||||
quickSuggestions: false,
|
||||
minimap: { enabled: false },
|
||||
lineNumbersMinChars: 1, // would change line num width
|
||||
wordWrap: 'on', // auto line wrap
|
||||
// lineNumbers: (num) => {
|
||||
// return <div>{num}</div>
|
||||
// }
|
||||
@ -171,7 +149,6 @@ const CodeEditor: FC<Props> = ({
|
||||
},
|
||||
stickyScroll: { enabled: false },
|
||||
}}
|
||||
onMount={handleEditorDidMount}
|
||||
/>
|
||||
{!outPutValue && !isFocus && <div className="pointer-events-none absolute left-[36px] top-0 text-[13px] font-normal leading-[18px] text-components-input-text-placeholder">{placeholder}</div>}
|
||||
</>
|
||||
|
||||
@ -61,12 +61,12 @@ const Editor: FC<Props> = ({
|
||||
const setShowInputFieldPanel = useStore(s => s.setShowInputFieldPanel)
|
||||
|
||||
return (
|
||||
<div className={cn(className, 'relative')}>
|
||||
<div className={cn(className, 'relative min-h-8')}>
|
||||
<>
|
||||
<PromptEditor
|
||||
key={`${instanceId ?? 'input-support-select-var'}-${readOnly ? 'ro' : 'rw'}`}
|
||||
instanceId={instanceId}
|
||||
className={cn(promptMinHeightClassName, '!leading-[18px]')}
|
||||
className={cn(promptMinHeightClassName, 'leading-[18px]')}
|
||||
placeholder={placeholder}
|
||||
placeholderClassName={placeholderClassName}
|
||||
value={value}
|
||||
|
||||
@ -36,6 +36,7 @@ const VariableLabel = ({
|
||||
)}
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
{...(isExceptionVariable ? { 'data-testid': 'exception-variable' } : {})}
|
||||
>
|
||||
{isShowNodeLabel && (
|
||||
<VariableNodeLabel
|
||||
|
||||
@ -59,9 +59,9 @@ const KeyValueList: FC<Props> = ({
|
||||
return (
|
||||
<div className="overflow-hidden rounded-lg border border-divider-regular">
|
||||
<div className={cn('flex h-7 items-center leading-7 text-text-tertiary system-xs-medium-uppercase')}>
|
||||
<div className={cn('h-full border-r border-divider-regular pl-3', isSupportFile ? 'w-[140px]' : 'w-1/2')}>{t(`${i18nPrefix}.key`, { ns: 'workflow' })}</div>
|
||||
{isSupportFile && <div className="h-full w-[70px] shrink-0 border-r border-divider-regular pl-3">{t(`${i18nPrefix}.type`, { ns: 'workflow' })}</div>}
|
||||
<div className={cn('h-full items-center justify-between pl-3 pr-1', isSupportFile ? 'grow' : 'w-1/2')}>{t(`${i18nPrefix}.value`, { ns: 'workflow' })}</div>
|
||||
<div className={cn('flex h-full items-center border-r border-divider-regular pl-3', isSupportFile ? 'w-[140px]' : 'w-1/2')}>{t(`${i18nPrefix}.key`, { ns: 'workflow' })}</div>
|
||||
{isSupportFile && <div className="flex h-full w-[70px] shrink-0 items-center border-r border-divider-regular pl-3">{t(`${i18nPrefix}.type`, { ns: 'workflow' })}</div>}
|
||||
<div className={cn('flex h-full items-center justify-between pl-3 pr-1', isSupportFile ? 'grow' : 'w-1/2')}>{t(`${i18nPrefix}.value`, { ns: 'workflow' })}</div>
|
||||
</div>
|
||||
{
|
||||
list.map((item, index) => (
|
||||
|
||||
@ -59,12 +59,12 @@ const InputItem: FC<Props> = ({
|
||||
}, [onRemove])
|
||||
|
||||
return (
|
||||
<div className={cn(className, 'hover:cursor-text hover:bg-state-base-hover', 'relative flex !h-[30px] items-center')}>
|
||||
<div className={cn(className, 'hover:cursor-text hover:bg-state-base-hover', 'relative flex')}>
|
||||
{(!readOnly)
|
||||
? (
|
||||
<Input
|
||||
instanceId={instanceId}
|
||||
className={cn(isFocus ? 'bg-components-input-bg-active' : 'bg-width', 'h-full w-0 grow px-3 py-1')}
|
||||
className={cn(isFocus ? 'bg-components-input-bg-active' : '', 'clamp group w-0 grow px-3 py-1')}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
readOnly={readOnly}
|
||||
@ -73,7 +73,6 @@ const InputItem: FC<Props> = ({
|
||||
onFocusChange={setIsFocus}
|
||||
placeholder={t('nodes.http.insertVarPlaceholder', { ns: 'workflow' })!}
|
||||
placeholderClassName="!leading-[21px]"
|
||||
promptMinHeightClassName="h-full"
|
||||
insertVarTipToLeft={insertVarTipToLeft}
|
||||
/>
|
||||
)
|
||||
@ -85,7 +84,7 @@ const InputItem: FC<Props> = ({
|
||||
{hasValue && (
|
||||
<Input
|
||||
instanceId={instanceId}
|
||||
className={cn(isFocus ? 'border-components-input-border-active bg-components-input-bg-active shadow-xs' : 'border-components-input-border-hover bg-components-input-bg-normal', 'h-full w-0 grow rounded-lg border px-3 py-[6px]')}
|
||||
className={cn(isFocus ? 'border-components-input-border-active bg-components-input-bg-active shadow-xs' : 'border-components-input-border-hover bg-components-input-bg-normal', 'clamp group h-full w-0 grow rounded-lg border px-3 py-[6px]')}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
readOnly={readOnly}
|
||||
|
||||
@ -3,7 +3,7 @@ import type { Member } from '@/models/common'
|
||||
import { RiCloseCircleFill, RiErrorWarningFill } from '@remixicon/react'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Avatar from '@/app/components/base/avatar'
|
||||
import { Avatar } from '@/app/components/base/avatar'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
@ -34,8 +34,8 @@ const EmailItem = ({
|
||||
{isError && (
|
||||
<RiErrorWarningFill className="h-4 w-4 text-text-destructive" />
|
||||
)}
|
||||
{!isError && <Avatar avatar={data.avatar_url} size={16} name={data.name || data.email} />}
|
||||
<div title={data.email} className="max-w-[500px] truncate text-text-primary system-xs-regular">
|
||||
{!isError && <Avatar avatar={data.avatar_url} size="xxs" name={data.name || data.email} />}
|
||||
<div title={data.email} className="system-xs-regular max-w-[500px] truncate text-text-primary">
|
||||
{email === data.email ? data.name : data.email}
|
||||
{email === data.email && <span className="text-text-tertiary system-xs-regular">{t('members.you', { ns: 'common' })}</span>}
|
||||
</div>
|
||||
|
||||
@ -4,7 +4,7 @@ import type { Recipient } from '@/app/components/workflow/nodes/human-input/type
|
||||
import type { Member } from '@/models/common'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Avatar from '@/app/components/base/avatar'
|
||||
import { Avatar } from '@/app/components/base/avatar'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
@ -65,7 +65,7 @@ const MemberList: FC<Props> = ({ searchValue, list, value, onSearchChange, onSel
|
||||
onSelect(account.id)
|
||||
}}
|
||||
>
|
||||
<Avatar className={cn(value.some(item => item.user_id === account.id) && 'opacity-50')} avatar={account.avatar_url} size={24} name={account.name} />
|
||||
<Avatar className={cn(value.some(item => item.user_id === account.id) && 'opacity-50')} avatar={account.avatar_url} size="sm" name={account.name} />
|
||||
<div className={cn('grow', value.some(item => item.user_id === account.id) && 'opacity-50')}>
|
||||
<div className="text-text-secondary system-sm-medium">
|
||||
{account.name}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
import type { FC } from 'react'
|
||||
import type { FormInputItem, UserAction } from '../types'
|
||||
import type { ButtonProps } from '@/app/components/base/button'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
@ -14,6 +13,7 @@ import { useStore } from '@/app/components/workflow/store'
|
||||
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
|
||||
import { Note, rehypeNotes, rehypeVariable, Variable } from './variable-in-markdown'
|
||||
|
||||
const NODE_ID_RE = /#([^#.]+)([.#])/g
|
||||
const i18nPrefix = 'nodes.humanInput'
|
||||
|
||||
type FormContentPreviewProps = {
|
||||
@ -47,25 +47,25 @@ const FormContentPreview: FC<FormContentPreviewProps> = ({
|
||||
>
|
||||
<div className="flex h-[26px] items-center justify-between px-4">
|
||||
<Badge uppercase className="border-text-accent-secondary text-text-accent-secondary">{t(`${i18nPrefix}.formContent.preview`, { ns: 'workflow' })}</Badge>
|
||||
<ActionButton onClick={onClose}><RiCloseLine className="w-5 text-text-tertiary" /></ActionButton>
|
||||
<ActionButton onClick={onClose}><span className="i-ri-close-line size-5 text-text-tertiary" /></ActionButton>
|
||||
</div>
|
||||
<div className="max-h-[calc(100vh-167px)] overflow-y-auto px-4">
|
||||
<Markdown
|
||||
content={content}
|
||||
rehypePlugins={[rehypeVariable, rehypeNotes]}
|
||||
customComponents={{
|
||||
variable: ({ node }: { node: { properties?: { [key: string]: string } } }) => {
|
||||
const path = node.properties?.['data-path'] as string
|
||||
variable: ({ node }) => {
|
||||
const path = String(node?.properties?.dataPath ?? '')
|
||||
let newPath = path
|
||||
if (path) {
|
||||
newPath = path.replace(/#([^#.]+)([.#])/g, (match, nodeId, sep) => {
|
||||
newPath = path.replace(NODE_ID_RE, (match, nodeId, sep) => {
|
||||
return `#${nodeName(nodeId)}${sep}`
|
||||
})
|
||||
}
|
||||
return <Variable path={newPath} />
|
||||
},
|
||||
section: ({ node }: { node: { properties?: { [key: string]: string } } }) => (() => {
|
||||
const name = node.properties?.['data-name'] as string
|
||||
section: ({ node }) => (() => {
|
||||
const name = String(node?.properties?.dataName ?? '')
|
||||
const input = formInputs.find(i => i.output_variable_name === name)
|
||||
if (!input) {
|
||||
return (
|
||||
|
||||
@ -24,7 +24,7 @@ export function rehypeVariable() {
|
||||
parts.push({
|
||||
type: 'element',
|
||||
tagName: 'variable',
|
||||
properties: { 'data-path': m[0].trim() },
|
||||
properties: { dataPath: m[0].trim() },
|
||||
children: [],
|
||||
})
|
||||
|
||||
@ -77,7 +77,7 @@ export function rehypeNotes() {
|
||||
parts.push({
|
||||
type: 'element',
|
||||
tagName: 'section',
|
||||
properties: { 'data-name': name },
|
||||
properties: { dataName: name },
|
||||
children: [],
|
||||
})
|
||||
|
||||
|
||||
@ -76,14 +76,14 @@ const ConditionValue = ({
|
||||
}, [isSelect, t, value])
|
||||
|
||||
return (
|
||||
<div className="flex h-6 items-center rounded-md bg-workflow-block-parma-bg px-1">
|
||||
<div className="flex flex-wrap items-center rounded-md bg-workflow-block-parma-bg">
|
||||
<VariableLabelInText
|
||||
className="w-0 grow"
|
||||
className="flex min-w-0 shrink-0 items-center border-none bg-transparent shadow-none"
|
||||
variables={variableSelector}
|
||||
nodeTitle={node?.data.title}
|
||||
nodeType={node?.data.type}
|
||||
isExceptionVariable={isException}
|
||||
notShowFullPath
|
||||
notShowFullPath={false}
|
||||
/>
|
||||
<div
|
||||
className="mx-1 shrink-0 text-xs font-medium text-text-primary"
|
||||
@ -93,7 +93,7 @@ const ConditionValue = ({
|
||||
</div>
|
||||
{
|
||||
!notHasValue && (
|
||||
<div className="shrink-[3] truncate text-xs text-text-secondary" title={formatValue}>{isSelect ? selectName : formatValue}</div>
|
||||
<div className="grow truncate px-1.5 text-xs leading-6 text-text-secondary" title={formatValue}>{isSelect ? selectName : formatValue}</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
import type { FC } from 'react'
|
||||
import { Editor } from '@monaco-editor/react'
|
||||
import { RiClipboardLine, RiIndentIncrease } from '@remixicon/react'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
import { useCallback, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ModernMonacoEditor } from '@/app/components/base/modern-monaco/modern-monaco-editor'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import { Theme } from '@/types/app'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type CodeEditorProps = {
|
||||
@ -35,54 +33,11 @@ const CodeEditor: FC<CodeEditorProps> = ({
|
||||
onBlur,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { theme } = useTheme()
|
||||
const monacoRef = useRef<any>(null)
|
||||
const editorRef = useRef<any>(null)
|
||||
const [isMounted, setIsMounted] = React.useState(false)
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (monacoRef.current) {
|
||||
if (theme === Theme.light)
|
||||
monacoRef.current.editor.setTheme('light-theme')
|
||||
else
|
||||
monacoRef.current.editor.setTheme('dark-theme')
|
||||
}
|
||||
}, [theme])
|
||||
|
||||
const handleEditorDidMount = useCallback((editor: any, monaco: any) => {
|
||||
const handleEditorReady = useCallback((editor: any) => {
|
||||
editorRef.current = editor
|
||||
monacoRef.current = monaco
|
||||
|
||||
editor.onDidFocusEditorText(() => {
|
||||
onFocus?.()
|
||||
})
|
||||
editor.onDidBlurEditorText(() => {
|
||||
onBlur?.()
|
||||
})
|
||||
|
||||
monaco.editor.defineTheme('light-theme', {
|
||||
base: 'vs',
|
||||
inherit: true,
|
||||
rules: [],
|
||||
colors: {
|
||||
'editor.background': '#00000000',
|
||||
'editor.lineHighlightBackground': '#00000000',
|
||||
'focusBorder': '#00000000',
|
||||
},
|
||||
})
|
||||
monaco.editor.defineTheme('dark-theme', {
|
||||
base: 'vs-dark',
|
||||
inherit: true,
|
||||
rules: [],
|
||||
colors: {
|
||||
'editor.background': '#00000000',
|
||||
'editor.lineHighlightBackground': '#00000000',
|
||||
'focusBorder': '#00000000',
|
||||
},
|
||||
})
|
||||
monaco.editor.setTheme('light-theme')
|
||||
setIsMounted(true)
|
||||
editor.getModel()?.updateOptions({ tabSize: 2 })
|
||||
}, [])
|
||||
|
||||
const formatJsonContent = useCallback(() => {
|
||||
@ -95,24 +50,6 @@ const CodeEditor: FC<CodeEditorProps> = ({
|
||||
onUpdate?.(value)
|
||||
}, [onUpdate])
|
||||
|
||||
const editorTheme = useMemo(() => {
|
||||
if (theme === Theme.light)
|
||||
return 'light-theme'
|
||||
return 'dark-theme'
|
||||
}, [theme])
|
||||
useEffect(() => {
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
editorRef.current?.layout()
|
||||
})
|
||||
|
||||
if (containerRef.current)
|
||||
resizeObserver.observe(containerRef.current)
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={cn('flex h-full flex-col overflow-hidden bg-components-input-bg-normal', hideTopMenu && 'pt-2', className)}>
|
||||
{!hideTopMenu && (
|
||||
@ -146,19 +83,17 @@ const CodeEditor: FC<CodeEditorProps> = ({
|
||||
)}
|
||||
{topContent}
|
||||
<div className={cn('relative overflow-hidden', editorWrapperClassName)}>
|
||||
<Editor
|
||||
defaultLanguage="json"
|
||||
theme={isMounted ? editorTheme : 'default-theme'} // sometimes not load the default theme
|
||||
<ModernMonacoEditor
|
||||
language="json"
|
||||
value={value}
|
||||
readOnly={readOnly}
|
||||
onChange={handleEditorChange}
|
||||
onMount={handleEditorDidMount}
|
||||
onReady={handleEditorReady}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
loading={<span className="text-text-primary">{t('loading', { ns: 'common' })}</span>}
|
||||
options={{
|
||||
readOnly,
|
||||
domReadOnly: true,
|
||||
minimap: { enabled: false },
|
||||
tabSize: 2,
|
||||
scrollBeyondLastLine: false,
|
||||
wordWrap: 'on',
|
||||
wrappingIndent: 'same',
|
||||
overviewRulerBorder: false,
|
||||
hideCursorInOverviewRuler: true,
|
||||
|
||||
@ -103,11 +103,12 @@ export const useNodeLoopInteractions = () => {
|
||||
handleNodeLoopRerender(parentId)
|
||||
}, [collaborativeWorkflow, handleNodeLoopRerender])
|
||||
|
||||
const handleNodeLoopChildrenCopy = useCallback((nodeId: string, newNodeId: string) => {
|
||||
const handleNodeLoopChildrenCopy = useCallback((nodeId: string, newNodeId: string, idMapping: Record<string, string>) => {
|
||||
const { nodes } = collaborativeWorkflow.getState()
|
||||
const childrenNodes = nodes.filter(n => n.parentId === nodeId && n.type !== CUSTOM_LOOP_START_NODE)
|
||||
const newIdMapping = { ...idMapping }
|
||||
|
||||
return childrenNodes.map((child, index) => {
|
||||
const copyChildren = childrenNodes.map((child, index) => {
|
||||
const childNodeType = child.data.type as BlockEnum
|
||||
const defaultValue = nodesMetaDataMap?.[childNodeType]?.defaultValue ?? {}
|
||||
const nodesWithSameType = nodes.filter(node => node.data.type === childNodeType)
|
||||
@ -133,8 +134,14 @@ export const useNodeLoopInteractions = () => {
|
||||
zIndex: LOOP_CHILDREN_Z_INDEX,
|
||||
})
|
||||
newNode.id = `${newNodeId}${newNode.id + index}`
|
||||
newIdMapping[child.id] = newNode.id
|
||||
return newNode
|
||||
})
|
||||
|
||||
return {
|
||||
copyChildren,
|
||||
newIdMapping,
|
||||
}
|
||||
}, [collaborativeWorkflow, nodesMetaDataMap])
|
||||
|
||||
return {
|
||||
|
||||
@ -174,7 +174,7 @@ const AddExtractParameter: FC<Props> = ({
|
||||
<Field title={t(`${i18nPrefix}.addExtractParameterContent.required`, { ns: 'workflow' })}>
|
||||
<>
|
||||
<div className="mb-1.5 text-xs font-normal leading-[18px] text-text-tertiary">{t(`${i18nPrefix}.addExtractParameterContent.requiredContent`, { ns: 'workflow' })}</div>
|
||||
<Switch size="l" value={param.required ?? false} onChange={handleParamChange('required')} />
|
||||
<Switch size="lg" value={param.required ?? false} onChange={handleParamChange('required')} />
|
||||
</>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
@ -96,7 +96,10 @@ const GenericTable: FC<GenericTableProps> = ({
|
||||
})
|
||||
|
||||
// If the last configured row has content, append a trailing empty row
|
||||
const lastHasContent = !isEmptyRow(data[data.length - 1])
|
||||
const lastRow = data.at(-1)
|
||||
if (!lastRow)
|
||||
return rows
|
||||
const lastHasContent = !isEmptyRow(lastRow)
|
||||
if (lastHasContent)
|
||||
rows.push({ row: { ...emptyRowData }, dataIndex: null, isVirtual: true })
|
||||
|
||||
@ -217,7 +220,7 @@ const GenericTable: FC<GenericTableProps> = ({
|
||||
<div
|
||||
key={column.key}
|
||||
className={cn(
|
||||
'h-full pl-3',
|
||||
'flex h-full items-center pl-3',
|
||||
column.width && column.width.startsWith('w-') ? 'shrink-0' : 'flex-1',
|
||||
column.width,
|
||||
// Add right border except for last column
|
||||
|
||||
Reference in New Issue
Block a user