Просмотр исходного кода

chore: rename variable-plugin in demo to sync-variable-plugin

sanmaopep 10 месяцев назад
Родитель
Сommit
f9febe5fc3
30 измененных файлов с 234 добавлено и 166 удалено
  1. 2 2
      apps/demo-fixed-layout/src/form-components/fx-expression/index.tsx
  2. 6 6
      apps/demo-fixed-layout/src/form-components/properties-edit/property-edit.tsx
  3. 4 4
      apps/demo-fixed-layout/src/form-components/type-selector.tsx
  4. 1 1
      apps/demo-fixed-layout/src/form-components/type-tag.tsx
  5. 2 2
      apps/demo-fixed-layout/src/hooks/use-editor-props.ts
  6. 1 1
      apps/demo-fixed-layout/src/plugins/index.ts
  7. 0 0
      apps/demo-fixed-layout/src/plugins/sync-variable-plugin/icons.tsx
  8. 1 0
      apps/demo-fixed-layout/src/plugins/sync-variable-plugin/index.ts
  9. 78 0
      apps/demo-fixed-layout/src/plugins/sync-variable-plugin/sync-variable-plugin.ts
  10. 22 1
      apps/demo-fixed-layout/src/plugins/sync-variable-plugin/utils.ts
  11. 0 0
      apps/demo-fixed-layout/src/plugins/sync-variable-plugin/variable-selector/index.module.less
  12. 0 0
      apps/demo-fixed-layout/src/plugins/sync-variable-plugin/variable-selector/index.tsx
  13. 0 0
      apps/demo-fixed-layout/src/plugins/sync-variable-plugin/variable-selector/use-variable-tree.ts
  14. 0 1
      apps/demo-fixed-layout/src/plugins/variable-plugin/index.ts
  15. 0 65
      apps/demo-fixed-layout/src/plugins/variable-plugin/variable-plugin.ts
  16. 2 2
      apps/demo-free-layout/src/form-components/fx-expression/index.tsx
  17. 6 6
      apps/demo-free-layout/src/form-components/properties-edit/property-edit.tsx
  18. 4 4
      apps/demo-free-layout/src/form-components/type-selector.tsx
  19. 1 1
      apps/demo-free-layout/src/form-components/type-tag.tsx
  20. 2 2
      apps/demo-free-layout/src/hooks/use-editor-props.tsx
  21. 1 1
      apps/demo-free-layout/src/plugins/index.ts
  22. 0 0
      apps/demo-free-layout/src/plugins/sync-variable-plugin/icons.tsx
  23. 1 0
      apps/demo-free-layout/src/plugins/sync-variable-plugin/index.ts
  24. 78 0
      apps/demo-free-layout/src/plugins/sync-variable-plugin/sync-variable-plugin.ts
  25. 22 1
      apps/demo-free-layout/src/plugins/sync-variable-plugin/utils.ts
  26. 0 0
      apps/demo-free-layout/src/plugins/sync-variable-plugin/variable-selector/index.module.less
  27. 0 0
      apps/demo-free-layout/src/plugins/sync-variable-plugin/variable-selector/index.tsx
  28. 0 0
      apps/demo-free-layout/src/plugins/sync-variable-plugin/variable-selector/use-variable-tree.ts
  29. 0 1
      apps/demo-free-layout/src/plugins/variable-plugin/index.ts
  30. 0 65
      apps/demo-free-layout/src/plugins/variable-plugin/variable-plugin.ts

+ 2 - 2
apps/demo-fixed-layout/src/form-components/fx-expression/index.tsx

@@ -3,7 +3,7 @@ import { type SVGProps } from 'react';
 import { Input, Button } from '@douyinfe/semi-ui';
 
 import { FlowValueSchema, FlowRefValueSchema } from '../../typings';
-import { VariableSelector } from '../../plugins/variable-plugin/variable-selector';
+import { VariableSelector } from '../../plugins/sync-variable-plugin/variable-selector';
 
 export function FxIcon(props: SVGProps<SVGSVGElement>) {
   return (
@@ -43,7 +43,7 @@ export function FxExpression(props: FxExpressionProps) {
         <VariableSelector
           value={value.content}
           style={{ flexGrow: 1 }}
-          onChange={v => onChange({ type: 'expression', content: v })}
+          onChange={(v) => onChange({ type: 'expression', content: v })}
           disabled={disabled}
         />
       ) : (

+ 6 - 6
apps/demo-fixed-layout/src/form-components/properties-edit/property-edit.tsx

@@ -5,7 +5,7 @@ import { IconCrossCircleStroked } from '@douyinfe/semi-icons';
 
 import { TypeSelector } from '../type-selector';
 import { JsonSchema } from '../../typings';
-import { VariableSelector } from '../../plugins/variable-plugin/variable-selector';
+import { VariableSelector } from '../../plugins/sync-variable-plugin/variable-selector';
 import { LeftColumn, Row } from './styles';
 
 export interface PropertyEditProps {
@@ -17,7 +17,7 @@ export interface PropertyEditProps {
   onDelete?: () => void;
 }
 
-export const PropertyEdit: React.FC<PropertyEditProps> = props => {
+export const PropertyEdit: React.FC<PropertyEditProps> = (props) => {
   const { value, disabled } = props;
   const [inputKey, updateKey] = useState(props.propertyKey);
   const updateProperty = (key: keyof JsonSchema, val: any) => {
@@ -34,12 +34,12 @@ export const PropertyEdit: React.FC<PropertyEditProps> = props => {
           value={value.type}
           disabled={disabled}
           style={{ position: 'absolute', top: 6, left: 4, zIndex: 1 }}
-          onChange={val => updateProperty('type', val)}
+          onChange={(val) => updateProperty('type', val)}
         />
         <Input
           value={inputKey}
           disabled={disabled}
-          onChange={v => updateKey(v.trim())}
+          onChange={(v) => updateKey(v.trim())}
           onBlur={() => {
             if (inputKey !== '') {
               props.onChange(value, props.propertyKey, inputKey);
@@ -54,14 +54,14 @@ export const PropertyEdit: React.FC<PropertyEditProps> = props => {
         <VariableSelector
           value={value.default}
           disabled={disabled}
-          onChange={val => updateProperty('default', val)}
+          onChange={(val) => updateProperty('default', val)}
           style={{ flexGrow: 1, height: 32 }}
         />
       ) : (
         <Input
           disabled={disabled}
           value={value.default}
-          onChange={val => updateProperty('default', val)}
+          onChange={(val) => updateProperty('default', val)}
         />
       )}
       {props.onDelete && !disabled && (

+ 4 - 4
apps/demo-fixed-layout/src/form-components/type-selector.tsx

@@ -2,7 +2,7 @@ import React from 'react';
 
 import { Tag, Dropdown } from '@douyinfe/semi-ui';
 
-import { VariableTypeIcons } from '../plugins/variable-plugin/icons';
+import { VariableTypeIcons } from '../plugins/sync-variable-plugin/icons';
 
 export interface TypeSelectorProps {
   value?: string;
@@ -12,7 +12,7 @@ export interface TypeSelectorProps {
 }
 const dropdownMenus = ['object', 'boolean', 'array', 'string', 'integer', 'number'];
 
-export const TypeSelector: React.FC<TypeSelectorProps> = props => {
+export const TypeSelector: React.FC<TypeSelectorProps> = (props) => {
   const { value, disabled } = props;
   const icon = VariableTypeIcons[value as any];
   return (
@@ -22,7 +22,7 @@ export const TypeSelector: React.FC<TypeSelectorProps> = props => {
       disabled={disabled}
       render={
         <Dropdown.Menu>
-          {dropdownMenus.map(key => (
+          {dropdownMenus.map((key) => (
             <Dropdown.Item
               key={key}
               onClick={() => {
@@ -39,7 +39,7 @@ export const TypeSelector: React.FC<TypeSelectorProps> = props => {
       <Tag
         color="white"
         style={props.style}
-        onClick={e => {
+        onClick={(e) => {
           e.stopPropagation();
           e.preventDefault();
         }}

+ 1 - 1
apps/demo-fixed-layout/src/form-components/type-tag.tsx

@@ -1,7 +1,7 @@
 import styled from 'styled-components';
 import { Tag, Tooltip } from '@douyinfe/semi-ui';
 
-import { VariableTypeIcons, ArrayIcons } from '../plugins/variable-plugin/icons';
+import { VariableTypeIcons, ArrayIcons } from '../plugins/sync-variable-plugin/icons';
 
 interface PropsType {
   name?: string | JSX.Element;

+ 2 - 2
apps/demo-fixed-layout/src/hooks/use-editor-props.ts

@@ -14,7 +14,7 @@ import {
 import { type FlowNodeRegistry } from '../typings';
 import { shortcutGetter } from '../shortcuts';
 import { GroupBoxHeader, GroupNode } from '../plugins/group-plugin';
-import { createVariablePlugin, createClipboardPlugin } from '../plugins';
+import { createSyncVariablePlugin, createClipboardPlugin } from '../plugins';
 import { defaultFormMeta } from '../nodes';
 import { SelectorBoxPopover } from '../components/selector-box-popover';
 import NodeAdder from '../components/node-adder';
@@ -227,7 +227,7 @@ export function useEditorProps(
          * Variable plugin
          * 变量插件
          */
-        createVariablePlugin({}),
+        createSyncVariablePlugin({}),
         /**
          * Clipboard plugin
          * 剪切板插件

+ 1 - 1
apps/demo-fixed-layout/src/plugins/index.ts

@@ -1,2 +1,2 @@
 export { createClipboardPlugin } from './clipboard-plugin/create-clipboard-plugin';
-export { createVariablePlugin } from './variable-plugin/variable-plugin';
+export { createSyncVariablePlugin } from './sync-variable-plugin/sync-variable-plugin';

+ 0 - 0
apps/demo-fixed-layout/src/plugins/variable-plugin/icons.tsx → apps/demo-fixed-layout/src/plugins/sync-variable-plugin/icons.tsx


+ 1 - 0
apps/demo-fixed-layout/src/plugins/sync-variable-plugin/index.ts

@@ -0,0 +1 @@
+export * from './sync-variable-plugin';

+ 78 - 0
apps/demo-fixed-layout/src/plugins/sync-variable-plugin/sync-variable-plugin.ts

@@ -0,0 +1,78 @@
+import {
+  definePluginCreator,
+  FlowNodeVariableData,
+  getNodeForm,
+  PluginCreator,
+  FixedLayoutPluginContext,
+  ASTFactory,
+} from '@flowgram.ai/fixed-layout-editor';
+
+import { createASTFromJSONSchema } from './utils';
+
+export interface SyncVariablePluginOptions {}
+
+/**
+ * Creates a plugin to synchronize output data to the variable engine when nodes are created or updated.
+ * @param ctx - The plugin context, containing the document and other relevant information.
+ * @param options - Plugin options, currently an empty object.
+ */
+export const createSyncVariablePlugin: PluginCreator<SyncVariablePluginOptions> =
+  definePluginCreator<SyncVariablePluginOptions, FixedLayoutPluginContext>({
+    onInit(ctx, options) {
+      const flowDocument = ctx.document;
+
+      // Listen for node creation events
+      flowDocument.onNodeCreate(({ node }) => {
+        const form = getNodeForm(node);
+        const variableData = node.getData(FlowNodeVariableData);
+
+        /**
+         * Synchronizes output data to the variable engine.
+         * @param value - The output data to synchronize.
+         */
+        const syncOutputs = (value: any) => {
+          if (!value) {
+            // If the output data is empty, clear the variable
+            variableData.clearVar();
+            return;
+          }
+
+          // Create an Type AST from the output data's JSON schema
+          // NOTICE: You can create a new function to generate an AST based on YOUR CUSTOM DSL
+          const typeAST = createASTFromJSONSchema(value);
+
+          if (typeAST) {
+            // Use the node's title or its ID as the title for the variable
+            const title = form?.getValueIn('title') || node.id;
+
+            // Set the variable in the variable engine
+            variableData.setVar(
+              ASTFactory.createVariableDeclaration({
+                meta: {
+                  title: `${title}.outputs`,
+                  // NOTICE: You can add more metadata here as needed
+                },
+                key: `${node.id}.outputs`,
+                type: typeAST,
+              })
+            );
+          } else {
+            // If the AST cannot be created, clear the variable
+            variableData.clearVar();
+          }
+        };
+
+        if (form) {
+          // Initially synchronize the output data
+          syncOutputs(form.getValueIn('outputs'));
+
+          // Listen for changes in the form values and re-synchronize when outputs change
+          form.onFormValuesChange((props) => {
+            if (props.name.match(/^outputs/)) {
+              syncOutputs(form.getValueIn('outputs'));
+            }
+          });
+        }
+      });
+    },
+  });

+ 22 - 1
apps/demo-fixed-layout/src/plugins/variable-plugin/utils.ts → apps/demo-fixed-layout/src/plugins/sync-variable-plugin/utils.ts

@@ -3,12 +3,32 @@ import { ASTFactory, type ASTNodeJSON } from '@flowgram.ai/fixed-layout-editor';
 
 import { type JsonSchema } from '../../typings';
 
+/**
+ * Sorts the properties of a JSON schema based on the 'extra.index' field.
+ * If the 'extra.index' field is not present, the property will be treated as having an index of 0.
+ *
+ * @param properties - The properties of the JSON schema to sort.
+ * @returns A sorted array of property entries.
+ */
 function sortProperties(properties: Record<string, JsonSchema>) {
   return Object.entries(properties).sort(
     (a, b) => (get(a?.[1], 'extra.index') || 0) - (get(b?.[1], 'extra.index') || 0)
   );
 }
 
+/**
+ * Converts a JSON schema to an Abstract Syntax Tree (AST) representation.
+ * This function recursively processes the JSON schema and creates corresponding AST nodes.
+ *
+ * For more information on JSON Schema, refer to the official documentation:
+ * https://json-schema.org/
+ *
+ * Note: Depending on your business needs, you can use your own Domain-Specific Language (DSL)
+ * Create a new function to convert your custom DSL to AST directly.
+ *
+ * @param jsonSchema - The JSON schema to convert.
+ * @returns An AST node representing the JSON schema, or undefined if the schema type is not recognized.
+ */
 export function createASTFromJSONSchema(jsonSchema: JsonSchema): ASTNodeJSON | undefined {
   const { type } = jsonSchema || {};
 
@@ -39,7 +59,8 @@ export function createASTFromJSONSchema(jsonSchema: JsonSchema): ASTNodeJSON | u
       return ASTFactory.createInteger();
 
     default:
-      // Camel case to variable-core type
+      // If the type is not recognized, return undefined.
+      // You can extend this function to handle custom types if needed.
       return;
   }
 }

+ 0 - 0
apps/demo-fixed-layout/src/plugins/variable-plugin/variable-selector/index.module.less → apps/demo-fixed-layout/src/plugins/sync-variable-plugin/variable-selector/index.module.less


+ 0 - 0
apps/demo-fixed-layout/src/plugins/variable-plugin/variable-selector/index.tsx → apps/demo-fixed-layout/src/plugins/sync-variable-plugin/variable-selector/index.tsx


+ 0 - 0
apps/demo-fixed-layout/src/plugins/variable-plugin/variable-selector/use-variable-tree.ts → apps/demo-fixed-layout/src/plugins/sync-variable-plugin/variable-selector/use-variable-tree.ts


+ 0 - 1
apps/demo-fixed-layout/src/plugins/variable-plugin/index.ts

@@ -1 +0,0 @@
-export * from './variable-plugin';

+ 0 - 65
apps/demo-fixed-layout/src/plugins/variable-plugin/variable-plugin.ts

@@ -1,65 +0,0 @@
-import {
-  definePluginCreator,
-  FixedLayoutPluginContext,
-  FlowNodeVariableData,
-  getNodeForm,
-  PluginCreator,
-  ASTFactory,
-} from '@flowgram.ai/fixed-layout-editor';
-
-import { createASTFromJSONSchema } from './utils';
-
-export interface VariablePluginOptions {}
-
-export const createVariablePlugin: PluginCreator<VariablePluginOptions> = definePluginCreator<
-  VariablePluginOptions,
-  FixedLayoutPluginContext
->({
-  onInit(ctx, options) {
-    const flowDocument = ctx.document;
-
-    /**
-     * Listens to the creation of nodes and synchronizes outputs data to the variable engine
-     * 监听节点的创建,并将 outputs 数据同步给变量引擎
-     */
-    flowDocument.onNodeCreate(({ node }) => {
-      const form = getNodeForm(node);
-      const variableData = node.getData<FlowNodeVariableData>(FlowNodeVariableData);
-
-      const syncOutputs = (value: any) => {
-        if (!value) {
-          variableData.clearVar();
-          return;
-        }
-
-        const typeAST = createASTFromJSONSchema(value);
-
-        if (typeAST) {
-          const title = form?.getValueIn('title') || node.id;
-
-          variableData.setVar(
-            ASTFactory.createVariableDeclaration({
-              meta: {
-                title: `${title}.outputs`,
-              },
-              key: `${node.id}.outputs`,
-              type: typeAST,
-            })
-          );
-          return;
-        } else {
-          variableData.clearVar();
-        }
-      };
-      if (form) {
-        syncOutputs(form.getValueIn('outputs'));
-        // Listen outputs change
-        form.onFormValuesChange((props) => {
-          if (props.name.match(/^outputs/)) {
-            syncOutputs(form.getValueIn('outputs'));
-          }
-        });
-      }
-    });
-  },
-});

+ 2 - 2
apps/demo-free-layout/src/form-components/fx-expression/index.tsx

@@ -3,7 +3,7 @@ import React, { type SVGProps } from 'react';
 import { Input, Button } from '@douyinfe/semi-ui';
 
 import { FlowRefValueSchema, FlowLiteralValueSchema } from '../../typings';
-import { VariableSelector } from '../../plugins/variable-plugin/variable-selector';
+import { VariableSelector } from '../../plugins/sync-variable-plugin/variable-selector';
 
 export function FxIcon(props: SVGProps<SVGSVGElement>) {
   return (
@@ -45,7 +45,7 @@ export function FxExpression(props: FxExpressionProps) {
           value={value.content}
           hasError={props.hasError}
           style={{ flexGrow: 1 }}
-          onChange={v => onChange({ type: 'expression', content: v })}
+          onChange={(v) => onChange({ type: 'expression', content: v })}
           disabled={disabled}
         />
       ) : (

+ 6 - 6
apps/demo-free-layout/src/form-components/properties-edit/property-edit.tsx

@@ -5,7 +5,7 @@ import { IconCrossCircleStroked } from '@douyinfe/semi-icons';
 
 import { TypeSelector } from '../type-selector';
 import { JsonSchema } from '../../typings';
-import { VariableSelector } from '../../plugins/variable-plugin/variable-selector';
+import { VariableSelector } from '../../plugins/sync-variable-plugin/variable-selector';
 import { LeftColumn, Row } from './styles';
 
 export interface PropertyEditProps {
@@ -17,7 +17,7 @@ export interface PropertyEditProps {
   onDelete?: () => void;
 }
 
-export const PropertyEdit: React.FC<PropertyEditProps> = props => {
+export const PropertyEdit: React.FC<PropertyEditProps> = (props) => {
   const { value, disabled } = props;
   const [inputKey, updateKey] = useState(props.propertyKey);
   const updateProperty = (key: keyof JsonSchema, val: any) => {
@@ -34,12 +34,12 @@ export const PropertyEdit: React.FC<PropertyEditProps> = props => {
           value={value.type}
           disabled={disabled}
           style={{ position: 'absolute', top: 6, left: 4, zIndex: 1 }}
-          onChange={val => updateProperty('type', val)}
+          onChange={(val) => updateProperty('type', val)}
         />
         <Input
           value={inputKey}
           disabled={disabled}
-          onChange={v => updateKey(v.trim())}
+          onChange={(v) => updateKey(v.trim())}
           onBlur={() => {
             if (inputKey !== '') {
               props.onChange(value, props.propertyKey, inputKey);
@@ -54,14 +54,14 @@ export const PropertyEdit: React.FC<PropertyEditProps> = props => {
         <VariableSelector
           value={value.default}
           disabled={disabled}
-          onChange={val => updateProperty('default', val)}
+          onChange={(val) => updateProperty('default', val)}
           style={{ flexGrow: 1, height: 32 }}
         />
       ) : (
         <Input
           disabled={disabled}
           value={value.default}
-          onChange={val => updateProperty('default', val)}
+          onChange={(val) => updateProperty('default', val)}
         />
       )}
       {props.onDelete && !disabled && (

+ 4 - 4
apps/demo-free-layout/src/form-components/type-selector.tsx

@@ -2,7 +2,7 @@ import React from 'react';
 
 import { Tag, Dropdown } from '@douyinfe/semi-ui';
 
-import { VariableTypeIcons } from '../plugins/variable-plugin/icons';
+import { VariableTypeIcons } from '../plugins/sync-variable-plugin/icons';
 
 export interface TypeSelectorProps {
   value?: string;
@@ -12,7 +12,7 @@ export interface TypeSelectorProps {
 }
 const dropdownMenus = ['object', 'boolean', 'array', 'string', 'integer', 'number'];
 
-export const TypeSelector: React.FC<TypeSelectorProps> = props => {
+export const TypeSelector: React.FC<TypeSelectorProps> = (props) => {
   const { value, disabled } = props;
   const icon = VariableTypeIcons[value as any];
   return (
@@ -22,7 +22,7 @@ export const TypeSelector: React.FC<TypeSelectorProps> = props => {
       disabled={disabled}
       render={
         <Dropdown.Menu>
-          {dropdownMenus.map(key => (
+          {dropdownMenus.map((key) => (
             <Dropdown.Item
               key={key}
               onClick={() => {
@@ -39,7 +39,7 @@ export const TypeSelector: React.FC<TypeSelectorProps> = props => {
       <Tag
         color="white"
         style={props.style}
-        onClick={e => {
+        onClick={(e) => {
           e.stopPropagation();
           e.preventDefault();
         }}

+ 1 - 1
apps/demo-free-layout/src/form-components/type-tag.tsx

@@ -1,7 +1,7 @@
 import styled from 'styled-components';
 import { Tag, Tooltip } from '@douyinfe/semi-ui';
 
-import { VariableTypeIcons, ArrayIcons } from '../plugins/variable-plugin/icons';
+import { VariableTypeIcons, ArrayIcons } from '../plugins/sync-variable-plugin/icons';
 
 interface PropsType {
   name?: string | JSX.Element;

+ 2 - 2
apps/demo-free-layout/src/hooks/use-editor-props.tsx

@@ -12,7 +12,7 @@ import { FreeLayoutProps } from '@flowgram.ai/free-layout-editor';
 
 import { FlowNodeRegistry, FlowDocumentJSON } from '../typings';
 import { shortcuts } from '../shortcuts';
-import { createVariablePlugin } from '../plugins';
+import { createSyncVariablePlugin } from '../plugins';
 import { defaultFormMeta } from '../nodes/default-form-meta';
 import { SelectorBoxPopover } from '../components/selector-box-popover';
 import { BaseNode, LineAddButton, NodePanel } from '../components';
@@ -184,7 +184,7 @@ export function useEditorProps(
          * Variable plugin
          * 变量插件
          */
-        createVariablePlugin({}),
+        createSyncVariablePlugin({}),
         /**
          * Snap plugin
          * 自动对齐及辅助线插件

+ 1 - 1
apps/demo-free-layout/src/plugins/index.ts

@@ -1 +1 @@
-export { createVariablePlugin } from './variable-plugin/variable-plugin';
+export { createSyncVariablePlugin } from './sync-variable-plugin/sync-variable-plugin';

+ 0 - 0
apps/demo-free-layout/src/plugins/variable-plugin/icons.tsx → apps/demo-free-layout/src/plugins/sync-variable-plugin/icons.tsx


+ 1 - 0
apps/demo-free-layout/src/plugins/sync-variable-plugin/index.ts

@@ -0,0 +1 @@
+export * from './sync-variable-plugin';

+ 78 - 0
apps/demo-free-layout/src/plugins/sync-variable-plugin/sync-variable-plugin.ts

@@ -0,0 +1,78 @@
+import {
+  definePluginCreator,
+  FlowNodeVariableData,
+  getNodeForm,
+  PluginCreator,
+  FreeLayoutPluginContext,
+  ASTFactory,
+} from '@flowgram.ai/free-layout-editor';
+
+import { createASTFromJSONSchema } from './utils';
+
+export interface SyncVariablePluginOptions {}
+
+/**
+ * Creates a plugin to synchronize output data to the variable engine when nodes are created or updated.
+ * @param ctx - The plugin context, containing the document and other relevant information.
+ * @param options - Plugin options, currently an empty object.
+ */
+export const createSyncVariablePlugin: PluginCreator<SyncVariablePluginOptions> =
+  definePluginCreator<SyncVariablePluginOptions, FreeLayoutPluginContext>({
+    onInit(ctx, options) {
+      const flowDocument = ctx.document;
+
+      // Listen for node creation events
+      flowDocument.onNodeCreate(({ node }) => {
+        const form = getNodeForm(node);
+        const variableData = node.getData(FlowNodeVariableData);
+
+        /**
+         * Synchronizes output data to the variable engine.
+         * @param value - The output data to synchronize.
+         */
+        const syncOutputs = (value: any) => {
+          if (!value) {
+            // If the output data is empty, clear the variable
+            variableData.clearVar();
+            return;
+          }
+
+          // Create an Type AST from the output data's JSON schema
+          // NOTICE: You can create a new function to generate an AST based on YOUR CUSTOM DSL
+          const typeAST = createASTFromJSONSchema(value);
+
+          if (typeAST) {
+            // Use the node's title or its ID as the title for the variable
+            const title = form?.getValueIn('title') || node.id;
+
+            // Set the variable in the variable engine
+            variableData.setVar(
+              ASTFactory.createVariableDeclaration({
+                meta: {
+                  title: `${title}.outputs`,
+                  // NOTICE: You can add more metadata here as needed
+                },
+                key: `${node.id}.outputs`,
+                type: typeAST,
+              })
+            );
+          } else {
+            // If the AST cannot be created, clear the variable
+            variableData.clearVar();
+          }
+        };
+
+        if (form) {
+          // Initially synchronize the output data
+          syncOutputs(form.getValueIn('outputs'));
+
+          // Listen for changes in the form values and re-synchronize when outputs change
+          form.onFormValuesChange((props) => {
+            if (props.name.match(/^outputs/)) {
+              syncOutputs(form.getValueIn('outputs'));
+            }
+          });
+        }
+      });
+    },
+  });

+ 22 - 1
apps/demo-free-layout/src/plugins/variable-plugin/utils.ts → apps/demo-free-layout/src/plugins/sync-variable-plugin/utils.ts

@@ -3,12 +3,32 @@ import { ASTFactory, type ASTNodeJSON } from '@flowgram.ai/free-layout-editor';
 
 import { type JsonSchema } from '../../typings';
 
+/**
+ * Sorts the properties of a JSON schema based on the 'extra.index' field.
+ * If the 'extra.index' field is not present, the property will be treated as having an index of 0.
+ *
+ * @param properties - The properties of the JSON schema to sort.
+ * @returns A sorted array of property entries.
+ */
 function sortProperties(properties: Record<string, JsonSchema>) {
   return Object.entries(properties).sort(
     (a, b) => (get(a?.[1], 'extra.index') || 0) - (get(b?.[1], 'extra.index') || 0)
   );
 }
 
+/**
+ * Converts a JSON schema to an Abstract Syntax Tree (AST) representation.
+ * This function recursively processes the JSON schema and creates corresponding AST nodes.
+ *
+ * For more information on JSON Schema, refer to the official documentation:
+ * https://json-schema.org/
+ *
+ * Note: Depending on your business needs, you can use your own Domain-Specific Language (DSL)
+ * Create a new function to convert your custom DSL to AST directly.
+ *
+ * @param jsonSchema - The JSON schema to convert.
+ * @returns An AST node representing the JSON schema, or undefined if the schema type is not recognized.
+ */
 export function createASTFromJSONSchema(jsonSchema: JsonSchema): ASTNodeJSON | undefined {
   const { type } = jsonSchema || {};
 
@@ -39,7 +59,8 @@ export function createASTFromJSONSchema(jsonSchema: JsonSchema): ASTNodeJSON | u
       return ASTFactory.createInteger();
 
     default:
-      // Camel case to variable-core type
+      // If the type is not recognized, return undefined.
+      // You can extend this function to handle custom types if needed.
       return;
   }
 }

+ 0 - 0
apps/demo-free-layout/src/plugins/variable-plugin/variable-selector/index.module.less → apps/demo-free-layout/src/plugins/sync-variable-plugin/variable-selector/index.module.less


+ 0 - 0
apps/demo-free-layout/src/plugins/variable-plugin/variable-selector/index.tsx → apps/demo-free-layout/src/plugins/sync-variable-plugin/variable-selector/index.tsx


+ 0 - 0
apps/demo-free-layout/src/plugins/variable-plugin/variable-selector/use-variable-tree.ts → apps/demo-free-layout/src/plugins/sync-variable-plugin/variable-selector/use-variable-tree.ts


+ 0 - 1
apps/demo-free-layout/src/plugins/variable-plugin/index.ts

@@ -1 +0,0 @@
-export * from './variable-plugin';

+ 0 - 65
apps/demo-free-layout/src/plugins/variable-plugin/variable-plugin.ts

@@ -1,65 +0,0 @@
-import {
-  definePluginCreator,
-  FlowNodeVariableData,
-  getNodeForm,
-  PluginCreator,
-  FreeLayoutPluginContext,
-  ASTFactory,
-} from '@flowgram.ai/free-layout-editor';
-
-import { createASTFromJSONSchema } from './utils';
-
-export interface VariablePluginOptions {}
-
-export const createVariablePlugin: PluginCreator<VariablePluginOptions> = definePluginCreator<
-  VariablePluginOptions,
-  FreeLayoutPluginContext
->({
-  onInit(ctx, options) {
-    const flowDocument = ctx.document;
-
-    /**
-     * Listens to the creation of nodes and synchronizes outputs data to the variable engine
-     * 监听节点的创建,并将 outputs 数据同步给变量引擎
-     */
-    flowDocument.onNodeCreate(({ node }) => {
-      const form = getNodeForm(node);
-      const variableData = node.getData<FlowNodeVariableData>(FlowNodeVariableData);
-
-      const syncOutputs = (value: any) => {
-        if (!value) {
-          variableData.clearVar();
-          return;
-        }
-
-        const typeAST = createASTFromJSONSchema(value);
-
-        if (typeAST) {
-          const title = form?.getValueIn('title') || node.id;
-
-          variableData.setVar(
-            ASTFactory.createVariableDeclaration({
-              meta: {
-                title: `${title}.outputs`,
-              },
-              key: `${node.id}.outputs`,
-              type: typeAST,
-            })
-          );
-          return;
-        } else {
-          variableData.clearVar();
-        }
-      };
-      if (form) {
-        syncOutputs(form.getValueIn('outputs'));
-        // Listen outputs change
-        form.onFormValuesChange((props) => {
-          if (props.name.match(/^outputs/)) {
-            syncOutputs(form.getValueIn('outputs'));
-          }
-        });
-      }
-    });
-  },
-});