mirror of
https://github.com/langgenius/dify.git
synced 2026-04-28 11:56:55 +08:00
feat: debug show big data
This commit is contained in:
parent
f5033c5a0e
commit
2391e582f2
@ -15,6 +15,7 @@ type CodeEditorProps = {
|
|||||||
editorWrapperClassName?: string
|
editorWrapperClassName?: string
|
||||||
readOnly?: boolean
|
readOnly?: boolean
|
||||||
hideTopMenu?: boolean
|
hideTopMenu?: boolean
|
||||||
|
topContent?: React.ReactNode
|
||||||
} & React.HTMLAttributes<HTMLDivElement>
|
} & React.HTMLAttributes<HTMLDivElement>
|
||||||
|
|
||||||
const CodeEditor: FC<CodeEditorProps> = ({
|
const CodeEditor: FC<CodeEditorProps> = ({
|
||||||
@ -24,6 +25,7 @@ const CodeEditor: FC<CodeEditorProps> = ({
|
|||||||
editorWrapperClassName,
|
editorWrapperClassName,
|
||||||
readOnly = false,
|
readOnly = false,
|
||||||
hideTopMenu = false,
|
hideTopMenu = false,
|
||||||
|
topContent,
|
||||||
className,
|
className,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -127,6 +129,7 @@ const CodeEditor: FC<CodeEditorProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{topContent}
|
||||||
<div className={classNames('relative overflow-hidden', editorWrapperClassName)}>
|
<div className={classNames('relative overflow-hidden', editorWrapperClassName)}>
|
||||||
<Editor
|
<Editor
|
||||||
defaultLanguage='json'
|
defaultLanguage='json'
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React, { type FC } from 'react'
|
import React, { type FC } from 'react'
|
||||||
import CodeEditor from './code-editor'
|
import CodeEditor from './code-editor'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
import LargeDataAlert from '@/app/components/workflow/variable-inspect/large-data-alert'
|
||||||
|
|
||||||
type SchemaEditorProps = {
|
type SchemaEditorProps = {
|
||||||
schema: string
|
schema: string
|
||||||
@ -8,6 +9,7 @@ type SchemaEditorProps = {
|
|||||||
hideTopMenu?: boolean
|
hideTopMenu?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
readonly?: boolean
|
readonly?: boolean
|
||||||
|
isTruncated?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const SchemaEditor: FC<SchemaEditorProps> = ({
|
const SchemaEditor: FC<SchemaEditorProps> = ({
|
||||||
@ -16,6 +18,7 @@ const SchemaEditor: FC<SchemaEditorProps> = ({
|
|||||||
hideTopMenu,
|
hideTopMenu,
|
||||||
className,
|
className,
|
||||||
readonly = false,
|
readonly = false,
|
||||||
|
isTruncated,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
@ -25,6 +28,7 @@ const SchemaEditor: FC<SchemaEditorProps> = ({
|
|||||||
value={schema}
|
value={schema}
|
||||||
onUpdate={onUpdate}
|
onUpdate={onUpdate}
|
||||||
hideTopMenu={hideTopMenu}
|
hideTopMenu={hideTopMenu}
|
||||||
|
topContent={isTruncated && <LargeDataAlert className='mx-1 mb-3 mt-[-4px]' />}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
'use client'
|
||||||
|
import { RiInformation2Fill } from '@remixicon/react'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
downloadUrl?: string
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const LargeDataAlert: FC<Props> = ({
|
||||||
|
downloadUrl,
|
||||||
|
className,
|
||||||
|
}) => {
|
||||||
|
const isShowDownload = !!downloadUrl
|
||||||
|
const text = isShowDownload ? 'Large data - partial preview only' : 'Large data, read-only preview. Export to view all.'
|
||||||
|
return (
|
||||||
|
<div className={cn('flex h-8 items-center justify-between rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur px-2 shadow-xs', className)}>
|
||||||
|
<div className='flex h-full items-center space-x-1'>
|
||||||
|
<RiInformation2Fill className='size-4 text-text-accent' />
|
||||||
|
<div className='system-xs-regular text-text-primary'>{text}</div>
|
||||||
|
</div>
|
||||||
|
{isShowDownload && (
|
||||||
|
<div className='system-xs-medium-uppercase cursor-pointer text-text-accent'>Export</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default React.memo(LargeDataAlert)
|
||||||
@ -2,6 +2,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import {
|
import {
|
||||||
RiArrowGoBackLine,
|
RiArrowGoBackLine,
|
||||||
RiCloseLine,
|
RiCloseLine,
|
||||||
|
RiFileDownloadFill,
|
||||||
RiMenuLine,
|
RiMenuLine,
|
||||||
RiSparklingFill,
|
RiSparklingFill,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
@ -52,6 +53,13 @@ const Right = ({
|
|||||||
const bottomPanelWidth = useStore(s => s.bottomPanelWidth)
|
const bottomPanelWidth = useStore(s => s.bottomPanelWidth)
|
||||||
const setShowVariableInspectPanel = useStore(s => s.setShowVariableInspectPanel)
|
const setShowVariableInspectPanel = useStore(s => s.setShowVariableInspectPanel)
|
||||||
const setCurrentFocusNodeId = useStore(s => s.setCurrentFocusNodeId)
|
const setCurrentFocusNodeId = useStore(s => s.setCurrentFocusNodeId)
|
||||||
|
const isTruncated = currentNodeVar?.var.is_truncated
|
||||||
|
const fullContent = currentNodeVar?.var.full_content
|
||||||
|
// const isTruncated = true
|
||||||
|
// const fullContent = {
|
||||||
|
// size_bytes: 11289600,
|
||||||
|
// download_url: 'https://upload.dify.ai/files/222bc6e7-40bd-4433-9ba8-4b9ecda88b14/file-preview?timestamp=1754976824&nonce=d970eb39b119f76ec94a9b026f2825b3&sign=ltJO4vS0jrwxuBl4GU74E1Sg_Tia2Y4g2LoBoPh3970=&as_attachment=true',
|
||||||
|
// }
|
||||||
|
|
||||||
const {
|
const {
|
||||||
resetConversationVar,
|
resetConversationVar,
|
||||||
@ -183,7 +191,16 @@ const Right = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div title={currentNodeVar.var.name} className='system-sm-semibold truncate text-text-secondary'>{currentNodeVar.var.name}</div>
|
<div title={currentNodeVar.var.name} className='system-sm-semibold truncate text-text-secondary'>{currentNodeVar.var.name}</div>
|
||||||
<div className='system-xs-medium ml-1 shrink-0 text-text-tertiary'>{currentNodeVar.var.value_type}</div>
|
<div className='system-xs-medium ml-1 shrink-0 space-x-2 text-text-tertiary'>
|
||||||
|
<span>{currentNodeVar.var.value_type}</span>
|
||||||
|
{isTruncated && (
|
||||||
|
<>
|
||||||
|
<span>·</span>
|
||||||
|
<span>{((fullContent?.size_bytes || 0) / 1024 / 1024).toFixed(1)}MB</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -200,20 +217,32 @@ const Right = ({
|
|||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{currentNodeVar.var.edited && (
|
{isTruncated && (
|
||||||
|
<Tooltip popupContent={t('workflow.debug.variableInspect.exportToolTip')}>
|
||||||
|
<ActionButton>
|
||||||
|
<a
|
||||||
|
href={fullContent?.download_url}
|
||||||
|
target='_blank'
|
||||||
|
>
|
||||||
|
<RiFileDownloadFill className='size-4' />
|
||||||
|
</a>
|
||||||
|
</ActionButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
{!isTruncated && currentNodeVar.var.edited && (
|
||||||
<Badge>
|
<Badge>
|
||||||
<span className='ml-[2.5px] mr-[4.5px] h-[3px] w-[3px] rounded bg-text-accent-secondary'></span>
|
<span className='ml-[2.5px] mr-[4.5px] h-[3px] w-[3px] rounded bg-text-accent-secondary'></span>
|
||||||
<span className='system-2xs-semibold-uupercase'>{t('workflow.debug.variableInspect.edited')}</span>
|
<span className='system-2xs-semibold-uupercase'>{t('workflow.debug.variableInspect.edited')}</span>
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{currentNodeVar.var.edited && currentNodeVar.var.type !== VarInInspectType.conversation && (
|
{!isTruncated && currentNodeVar.var.edited && currentNodeVar.var.type !== VarInInspectType.conversation && (
|
||||||
<Tooltip popupContent={t('workflow.debug.variableInspect.reset')}>
|
<Tooltip popupContent={t('workflow.debug.variableInspect.reset')}>
|
||||||
<ActionButton onClick={resetValue}>
|
<ActionButton onClick={resetValue}>
|
||||||
<RiArrowGoBackLine className='h-4 w-4' />
|
<RiArrowGoBackLine className='h-4 w-4' />
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{currentNodeVar.var.edited && currentNodeVar.var.type === VarInInspectType.conversation && (
|
{!isTruncated && currentNodeVar.var.edited && currentNodeVar.var.type === VarInInspectType.conversation && (
|
||||||
<Tooltip popupContent={t('workflow.debug.variableInspect.resetConversationVar')}>
|
<Tooltip popupContent={t('workflow.debug.variableInspect.resetConversationVar')}>
|
||||||
<ActionButton onClick={handleClear}>
|
<ActionButton onClick={handleClear}>
|
||||||
<RiArrowGoBackLine className='h-4 w-4' />
|
<RiArrowGoBackLine className='h-4 w-4' />
|
||||||
@ -238,7 +267,7 @@ const Right = ({
|
|||||||
<Loading />
|
<Loading />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{currentNodeVar && !isValueFetching && <ValueContent currentVar={currentNodeVar.var} handleValueChange={handleValueChange} />}
|
{currentNodeVar && !isValueFetching && <ValueContent currentVar={currentNodeVar.var} handleValueChange={handleValueChange} isTruncated={!!isTruncated} />}
|
||||||
</div>
|
</div>
|
||||||
{isShowPromptGenerator && (
|
{isShowPromptGenerator && (
|
||||||
isCodeBlock
|
isCodeBlock
|
||||||
|
|||||||
@ -25,11 +25,13 @@ import cn from '@/utils/classnames'
|
|||||||
type Props = {
|
type Props = {
|
||||||
currentVar: VarInInspect
|
currentVar: VarInInspect
|
||||||
handleValueChange: (varId: string, value: any) => void
|
handleValueChange: (varId: string, value: any) => void
|
||||||
|
isTruncated: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const ValueContent = ({
|
const ValueContent = ({
|
||||||
currentVar,
|
currentVar,
|
||||||
handleValueChange,
|
handleValueChange,
|
||||||
|
isTruncated,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const contentContainerRef = useRef<HTMLDivElement>(null)
|
const contentContainerRef = useRef<HTMLDivElement>(null)
|
||||||
const errorMessageRef = useRef<HTMLDivElement>(null)
|
const errorMessageRef = useRef<HTMLDivElement>(null)
|
||||||
@ -72,7 +74,6 @@ const ValueContent = ({
|
|||||||
|
|
||||||
if (showFileEditor)
|
if (showFileEditor)
|
||||||
setFileValue(formatFileValue(currentVar))
|
setFileValue(formatFileValue(currentVar))
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [currentVar.id, currentVar.value])
|
}, [currentVar.id, currentVar.value])
|
||||||
|
|
||||||
const handleTextChange = (value: string) => {
|
const handleTextChange = (value: string) => {
|
||||||
@ -185,6 +186,7 @@ const ValueContent = ({
|
|||||||
hideTopMenu
|
hideTopMenu
|
||||||
schema={json}
|
schema={json}
|
||||||
onUpdate={handleEditorChange}
|
onUpdate={handleEditorChange}
|
||||||
|
isTruncated={isTruncated}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{showFileEditor && (
|
{showFileEditor && (
|
||||||
|
|||||||
@ -987,6 +987,7 @@ const translation = {
|
|||||||
envNode: 'Environment',
|
envNode: 'Environment',
|
||||||
chatNode: 'Conversation',
|
chatNode: 'Conversation',
|
||||||
systemNode: 'System',
|
systemNode: 'System',
|
||||||
|
exportToolTip: 'Export Variable as File',
|
||||||
},
|
},
|
||||||
lastOutput: 'Last Output',
|
lastOutput: 'Last Output',
|
||||||
relations: {
|
relations: {
|
||||||
|
|||||||
@ -987,6 +987,7 @@ const translation = {
|
|||||||
envNode: '环境变量',
|
envNode: '环境变量',
|
||||||
chatNode: '会话变量',
|
chatNode: '会话变量',
|
||||||
systemNode: '系统变量',
|
systemNode: '系统变量',
|
||||||
|
exportToolTip: '导出变量为文件',
|
||||||
},
|
},
|
||||||
lastOutput: '上次输出',
|
lastOutput: '上次输出',
|
||||||
relations: {
|
relations: {
|
||||||
|
|||||||
@ -378,6 +378,11 @@ export enum VarInInspectType {
|
|||||||
system = 'sys',
|
system = 'sys',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type FullContent = {
|
||||||
|
size_bytes: number
|
||||||
|
download_url: string
|
||||||
|
}
|
||||||
|
|
||||||
export type VarInInspect = {
|
export type VarInInspect = {
|
||||||
id: string
|
id: string
|
||||||
type: VarInInspectType
|
type: VarInInspectType
|
||||||
@ -388,6 +393,8 @@ export type VarInInspect = {
|
|||||||
value: any
|
value: any
|
||||||
edited: boolean
|
edited: boolean
|
||||||
visible: boolean
|
visible: boolean
|
||||||
|
is_truncated: boolean
|
||||||
|
full_content: FullContent
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NodeWithVar = {
|
export type NodeWithVar = {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user