| | |
| | | import Editor, { loader } from '@monaco-editor/react' |
| | | import React, { useEffect, useMemo, useRef, useState } from 'react' |
| | | import Base from '../base' |
| | | import { WEB_PREFIX } from '@/config' |
| | | import cn from '@/utils/classnames' |
| | | import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' |
| | | import { |
| | | getFilesInLogs, |
| | | } from '@/app/components/base/file-uploader/utils' |
| | | import { Theme } from '@/types/app' |
| | | import useTheme from '@/hooks/use-theme' |
| | | |
| | | import './style.css' |
| | | import { noop } from 'lodash-es' |
| | | |
| | | // load file from local instead of cdn https://github.com/suren-atoyan/monaco-react/issues/482 |
| | | loader.config({ paths: { vs: `${WEB_PREFIX}/vs` } }) |
| | | loader.config({ paths: { vs: '/vs' } }) |
| | | |
| | | const CODE_EDITOR_LINE_HEIGHT = 18 |
| | | |
| | | export type Props = { |
| | | value?: string | object |
| | | placeholder?: React.JSX.Element | string |
| | | placeholder?: JSX.Element | string |
| | | onChange?: (value: string) => void |
| | | title?: React.JSX.Element |
| | | title?: JSX.Element |
| | | language: CodeLanguage |
| | | headerRight?: React.JSX.Element |
| | | headerRight?: JSX.Element |
| | | readOnly?: boolean |
| | | isJSONStringifyBeauty?: boolean |
| | | height?: number |
| | |
| | | onGenerated?: (value: string) => void |
| | | showCodeGenerator?: boolean |
| | | className?: string |
| | | tip?: React.JSX.Element |
| | | tip?: JSX.Element |
| | | } |
| | | |
| | | export const languageMap = { |
| | |
| | | [CodeLanguage.json]: 'json', |
| | | } |
| | | |
| | | const DEFAULT_THEME = { |
| | | base: 'vs', |
| | | inherit: true, |
| | | rules: [], |
| | | colors: { |
| | | 'editor.background': '#F2F4F7', // #00000000 transparent. But it will has a blue border |
| | | }, |
| | | } |
| | | |
| | | const CodeEditor: FC<Props> = ({ |
| | | value = '', |
| | | placeholder = '', |
| | | onChange = noop, |
| | | onChange = () => { }, |
| | | title = '', |
| | | headerRight, |
| | | language, |
| | |
| | | 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 |
| | |
| | | setIsFocus(false) |
| | | }) |
| | | |
| | | monaco.editor.setTheme(appTheme === Theme.light ? 'light' : 'vs-dark') // Fix: sometimes not load the default theme |
| | | monaco.editor.defineTheme('default-theme', DEFAULT_THEME) |
| | | |
| | | monaco.editor.defineTheme('blur-theme', { |
| | | base: 'vs', |
| | | inherit: true, |
| | | rules: [], |
| | | colors: { |
| | | 'editor.background': '#F2F4F7', |
| | | }, |
| | | }) |
| | | |
| | | monaco.editor.defineTheme('focus-theme', { |
| | | base: 'vs', |
| | | inherit: true, |
| | | rules: [], |
| | | colors: { |
| | | 'editor.background': '#ffffff', |
| | | }, |
| | | }) |
| | | |
| | | monaco.editor.setTheme('default-theme') // Fix: sometimes not load the default theme |
| | | |
| | | onMount?.(editor, monaco) |
| | | setIsMounted(true) |
| | |
| | | try { |
| | | return JSON.stringify(value as object, null, 2) |
| | | } |
| | | catch { |
| | | catch (e) { |
| | | return value as string |
| | | } |
| | | })() |
| | | |
| | | const theme = useMemo(() => { |
| | | if (appTheme === Theme.light) |
| | | return 'light' |
| | | return 'vs-dark' |
| | | }, [appTheme]) |
| | | const theme = (() => { |
| | | if (noWrapper) |
| | | return 'default-theme' |
| | | |
| | | return isFocus ? 'focus-theme' : 'blur-theme' |
| | | })() |
| | | |
| | | const main = ( |
| | | <> |
| | |
| | | }} |
| | | onMount={handleEditorDidMount} |
| | | /> |
| | | {!outPutValue && !isFocus && <div className='pointer-events-none absolute left-[36px] top-0 text-[13px] font-normal leading-[18px] text-gray-300'>{placeholder}</div>} |
| | | {!outPutValue && !isFocus && <div className='pointer-events-none absolute left-[36px] top-0 leading-[18px] text-[13px] font-normal text-gray-300'>{placeholder}</div>} |
| | | </> |
| | | ) |
| | | |
| | | return ( |
| | | <div className={cn(isExpand && 'h-full', className)}> |
| | | {noWrapper |
| | | ? <div className='no-wrapper relative' style={{ |
| | | ? <div className='relative no-wrapper' style={{ |
| | | height: isExpand ? '100%' : (editorContentHeight) / 2 + CODE_EDITOR_LINE_HEIGHT, // In IDE, the last line can always be in lop line. So there is some blank space in the bottom. |
| | | minHeight: CODE_EDITOR_LINE_HEIGHT, |
| | | }}> |