feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useRef, useState } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozWarningCircleFill } from '@coze-arch/coze-design/icons';
|
||||
import { Modal } from '@coze-arch/coze-design';
|
||||
|
||||
import { Layout } from '../layout';
|
||||
import { type EditorProps, type LanguageType } from '../../interface';
|
||||
import { Editor } from './editor';
|
||||
|
||||
export const BizEditor = (props: EditorProps) => {
|
||||
const editorApi = useRef<undefined | { getValue?: () => string }>(undefined);
|
||||
|
||||
const [language, setLanguage] = useState<LanguageType>(props.defaultLanguage);
|
||||
const [content, setContent] = useState<string | undefined>(
|
||||
props.defaultContent,
|
||||
);
|
||||
|
||||
const handleLanguageChange = (value: LanguageType) => {
|
||||
const langTemplate = props.languageTemplates?.find(
|
||||
e => e.language === value,
|
||||
);
|
||||
|
||||
const preLangTemplate = props.languageTemplates?.find(
|
||||
e => e.language === language,
|
||||
);
|
||||
|
||||
if (preLangTemplate?.template === editorApi.current?.getValue?.()) {
|
||||
setLanguage(value);
|
||||
setContent(langTemplate?.template);
|
||||
props.onChange?.(langTemplate?.template || '', value);
|
||||
return;
|
||||
}
|
||||
|
||||
Modal.warning({
|
||||
icon: (
|
||||
<IconCozWarningCircleFill
|
||||
style={{ color: 'rgba(var(--coze-yellow-5), 1)' }}
|
||||
/>
|
||||
),
|
||||
title: I18n.t('code_node_switch_language'),
|
||||
content: I18n.t('code_node_switch_language_description'),
|
||||
okType: 'warning',
|
||||
okText: I18n.t('Confirm'),
|
||||
cancelText: I18n.t('Cancel'),
|
||||
closable: true,
|
||||
width: 448,
|
||||
height: 160,
|
||||
onOk: () => {
|
||||
setLanguage(value);
|
||||
setContent(langTemplate?.template);
|
||||
props.onChange?.(langTemplate?.template || '', value);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Layout
|
||||
{...props}
|
||||
language={language}
|
||||
onLanguageSelect={handleLanguageChange}
|
||||
>
|
||||
<Editor
|
||||
{...props}
|
||||
defaultContent={content}
|
||||
language={language}
|
||||
didMount={api => {
|
||||
editorApi.current = api;
|
||||
}}
|
||||
/>
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { type FC, lazy, Suspense, useMemo } from 'react';
|
||||
|
||||
import { type EditorOtherProps, type EditorProps } from '../../interface';
|
||||
|
||||
const LazyPythonEditor: FC<EditorProps> = lazy(async () => {
|
||||
const { PythonEditor } = await import('./python-editor');
|
||||
return { default: PythonEditor };
|
||||
});
|
||||
|
||||
const PythonEditor: FC<EditorProps> = props => (
|
||||
<Suspense>
|
||||
<LazyPythonEditor {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
const LazyTypescriptEditor: FC<EditorProps> = lazy(async () => {
|
||||
const { TypescriptEditor } = await import('./typescript-editor');
|
||||
return { default: TypescriptEditor };
|
||||
});
|
||||
|
||||
const TypescriptEditor: FC<EditorProps> = props => (
|
||||
<Suspense>
|
||||
<LazyTypescriptEditor {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
export const Editor = (props: EditorProps & EditorOtherProps) => {
|
||||
const language = useMemo(
|
||||
() => props.language || props.defaultLanguage,
|
||||
[props.defaultLanguage, props.language],
|
||||
);
|
||||
|
||||
if (language === 'python') {
|
||||
return <PythonEditor {...props} />;
|
||||
}
|
||||
|
||||
return <TypescriptEditor {...props} />;
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { Editor } from './editor';
|
||||
export { BizEditor } from './biz-editor';
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { api, type InferEditorAPIFromPlugins } from '@coze-editor/editor/react';
|
||||
import preset from '@coze-editor/editor/preset-code';
|
||||
import { type EditorView } from '@codemirror/view';
|
||||
|
||||
// 忽略 readOnly 强制设置值
|
||||
const forceSetValue =
|
||||
({ view }: { view: EditorView }) =>
|
||||
(value: string) => {
|
||||
const { state } = view;
|
||||
view.dispatch(
|
||||
state.update({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: state.doc.length,
|
||||
insert: value ?? '',
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const customPreset = [...preset, api('forceSetValue', forceSetValue)];
|
||||
|
||||
export type EditorAPI = InferEditorAPIFromPlugins<typeof customPreset>;
|
||||
|
||||
export default customPreset;
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { Renderer, EditorProvider } from '@coze-editor/editor/react';
|
||||
import { languages } from '@coze-editor/editor/preset-code';
|
||||
import { python } from '@coze-editor/editor/language-python';
|
||||
import { EditorView } from '@codemirror/view';
|
||||
|
||||
import { type EditorOtherProps, type EditorProps } from '../../interface';
|
||||
import preset from './preset';
|
||||
|
||||
languages.register('python', python);
|
||||
|
||||
export const PythonEditor = (props: EditorProps & EditorOtherProps) => {
|
||||
const {
|
||||
defaultContent,
|
||||
uuid,
|
||||
readonly,
|
||||
height,
|
||||
didMount,
|
||||
onChange,
|
||||
defaultLanguage,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<EditorProvider>
|
||||
<Renderer
|
||||
plugins={preset}
|
||||
domProps={{
|
||||
style: {
|
||||
height: 'calc(100% - 48px)',
|
||||
},
|
||||
}}
|
||||
didMount={api => {
|
||||
didMount?.(api);
|
||||
api.$on('change', ({ value }) => {
|
||||
onChange?.(value, defaultLanguage);
|
||||
});
|
||||
}}
|
||||
defaultValue={defaultContent}
|
||||
extensions={[
|
||||
EditorView.theme({
|
||||
'&.cm-focused': {
|
||||
outline: 'none',
|
||||
},
|
||||
'&.cm-editor': {
|
||||
height: height || 'unset',
|
||||
},
|
||||
'.cm-content': {
|
||||
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
||||
},
|
||||
'.cm-content *': {
|
||||
fontFamily: 'inherit',
|
||||
},
|
||||
}),
|
||||
]}
|
||||
options={{
|
||||
uri: `file:///py_editor_${uuid}.py`,
|
||||
languageId: 'python',
|
||||
theme: 'code-editor-dark',
|
||||
height,
|
||||
readOnly: readonly,
|
||||
editable: !readonly,
|
||||
fontSize: 12,
|
||||
tabSize: 4,
|
||||
}}
|
||||
/>
|
||||
</EditorProvider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ViewVariableType } from '@coze-workflow/base';
|
||||
import { languages } from '@coze-editor/editor/preset-code';
|
||||
import { typescript } from '@coze-editor/editor/language-typescript';
|
||||
|
||||
import { type Input, ModuleDetectionKind, type Output } from '../../interface';
|
||||
|
||||
export const initTypescriptServer = () => {
|
||||
languages.register('typescript', typescript);
|
||||
|
||||
const tsWorker = new Worker(
|
||||
new URL('@coze-editor/editor/language-typescript/worker', import.meta.url),
|
||||
{ type: 'module' },
|
||||
);
|
||||
|
||||
typescript.languageService.initialize(tsWorker, {
|
||||
compilerOptions: {
|
||||
// eliminate Promise error
|
||||
lib: ['es2015'],
|
||||
moduleDetection: ModuleDetectionKind.Force,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const mapVariableType = (type?: ViewVariableType): string => {
|
||||
switch (type) {
|
||||
case ViewVariableType.String:
|
||||
return 'string';
|
||||
case ViewVariableType.Integer:
|
||||
return 'number';
|
||||
case ViewVariableType.Boolean:
|
||||
return 'boolean';
|
||||
case ViewVariableType.Number:
|
||||
return 'number';
|
||||
case ViewVariableType.Object:
|
||||
return 'object';
|
||||
case ViewVariableType.Image:
|
||||
case ViewVariableType.File:
|
||||
case ViewVariableType.Doc:
|
||||
case ViewVariableType.Code:
|
||||
case ViewVariableType.Ppt:
|
||||
case ViewVariableType.Txt:
|
||||
case ViewVariableType.Excel:
|
||||
case ViewVariableType.Audio:
|
||||
case ViewVariableType.Zip:
|
||||
case ViewVariableType.Video:
|
||||
case ViewVariableType.Svg:
|
||||
case ViewVariableType.Voice:
|
||||
return 'string'; // Assuming file-like types are represented as strings (e.g., file paths or URLs)
|
||||
case ViewVariableType.Time:
|
||||
return 'Date';
|
||||
case ViewVariableType.ArrayString:
|
||||
return 'string[]';
|
||||
case ViewVariableType.ArrayInteger:
|
||||
return 'number[]';
|
||||
case ViewVariableType.ArrayBoolean:
|
||||
return 'boolean[]';
|
||||
case ViewVariableType.ArrayNumber:
|
||||
return 'number[]';
|
||||
case ViewVariableType.ArrayObject:
|
||||
return 'object[]';
|
||||
case ViewVariableType.ArrayImage:
|
||||
case ViewVariableType.ArrayFile:
|
||||
case ViewVariableType.ArrayDoc:
|
||||
case ViewVariableType.ArrayCode:
|
||||
case ViewVariableType.ArrayPpt:
|
||||
case ViewVariableType.ArrayTxt:
|
||||
case ViewVariableType.ArrayExcel:
|
||||
case ViewVariableType.ArrayAudio:
|
||||
case ViewVariableType.ArrayZip:
|
||||
case ViewVariableType.ArrayVideo:
|
||||
case ViewVariableType.ArraySvg:
|
||||
case ViewVariableType.ArrayVoice:
|
||||
return 'string[]';
|
||||
case ViewVariableType.ArrayTime:
|
||||
return 'Date[]';
|
||||
default:
|
||||
return 'any'; // Fallback type
|
||||
}
|
||||
};
|
||||
|
||||
export function generateTypeDefinition(output: Output, indent = ' '): string {
|
||||
let definition = '';
|
||||
|
||||
if (output.type === ViewVariableType.Object) {
|
||||
definition += `${indent}${output.name}: {\n`;
|
||||
if (output.children && output.children.length > 0) {
|
||||
output.children.forEach(child => {
|
||||
definition += `${indent} ${generateTypeDefinition(child, `${indent} `)}`;
|
||||
});
|
||||
}
|
||||
|
||||
definition += `${indent}}\n`;
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
if (output.type === ViewVariableType.ArrayObject) {
|
||||
definition += `${indent}${output.name}: {\n`;
|
||||
if (output.children && output.children.length > 0) {
|
||||
output.children.forEach(child => {
|
||||
definition += `${indent} ${generateTypeDefinition(child, `${indent} `)}`;
|
||||
});
|
||||
definition += `${indent}}[]\n`;
|
||||
}
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
definition += `${indent}${output.name}: ${mapVariableType(output.type)};\n`;
|
||||
|
||||
definition += '\n';
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
export const initInputAndOutput = async (
|
||||
inputs: Input[] = [],
|
||||
outputs: Output[] = [],
|
||||
uuid = '',
|
||||
): Promise<void> => {
|
||||
const typeDefinition = `
|
||||
declare module '/ts_editor_${uuid}' {
|
||||
interface Args {
|
||||
${generateTypeDefinition(
|
||||
{
|
||||
name: 'params',
|
||||
type: ViewVariableType.Object,
|
||||
children: inputs,
|
||||
},
|
||||
' ',
|
||||
)}
|
||||
}
|
||||
|
||||
interface Output {
|
||||
${outputs.map(output => generateTypeDefinition(output, ' ')).join('\n')}
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
||||
`;
|
||||
|
||||
await typescript.languageService.addExtraFiles({
|
||||
[`/ts_editor_${uuid}.d.ts`]: typeDefinition,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import { Renderer, EditorProvider } from '@coze-editor/editor/react';
|
||||
import { EditorView } from '@codemirror/view';
|
||||
|
||||
import { type EditorOtherProps, type EditorProps } from '../../interface';
|
||||
import {
|
||||
initInputAndOutput,
|
||||
initTypescriptServer,
|
||||
} from './typescript-editor-utils';
|
||||
import preset from './preset';
|
||||
|
||||
initTypescriptServer();
|
||||
|
||||
export const TypescriptEditor = (props: EditorProps & EditorOtherProps) => {
|
||||
const {
|
||||
defaultContent,
|
||||
uuid,
|
||||
readonly,
|
||||
height,
|
||||
didMount,
|
||||
onChange,
|
||||
defaultLanguage,
|
||||
input,
|
||||
output,
|
||||
} = props;
|
||||
|
||||
const uri = `file:///ts_editor_${uuid}.ts`;
|
||||
|
||||
useEffect(() => {
|
||||
initInputAndOutput(input, output, uuid);
|
||||
}, [uuid]);
|
||||
|
||||
return (
|
||||
<EditorProvider>
|
||||
<Renderer
|
||||
plugins={preset}
|
||||
domProps={{
|
||||
style: {
|
||||
height: 'calc(100% - 48px)',
|
||||
},
|
||||
}}
|
||||
didMount={api => {
|
||||
didMount?.(api);
|
||||
api.$on('change', ({ value }) => {
|
||||
onChange?.(value, defaultLanguage);
|
||||
});
|
||||
}}
|
||||
defaultValue={defaultContent}
|
||||
extensions={[
|
||||
EditorView.theme({
|
||||
'&.cm-focused': {
|
||||
outline: 'none',
|
||||
},
|
||||
'&.cm-editor': {
|
||||
height: height || 'unset',
|
||||
},
|
||||
'.cm-content': {
|
||||
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
||||
},
|
||||
'.cm-content *': {
|
||||
fontFamily: 'inherit',
|
||||
},
|
||||
}),
|
||||
]}
|
||||
options={{
|
||||
uri,
|
||||
languageId: 'typescript',
|
||||
theme: 'code-editor-dark',
|
||||
height,
|
||||
readOnly: readonly,
|
||||
editable: !readonly,
|
||||
fontSize: 12,
|
||||
tabSize: 4,
|
||||
}}
|
||||
/>
|
||||
</EditorProvider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useMemo, type ReactNode } from 'react';
|
||||
|
||||
import { concatTestId, useNodeTestId } from '@coze-workflow/base';
|
||||
import {
|
||||
IconCozCodeFill,
|
||||
IconCozPlayCircle,
|
||||
IconCozSideCollapse,
|
||||
} from '@coze-arch/coze-design/icons';
|
||||
import {
|
||||
Select,
|
||||
Tooltip,
|
||||
Button,
|
||||
IconButton,
|
||||
Typography,
|
||||
} from '@coze-arch/coze-design';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
import { type EditorProps, type LanguageType } from '../../interface';
|
||||
|
||||
import style from './style.module.less';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
|
||||
const HELP_DOCUMENT_LINK = IS_OVERSEA
|
||||
? '/docs/guides/code_node?_lang=en'
|
||||
: '/docs/guides/code_node';
|
||||
|
||||
interface Props extends EditorProps {
|
||||
children: ReactNode;
|
||||
onLanguageSelect?: (language: LanguageType) => void;
|
||||
language: LanguageType;
|
||||
}
|
||||
|
||||
export const Layout = ({
|
||||
children,
|
||||
title,
|
||||
language,
|
||||
onClose,
|
||||
onTestRun,
|
||||
testRunIcon,
|
||||
onLanguageSelect,
|
||||
languageTemplates,
|
||||
}: Props) => {
|
||||
const optionList = useMemo(
|
||||
() =>
|
||||
languageTemplates?.map(e => ({
|
||||
value: e.language,
|
||||
label: e.displayName,
|
||||
})),
|
||||
[languageTemplates],
|
||||
);
|
||||
|
||||
const { getNodeSetterId } = useNodeTestId();
|
||||
const setterTestId = getNodeSetterId('biz-editor-layout');
|
||||
|
||||
return (
|
||||
<div className={style.container}>
|
||||
<div className={style.header}>
|
||||
<div className={style.title}>
|
||||
<div className={style['title-icon']}>
|
||||
<IconCozCodeFill />
|
||||
</div>
|
||||
<div className={style['title-content']}>{title}</div>
|
||||
|
||||
<Tooltip
|
||||
content={
|
||||
<div>
|
||||
{I18n.t('code_node_more_info')}
|
||||
<Text link={{ href: HELP_DOCUMENT_LINK, target: '_blank' }}>
|
||||
{I18n.t('code_node_help_doc')}
|
||||
</Text>
|
||||
</div>
|
||||
}
|
||||
theme={'dark'}
|
||||
>
|
||||
<Select
|
||||
onChange={value => onLanguageSelect?.(value as LanguageType)}
|
||||
value={language}
|
||||
data-testid={concatTestId(setterTestId, 'language-select')}
|
||||
renderSelectedItem={item => (
|
||||
<span
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: 'var(--coz-fg-secondary)',
|
||||
}}
|
||||
>
|
||||
{I18n.t('code_node_language')} {item.label}
|
||||
</span>
|
||||
)}
|
||||
size={'small'}
|
||||
optionList={optionList}
|
||||
></Select>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<Button
|
||||
color={'highlight'}
|
||||
data-testid={concatTestId(setterTestId, 'test-run')}
|
||||
icon={
|
||||
testRunIcon ? (
|
||||
<span
|
||||
style={{
|
||||
fontSize: 14,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{testRunIcon}
|
||||
</span>
|
||||
) : (
|
||||
<IconCozPlayCircle style={{ fontSize: 14 }} />
|
||||
)
|
||||
}
|
||||
size={'small'}
|
||||
onClick={onTestRun}
|
||||
>
|
||||
{I18n.t('code_node_test_code')}
|
||||
</Button>
|
||||
|
||||
<IconButton
|
||||
onClick={onClose}
|
||||
color={'secondary'}
|
||||
size={'small'}
|
||||
icon={<IconCozSideCollapse style={{ fontSize: 18 }} />}
|
||||
data-testid={concatTestId(setterTestId, 'expand-button')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
min-width: 214px;
|
||||
height: 48px;
|
||||
margin-bottom: 0;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
|
||||
font-size: 18px;
|
||||
color: #fff;
|
||||
|
||||
background-color: #28CAC8;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.title-content {
|
||||
min-width: 0;
|
||||
max-width: calc(100% - 160px);
|
||||
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: var(--coz-fg-primary);
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
|
||||
import { type EditorAPI } from '../editor/preset';
|
||||
import { Editor } from '../editor';
|
||||
import { type PreviewerProps } from '../../interface';
|
||||
|
||||
export const Previewer = (props: PreviewerProps) => {
|
||||
const apiRef = useRef<EditorAPI | null>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!apiRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (props.content !== apiRef.current.getValue()) {
|
||||
apiRef.current.forceSetValue(props.content);
|
||||
}
|
||||
}, [props.content]);
|
||||
|
||||
return (
|
||||
<Editor
|
||||
uuid={`previewer_${new Date().getTime()}`}
|
||||
height={`${props.height}px`}
|
||||
defaultLanguage={props.language}
|
||||
defaultContent={props.content}
|
||||
readonly
|
||||
didMount={api => {
|
||||
apiRef.current = api;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createTheme } from '@coze-editor/editor/preset-code';
|
||||
import { type Extension } from '@codemirror/state';
|
||||
|
||||
const colors = {
|
||||
background: '#151B27',
|
||||
};
|
||||
|
||||
export const createDarkTheme: () => Extension = () =>
|
||||
createTheme({
|
||||
variant: 'dark',
|
||||
settings: {
|
||||
background: colors.background,
|
||||
foreground: '#fff',
|
||||
caret: '#AEAFAD',
|
||||
selection: '#d9d9d942',
|
||||
gutterBackground: colors.background,
|
||||
gutterForeground: '#FFFFFF63',
|
||||
gutterBorderColor: 'transparent',
|
||||
gutterBorderWidth: 0,
|
||||
lineHighlight: '#272e3d36',
|
||||
bracketColors: ['#FFEF61', '#DD99FF', '#78B0FF'],
|
||||
tooltip: {
|
||||
backgroundColor: '#363D4D',
|
||||
color: '#fff',
|
||||
border: 'none',
|
||||
},
|
||||
completionItemHover: {
|
||||
backgroundColor: '#FFFFFF0F',
|
||||
},
|
||||
completionItemSelected: {
|
||||
backgroundColor: '#FFFFFF17',
|
||||
},
|
||||
completionItemIcon: {
|
||||
color: '#FFFFFFC9',
|
||||
},
|
||||
completionItemLabel: {
|
||||
color: '#FFFFFFC9',
|
||||
},
|
||||
completionItemDetail: {
|
||||
color: '#FFFFFF63',
|
||||
},
|
||||
},
|
||||
styles: [],
|
||||
});
|
||||
Reference in New Issue
Block a user