feat: add SchemaEditor component and integrate it into JSON Schema config modal
This commit is contained in:
parent
a07831bc05
commit
183edf0fd5
@ -9,6 +9,7 @@ import JsonImporter from './json-importer'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import VisualEditor from './visual-editor'
|
import VisualEditor from './visual-editor'
|
||||||
|
import SchemaEditor from './schema-editor'
|
||||||
|
|
||||||
type JsonSchemaConfigModalProps = {
|
type JsonSchemaConfigModalProps = {
|
||||||
isShow: boolean
|
isShow: boolean
|
||||||
@ -43,6 +44,7 @@ const JsonSchemaConfigModal: FC<JsonSchemaConfigModalProps> = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [currentTab, setCurrentTab] = useState(SchemaView.VisualEditor)
|
const [currentTab, setCurrentTab] = useState(SchemaView.VisualEditor)
|
||||||
const [jsonSchema, setJsonSchema] = useState(defaultSchema || DEFAULT_SCHEMA)
|
const [jsonSchema, setJsonSchema] = useState(defaultSchema || DEFAULT_SCHEMA)
|
||||||
|
const [json, setJson] = useState(JSON.stringify(jsonSchema, null, 2))
|
||||||
const [btnWidth, setBtnWidth] = useState(0)
|
const [btnWidth, setBtnWidth] = useState(0)
|
||||||
|
|
||||||
const updateBtnWidth = useCallback((width: number) => {
|
const updateBtnWidth = useCallback((width: number) => {
|
||||||
@ -57,6 +59,10 @@ const JsonSchemaConfigModal: FC<JsonSchemaConfigModalProps> = ({
|
|||||||
setJsonSchema(schema)
|
setJsonSchema(schema)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const handleSchemaEditorUpdate = useCallback((schema: string) => {
|
||||||
|
setJson(schema)
|
||||||
|
}, [])
|
||||||
|
|
||||||
const handleResetDefaults = useCallback(() => {
|
const handleResetDefaults = useCallback(() => {
|
||||||
setJsonSchema(defaultSchema || DEFAULT_SCHEMA)
|
setJsonSchema(defaultSchema || DEFAULT_SCHEMA)
|
||||||
}, [defaultSchema])
|
}, [defaultSchema])
|
||||||
@ -118,7 +124,10 @@ const JsonSchemaConfigModal: FC<JsonSchemaConfigModalProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{currentTab === SchemaView.JsonSchema && (
|
{currentTab === SchemaView.JsonSchema && (
|
||||||
<div className='h-full rounded-xl bg-components-input-bg-normal'>JSON Schema</div>
|
<SchemaEditor
|
||||||
|
schema={json}
|
||||||
|
onUpdate={handleSchemaEditorUpdate}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
|
|||||||
@ -166,7 +166,6 @@ const JsonImporter: FC<JsonImporterProps> = ({
|
|||||||
alwaysConsumeMouseWheel: false,
|
alwaysConsumeMouseWheel: false,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -131,7 +131,6 @@ const GeneratedResult: FC<GeneratedResultProps> = ({
|
|||||||
alwaysConsumeMouseWheel: false,
|
alwaysConsumeMouseWheel: false,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -0,0 +1,102 @@
|
|||||||
|
import { Editor } from '@monaco-editor/react'
|
||||||
|
import { RiClipboardLine, RiIndentIncrease } from '@remixicon/react'
|
||||||
|
import copy from 'copy-to-clipboard'
|
||||||
|
import React, { type FC, useCallback, useRef } from 'react'
|
||||||
|
|
||||||
|
type SchemaEditorProps = {
|
||||||
|
schema: string
|
||||||
|
onUpdate: (schema: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const SchemaEditor: FC<SchemaEditorProps> = ({
|
||||||
|
schema,
|
||||||
|
onUpdate,
|
||||||
|
}) => {
|
||||||
|
const monacoRef = useRef<any>(null)
|
||||||
|
const editorRef = useRef<any>(null)
|
||||||
|
|
||||||
|
const handleEditorDidMount = useCallback((editor: any, monaco: any) => {
|
||||||
|
editorRef.current = editor
|
||||||
|
monacoRef.current = monaco
|
||||||
|
monaco.editor.defineTheme('light-theme', {
|
||||||
|
base: 'vs',
|
||||||
|
inherit: true,
|
||||||
|
rules: [],
|
||||||
|
colors: {
|
||||||
|
'editor.background': '#00000000',
|
||||||
|
'editor.lineHighlightBackground': '#00000000',
|
||||||
|
'focusBorder': '#00000000',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
monaco.editor.setTheme('light-theme')
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const formatJsonContent = useCallback(() => {
|
||||||
|
if (editorRef.current)
|
||||||
|
editorRef.current.getAction('editor.action.formatDocument')?.run()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleEditorChange = useCallback((value: string | undefined) => {
|
||||||
|
if (!value)
|
||||||
|
return
|
||||||
|
onUpdate(value)
|
||||||
|
}, [onUpdate])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col h-full rounded-xl bg-components-input-bg-normal overflow-hidden'>
|
||||||
|
<div className='flex items-center justify-between pl-2 pt-1 pr-1'>
|
||||||
|
<div className='py-0.5 text-text-secondary system-xs-semibold-uppercase'>
|
||||||
|
<span className='px-1 py-0.5'>JSON</span>
|
||||||
|
</div>
|
||||||
|
<div className='flex items-center gap-x-0.5'>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='flex items-center justify-center h-6 w-6'
|
||||||
|
onClick={formatJsonContent}
|
||||||
|
>
|
||||||
|
<RiIndentIncrease className='w-4 h-4 text-text-tertiary' />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='flex items-center justify-center h-6 w-6'
|
||||||
|
onClick={() => copy(schema)}>
|
||||||
|
<RiClipboardLine className='w-4 h-4 text-text-tertiary' />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='relative grow'>
|
||||||
|
<Editor
|
||||||
|
height='100%'
|
||||||
|
defaultLanguage='json'
|
||||||
|
value={schema}
|
||||||
|
onChange={handleEditorChange}
|
||||||
|
onMount={handleEditorDidMount}
|
||||||
|
options={{
|
||||||
|
readOnly: false,
|
||||||
|
domReadOnly: true,
|
||||||
|
minimap: { enabled: false },
|
||||||
|
tabSize: 2,
|
||||||
|
scrollBeyondLastLine: false,
|
||||||
|
wordWrap: 'on',
|
||||||
|
wrappingIndent: 'same',
|
||||||
|
// Add these options
|
||||||
|
overviewRulerBorder: false,
|
||||||
|
hideCursorInOverviewRuler: true,
|
||||||
|
renderLineHighlightOnlyWhenFocus: false,
|
||||||
|
renderLineHighlight: 'none',
|
||||||
|
// Hide scrollbar borders
|
||||||
|
scrollbar: {
|
||||||
|
vertical: 'hidden',
|
||||||
|
horizontal: 'hidden',
|
||||||
|
verticalScrollbarSize: 0,
|
||||||
|
horizontalScrollbarSize: 0,
|
||||||
|
alwaysConsumeMouseWheel: false,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SchemaEditor
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
import { type FC, useState } from 'react'
|
||||||
|
import type { Field } from '../../../types'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import { RiAddCircleFill } from '@remixicon/react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
type AddToRootProps = {
|
||||||
|
addField: (path: string[], updates: Field) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddField: FC<AddToRootProps> = ({
|
||||||
|
addField,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const [isEditing, setIsEditing] = useState(false)
|
||||||
|
|
||||||
|
const handleAddField = () => {
|
||||||
|
setIsEditing(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button size='small' className='flex items-center gap-x-[1px]' onClick={handleAddField}>
|
||||||
|
<RiAddCircleFill className='w-3.5 h-3.5'/>
|
||||||
|
<span className='px-[3px]'>{t('workflow.nodes.llm.jsonSchema.addField')}</span>
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddField
|
||||||
Loading…
x
Reference in New Issue
Block a user