) => {
- const selectedOptionIndex = options.findIndex(option => option.value === value)
-
- return (
-
- {options.map((option, index) => {
- const { Icon, text, count, disabled } = option
- const isSelected = index === selectedOptionIndex
- const isNextSelected = index === selectedOptionIndex - 1
- const isLast = index === options.length - 1
- return (
-
- )
- })}
-
- )
-}
-
-export default React.memo(SegmentedControl)
diff --git a/web/app/components/develop/__tests__/code.spec.tsx b/web/app/components/develop/__tests__/code.spec.tsx
index e5eaebb600..34a1f22380 100644
--- a/web/app/components/develop/__tests__/code.spec.tsx
+++ b/web/app/components/develop/__tests__/code.spec.tsx
@@ -139,6 +139,7 @@ describe('code.tsx components', () => {
await waitFor(() => {
expect(screen.getByText('second content')).toBeInTheDocument()
})
+ expect(tab2).toHaveAttribute('aria-selected', 'true')
})
it('should use "Code" as default title when title not provided', () => {
@@ -329,7 +330,8 @@ describe('code.tsx components', () => {
fallback
,
)
- expect(screen.getByRole('tablist')).toBeInTheDocument()
+ expect(screen.getByRole('tablist')).toHaveClass('-mb-px', 'gap-4', 'bg-transparent')
+ expect(screen.getByRole('tab', { name: 'cURL' })).toHaveClass('data-active:text-emerald-400')
})
})
diff --git a/web/app/components/develop/code.tsx b/web/app/components/develop/code.tsx
index 86d57c806d..2ecd13f4ab 100644
--- a/web/app/components/develop/code.tsx
+++ b/web/app/components/develop/code.tsx
@@ -1,7 +1,12 @@
'use client'
import type { PropsWithChildren, ReactElement, ReactNode } from 'react'
-import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
import { cn } from '@langgenius/dify-ui/cn'
+import {
+ Tabs,
+ TabsList,
+ TabsPanel,
+ TabsTab,
+} from '@langgenius/dify-ui/tabs'
import {
Children,
createContext,
@@ -103,6 +108,11 @@ type CodeExample = {
code: string
}
+type CodeTab = {
+ title: string
+ value: string
+}
+
type ICodePanelProps = {
children?: React.ReactNode
tag?: string
@@ -142,12 +152,11 @@ function CodePanel({ tag, label, children, targetCode }: ICodePanelProps) {
type CodeGroupHeaderProps = {
title?: string
- tabTitles?: string[]
- selectedIndex?: number
+ tabs?: CodeTab[]
}
-function CodeGroupHeader({ title, tabTitles, selectedIndex }: CodeGroupHeaderProps) {
- const hasTabs = (tabTitles?.length ?? 0) > 1
+function CodeGroupHeader({ title, tabs }: CodeGroupHeaderProps) {
+ const hasTabs = (tabs?.length ?? 0) > 1
return (
@@ -157,18 +166,19 @@ function CodeGroupHeader({ title, tabTitles, selectedIndex }: CodeGroupHeaderPro
)}
{hasTabs && (
-
- {tabTitles!.map((tabTitle, tabIndex) => (
-
+ {tabs!.map(tab => (
+
- {tabTitle}
-
+ {tab.title}
+
))}
-
+
)}
)
@@ -176,19 +186,24 @@ function CodeGroupHeader({ title, tabTitles, selectedIndex }: CodeGroupHeaderPro
type ICodeGroupPanelsProps = PropsWithChildren<{
targetCode?: CodeExample[]
+ tabs?: CodeTab[]
[key: string]: any
}>
-function CodeGroupPanels({ children, targetCode, ...props }: ICodeGroupPanelsProps) {
- if ((targetCode?.length ?? 0) > 1) {
+function CodeGroupPanels({ children, targetCode, tabs, ...props }: ICodeGroupPanelsProps) {
+ if ((targetCode?.length ?? 0) > 1 && tabs) {
return (
-
- {targetCode!.map((code, index) => (
-
-
-
- ))}
-
+ <>
+ {targetCode!.map((code, index) => {
+ const tab = tabs[index]
+
+ return (
+
+
+
+ )
+ })}
+ >
)
}
@@ -201,18 +216,27 @@ function usePreventLayoutShift() {
useEffect(() => {
return () => {
- window.cancelAnimationFrame(rafRef.current)
+ if (rafRef.current)
+ window.cancelAnimationFrame(rafRef.current)
}
}, [])
return {
positionRef,
- preventLayoutShift(callback: () => {}) {
+ preventLayoutShift(callback: () => void) {
+ if (!positionRef.current) {
+ callback()
+ return
+ }
+
const initialTop = positionRef.current.getBoundingClientRect().top
callback()
rafRef.current = window.requestAnimationFrame(() => {
+ if (!positionRef.current)
+ return
+
const newTop = positionRef.current.getBoundingClientRect().top
window.scrollBy(0, newTop - initialTop)
})
@@ -220,27 +244,27 @@ function usePreventLayoutShift() {
}
}
-function useTabGroupProps(availableLanguages: string[]) {
- const [preferredLanguages, addPreferredLanguage] = useState([])
- const [selectedIndex, setSelectedIndex] = useState(0)
- const activeLanguage = [...(availableLanguages || [])].sort(
- (a, z) => preferredLanguages.indexOf(z) - preferredLanguages.indexOf(a),
- )[0]
- const languageIndex = availableLanguages?.indexOf(activeLanguage!) || 0
- const newSelectedIndex = languageIndex === -1 ? selectedIndex : languageIndex
- if (newSelectedIndex !== selectedIndex)
- setSelectedIndex(newSelectedIndex)
-
+function useTabGroupProps(tabValues: string[]) {
+ const [selectedValue, setSelectedValue] = useState(tabValues[0] ?? '')
const { positionRef, preventLayoutShift } = usePreventLayoutShift()
+ const value = tabValues.includes(selectedValue)
+ ? selectedValue
+ : tabValues[0] ?? ''
return {
- as: 'div',
ref: positionRef,
- selectedIndex,
- onChange: (newSelectedIndex: number) => {
- preventLayoutShift(() =>
- (addPreferredLanguage(availableLanguages[newSelectedIndex]) as any),
- )
+ value,
+ onValueChange: (newValue: string | number | null) => {
+ if (newValue == null)
+ return
+
+ const nextValue = String(newValue)
+ if (!tabValues.includes(nextValue))
+ return
+
+ preventLayoutShift(() => {
+ setSelectedValue(nextValue)
+ })
},
}
}
@@ -260,24 +284,35 @@ type CodeGroupProps = PropsWithChildren<{
export function CodeGroup({ children, title, targetCode, ...props }: CodeGroupProps) {
const examples = typeof targetCode === 'string' ? [{ code: targetCode }] as CodeExample[] : targetCode
- const tabTitles = examples?.map(({ title }) => title || 'Code') || []
- const tabGroupProps = useTabGroupProps(tabTitles)
- const hasTabs = tabTitles.length > 1
- const Container = hasTabs ? TabGroup : 'div'
- const containerProps = hasTabs ? tabGroupProps : {}
- const headerProps = hasTabs
- ? { selectedIndex: tabGroupProps.selectedIndex, tabTitles }
- : {}
+ const tabs = examples?.map(({ title }, index) => ({
+ title: title || 'Code',
+ value: String(index),
+ })) || []
+ const tabGroupProps = useTabGroupProps(tabs.map(tab => tab.value))
+ const hasTabs = tabs.length > 1
+ const content = (
+ <>
+
+ {children}
+ >
+ )
return (
-
-
- {children}
-
+ {hasTabs
+ ? (
+
+ {content}
+
+ )
+ : (
+
+ {content}
+
+ )}
)
}
diff --git a/web/app/components/workflow/nodes/llm/components/__tests__/panel-output-section.spec.tsx b/web/app/components/workflow/nodes/llm/components/__tests__/panel-output-section.spec.tsx
index 65ff728292..97d5a8eb27 100644
--- a/web/app/components/workflow/nodes/llm/components/__tests__/panel-output-section.spec.tsx
+++ b/web/app/components/workflow/nodes/llm/components/__tests__/panel-output-section.spec.tsx
@@ -33,7 +33,7 @@ vi.mock('@/app/components/workflow/nodes/_base/components/output-vars', () => ({
vi.mock('../structure-output', () => ({
__esModule: true,
- default: (props: { className?: string, value?: StructuredOutput, onChange: (value: StructuredOutput) => void }) => {
+ StructureOutput: (props: { className?: string, value?: StructuredOutput, onChange: (value: StructuredOutput) => void }) => {
mockStructureOutput(props)
return structured-output
},
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx
index 66ea3bfc59..fa5166a06f 100644
--- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx
+++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx
@@ -1,8 +1,6 @@
-import type { FC } from 'react'
import type { SchemaRoot } from '../../types'
-import * as React from 'react'
import Modal from '../../../../../base/modal'
-import JsonSchemaConfig from './json-schema-config'
+import { JsonSchemaConfig } from './json-schema-config'
type JsonSchemaConfigModalProps = {
isShow: boolean
@@ -11,12 +9,12 @@ type JsonSchemaConfigModalProps = {
onClose: () => void
}
-const JsonSchemaConfigModal: FC = ({
+export function JsonSchemaConfigModal({
isShow,
defaultSchema,
onSave,
onClose,
-}) => {
+}: JsonSchemaConfigModalProps) {
return (
= ({
)
}
-
-export default JsonSchemaConfigModal
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx
index 96c508b48f..c8dbf3ab90 100644
--- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx
+++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx
@@ -1,13 +1,12 @@
-import type { FC } from 'react'
import type { SchemaRoot } from '../../types'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { toast } from '@langgenius/dify-ui/toast'
-import { useCallback, useState } from 'react'
+import { ToggleGroup, ToggleGroupItem } from '@langgenius/dify-ui/toggle-group'
+import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import Divider from '@/app/components/base/divider'
import { JSON_SCHEMA_MAX_DEPTH } from '@/config'
-import { SegmentedControl } from '../../../../../base/segmented-control'
import { Type } from '../../types'
import {
checkJsonSchemaDepth,
@@ -35,11 +34,15 @@ enum SchemaView {
JsonSchema = 'jsonSchema',
}
-const TimelineViewIcon: FC<{ className?: string }> = ({ className }) => {
+type IconProps = {
+ className?: string
+}
+
+function TimelineViewIcon({ className }: IconProps) {
return
}
-const BracesIcon: FC<{ className?: string }> = ({ className }) => {
+function BracesIcon({ className }: IconProps) {
return
}
@@ -55,13 +58,13 @@ const DEFAULT_SCHEMA: SchemaRoot = {
additionalProperties: false,
}
-const JsonSchemaConfig: FC = ({
+function JsonSchemaConfigContent({
defaultSchema,
onSave,
onClose,
-}) => {
+}: JsonSchemaConfigProps) {
const { t } = useTranslation()
- const [currentTab, setCurrentTab] = useState(SchemaView.VisualEditor)
+ const [currentTab, setCurrentTab] = useState([SchemaView.VisualEditor])
const [jsonSchema, setJsonSchema] = useState(defaultSchema || DEFAULT_SCHEMA)
const [json, setJson] = useState(() => JSON.stringify(jsonSchema, null, 2))
const [btnWidth, setBtnWidth] = useState(0)
@@ -73,15 +76,16 @@ const JsonSchemaConfig: FC = ({
const setIsAddingNewField = useVisualEditorStore(state => state.setIsAddingNewField)
const setHoveringProperty = useVisualEditorStore(state => state.setHoveringProperty)
const { emit } = useMittContext()
+ const selectedTab = currentTab[0] ?? SchemaView.VisualEditor
- const updateBtnWidth = useCallback((width: number) => {
+ function updateBtnWidth(width: number) {
setBtnWidth(width + 32)
- }, [])
+ }
- const handleTabChange = useCallback((value: SchemaView) => {
- if (currentTab === value)
+ function handleTabChange(value: SchemaView) {
+ if (selectedTab === value)
return
- if (currentTab === SchemaView.JsonSchema) {
+ if (selectedTab === SchemaView.JsonSchema) {
try {
const schema = JSON.parse(json)
setParseError(null)
@@ -112,41 +116,41 @@ const JsonSchemaConfig: FC = ({
return
}
}
- else if (currentTab === SchemaView.VisualEditor) {
+ else if (selectedTab === SchemaView.VisualEditor) {
if (advancedEditing || isAddingNewField)
emit('quitEditing', { callback: (backup: SchemaRoot) => setJson(JSON.stringify(backup || jsonSchema, null, 2)) })
else
setJson(JSON.stringify(jsonSchema, null, 2))
}
- setCurrentTab(value)
- }, [currentTab, jsonSchema, json, advancedEditing, isAddingNewField, emit])
+ setCurrentTab([value])
+ }
- const handleApplySchema = useCallback((schema: SchemaRoot) => {
- if (currentTab === SchemaView.VisualEditor)
+ function handleApplySchema(schema: SchemaRoot) {
+ if (selectedTab === SchemaView.VisualEditor)
setJsonSchema(schema)
- else if (currentTab === SchemaView.JsonSchema)
+ else if (selectedTab === SchemaView.JsonSchema)
setJson(JSON.stringify(schema, null, 2))
- }, [currentTab])
+ }
- const handleSubmit = useCallback((schema: Record) => {
+ function handleSubmit(schema: Record) {
const jsonSchema = jsonToSchema(schema) as SchemaRoot
- if (currentTab === SchemaView.VisualEditor)
+ if (selectedTab === SchemaView.VisualEditor)
setJsonSchema(jsonSchema)
- else if (currentTab === SchemaView.JsonSchema)
+ else if (selectedTab === SchemaView.JsonSchema)
setJson(JSON.stringify(jsonSchema, null, 2))
- }, [currentTab])
+ }
- const handleVisualEditorUpdate = useCallback((schema: SchemaRoot) => {
+ function handleVisualEditorUpdate(schema: SchemaRoot) {
setJsonSchema(schema)
- }, [])
+ }
- const handleSchemaEditorUpdate = useCallback((schema: string) => {
+ function handleSchemaEditorUpdate(schema: string) {
setJson(schema)
- }, [])
+ }
- const handleResetDefaults = useCallback(() => {
- if (currentTab === SchemaView.VisualEditor) {
+ function handleResetDefaults() {
+ if (selectedTab === SchemaView.VisualEditor) {
setHoveringProperty(null)
if (advancedEditing)
setAdvancedEditing(false)
@@ -155,15 +159,15 @@ const JsonSchemaConfig: FC = ({
}
setJsonSchema(DEFAULT_SCHEMA)
setJson(JSON.stringify(DEFAULT_SCHEMA, null, 2))
- }, [currentTab, advancedEditing, isAddingNewField, setAdvancedEditing, setIsAddingNewField, setHoveringProperty])
+ }
- const handleCancel = useCallback(() => {
+ function handleCancel() {
onClose()
- }, [onClose])
+ }
- const handleSave = useCallback(() => {
+ function handleSave() {
let schema = jsonSchema
- if (currentTab === SchemaView.JsonSchema) {
+ if (selectedTab === SchemaView.JsonSchema) {
try {
schema = JSON.parse(json)
setParseError(null)
@@ -194,7 +198,7 @@ const JsonSchemaConfig: FC = ({
return
}
}
- else if (currentTab === SchemaView.VisualEditor) {
+ else if (selectedTab === SchemaView.VisualEditor) {
if (advancedEditing || isAddingNewField) {
toast.warning(t('nodes.llm.jsonSchema.warningTips.saveSchema', { ns: 'workflow' }))
return
@@ -202,7 +206,7 @@ const JsonSchemaConfig: FC = ({
}
onSave(schema)
onClose()
- }, [currentTab, jsonSchema, json, onSave, onClose, advancedEditing, isAddingNewField, t])
+ }
return (
@@ -211,18 +215,34 @@ const JsonSchemaConfig: FC
= ({
{t('nodes.llm.jsonSchema.title', { ns: 'workflow' })}
-
+
+
{/* Content */}
{/* Tab */}
-
- options={VIEW_TABS}
+
+ aria-label={t('nodes.llm.jsonSchema.title', { ns: 'workflow' })}
value={currentTab}
- onChange={handleTabChange}
- />
+ onValueChange={(nextTab) => {
+ const value = nextTab[0]
+ if (value)
+ handleTabChange(value)
+ }}
+ >
+ {VIEW_TABS.map(({ Icon, text, value }) => (
+
+
+ {text}
+
+ ))}
+
{/* JSON Schema Generator */}
= ({
- {currentTab === SchemaView.VisualEditor && (
+ {selectedTab === SchemaView.VisualEditor && (
)}
- {currentTab === SchemaView.JsonSchema && (
+ {selectedTab === SchemaView.JsonSchema && (
= ({
)
}
-const JsonSchemaConfigWrapper: FC = (props) => {
+export function JsonSchemaConfig(props: JsonSchemaConfigProps) {
return (
-
+
)
}
-
-export default JsonSchemaConfigWrapper
diff --git a/web/app/components/workflow/nodes/llm/components/panel-output-section.tsx b/web/app/components/workflow/nodes/llm/components/panel-output-section.tsx
index 53f7161fba..82b0a24aef 100644
--- a/web/app/components/workflow/nodes/llm/components/panel-output-section.tsx
+++ b/web/app/components/workflow/nodes/llm/components/panel-output-section.tsx
@@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'
import { Infotip } from '@/app/components/base/infotip'
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
import Split from '@/app/components/workflow/nodes/_base/components/split'
-import StructureOutput from './structure-output'
+import { StructureOutput } from './structure-output'
type Props = {
readOnly: boolean
diff --git a/web/app/components/workflow/nodes/llm/components/structure-output.tsx b/web/app/components/workflow/nodes/llm/components/structure-output.tsx
index d8b3e132bb..c7dadab269 100644
--- a/web/app/components/workflow/nodes/llm/components/structure-output.tsx
+++ b/web/app/components/workflow/nodes/llm/components/structure-output.tsx
@@ -1,16 +1,12 @@
'use client'
-import type { FC } from 'react'
import type { SchemaRoot, StructuredOutput } from '../types'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
-import { RiEditLine } from '@remixicon/react'
import { useBoolean } from 'ahooks'
-import * as React from 'react'
-import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import ShowPanel from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show'
import { Type } from '../types'
-import JsonSchemaConfigModal from './json-schema-config-modal'
+import { JsonSchemaConfigModal } from './json-schema-config-modal'
type Props = {
className?: string
@@ -18,22 +14,23 @@ type Props = {
onChange: (value: StructuredOutput) => void
}
-const StructureOutput: FC = ({
+export function StructureOutput({
className,
value,
onChange,
-}) => {
+}: Props) {
const { t } = useTranslation()
const [showConfig, {
setTrue: showConfigModal,
setFalse: hideConfigModal,
}] = useBoolean(false)
- const handleChange = useCallback((value: SchemaRoot) => {
+ function handleChange(value: SchemaRoot) {
onChange({
schema: value,
})
- }, [onChange])
+ }
+
return (
@@ -47,7 +44,7 @@ const StructureOutput: FC
= ({
className="flex"
onClick={showConfigModal}
>
-
+
{t('structOutput.configure', { ns: 'app' })}
@@ -58,7 +55,13 @@ const StructureOutput: FC
= ({
/>
)
: (
- {t('structOutput.notConfiguredTip', { ns: 'app' })}
+
)}
{showConfig && (
@@ -77,4 +80,3 @@ const StructureOutput: FC = ({
)
}
-export default React.memo(StructureOutput)
diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/__tests__/integration.spec.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/__tests__/integration.spec.tsx
index c0a769ffb0..26609885d2 100644
--- a/web/app/components/workflow/nodes/trigger-schedule/components/__tests__/integration.spec.tsx
+++ b/web/app/components/workflow/nodes/trigger-schedule/components/__tests__/integration.spec.tsx
@@ -3,7 +3,6 @@ import type { ScheduleTriggerNodeType } from '../../types'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import FrequencySelector from '../frequency-selector'
-import ModeSwitcher from '../mode-switcher'
import ModeToggle from '../mode-toggle'
import MonthlyDaysSelector from '../monthly-days-selector'
import NextExecutionTimes from '../next-execution-times'
@@ -53,16 +52,6 @@ describe('trigger-schedule components', () => {
})
})
- it('should switch between visual and cron modes', async () => {
- const user = userEvent.setup()
- const onChange = vi.fn()
- render()
-
- await user.click(screen.getByText('workflow.nodes.triggerSchedule.modeCron'))
-
- expect(onChange).toHaveBeenCalledWith('cron')
- })
-
it('should toggle the mode from visual to cron', async () => {
const user = userEvent.setup()
const onChange = vi.fn()
diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/__tests__/mode-switcher.spec.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/__tests__/mode-switcher.spec.tsx
deleted file mode 100644
index c4d5af5b11..0000000000
--- a/web/app/components/workflow/nodes/trigger-schedule/components/__tests__/mode-switcher.spec.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { render, screen } from '@testing-library/react'
-import userEvent from '@testing-library/user-event'
-import ModeSwitcher from '../mode-switcher'
-
-describe('trigger-schedule/mode-switcher', () => {
- it('switches between visual and cron modes', async () => {
- const user = userEvent.setup()
- const onChange = vi.fn()
-
- render()
-
- await user.click(screen.getByText('workflow.nodes.triggerSchedule.modeCron'))
-
- expect(onChange).toHaveBeenCalledWith('cron')
- })
-})
diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx
deleted file mode 100644
index bb914e313f..0000000000
--- a/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import type { ScheduleMode } from '../types'
-import { RiCalendarLine, RiCodeLine } from '@remixicon/react'
-import * as React from 'react'
-import { useTranslation } from 'react-i18next'
-import { SegmentedControl } from '@/app/components/base/segmented-control'
-
-type ModeSwitcherProps = {
- mode: ScheduleMode
- onChange: (mode: ScheduleMode) => void
-}
-
-const ModeSwitcher = ({ mode, onChange }: ModeSwitcherProps) => {
- const { t } = useTranslation()
-
- const options = [
- {
- Icon: RiCalendarLine,
- text: t('nodes.triggerSchedule.modeVisual', { ns: 'workflow' }),
- value: 'visual' as const,
- },
- {
- Icon: RiCodeLine,
- text: t('nodes.triggerSchedule.modeCron', { ns: 'workflow' }),
- value: 'cron' as const,
- },
- ]
-
- return (
-
- )
-}
-
-export default ModeSwitcher
diff --git a/web/app/components/workflow/variable-inspect/__tests__/display-content.spec.tsx b/web/app/components/workflow/variable-inspect/__tests__/display-content.spec.tsx
index bbc4714a92..9ba5763576 100644
--- a/web/app/components/workflow/variable-inspect/__tests__/display-content.spec.tsx
+++ b/web/app/components/workflow/variable-inspect/__tests__/display-content.spec.tsx
@@ -1,5 +1,5 @@
import { fireEvent, render, screen } from '@testing-library/react'
-import DisplayContent from '../display-content'
+import { DisplayContent } from '../display-content'
import { PreviewType } from '../types'
describe('variable inspect display content', () => {
diff --git a/web/app/components/workflow/variable-inspect/display-content.tsx b/web/app/components/workflow/variable-inspect/display-content.tsx
index 2edd84d0de..11ee2a7a42 100644
--- a/web/app/components/workflow/variable-inspect/display-content.tsx
+++ b/web/app/components/workflow/variable-inspect/display-content.tsx
@@ -2,12 +2,10 @@ import type { VarType } from '../types'
import type { ChunkInfo } from '@/app/components/rag-pipeline/components/chunk-card-list/types'
import type { ParentMode } from '@/models/datasets'
import { cn } from '@langgenius/dify-ui/cn'
-import { RiBracesLine, RiEyeLine } from '@remixicon/react'
-import * as React from 'react'
+import { ToggleGroup, ToggleGroupItem } from '@langgenius/dify-ui/toggle-group'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Markdown } from '@/app/components/base/markdown'
-import { SegmentedControl } from '@/app/components/base/segmented-control'
import Textarea from '@/app/components/base/textarea'
import { ChunkCardList } from '@/app/components/rag-pipeline/components/chunk-card-list'
import SchemaEditor from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor'
@@ -26,11 +24,16 @@ type DisplayContentProps = {
className?: string
}
-const DisplayContent = (props: DisplayContentProps) => {
+export function DisplayContent(props: DisplayContentProps) {
const { previewType, varType, schemaType, mdString, jsonString, readonly, handleTextChange, handleEditorChange, className } = props
- const [viewMode, setViewMode] = useState(ViewMode.Code)
+ const [viewMode, setViewMode] = useState([ViewMode.Code])
const [isFocused, setIsFocused] = useState(false)
const { t } = useTranslation()
+ const viewOptions = [
+ { value: ViewMode.Code, label: t('nodes.templateTransform.code', { ns: 'workflow' }), iconClassName: 'i-ri-braces-line' },
+ { value: ViewMode.Preview, label: t('common.preview', { ns: 'workflow' }), iconClassName: 'i-ri-eye-line' },
+ ]
+ const selectedViewMode = viewMode[0] ?? ViewMode.Code
const chunkType = useMemo(() => {
if (previewType !== PreviewType.Chunks || !schemaType)
@@ -65,22 +68,26 @@ const DisplayContent = (props: DisplayContentProps) => {
{schemaType ? `(${schemaType})` : ''}
)}
-
+ aria-label={t('common.preview', { ns: 'workflow' })}
value={viewMode}
- onChange={setViewMode}
- size="small"
- padding="with"
- activeClassName="text-text-accent-light-mode-only!"
- btnClassName="pl-1.5! pr-0.5! gap-[3px]"
- className="shrink-0"
- />
+ onValueChange={setViewMode}
+ className="shrink-0 rounded-md p-px"
+ >
+ {viewOptions.map(({ value, label, iconClassName }) => (
+
+
+ {label}
+
+ ))}
+
- {viewMode === ViewMode.Code && (
+ {selectedViewMode === ViewMode.Code && (
previewType === PreviewType.Markdown
? (
)
}
-
-export default React.memo(DisplayContent)
diff --git a/web/app/components/workflow/variable-inspect/value-content-sections.tsx b/web/app/components/workflow/variable-inspect/value-content-sections.tsx
index 8d892343cd..8fb6c14d2f 100644
--- a/web/app/components/workflow/variable-inspect/value-content-sections.tsx
+++ b/web/app/components/workflow/variable-inspect/value-content-sections.tsx
@@ -11,7 +11,7 @@ import { SupportUploadFileTypes } from '@/app/components/workflow/types'
import { TransferMethod } from '@/types/app'
import { PreviewMode } from '../../base/features/types'
import BoolValue from '../panel/chat-variable-panel/components/bool-value'
-import DisplayContent from './display-content'
+import { DisplayContent } from './display-content'
import LargeDataAlert from './large-data-alert'
import { PreviewType } from './types'
diff --git a/web/app/styles/tailwind-core.css b/web/app/styles/tailwind-core.css
index 91af9df192..614a46652b 100644
--- a/web/app/styles/tailwind-core.css
+++ b/web/app/styles/tailwind-core.css
@@ -28,7 +28,6 @@
@import '../components/base/action-button/index.css';
@import '../components/base/badge/index.css';
@import '../components/base/premium-badge/index.css';
-@import '../components/base/segmented-control/index.css';
/* ---------- JS plugins ------------------------------------------------ */
@plugin './plugins/icons.ts';