@flowgram.ai/form-materials 0.2.20 → 0.2.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.js +683 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +42 -10
- package/dist/index.d.ts +42 -10
- package/dist/index.js +707 -48
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
- package/src/components/code-editor/config.json +9 -0
- package/src/components/code-editor/index.tsx +59 -0
- package/src/components/code-editor/language-features.ts +24 -0
- package/src/components/code-editor/theme/dark.ts +119 -0
- package/src/components/code-editor/theme/index.ts +12 -0
- package/src/components/code-editor/theme/light.ts +119 -0
- package/src/components/code-editor/utils.ts +20 -0
- package/src/components/index.ts +3 -0
- package/src/components/json-editor-with-variables/config.json +13 -0
- package/src/components/json-editor-with-variables/extensions/variable-tag.tsx +173 -0
- package/src/components/json-editor-with-variables/extensions/variable-tree.tsx +83 -0
- package/src/components/json-editor-with-variables/index.tsx +19 -0
- package/src/components/json-editor-with-variables/styles.tsx +44 -0
- package/src/components/prompt-editor/config.json +1 -1
- package/src/components/prompt-editor/index.tsx +15 -2
- package/src/components/prompt-editor/types.tsx +2 -0
- package/src/components/prompt-editor-with-inputs/config.json +13 -0
- package/src/components/prompt-editor-with-inputs/extensions/inputs-tree.tsx +82 -0
- package/src/components/prompt-editor-with-inputs/index.tsx +22 -0
- package/src/components/prompt-editor-with-inputs/inputs-picker.tsx +100 -0
- package/src/components/prompt-editor-with-variables/config.json +4 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowgram.ai/form-materials",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.22",
|
|
4
4
|
"homepage": "https://flowgram.ai/",
|
|
5
5
|
"repository": "https://github.com/bytedance/flowgram.ai",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,9 +30,10 @@
|
|
|
30
30
|
"chalk": "^5.3.0",
|
|
31
31
|
"inquirer": "^9.2.7",
|
|
32
32
|
"immer": "~10.1.1",
|
|
33
|
-
"@coze-editor/editor": "0.1.0-alpha.
|
|
33
|
+
"@coze-editor/editor": "0.1.0-alpha.879fbb",
|
|
34
34
|
"@codemirror/view": "~6.38.0",
|
|
35
|
-
"@
|
|
35
|
+
"@codemirror/state": "~6.5.2",
|
|
36
|
+
"@flowgram.ai/editor": "0.2.22"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
39
|
"@types/lodash": "^4.14.137",
|
|
@@ -48,8 +49,8 @@
|
|
|
48
49
|
"tsup": "^8.0.1",
|
|
49
50
|
"typescript": "^5.0.4",
|
|
50
51
|
"vitest": "^0.34.6",
|
|
51
|
-
"@flowgram.ai/eslint-config": "0.2.
|
|
52
|
-
"@flowgram.ai/ts-config": "0.2.
|
|
52
|
+
"@flowgram.ai/eslint-config": "0.2.22",
|
|
53
|
+
"@flowgram.ai/ts-config": "0.2.22"
|
|
53
54
|
},
|
|
54
55
|
"peerDependencies": {
|
|
55
56
|
"react": ">=16.8",
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useRef } from 'react';
|
|
7
|
+
|
|
8
|
+
import { createRenderer, EditorProvider } from '@coze-editor/editor/react';
|
|
9
|
+
import preset, { type EditorAPI } from '@coze-editor/editor/preset-code';
|
|
10
|
+
import { EditorView } from '@codemirror/view';
|
|
11
|
+
|
|
12
|
+
import { getSuffixByLanguageId } from './utils';
|
|
13
|
+
|
|
14
|
+
import './theme';
|
|
15
|
+
import './language-features';
|
|
16
|
+
|
|
17
|
+
const OriginCodeEditor = createRenderer(preset, [
|
|
18
|
+
EditorView.theme({
|
|
19
|
+
'&.cm-focused': {
|
|
20
|
+
outline: 'none',
|
|
21
|
+
},
|
|
22
|
+
}),
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
export interface CodeEditorPropsType extends React.PropsWithChildren<{}> {
|
|
26
|
+
value?: string;
|
|
27
|
+
onChange?: (value: string) => void;
|
|
28
|
+
languageId: 'python' | 'typescript' | 'shell' | 'json';
|
|
29
|
+
theme?: 'dark' | 'light';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function CodeEditor({
|
|
33
|
+
value,
|
|
34
|
+
onChange,
|
|
35
|
+
languageId = 'python',
|
|
36
|
+
theme = 'light',
|
|
37
|
+
children,
|
|
38
|
+
}: CodeEditorPropsType) {
|
|
39
|
+
const editorRef = useRef<EditorAPI | null>(null);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<EditorProvider>
|
|
43
|
+
<OriginCodeEditor
|
|
44
|
+
defaultValue={value}
|
|
45
|
+
options={{
|
|
46
|
+
uri: `file:///untitled${getSuffixByLanguageId(languageId)}`,
|
|
47
|
+
languageId,
|
|
48
|
+
theme,
|
|
49
|
+
}}
|
|
50
|
+
didMount={(editor: EditorAPI) => {
|
|
51
|
+
editorRef.current = editor;
|
|
52
|
+
}}
|
|
53
|
+
onChange={(e) => onChange?.(e.value)}
|
|
54
|
+
>
|
|
55
|
+
{children}
|
|
56
|
+
</OriginCodeEditor>
|
|
57
|
+
</EditorProvider>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { languages } from '@coze-editor/editor/preset-code';
|
|
7
|
+
// import { typescript } from '@coze-editor/editor/language-typescript';
|
|
8
|
+
import { shell } from '@coze-editor/editor/language-shell';
|
|
9
|
+
import { python } from '@coze-editor/editor/language-python';
|
|
10
|
+
import { json } from '@coze-editor/editor/language-json';
|
|
11
|
+
import { mixLanguages } from '@coze-editor/editor';
|
|
12
|
+
|
|
13
|
+
languages.register('python', python);
|
|
14
|
+
// languages.register('typescript', typescript);
|
|
15
|
+
|
|
16
|
+
languages.register('json', {
|
|
17
|
+
// mixLanguages is used to solve the problem that interpolation also uses parentheses, which causes incorrect highlighting
|
|
18
|
+
language: mixLanguages({
|
|
19
|
+
outerLanguage: json.language,
|
|
20
|
+
}),
|
|
21
|
+
languageService: json.languageService,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
languages.register('shell', shell);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createTheme, tags as t } from '@coze-editor/editor/preset-code';
|
|
7
|
+
import { type Extension } from '@codemirror/state';
|
|
8
|
+
|
|
9
|
+
const colors = {
|
|
10
|
+
background: '#151B27',
|
|
11
|
+
// syntax
|
|
12
|
+
comment: '#FFFFFF63',
|
|
13
|
+
key: '#39E5D7',
|
|
14
|
+
string: '#FF94D2',
|
|
15
|
+
number: '#FF9933',
|
|
16
|
+
boolean: '#78B0FF',
|
|
17
|
+
null: '#78B0FF',
|
|
18
|
+
separator: '#FFFFFFC9',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const darkTheme: Extension = createTheme({
|
|
22
|
+
variant: 'dark',
|
|
23
|
+
settings: {
|
|
24
|
+
background: colors.background,
|
|
25
|
+
foreground: '#fff',
|
|
26
|
+
caret: '#AEAFAD',
|
|
27
|
+
selection: '#d9d9d942',
|
|
28
|
+
gutterBackground: colors.background,
|
|
29
|
+
gutterForeground: '#FFFFFF63',
|
|
30
|
+
gutterBorderColor: 'transparent',
|
|
31
|
+
gutterBorderWidth: 0,
|
|
32
|
+
lineHighlight: '#272e3d36',
|
|
33
|
+
bracketColors: ['#FFEF61', '#DD99FF', '#78B0FF'],
|
|
34
|
+
tooltip: {
|
|
35
|
+
backgroundColor: '#363D4D',
|
|
36
|
+
color: '#fff',
|
|
37
|
+
border: 'none',
|
|
38
|
+
},
|
|
39
|
+
link: {
|
|
40
|
+
color: '#4daafc',
|
|
41
|
+
},
|
|
42
|
+
completionItemHover: {
|
|
43
|
+
backgroundColor: '#FFFFFF0F',
|
|
44
|
+
},
|
|
45
|
+
completionItemSelected: {
|
|
46
|
+
backgroundColor: '#FFFFFF17',
|
|
47
|
+
},
|
|
48
|
+
completionItemIcon: {
|
|
49
|
+
color: '#FFFFFFC9',
|
|
50
|
+
},
|
|
51
|
+
completionItemLabel: {
|
|
52
|
+
color: '#FFFFFFC9',
|
|
53
|
+
},
|
|
54
|
+
completionItemInfo: {
|
|
55
|
+
color: '#FFFFFFC9',
|
|
56
|
+
},
|
|
57
|
+
completionItemDetail: {
|
|
58
|
+
color: '#FFFFFF63',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
styles: [
|
|
62
|
+
// json
|
|
63
|
+
{
|
|
64
|
+
tag: t.comment,
|
|
65
|
+
color: colors.comment,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
tag: [t.propertyName],
|
|
69
|
+
color: colors.key,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
tag: [t.string],
|
|
73
|
+
color: colors.string,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
tag: [t.number],
|
|
77
|
+
color: colors.number,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
tag: [t.bool],
|
|
81
|
+
color: colors.boolean,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
tag: [t.null],
|
|
85
|
+
color: colors.null,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
tag: [t.separator],
|
|
89
|
+
color: colors.separator,
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// markdown
|
|
93
|
+
{
|
|
94
|
+
tag: [t.heading],
|
|
95
|
+
color: '#6b6bff',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
tag: [t.processingInstruction],
|
|
99
|
+
color: '#6b6bff',
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
// shell
|
|
103
|
+
// curl
|
|
104
|
+
{
|
|
105
|
+
tag: [t.standard(t.variableName)],
|
|
106
|
+
color: '#3BEB84',
|
|
107
|
+
},
|
|
108
|
+
// -X
|
|
109
|
+
{
|
|
110
|
+
tag: [t.attributeName],
|
|
111
|
+
color: '#FF9933',
|
|
112
|
+
},
|
|
113
|
+
// url in string (includes quotes), e.g. "https://..."
|
|
114
|
+
{
|
|
115
|
+
tag: [t.special(t.string)],
|
|
116
|
+
color: '#78B0FF',
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { themes } from '@coze-editor/editor/preset-code';
|
|
7
|
+
|
|
8
|
+
import { lightTheme } from './light';
|
|
9
|
+
import { darkTheme } from './dark';
|
|
10
|
+
|
|
11
|
+
themes.register('dark', darkTheme);
|
|
12
|
+
themes.register('light', lightTheme);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createTheme, tags as t } from '@coze-editor/editor/preset-code';
|
|
7
|
+
import { type Extension } from '@codemirror/state';
|
|
8
|
+
|
|
9
|
+
const colors = {
|
|
10
|
+
background: '#F7F7FC',
|
|
11
|
+
// syntax
|
|
12
|
+
comment: '#000A298A',
|
|
13
|
+
key: '#00818C',
|
|
14
|
+
string: '#D1009D',
|
|
15
|
+
number: '#C74200',
|
|
16
|
+
boolean: '#2B57D9',
|
|
17
|
+
null: '#2B57D9',
|
|
18
|
+
separator: '#0F1529D1',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const lightTheme: Extension = createTheme({
|
|
22
|
+
variant: 'light',
|
|
23
|
+
settings: {
|
|
24
|
+
background: '#fff',
|
|
25
|
+
foreground: '#000',
|
|
26
|
+
caret: '#000',
|
|
27
|
+
selection: '#d9d9d9',
|
|
28
|
+
gutterBackground: '#f0f0f0',
|
|
29
|
+
gutterForeground: '#666',
|
|
30
|
+
gutterBorderColor: 'transparent',
|
|
31
|
+
gutterBorderWidth: 0,
|
|
32
|
+
lineHighlight: '#f0f0f0',
|
|
33
|
+
bracketColors: ['#FFEF61', '#DD99FF', '#78B0FF'],
|
|
34
|
+
tooltip: {
|
|
35
|
+
backgroundColor: '#f0f0f0',
|
|
36
|
+
color: '#000',
|
|
37
|
+
border: '1px solid #ccc',
|
|
38
|
+
},
|
|
39
|
+
link: {
|
|
40
|
+
color: '#007bff',
|
|
41
|
+
},
|
|
42
|
+
completionItemHover: {
|
|
43
|
+
backgroundColor: '#f0f0f0',
|
|
44
|
+
},
|
|
45
|
+
completionItemSelected: {
|
|
46
|
+
backgroundColor: '#e0e0e0',
|
|
47
|
+
},
|
|
48
|
+
completionItemIcon: {
|
|
49
|
+
color: '#333',
|
|
50
|
+
},
|
|
51
|
+
completionItemLabel: {
|
|
52
|
+
color: '#333',
|
|
53
|
+
},
|
|
54
|
+
completionItemInfo: {
|
|
55
|
+
color: '#333',
|
|
56
|
+
},
|
|
57
|
+
completionItemDetail: {
|
|
58
|
+
color: '#666',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
styles: [
|
|
62
|
+
// JSON
|
|
63
|
+
{
|
|
64
|
+
tag: t.comment,
|
|
65
|
+
color: colors.comment,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
tag: [t.propertyName],
|
|
69
|
+
color: colors.key,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
tag: [t.string],
|
|
73
|
+
color: colors.string,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
tag: [t.number],
|
|
77
|
+
color: colors.number,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
tag: [t.bool],
|
|
81
|
+
color: colors.boolean,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
tag: [t.null],
|
|
85
|
+
color: colors.null,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
tag: [t.separator],
|
|
89
|
+
color: colors.separator,
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// markdown
|
|
93
|
+
{
|
|
94
|
+
tag: [t.heading],
|
|
95
|
+
color: '#3e76ef',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
tag: [t.processingInstruction],
|
|
99
|
+
color: '#3e76ef',
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
// shell
|
|
103
|
+
// curl
|
|
104
|
+
{
|
|
105
|
+
tag: [t.standard(t.variableName)],
|
|
106
|
+
color: '#00804A',
|
|
107
|
+
},
|
|
108
|
+
// -X
|
|
109
|
+
{
|
|
110
|
+
tag: [t.attributeName],
|
|
111
|
+
color: '#C74200',
|
|
112
|
+
},
|
|
113
|
+
// url in string (includes quotes), e.g. "https://..."
|
|
114
|
+
{
|
|
115
|
+
tag: [t.special(t.string)],
|
|
116
|
+
color: '#2B57D9',
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export function getSuffixByLanguageId(languageId: string) {
|
|
7
|
+
if (languageId === 'python') {
|
|
8
|
+
return '.py';
|
|
9
|
+
}
|
|
10
|
+
if (languageId === 'typescript') {
|
|
11
|
+
return '.ts';
|
|
12
|
+
}
|
|
13
|
+
if (languageId === 'shell') {
|
|
14
|
+
return '.sh';
|
|
15
|
+
}
|
|
16
|
+
if (languageId === 'json') {
|
|
17
|
+
return '.json';
|
|
18
|
+
}
|
|
19
|
+
return '';
|
|
20
|
+
}
|
package/src/components/index.ts
CHANGED
|
@@ -13,3 +13,6 @@ export * from './condition-row';
|
|
|
13
13
|
export * from './batch-outputs';
|
|
14
14
|
export * from './prompt-editor';
|
|
15
15
|
export * from './prompt-editor-with-variables';
|
|
16
|
+
export * from './prompt-editor-with-inputs';
|
|
17
|
+
export * from './code-editor';
|
|
18
|
+
export * from './json-editor-with-variables';
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useLayoutEffect } from 'react';
|
|
7
|
+
|
|
8
|
+
import { createRoot, Root } from 'react-dom/client';
|
|
9
|
+
import { isEqual, last } from 'lodash';
|
|
10
|
+
import {
|
|
11
|
+
BaseVariableField,
|
|
12
|
+
Disposable,
|
|
13
|
+
DisposableCollection,
|
|
14
|
+
Scope,
|
|
15
|
+
useCurrentScope,
|
|
16
|
+
} from '@flowgram.ai/editor';
|
|
17
|
+
import { Popover } from '@douyinfe/semi-ui';
|
|
18
|
+
import { IconIssueStroked } from '@douyinfe/semi-icons';
|
|
19
|
+
import { useInjector } from '@coze-editor/editor/react';
|
|
20
|
+
import {
|
|
21
|
+
Decoration,
|
|
22
|
+
DecorationSet,
|
|
23
|
+
EditorView,
|
|
24
|
+
MatchDecorator,
|
|
25
|
+
ViewPlugin,
|
|
26
|
+
WidgetType,
|
|
27
|
+
} from '@codemirror/view';
|
|
28
|
+
|
|
29
|
+
import { UIPopoverContent, UIRootTitle, UITag, UIVarName } from '../styles';
|
|
30
|
+
|
|
31
|
+
class VariableTagWidget extends WidgetType {
|
|
32
|
+
keyPath?: string[];
|
|
33
|
+
|
|
34
|
+
toDispose = new DisposableCollection();
|
|
35
|
+
|
|
36
|
+
scope: Scope;
|
|
37
|
+
|
|
38
|
+
root: Root;
|
|
39
|
+
|
|
40
|
+
constructor({ keyPath, scope }: { keyPath?: string[]; scope: Scope }) {
|
|
41
|
+
super();
|
|
42
|
+
|
|
43
|
+
this.keyPath = keyPath;
|
|
44
|
+
this.scope = scope;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
renderIcon = (icon: string | JSX.Element) => {
|
|
48
|
+
if (typeof icon === 'string') {
|
|
49
|
+
return <img style={{ marginRight: 8 }} width={12} height={12} src={icon} />;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return icon;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
renderVariable(v?: BaseVariableField) {
|
|
56
|
+
if (!v) {
|
|
57
|
+
this.root.render(
|
|
58
|
+
<UITag prefixIcon={<IconIssueStroked />} color="amber">
|
|
59
|
+
Unknown
|
|
60
|
+
</UITag>
|
|
61
|
+
);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const rootField = last(v.parentFields);
|
|
66
|
+
|
|
67
|
+
const rootTitle = (
|
|
68
|
+
<UIRootTitle>{rootField?.meta.title ? `${rootField.meta.title} -` : ''}</UIRootTitle>
|
|
69
|
+
);
|
|
70
|
+
const rootIcon = this.renderIcon(rootField?.meta.icon);
|
|
71
|
+
|
|
72
|
+
this.root.render(
|
|
73
|
+
<Popover
|
|
74
|
+
content={
|
|
75
|
+
<UIPopoverContent>
|
|
76
|
+
{rootIcon}
|
|
77
|
+
{rootTitle}
|
|
78
|
+
<UIVarName>{v?.keyPath.slice(1).join('.')}</UIVarName>
|
|
79
|
+
</UIPopoverContent>
|
|
80
|
+
}
|
|
81
|
+
>
|
|
82
|
+
<UITag prefixIcon={rootIcon}>
|
|
83
|
+
{rootTitle}
|
|
84
|
+
<UIVarName>{v?.key}</UIVarName>
|
|
85
|
+
</UITag>
|
|
86
|
+
</Popover>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
toDOM(view: EditorView): HTMLElement {
|
|
91
|
+
const dom = document.createElement('span');
|
|
92
|
+
|
|
93
|
+
this.root = createRoot(dom);
|
|
94
|
+
|
|
95
|
+
this.toDispose.push(
|
|
96
|
+
Disposable.create(() => {
|
|
97
|
+
this.root.unmount();
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
this.toDispose.push(
|
|
102
|
+
this.scope.available.trackByKeyPath(
|
|
103
|
+
this.keyPath,
|
|
104
|
+
(v) => {
|
|
105
|
+
this.renderVariable(v);
|
|
106
|
+
},
|
|
107
|
+
{ triggerOnInit: false }
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
this.renderVariable(this.scope.available.getByKeyPath(this.keyPath));
|
|
112
|
+
|
|
113
|
+
return dom;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
eq(other: VariableTagWidget) {
|
|
117
|
+
return isEqual(this.keyPath, other.keyPath);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
ignoreEvent(): boolean {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
destroy(dom: HTMLElement): void {
|
|
125
|
+
this.toDispose.dispose();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function VariableTagInject() {
|
|
130
|
+
const injector = useInjector();
|
|
131
|
+
|
|
132
|
+
const scope = useCurrentScope();
|
|
133
|
+
|
|
134
|
+
// 基于 {{var}} 的正则进行匹配,匹配后进行自定义渲染
|
|
135
|
+
useLayoutEffect(() => {
|
|
136
|
+
const atMatcher = new MatchDecorator({
|
|
137
|
+
regexp: /\{\{([^\}]+)\}\}/g,
|
|
138
|
+
decoration: (match) =>
|
|
139
|
+
Decoration.replace({
|
|
140
|
+
widget: new VariableTagWidget({
|
|
141
|
+
keyPath: match[1]?.split('.') ?? [],
|
|
142
|
+
scope,
|
|
143
|
+
}),
|
|
144
|
+
}),
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return injector.inject([
|
|
148
|
+
ViewPlugin.fromClass(
|
|
149
|
+
class {
|
|
150
|
+
decorations: DecorationSet;
|
|
151
|
+
|
|
152
|
+
constructor(private view: EditorView) {
|
|
153
|
+
this.decorations = atMatcher.createDeco(view);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
update() {
|
|
157
|
+
this.decorations = atMatcher.createDeco(this.view);
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
decorations: (p) => p.decorations,
|
|
162
|
+
provide(p) {
|
|
163
|
+
return EditorView.atomicRanges.of(
|
|
164
|
+
(view) => view.plugin(p)?.decorations ?? Decoration.none
|
|
165
|
+
);
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
),
|
|
169
|
+
]);
|
|
170
|
+
}, [injector]);
|
|
171
|
+
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useEffect, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { Popover, Tree } from '@douyinfe/semi-ui';
|
|
9
|
+
import {
|
|
10
|
+
Mention,
|
|
11
|
+
MentionOpenChangeEvent,
|
|
12
|
+
getCurrentMentionReplaceRange,
|
|
13
|
+
useEditor,
|
|
14
|
+
PositionMirror,
|
|
15
|
+
} from '@coze-editor/editor/react';
|
|
16
|
+
import { EditorAPI } from '@coze-editor/editor/preset-prompt';
|
|
17
|
+
|
|
18
|
+
import { useVariableTree } from '../../variable-selector';
|
|
19
|
+
|
|
20
|
+
export function VariableTree() {
|
|
21
|
+
const [posKey, setPosKey] = useState('');
|
|
22
|
+
const [visible, setVisible] = useState(false);
|
|
23
|
+
const [position, setPosition] = useState(-1);
|
|
24
|
+
const editor = useEditor<EditorAPI>();
|
|
25
|
+
|
|
26
|
+
function insert(variablePath: string) {
|
|
27
|
+
const range = getCurrentMentionReplaceRange(editor.$view.state);
|
|
28
|
+
|
|
29
|
+
if (!range) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
editor.replaceText({
|
|
34
|
+
...range,
|
|
35
|
+
text: '{{' + variablePath + '}}',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
setVisible(false);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function handleOpenChange(e: MentionOpenChangeEvent) {
|
|
42
|
+
setPosition(e.state.selection.main.head);
|
|
43
|
+
setVisible(e.value);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!editor) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}, [editor, visible]);
|
|
51
|
+
|
|
52
|
+
const treeData = useVariableTree({});
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
<Mention triggerCharacters={['@']} onOpenChange={handleOpenChange} />
|
|
57
|
+
|
|
58
|
+
<Popover
|
|
59
|
+
visible={visible}
|
|
60
|
+
trigger="custom"
|
|
61
|
+
position="topLeft"
|
|
62
|
+
rePosKey={posKey}
|
|
63
|
+
content={
|
|
64
|
+
<div style={{ width: 300 }}>
|
|
65
|
+
<Tree
|
|
66
|
+
treeData={treeData}
|
|
67
|
+
onSelect={(v) => {
|
|
68
|
+
insert(v);
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
71
|
+
</div>
|
|
72
|
+
}
|
|
73
|
+
>
|
|
74
|
+
{/* PositionMirror allows the Popover to appear at the specified cursor position */}
|
|
75
|
+
<PositionMirror
|
|
76
|
+
position={position}
|
|
77
|
+
// When Doc scroll, update position
|
|
78
|
+
onChange={() => setPosKey(String(Math.random()))}
|
|
79
|
+
/>
|
|
80
|
+
</Popover>
|
|
81
|
+
</>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import { VariableTree } from './extensions/variable-tree';
|
|
9
|
+
import { VariableTagInject } from './extensions/variable-tag';
|
|
10
|
+
import { CodeEditor, type CodeEditorPropsType } from '../code-editor';
|
|
11
|
+
|
|
12
|
+
export function JsonEditorWithVariables(props: Omit<CodeEditorPropsType, 'languageId'>) {
|
|
13
|
+
return (
|
|
14
|
+
<CodeEditor languageId="json" {...props}>
|
|
15
|
+
<VariableTree />
|
|
16
|
+
<VariableTagInject />
|
|
17
|
+
</CodeEditor>
|
|
18
|
+
);
|
|
19
|
+
}
|