|
@@ -3,7 +3,7 @@
|
|
|
* SPDX-License-Identifier: MIT
|
|
* SPDX-License-Identifier: MIT
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
-import React, { useMemo } from 'react';
|
|
|
|
|
|
|
+import React, { useMemo, useEffect, useState } from 'react';
|
|
|
|
|
|
|
|
import { isPlainObject, last } from 'lodash-es';
|
|
import { isPlainObject, last } from 'lodash-es';
|
|
|
import {
|
|
import {
|
|
@@ -13,10 +13,18 @@ import {
|
|
|
type BaseVariableField,
|
|
type BaseVariableField,
|
|
|
useScopeAvailable,
|
|
useScopeAvailable,
|
|
|
} from '@flowgram.ai/editor';
|
|
} from '@flowgram.ai/editor';
|
|
|
-import { TreeNodeData } from '@douyinfe/semi-ui/lib/es/tree';
|
|
|
|
|
-import { Tree } from '@douyinfe/semi-ui';
|
|
|
|
|
-
|
|
|
|
|
-import { FlowValueUtils } from '@/shared';
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ Mention,
|
|
|
|
|
+ MentionOpenChangeEvent,
|
|
|
|
|
+ getCurrentMentionReplaceRange,
|
|
|
|
|
+ useEditor,
|
|
|
|
|
+ PositionMirror,
|
|
|
|
|
+} from '@flowgram.ai/coze-editor/react';
|
|
|
|
|
+import { EditorAPI } from '@flowgram.ai/coze-editor/preset-prompt';
|
|
|
|
|
+import { type TreeNodeData } from '@douyinfe/semi-ui/lib/es/tree';
|
|
|
|
|
+import { Tree, Popover } from '@douyinfe/semi-ui';
|
|
|
|
|
+
|
|
|
|
|
+import { IFlowValue, FlowValueUtils } from '@/shared';
|
|
|
|
|
|
|
|
type VariableField = BaseVariableField<{ icon?: string | JSX.Element; title?: string }>;
|
|
type VariableField = BaseVariableField<{ icon?: string | JSX.Element; title?: string }>;
|
|
|
|
|
|
|
@@ -115,3 +123,85 @@ export function InputsPicker({
|
|
|
|
|
|
|
|
return <Tree treeData={treeData} onSelect={(v) => onSelect(v)} />;
|
|
return <Tree treeData={treeData} onSelect={(v) => onSelect(v)} />;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+const DEFAULT_TRIGGER_CHARACTERS = ['{', '{}', '@'];
|
|
|
|
|
+
|
|
|
|
|
+export function InputsTree({
|
|
|
|
|
+ inputsValues,
|
|
|
|
|
+ triggerCharacters = DEFAULT_TRIGGER_CHARACTERS,
|
|
|
|
|
+}: {
|
|
|
|
|
+ inputsValues: Record<string, IFlowValue>;
|
|
|
|
|
+ triggerCharacters?: string[];
|
|
|
|
|
+}) {
|
|
|
|
|
+ const [posKey, setPosKey] = useState('');
|
|
|
|
|
+ const [visible, setVisible] = useState(false);
|
|
|
|
|
+ const [position, setPosition] = useState(-1);
|
|
|
|
|
+ const editor = useEditor<EditorAPI>();
|
|
|
|
|
+
|
|
|
|
|
+ function insert(variablePath: string) {
|
|
|
|
|
+ const range = getCurrentMentionReplaceRange(editor.$view.state);
|
|
|
|
|
+
|
|
|
|
|
+ if (!range) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * When user input {{xxxx}}, {{{xxx}}}(more brackets if possible), replace all brackets with {{xxxx}}
|
|
|
|
|
+ */
|
|
|
|
|
+ let { from, to } = range;
|
|
|
|
|
+ while (editor.$view.state.doc.sliceString(from - 1, from) === '{') {
|
|
|
|
|
+ from--;
|
|
|
|
|
+ }
|
|
|
|
|
+ while (editor.$view.state.doc.sliceString(to, to + 1) === '}') {
|
|
|
|
|
+ to++;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ editor.replaceText({
|
|
|
|
|
+ ...range,
|
|
|
|
|
+ text: '{{' + variablePath + '}}',
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ setVisible(false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function handleOpenChange(e: MentionOpenChangeEvent) {
|
|
|
|
|
+ setPosition(e.state.selection.main.head);
|
|
|
|
|
+ setVisible(e.value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (!editor) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [editor, visible]);
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <Mention triggerCharacters={triggerCharacters} onOpenChange={handleOpenChange} />
|
|
|
|
|
+
|
|
|
|
|
+ <Popover
|
|
|
|
|
+ visible={visible}
|
|
|
|
|
+ trigger="custom"
|
|
|
|
|
+ position="topLeft"
|
|
|
|
|
+ rePosKey={posKey}
|
|
|
|
|
+ content={
|
|
|
|
|
+ <div style={{ width: 300, maxHeight: 300, overflowY: 'auto' }}>
|
|
|
|
|
+ <InputsPicker
|
|
|
|
|
+ inputsValues={inputsValues}
|
|
|
|
|
+ onSelect={(v) => {
|
|
|
|
|
+ insert(v);
|
|
|
|
|
+ }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
|
|
+ >
|
|
|
|
|
+ {/* PositionMirror allows the Popover to appear at the specified cursor position */}
|
|
|
|
|
+ <PositionMirror
|
|
|
|
|
+ position={position}
|
|
|
|
|
+ // When Doc scroll, update position
|
|
|
|
|
+ onChange={() => setPosKey(String(Math.random()))}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Popover>
|
|
|
|
|
+ </>
|
|
|
|
|
+ );
|
|
|
|
|
+}
|