mirror of
https://github.com/langgenius/dify.git
synced 2026-04-27 02:36:29 +08:00
feat: variable preview
This commit is contained in:
parent
4a5c883988
commit
ae183b348c
@ -29,6 +29,11 @@ export type SensitiveWordAvoidance = EnabledOrDisabled & {
|
|||||||
config?: any
|
config?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum PreviewMode {
|
||||||
|
NewPage = 'new_page',
|
||||||
|
CurrentPage = 'current_page',
|
||||||
|
}
|
||||||
|
|
||||||
export type FileUpload = {
|
export type FileUpload = {
|
||||||
image?: EnabledOrDisabled & {
|
image?: EnabledOrDisabled & {
|
||||||
detail?: Resolution
|
detail?: Resolution
|
||||||
@ -56,6 +61,10 @@ export type FileUpload = {
|
|||||||
allowed_file_upload_methods?: TransferMethod[]
|
allowed_file_upload_methods?: TransferMethod[]
|
||||||
number_limits?: number
|
number_limits?: number
|
||||||
fileUploadConfig?: FileUploadConfigResponse
|
fileUploadConfig?: FileUploadConfigResponse
|
||||||
|
preview_config?: {
|
||||||
|
mode?: PreviewMode
|
||||||
|
file_type_list?: string[]
|
||||||
|
}
|
||||||
} & EnabledOrDisabled
|
} & EnabledOrDisabled
|
||||||
|
|
||||||
export type AnnotationReplyConfig = {
|
export type AnnotationReplyConfig = {
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import cn from '@/utils/classnames'
|
|||||||
import { ReplayLine } from '@/app/components/base/icons/src/vender/other'
|
import { ReplayLine } from '@/app/components/base/icons/src/vender/other'
|
||||||
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||||
import ImagePreview from '@/app/components/base/image-uploader/image-preview'
|
import ImagePreview from '@/app/components/base/image-uploader/image-preview'
|
||||||
|
import { PreviewMode } from '@/app/components/base/features/types'
|
||||||
|
|
||||||
type FileInAttachmentItemProps = {
|
type FileInAttachmentItemProps = {
|
||||||
file: FileEntity
|
file: FileEntity
|
||||||
@ -31,6 +32,7 @@ type FileInAttachmentItemProps = {
|
|||||||
onRemove?: (fileId: string) => void
|
onRemove?: (fileId: string) => void
|
||||||
onReUpload?: (fileId: string) => void
|
onReUpload?: (fileId: string) => void
|
||||||
canPreview?: boolean
|
canPreview?: boolean
|
||||||
|
previewMode?: PreviewMode
|
||||||
}
|
}
|
||||||
const FileInAttachmentItem = ({
|
const FileInAttachmentItem = ({
|
||||||
file,
|
file,
|
||||||
@ -39,6 +41,7 @@ const FileInAttachmentItem = ({
|
|||||||
onRemove,
|
onRemove,
|
||||||
onReUpload,
|
onReUpload,
|
||||||
canPreview,
|
canPreview,
|
||||||
|
previewMode = PreviewMode.CurrentPage,
|
||||||
}: FileInAttachmentItemProps) => {
|
}: FileInAttachmentItemProps) => {
|
||||||
const { id, name, type, progress, supportFileType, base64Url, url, isRemote } = file
|
const { id, name, type, progress, supportFileType, base64Url, url, isRemote } = file
|
||||||
const ext = getFileExtension(name, type, isRemote)
|
const ext = getFileExtension(name, type, isRemote)
|
||||||
@ -49,7 +52,13 @@ const FileInAttachmentItem = ({
|
|||||||
<div className={cn(
|
<div className={cn(
|
||||||
'flex h-12 items-center rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg pr-3 shadow-xs',
|
'flex h-12 items-center rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg pr-3 shadow-xs',
|
||||||
progress === -1 && 'border-state-destructive-border bg-state-destructive-hover',
|
progress === -1 && 'border-state-destructive-border bg-state-destructive-hover',
|
||||||
)}>
|
canPreview && previewMode === PreviewMode.NewPage && 'cursor-pointer',
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
if (canPreview && previewMode === PreviewMode.NewPage)
|
||||||
|
window.open(url || base64Url || '', '_blank')
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className='flex h-12 w-12 items-center justify-center'>
|
<div className='flex h-12 w-12 items-center justify-center'>
|
||||||
{
|
{
|
||||||
isImageFile && (
|
isImageFile && (
|
||||||
|
|||||||
@ -106,6 +106,8 @@ const FileUploaderInAttachment = ({
|
|||||||
showDownloadAction={false}
|
showDownloadAction={false}
|
||||||
onRemove={() => handleRemoveFile(file.id)}
|
onRemove={() => handleRemoveFile(file.id)}
|
||||||
onReUpload={() => handleReUploadFile(file.id)}
|
onReUpload={() => handleReUploadFile(file.id)}
|
||||||
|
canPreview={fileConfig.preview_config?.file_type_list?.includes(file.type)}
|
||||||
|
previewMode={fileConfig.preview_config?.mode}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ type SegmentedControlProps<T extends string | number | symbol> = {
|
|||||||
onChange: (value: T) => void
|
onChange: (value: T) => void
|
||||||
className?: string
|
className?: string
|
||||||
activeClassName?: string
|
activeClassName?: string
|
||||||
|
btnClassName?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const SegmentedControlVariants = cva(
|
const SegmentedControlVariants = cva(
|
||||||
@ -90,6 +91,7 @@ export const SegmentedControl = <T extends string | number | symbol>({
|
|||||||
padding,
|
padding,
|
||||||
activeState,
|
activeState,
|
||||||
activeClassName,
|
activeClassName,
|
||||||
|
btnClassName,
|
||||||
}: SegmentedControlProps<T>
|
}: SegmentedControlProps<T>
|
||||||
& VariantProps<typeof SegmentedControlVariants>
|
& VariantProps<typeof SegmentedControlVariants>
|
||||||
& VariantProps<typeof SegmentedControlItemVariants>
|
& VariantProps<typeof SegmentedControlItemVariants>
|
||||||
@ -115,6 +117,7 @@ export const SegmentedControl = <T extends string | number | symbol>({
|
|||||||
SegmentedControlItemVariants({ size, activeState: isSelected ? activeState : 'default' }),
|
SegmentedControlItemVariants({ size, activeState: isSelected ? activeState : 'default' }),
|
||||||
isSelected && activeClassName,
|
isSelected && activeClassName,
|
||||||
disabled && 'disabled',
|
disabled && 'disabled',
|
||||||
|
btnClassName,
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!isSelected)
|
if (!isSelected)
|
||||||
|
|||||||
@ -24,12 +24,16 @@ export type TextareaProps = {
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
destructive?: boolean
|
destructive?: boolean
|
||||||
styleCss?: CSSProperties
|
styleCss?: CSSProperties
|
||||||
|
onFocus?: () => void
|
||||||
|
onBlur?: () => void
|
||||||
} & React.TextareaHTMLAttributes<HTMLTextAreaElement> & VariantProps<typeof textareaVariants>
|
} & React.TextareaHTMLAttributes<HTMLTextAreaElement> & VariantProps<typeof textareaVariants>
|
||||||
|
|
||||||
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||||
({ className, value, onChange, disabled, size, destructive, styleCss, ...props }, ref) => {
|
({ className, value, onChange, disabled, size, destructive, styleCss, onFocus, onBlur, ...props }, ref) => {
|
||||||
return (
|
return (
|
||||||
<textarea
|
<textarea
|
||||||
|
onFocus={onFocus}
|
||||||
|
onBlur={onBlur}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
style={styleCss}
|
style={styleCss}
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@ -0,0 +1,141 @@
|
|||||||
|
import { useMemo } from 'react'
|
||||||
|
import SegmentIndexTag from '@/app/components/datasets/documents/detail/completed/common/segment-index-tag'
|
||||||
|
import Dot from '@/app/components/datasets/documents/detail/completed/common/dot'
|
||||||
|
import { PreviewSlice } from '@/app/components/datasets/formatted-text/flavours/preview-slice'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { formatNumber } from '@/utils/format'
|
||||||
|
|
||||||
|
enum QAItemType {
|
||||||
|
Question = 'question',
|
||||||
|
Answer = 'answer',
|
||||||
|
}
|
||||||
|
|
||||||
|
type QAItemProps = {
|
||||||
|
type: QAItemType
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const QAItem = (props: QAItemProps) => {
|
||||||
|
const { type, text } = props
|
||||||
|
return <div className="inline-flex items-start justify-start gap-1 self-stretch">
|
||||||
|
<div className="w-4 text-[13px] font-medium leading-5 text-text-tertiary">{type === QAItemType.Question ? 'Q' : 'A'}</div>
|
||||||
|
<div className="body-md-regular flex-1 text-text-secondary">{text}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ChunkType {
|
||||||
|
General = 'genaral',
|
||||||
|
Paragraph = 'paragraph',
|
||||||
|
FullDoc = 'full-doc',
|
||||||
|
QA = 'qa',
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChunkCardProps = {
|
||||||
|
type: ChunkType
|
||||||
|
content: string | string[] | QAChunk
|
||||||
|
positionId?: string | number
|
||||||
|
wordCount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChunkCard = (props: ChunkCardProps) => {
|
||||||
|
const { type, content, positionId, wordCount } = props
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const renderContent = () => {
|
||||||
|
// ChunkType.Paragraph && ChunkType.FullDoc
|
||||||
|
if (Array.isArray(content)) {
|
||||||
|
return content.map((child, index) => {
|
||||||
|
const indexForLabel = index + 1
|
||||||
|
return (
|
||||||
|
<PreviewSlice
|
||||||
|
key={child}
|
||||||
|
label={`C-${indexForLabel}`}
|
||||||
|
text={child}
|
||||||
|
tooltip={`Child-chunk-${indexForLabel} · ${child.length} Characters`}
|
||||||
|
labelInnerClassName='text-[10px] font-semibold align-bottom leading-7'
|
||||||
|
dividerClassName='leading-7'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChunkType.QA
|
||||||
|
if (typeof content === 'object') {
|
||||||
|
return <div className="flex flex-col gap-2">
|
||||||
|
<QAItem type={QAItemType.Question} text={(content as QAChunk).question} />
|
||||||
|
<QAItem type={QAItemType.Answer} text={(content as QAChunk).answer} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChunkType.General
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="inline-flex flex-col gap-1 self-stretch rounded-lg bg-components-panel-bg px-3 py-2.5">
|
||||||
|
{type !== ChunkType.FullDoc && <div className="inline-flex items-center justify-start gap-2">
|
||||||
|
<SegmentIndexTag
|
||||||
|
positionId={positionId}
|
||||||
|
labelPrefix={type === ChunkType.Paragraph ? 'Parent-Chunk' : 'Chunk'}
|
||||||
|
/>
|
||||||
|
<Dot />
|
||||||
|
<div className='system-xs-medium text-text-tertiary'>{formatNumber(wordCount)} {t('datasetDocuments.segment.characters', { count: wordCount })}</div>
|
||||||
|
</div>}
|
||||||
|
<div className="body-md-regular text-text-secondary">{renderContent()}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChunkInfo = {
|
||||||
|
general_chunks?: string[]
|
||||||
|
parent_child_chunks?: ParentChildChunk[]
|
||||||
|
parent_mode?: string
|
||||||
|
qa_chunks?: QAChunk[]
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParentChildChunk = {
|
||||||
|
child_contents: string[]
|
||||||
|
parent_content: string
|
||||||
|
parent_mode: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type QAChunk = {
|
||||||
|
question: string
|
||||||
|
answer: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChunkCardListProps = {
|
||||||
|
chunkInfo: ChunkInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChunkCardList = (props: ChunkCardListProps) => {
|
||||||
|
const { chunkInfo } = props
|
||||||
|
|
||||||
|
const chunkType = useMemo(() => {
|
||||||
|
if (chunkInfo?.general_chunks)
|
||||||
|
return ChunkType.General
|
||||||
|
|
||||||
|
if (chunkInfo?.parent_child_chunks)
|
||||||
|
return chunkInfo.parent_mode as ChunkType
|
||||||
|
|
||||||
|
return ChunkType.QA
|
||||||
|
}, [chunkInfo])
|
||||||
|
|
||||||
|
return <div className='flex grow flex-col gap-1'>
|
||||||
|
{(chunkInfo.general_chunks ?? chunkInfo.parent_child_chunks ?? chunkInfo?.qa_chunks ?? []).map((seg: string | ParentChildChunk | QAChunk, index: number) => {
|
||||||
|
const isParentChildMode = [ChunkType.Paragraph, ChunkType.FullDoc].includes(chunkType!)
|
||||||
|
let wordCount = 0
|
||||||
|
if (isParentChildMode)
|
||||||
|
wordCount = (seg as ParentChildChunk)?.parent_content?.length
|
||||||
|
else if (typeof seg === 'string')
|
||||||
|
wordCount = seg.length
|
||||||
|
else
|
||||||
|
wordCount = (seg as QAChunk)?.question?.length + (seg as QAChunk)?.answer?.length
|
||||||
|
|
||||||
|
return <ChunkCard
|
||||||
|
type={chunkType}
|
||||||
|
content={isParentChildMode ? (seg as ParentChildChunk).child_contents : (seg as string | QAChunk)}
|
||||||
|
wordCount={wordCount}
|
||||||
|
positionId={index + 1}
|
||||||
|
/>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@ -15,6 +15,8 @@ type CodeEditorProps = {
|
|||||||
editorWrapperClassName?: string
|
editorWrapperClassName?: string
|
||||||
readOnly?: boolean
|
readOnly?: boolean
|
||||||
hideTopMenu?: boolean
|
hideTopMenu?: boolean
|
||||||
|
onFocus?: () => void
|
||||||
|
onBlur?: () => void
|
||||||
} & React.HTMLAttributes<HTMLDivElement>
|
} & React.HTMLAttributes<HTMLDivElement>
|
||||||
|
|
||||||
const CodeEditor: FC<CodeEditorProps> = ({
|
const CodeEditor: FC<CodeEditorProps> = ({
|
||||||
@ -25,6 +27,8 @@ const CodeEditor: FC<CodeEditorProps> = ({
|
|||||||
readOnly = false,
|
readOnly = false,
|
||||||
hideTopMenu = false,
|
hideTopMenu = false,
|
||||||
className,
|
className,
|
||||||
|
onFocus,
|
||||||
|
onBlur,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
@ -45,6 +49,14 @@ const CodeEditor: FC<CodeEditorProps> = ({
|
|||||||
const handleEditorDidMount = useCallback((editor: any, monaco: any) => {
|
const handleEditorDidMount = useCallback((editor: any, monaco: any) => {
|
||||||
editorRef.current = editor
|
editorRef.current = editor
|
||||||
monacoRef.current = monaco
|
monacoRef.current = monaco
|
||||||
|
|
||||||
|
editor.onDidFocusEditorText(() => {
|
||||||
|
onFocus?.()
|
||||||
|
})
|
||||||
|
editor.onDidBlurEditorText(() => {
|
||||||
|
onBlur?.()
|
||||||
|
})
|
||||||
|
|
||||||
monaco.editor.defineTheme('light-theme', {
|
monaco.editor.defineTheme('light-theme', {
|
||||||
base: 'vs',
|
base: 'vs',
|
||||||
inherit: true,
|
inherit: true,
|
||||||
|
|||||||
@ -8,6 +8,8 @@ type SchemaEditorProps = {
|
|||||||
hideTopMenu?: boolean
|
hideTopMenu?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
readonly?: boolean
|
readonly?: boolean
|
||||||
|
onFocus?: () => void
|
||||||
|
onBlur?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const SchemaEditor: FC<SchemaEditorProps> = ({
|
const SchemaEditor: FC<SchemaEditorProps> = ({
|
||||||
@ -16,6 +18,8 @@ const SchemaEditor: FC<SchemaEditorProps> = ({
|
|||||||
hideTopMenu,
|
hideTopMenu,
|
||||||
className,
|
className,
|
||||||
readonly = false,
|
readonly = false,
|
||||||
|
onFocus,
|
||||||
|
onBlur,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
@ -25,6 +29,8 @@ const SchemaEditor: FC<SchemaEditorProps> = ({
|
|||||||
value={schema}
|
value={schema}
|
||||||
onUpdate={onUpdate}
|
onUpdate={onUpdate}
|
||||||
hideTopMenu={hideTopMenu}
|
hideTopMenu={hideTopMenu}
|
||||||
|
onFocus={onFocus}
|
||||||
|
onBlur={onBlur}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useDebounceFn } from 'ahooks'
|
import { useDebounceFn } from 'ahooks'
|
||||||
|
import { RiBracesLine, RiEyeLine } from '@remixicon/react'
|
||||||
import Textarea from '@/app/components/base/textarea'
|
import Textarea from '@/app/components/base/textarea'
|
||||||
|
import { Markdown } from '@/app/components/base/markdown'
|
||||||
import SchemaEditor from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor'
|
import SchemaEditor from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor'
|
||||||
import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader'
|
import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader'
|
||||||
import ErrorMessage from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/error-message'
|
import ErrorMessage from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/error-message'
|
||||||
@ -13,6 +16,7 @@ import {
|
|||||||
validateJSONSchema,
|
validateJSONSchema,
|
||||||
} from '@/app/components/workflow/variable-inspect/utils'
|
} from '@/app/components/workflow/variable-inspect/utils'
|
||||||
import { getProcessedFiles, getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
|
import { getProcessedFiles, getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
|
||||||
|
import { SegmentedControl } from '@/app/components/base/segmented-control'
|
||||||
import { JSON_SCHEMA_MAX_DEPTH } from '@/config'
|
import { JSON_SCHEMA_MAX_DEPTH } from '@/config'
|
||||||
import { TransferMethod } from '@/types/app'
|
import { TransferMethod } from '@/types/app'
|
||||||
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
||||||
@ -21,6 +25,84 @@ import type { VarInInspect } from '@/types/workflow'
|
|||||||
import { VarInInspectType } from '@/types/workflow'
|
import { VarInInspectType } from '@/types/workflow'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { useStore } from '@/app/components/workflow/store'
|
import { useStore } from '@/app/components/workflow/store'
|
||||||
|
import { ChunkCardList, type ChunkInfo } from '@/app/components/rag-pipeline/components/chunk-card-list'
|
||||||
|
import { PreviewMode } from '../../base/features/types'
|
||||||
|
|
||||||
|
enum ViewMode {
|
||||||
|
Code = 'code',
|
||||||
|
Preview = 'preview',
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ContentType {
|
||||||
|
Markdown = 'markdown',
|
||||||
|
Chunks = 'chunks',
|
||||||
|
}
|
||||||
|
|
||||||
|
type DisplayContentProps = {
|
||||||
|
type: ContentType
|
||||||
|
mdString?: string
|
||||||
|
jsonString?: string
|
||||||
|
readonly: boolean
|
||||||
|
handleTextChange?: (value: string) => void
|
||||||
|
handleEditorChange?: (value: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const DisplayContent = (props: DisplayContentProps) => {
|
||||||
|
const { type, mdString, jsonString, readonly, handleTextChange, handleEditorChange } = props
|
||||||
|
const [viewMode, setViewMode] = useState<ViewMode>(ViewMode.Code)
|
||||||
|
const [isFocused, setIsFocused] = useState(false)
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn('flex h-full flex-col rounded-[10px] bg-components-input-bg-normal', isFocused && 'bg-components-input-bg-active outline outline-1 outline-components-input-border-active')}>
|
||||||
|
<div className='flex shrink-0 items-center justify-between p-1'>
|
||||||
|
<div className='system-xs-semibold-uppercase flex items-center px-2 py-0.5 text-text-secondary'>
|
||||||
|
{type.toUpperCase()}
|
||||||
|
</div>
|
||||||
|
<SegmentedControl
|
||||||
|
options={[
|
||||||
|
{ value: ViewMode.Code, text: t('workflow.nodes.templateTransform.code'), Icon: RiBracesLine },
|
||||||
|
{ value: ViewMode.Preview, text: t('workflow.common.preview'), Icon: RiEyeLine },
|
||||||
|
]}
|
||||||
|
value={viewMode}
|
||||||
|
onChange={setViewMode}
|
||||||
|
size='small'
|
||||||
|
padding='with'
|
||||||
|
activeClassName='!text-text-accent-light-mode-only'
|
||||||
|
btnClassName='!pl-1.5 !pr-0.5 gap-[3px]'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-1 overflow-auto rounded-b-[10px] pb-1 pl-3 pr-1'>
|
||||||
|
{viewMode === ViewMode.Code && (
|
||||||
|
type === ContentType.Markdown
|
||||||
|
? <Textarea
|
||||||
|
readOnly={readonly}
|
||||||
|
disabled={readonly}
|
||||||
|
className='h-full border-none bg-transparent p-0 text-text-secondary hover:bg-transparent focus:bg-transparent focus:shadow-none'
|
||||||
|
value={mdString as any}
|
||||||
|
onChange={e => handleTextChange?.(e.target.value)}
|
||||||
|
onFocus={() => setIsFocused(true)}
|
||||||
|
onBlur={() => setIsFocused(false)}
|
||||||
|
/>
|
||||||
|
: <SchemaEditor
|
||||||
|
readonly={readonly}
|
||||||
|
className='overflow-y-auto bg-transparent'
|
||||||
|
hideTopMenu
|
||||||
|
schema={jsonString!}
|
||||||
|
onUpdate={handleEditorChange!}
|
||||||
|
onFocus={() => setIsFocused(true)}
|
||||||
|
onBlur={() => setIsFocused(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{viewMode === ViewMode.Preview && (
|
||||||
|
type === ContentType.Markdown
|
||||||
|
? <Markdown className='grow overflow-auto rounded-lg !bg-white px-4 py-3' content={(mdString ?? '') as string} />
|
||||||
|
: <ChunkCardList chunkInfo={JSON.parse(jsonString!) as ChunkInfo} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
currentVar: VarInInspect
|
currentVar: VarInInspect
|
||||||
@ -42,6 +124,13 @@ const ValueContent = ({
|
|||||||
const JSONEditorDisabled = currentVar.value_type === 'array[any]'
|
const JSONEditorDisabled = currentVar.value_type === 'array[any]'
|
||||||
const fileUploadConfig = useStore(s => s.fileUploadConfig)
|
const fileUploadConfig = useStore(s => s.fileUploadConfig)
|
||||||
|
|
||||||
|
const hasChunks = useMemo(() => {
|
||||||
|
return currentVar.value_type === 'object'
|
||||||
|
&& currentVar.value
|
||||||
|
&& typeof currentVar.value === 'object'
|
||||||
|
&& ['parent_child_chunks', 'general_chunks', 'qa_chunks'].some(key => key in currentVar.value)
|
||||||
|
}, [currentVar.value_type, currentVar.value])
|
||||||
|
|
||||||
const formatFileValue = (value: VarInInspect) => {
|
const formatFileValue = (value: VarInInspect) => {
|
||||||
if (value.value_type === 'file')
|
if (value.value_type === 'file')
|
||||||
return value.value ? getProcessedFilesFromResponse([value.value]) : []
|
return value.value ? getProcessedFilesFromResponse([value.value]) : []
|
||||||
@ -72,7 +161,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) => {
|
||||||
@ -170,7 +258,14 @@ const ValueContent = ({
|
|||||||
>
|
>
|
||||||
<div className={cn('grow')} style={{ height: `${editorHeight}px` }}>
|
<div className={cn('grow')} style={{ height: `${editorHeight}px` }}>
|
||||||
{showTextEditor && (
|
{showTextEditor && (
|
||||||
<Textarea
|
currentVar.value_type === 'string' ? (
|
||||||
|
<DisplayContent
|
||||||
|
type={ContentType.Markdown}
|
||||||
|
mdString={value as any}
|
||||||
|
readonly={textEditorDisabled}
|
||||||
|
handleTextChange={handleTextChange}
|
||||||
|
/>
|
||||||
|
) : <Textarea
|
||||||
readOnly={textEditorDisabled}
|
readOnly={textEditorDisabled}
|
||||||
disabled={textEditorDisabled}
|
disabled={textEditorDisabled}
|
||||||
className='h-full'
|
className='h-full'
|
||||||
@ -179,13 +274,20 @@ const ValueContent = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{showJSONEditor && (
|
{showJSONEditor && (
|
||||||
<SchemaEditor
|
hasChunks
|
||||||
readonly={JSONEditorDisabled}
|
? <DisplayContent
|
||||||
className='overflow-y-auto'
|
type={ContentType.Chunks}
|
||||||
hideTopMenu
|
jsonString={json ?? '{}'}
|
||||||
schema={json}
|
readonly={JSONEditorDisabled}
|
||||||
onUpdate={handleEditorChange}
|
handleEditorChange={handleEditorChange}
|
||||||
/>
|
/>
|
||||||
|
: <SchemaEditor
|
||||||
|
readonly={JSONEditorDisabled}
|
||||||
|
className='overflow-y-auto'
|
||||||
|
hideTopMenu
|
||||||
|
schema={json}
|
||||||
|
onUpdate={handleEditorChange}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{showFileEditor && (
|
{showFileEditor && (
|
||||||
<div className='max-w-[460px]'>
|
<div className='max-w-[460px]'>
|
||||||
@ -208,6 +310,10 @@ const ValueContent = ({
|
|||||||
allowed_file_upload_methods: [TransferMethod.local_file, TransferMethod.remote_url],
|
allowed_file_upload_methods: [TransferMethod.local_file, TransferMethod.remote_url],
|
||||||
number_limits: currentVar.value_type === 'file' ? 1 : fileUploadConfig?.workflow_file_upload_limit || 5,
|
number_limits: currentVar.value_type === 'file' ? 1 : fileUploadConfig?.workflow_file_upload_limit || 5,
|
||||||
fileUploadConfig,
|
fileUploadConfig,
|
||||||
|
preview_config: {
|
||||||
|
mode: PreviewMode.NewPage,
|
||||||
|
file_type_list: ['application/pdf'],
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
isDisabled={textEditorDisabled}
|
isDisabled={textEditorDisabled}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user