@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.
Files changed (28) hide show
  1. package/dist/esm/index.js +683 -4
  2. package/dist/esm/index.js.map +1 -1
  3. package/dist/index.d.mts +42 -10
  4. package/dist/index.d.ts +42 -10
  5. package/dist/index.js +707 -48
  6. package/dist/index.js.map +1 -1
  7. package/package.json +6 -5
  8. package/src/components/code-editor/config.json +9 -0
  9. package/src/components/code-editor/index.tsx +59 -0
  10. package/src/components/code-editor/language-features.ts +24 -0
  11. package/src/components/code-editor/theme/dark.ts +119 -0
  12. package/src/components/code-editor/theme/index.ts +12 -0
  13. package/src/components/code-editor/theme/light.ts +119 -0
  14. package/src/components/code-editor/utils.ts +20 -0
  15. package/src/components/index.ts +3 -0
  16. package/src/components/json-editor-with-variables/config.json +13 -0
  17. package/src/components/json-editor-with-variables/extensions/variable-tag.tsx +173 -0
  18. package/src/components/json-editor-with-variables/extensions/variable-tree.tsx +83 -0
  19. package/src/components/json-editor-with-variables/index.tsx +19 -0
  20. package/src/components/json-editor-with-variables/styles.tsx +44 -0
  21. package/src/components/prompt-editor/config.json +1 -1
  22. package/src/components/prompt-editor/index.tsx +15 -2
  23. package/src/components/prompt-editor/types.tsx +2 -0
  24. package/src/components/prompt-editor-with-inputs/config.json +13 -0
  25. package/src/components/prompt-editor-with-inputs/extensions/inputs-tree.tsx +82 -0
  26. package/src/components/prompt-editor-with-inputs/index.tsx +22 -0
  27. package/src/components/prompt-editor-with-inputs/inputs-picker.tsx +100 -0
  28. 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.20",
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.8d7a30",
33
+ "@coze-editor/editor": "0.1.0-alpha.879fbb",
34
34
  "@codemirror/view": "~6.38.0",
35
- "@flowgram.ai/editor": "0.2.20"
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.20",
52
- "@flowgram.ai/ts-config": "0.2.20"
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,9 @@
1
+ {
2
+ "name": "code-editor",
3
+ "depMaterials": [],
4
+ "depPackages": [
5
+ "@coze-editor/editor@0.1.0-alpha.879fbb",
6
+ "@codemirror/view",
7
+ "@codemirror/state"
8
+ ]
9
+ }
@@ -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
+ }
@@ -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,13 @@
1
+ {
2
+ "name": "json-editor-with-variables",
3
+ "depMaterials": [
4
+ "variable-selector",
5
+ "code-editor"
6
+ ],
7
+ "depPackages": [
8
+ "@coze-editor/editor@0.1.0-alpha.879fbb",
9
+ "@codemirror/view",
10
+ "styled-components",
11
+ "@douyinfe/semi-ui"
12
+ ]
13
+ }
@@ -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
+ }