FastGPT/packages/web/components/common/Textarea/CodeEditor/Editor.tsx
Archer d571c768ea
Some checks are pending
Document deploy / sync-images (push) Waiting to run
Document deploy / generate-timestamp (push) Blocked by required conditions
Document deploy / build-images (map[domain:https://fastgpt.cn suffix:cn]) (push) Blocked by required conditions
Document deploy / build-images (map[domain:https://fastgpt.io suffix:io]) (push) Blocked by required conditions
Document deploy / update-images (map[deployment:fastgpt-docs domain:https://fastgpt.cn kube_config:KUBE_CONFIG_CN suffix:cn]) (push) Blocked by required conditions
Document deploy / update-images (map[deployment:fastgpt-docs domain:https://fastgpt.io kube_config:KUBE_CONFIG_IO suffix:io]) (push) Blocked by required conditions
Build FastGPT images in Personal warehouse / get-vars (push) Waiting to run
Build FastGPT images in Personal warehouse / build-fastgpt-images (map[arch:amd64 runs-on:ubuntu-24.04]) (push) Blocked by required conditions
Build FastGPT images in Personal warehouse / build-fastgpt-images (map[arch:arm64 runs-on:ubuntu-24.04-arm]) (push) Blocked by required conditions
Build FastGPT images in Personal warehouse / release-fastgpt-images (push) Blocked by required conditions
V4.14.1 feature (#5880)
* feat: app split (#5858)

* feat: app split script

* fix: app split

* chore: app split script try-catch

* adjust dashborad page (#5872)

* create page

* create page

* create button

* router name

* bot

* template

* create page

* mobile

* toolfolder

* fix

* fix

* fix build

* split tool select

* img

* doc

* rename enum

* fix page adjust (#5883)

* fix page adjust

* fix ad store

* fix: initv4141 (#5886)

* fix: create app

* doc

* hide api

* doc

* feat: payment pause interactive (#5892)

* fix: copy clbs (#5889)

* fix: copy clbs

* fix: copy clbs

* fix: http protocol handling in baseURL (#5890)

* fix: http protocol handling in baseURL

* ui fix

* auto saved version

* fix

* auto save

* fix: model permission modal (#5895)

* folder

* fix: del app

* navbar

* fix: plugin file selector (#5893)

* fix: plugin file selector

* fix: plugin file selector

* workflow tool inputform

* pick

---------

Co-authored-by: archer <545436317@qq.com>

* fix: workflow tool time

* doc

* fix workorder button (#5896)

* add inform track (#5897)

* remove invalid track

* comment

* feat: marketplace refresh api (#5884)

* marketplace refresh

* fix: helper bot menu button (#5898)

---------

Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
Co-authored-by: heheer <heheer@sealos.io>
Co-authored-by: 伍闲犬 <whoeverimf5@gmail.com>
2025-11-11 14:05:02 +08:00

177 lines
4.6 KiB
TypeScript

import React, { useCallback, useRef, useState, useEffect } from 'react';
import Editor, { type Monaco, loader } from '@monaco-editor/react';
import { Box, type BoxProps } from '@chakra-ui/react';
import MyIcon from '../../Icon';
import { getWebReqUrl } from '../../../../common/system/utils';
import usePythonCompletion from './usePythonCompletion';
loader.config({
paths: { vs: getWebReqUrl('/js/monaco-editor.0.45.0/vs') }
});
type EditorVariablePickerType = {
key: string;
label: string;
};
export type Props = Omit<BoxProps, 'resize' | 'onChange'> & {
resize?: boolean;
defaultValue?: string;
value?: string;
onChange?: (e: string) => void;
onOpenModal?: () => void;
variables?: EditorVariablePickerType[];
defaultHeight?: number;
language?: string;
};
const options = {
lineNumbers: 'on',
guides: {
indentation: false
},
automaticLayout: true,
minimap: {
enabled: false
},
scrollbar: {
verticalScrollbarSize: 4,
horizontalScrollbarSize: 8,
alwaysConsumeMouseWheel: false
},
lineNumbersMinChars: 4,
fontSize: 14,
scrollBeyondLastLine: false,
folding: true,
overviewRulerBorder: false,
tabSize: 2
};
const MyEditor = ({
defaultValue,
value,
onChange,
resize,
variables = [],
defaultHeight = 200,
onOpenModal,
language = 'typescript',
...props
}: Props) => {
const [height, setHeight] = useState(defaultHeight);
const initialY = useRef(0);
const registerPythonCompletion = usePythonCompletion();
const handleMouseDown = useCallback((e: React.MouseEvent) => {
initialY.current = e.clientY;
const handleMouseMove = (e: MouseEvent) => {
const deltaY = e.clientY - initialY.current;
initialY.current = e.clientY;
setHeight((prevHeight) => (prevHeight + deltaY < 100 ? 100 : prevHeight + deltaY));
};
const handleMouseUp = () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
}, []);
const editorRef = useRef<any>(null);
const monacoRef = useRef<Monaco | null>(null);
const handleEditorDidMount = useCallback((editor: any, monaco: Monaco) => {
editorRef.current = editor;
monacoRef.current = monaco;
}, []);
const beforeMount = useCallback(
(monaco: Monaco) => {
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: false,
allowComments: false,
schemas: [
{
uri: 'http://myserver/foo-schema.json', // 一个假设的 URI
fileMatch: ['*'], // 匹配所有文件
schema: {} // 空的 Schema
}
]
});
monaco.editor.defineTheme('JSONEditorTheme', {
base: 'vs', // 可以基于已有的主题进行定制
inherit: true, // 继承基础主题的设置
rules: [{ token: 'variable', foreground: '2B5FD9' }],
colors: {
'editor.background': '#ffffff00',
'editorLineNumber.foreground': '#aaa',
'editorOverviewRuler.border': '#ffffff00',
'editor.lineHighlightBackground': '#F7F8FA',
'scrollbarSlider.background': '#E8EAEC',
'editorIndentGuide.activeBackground': '#ddd',
'editorIndentGuide.background': '#eee'
}
});
registerPythonCompletion(monaco);
},
[registerPythonCompletion]
);
return (
<Box
borderWidth={'1px'}
borderRadius={'md'}
borderColor={'myGray.200'}
py={1}
height={height}
position={'relative'}
{...props}
>
<Editor
height={'100%'}
language={language}
options={options as any}
theme="JSONEditorTheme"
beforeMount={beforeMount}
defaultValue={defaultValue}
value={value}
onChange={(e) => {
onChange?.(e || '');
}}
onMount={handleEditorDidMount}
/>
{resize && (
<Box
position={'absolute'}
right={'-2.5'}
bottom={'-3.5'}
zIndex={10}
cursor={'ns-resize'}
px={'4px'}
onMouseDown={handleMouseDown}
>
<MyIcon name={'common/editor/resizer'} width={'16px'} height={'16px'} />
</Box>
)}
{!!onOpenModal && (
<Box
zIndex={10}
position={'absolute'}
bottom={0}
right={2}
cursor={'pointer'}
onClick={onOpenModal}
>
<MyIcon name={'common/fullScreenLight'} w={'14px'} color={'myGray.600'} />
</Box>
)}
</Box>
);
};
export default React.memo(MyEditor);