Browse Source

docs: auto layout plugin (#894)

Louis Young 3 months ago
parent
commit
1bd49fc17d

+ 31 - 10
apps/demo-free-layout/src/initial-data.ts

@@ -13,7 +13,7 @@ export const initialData: FlowDocumentJSON = {
       meta: {
       meta: {
         position: {
         position: {
           x: 180,
           x: 180,
-          y: 586.2,
+          y: 601.2,
         },
         },
       },
       },
       data: {
       data: {
@@ -53,7 +53,7 @@ export const initialData: FlowDocumentJSON = {
       meta: {
       meta: {
         position: {
         position: {
           x: 1100,
           x: 1100,
-          y: 531.2,
+          y: 546.2,
         },
         },
       },
       },
       data: {
       data: {
@@ -81,8 +81,8 @@ export const initialData: FlowDocumentJSON = {
       type: 'end',
       type: 'end',
       meta: {
       meta: {
         position: {
         position: {
-          x: 2928,
-          y: 586.2,
+          x: 2968,
+          y: 601.2,
         },
         },
       },
       },
       data: {
       data: {
@@ -119,7 +119,7 @@ export const initialData: FlowDocumentJSON = {
       meta: {
       meta: {
         position: {
         position: {
           x: 180,
           x: 180,
-          y: 760.2,
+          y: 775.2,
         },
         },
       },
       },
       data: {
       data: {
@@ -136,7 +136,7 @@ export const initialData: FlowDocumentJSON = {
       meta: {
       meta: {
         position: {
         position: {
           x: 640,
           x: 640,
-          y: 406.35,
+          y: 421.35,
         },
         },
       },
       },
       data: {
       data: {
@@ -176,8 +176,8 @@ export const initialData: FlowDocumentJSON = {
       type: 'loop',
       type: 'loop',
       meta: {
       meta: {
         position: {
         position: {
-          x: 1440,
-          y: 90,
+          x: 1460,
+          y: 0,
         },
         },
       },
       },
       data: {
       data: {
@@ -192,6 +192,18 @@ export const initialData: FlowDocumentJSON = {
             content: ['llm_6aSyo', 'result'],
             content: ['llm_6aSyo', 'result'],
           },
           },
         },
         },
+        outputs: {
+          type: 'object',
+          required: [],
+          properties: {
+            acm: {
+              type: 'array',
+              items: {
+                type: 'string',
+              },
+            },
+          },
+        },
       },
       },
       blocks: [
       blocks: [
         {
         {
@@ -391,8 +403,8 @@ export const initialData: FlowDocumentJSON = {
       type: 'group',
       type: 'group',
       meta: {
       meta: {
         position: {
         position: {
-          x: 1604,
-          y: 738.2,
+          x: 1624,
+          y: 698.2,
         },
         },
       },
       },
       data: {
       data: {
@@ -587,4 +599,13 @@ export const initialData: FlowDocumentJSON = {
       targetNodeID: 'llm_vTyMa',
       targetNodeID: 'llm_vTyMa',
     },
     },
   ],
   ],
+  globalVariable: {
+    type: 'object',
+    required: [],
+    properties: {
+      userId: {
+        type: 'string',
+      },
+    },
+  },
 };
 };

+ 261 - 0
apps/docs/src/en/guide/advanced/plugin/auto-layout-plugin.mdx

@@ -0,0 +1,261 @@
+import { PackageManagerTabs } from '@theme';
+
+# Auto Layout Plugin
+
+An automatic layout plugin based on the Dagre algorithm that provides intelligent node arrangement functionality for free layout canvases.
+
+## Features
+
+- Based on Dagre directed graph layout algorithm, automatically calculates optimal node positions
+- Supports multiple layout directions (left-to-right, top-to-bottom, etc.)
+- Configurable node spacing, margins, and other layout parameters
+- Supports recursive layout for nested containers
+- Provides animation effects and view adaptation functionality
+- Integrates with history system, supports undo/redo operations
+
+![Preview](@/public/plugin/auto-layout.gif)
+
+## Quick Start
+
+1. Installation
+
+<PackageManagerTabs command="install @flowgram.ai/free-auto-layout-plugin" />
+
+2. Register Plugin
+
+The plugin registration method is basically the same as other flowgram plugins. Just ensure not to create duplicates and pass it to the corresponding FreeLayoutEditorProvider.
+
+```tsx
+import { createFreeAutoLayoutPlugin } from '@flowgram.ai/free-auto-layout-plugin';
+
+const editorProps = useMemo(() => ({
+  plugins: () => [
+    createFreeAutoLayoutPlugin({
+      layoutConfig: {
+        rankdir: 'LR', // Layout direction: left to right
+        nodesep: 100,  // Node spacing
+        ranksep: 100,  // Rank spacing
+      }
+    })
+  ]
+}), []);
+
+return (
+  <FreeLayoutEditorProvider {...editorProps}>
+    <EditorRenderer />
+  </FreeLayoutEditorProvider>
+)
+```
+
+3. Use in React Components
+
+You can also trigger auto layout in components using utility classes:
+
+```tsx
+import { WorkflowAutoLayoutTool } from '@flowgram.ai/free-layout-editor';
+
+const AutoLayoutButton = () => {
+  const tools = usePlaygroundTools();
+  const playground = usePlayground();
+
+  const handleAutoLayout = async () => {
+    await tools.autoLayout({
+      enableAnimation: true,      // Enable animation effects
+      animationDuration: 1000,     // Animation duration
+      disableFitView: false,      // Auto fit view after layout
+    });
+  }
+
+  return (
+    <button onClick={handleAutoLayout}>
+      Auto Layout
+    </button>
+  );
+};
+```
+
+4. Use Auto Layout Service
+
+Execute layout by obtaining AutoLayoutService instance through dependency injection:
+
+```tsx
+import { AutoLayoutService } from '@flowgram.ai/free-auto-layout-plugin';
+
+class MyLayoutService {
+  @inject(AutoLayoutService)
+  private autoLayoutService: AutoLayoutService;
+
+  async performAutoLayout() {
+    await this.autoLayoutService.layout({
+      enableAnimation: true,      // Enable animation effects
+      animationDuration: 1000,     // Animation duration
+      disableFitView: false,      // Auto fit view after layout
+    });
+  }
+}
+```
+
+## Configuration Options
+
+### LayoutConfig
+
+Configuration parameters for the layout algorithm:
+
+```typescript
+interface LayoutConfig {
+  /** Layout direction */
+  rankdir?: 'TB' | 'BT' | 'LR' | 'RL';
+  /** Alignment */
+  align?: 'UL' | 'UR' | 'DL' | 'DR';
+  /** Node separation within same rank */
+  nodesep?: number;
+  /** Edge separation */
+  edgesep?: number;
+  /** Rank separation */
+  ranksep?: number;
+  /** Horizontal margin */
+  marginx?: number;
+  /** Vertical margin */
+  marginy?: number;
+  /** Cycle removal algorithm */
+  acyclicer?: string;
+  /** Ranking algorithm */
+  ranker?: 'network-simplex' | 'tight-tree' | 'longest-path';
+}
+```
+
+### LayoutOptions
+
+Options for layout execution:
+
+```typescript
+interface LayoutOptions {
+  /** Container node, defaults to root node */
+  containerNode?: WorkflowNodeEntity;
+  /** Function to get follow node */
+  getFollowNode?: GetFollowNode;
+  /** Disable auto fit view */
+  disableFitView?: boolean;
+  /** Enable animation effects */
+  enableAnimation?: boolean;
+  /** Animation duration (milliseconds) */
+  animationDuration?: number;
+}
+```
+
+## Layout Algorithm
+
+### Dagre Algorithm
+
+The plugin is implemented based on the Dagre library, which is a JavaScript library specifically designed for directed graph layout. Algorithm features:
+
+- **Hierarchical Layout**: Organizes nodes into different levels based on dependency relationships
+- **Minimize Crossings**: Attempts to minimize line crossings
+- **Even Distribution**: Evenly distributes nodes while satisfying constraints
+
+### Layout Process
+
+1. **Graph Construction**: Converts workflow nodes and connections into Dagre graph structure
+2. **Rank Calculation**: Calculates levels (ranks) based on node dependencies
+3. **Order Optimization**: Optimizes node order within each level to reduce crossings
+4. **Position Calculation**: Calculates final coordinate positions for each node
+5. **Animation Execution**: If animation is enabled, smoothly transitions to new positions
+
+## Advanced Usage
+
+### Custom Follow Nodes
+
+You can customize node following relationships through the `getFollowNode` function:
+
+```typescript
+const layoutOptions: LayoutOptions = {
+  getFollowNode: (node: LayoutNode) => {
+    // Return the node ID that should follow the current node
+    if (node.flowNodeType.type === 'comment') {
+      return getNearestNode(node);
+    }
+    return undefined;
+  }
+};
+```
+
+### Nested Container Layout
+
+The plugin supports recursive layout for nested containers and automatically handles node arrangement within containers:
+
+```typescript
+// Execute layout for specific container
+await autoLayoutService.layout({
+  containerNode: specificContainerNode,
+  enableAnimation: true,
+});
+```
+
+## FAQ
+
+### Q: How to trigger auto layout during initialization?
+
+A: Call the `ctx.tool.autoLayout()` method after the canvas rendering is complete to trigger auto layout.
+
+```typescript
+const editorProps = useMemo(() => ({
+  onAllLayersRendered: (ctx) => {
+    ctx.tool.autoLayout({
+      enableAnimation: false, // Disable animation during initialization for better user experience
+    }
+    );
+  }
+}), []);
+```
+
+### Q: How to implement custom layout directions?
+
+A: Method 1: Register AutoLayout plugin in EditorProps and control layout direction through the `rankdir` parameter:
+
+```typescript
+
+import { createFreeAutoLayoutPlugin } from '@flowgram.ai/free-auto-layout-plugin';
+
+const editorProps = useMemo(() => ({
+  plugins: () => [
+    createFreeAutoLayoutPlugin({
+      layoutConfig: {
+        rankdir: 'TB', // Top to bottom
+        // rankdir: 'LR', // Left to right (default)
+        // rankdir: 'RL', // Right to left
+        // rankdir: 'BT', // Bottom to top
+      }
+    })
+  ]
+}), []);
+```
+
+Method 2: Pass layout configuration through the `layoutConfig` parameter when calling the `autoLayout` method:
+
+```typescript
+const tools = usePlaygroundTools();
+const playground = usePlayground();
+
+const handleAutoLayout = async () => {
+  await tools.autoLayout({
+    layoutConfig: {
+      rankdir: 'TB', // Top to bottom
+      // rankdir: 'LR', // Left to right (default)
+      // rankdir: 'RL', // Right to left
+      // rankdir: 'BT', // Bottom to top
+    }
+  });
+}
+```
+
+### Q: How to optimize layout animation stuttering?
+
+A: For complex graphics, it's recommended to disable animation or reduce animation duration:
+
+```typescript
+layoutOptions: {
+  enableAnimation: false, // Disable animation
+  // or
+  animationDuration: 150, // Reduce animation duration
+}
+```

BIN
apps/docs/src/public/plugin/auto-layout.gif


+ 2 - 1
apps/docs/src/zh/guide/advanced/plugin/_meta.json

@@ -1,4 +1,5 @@
 [
 [
   "custom-plugin",
   "custom-plugin",
-  "panel-manager-plugin"
+  "panel-manager-plugin",
+  "free-auto-layout-plugin"
 ]
 ]

+ 261 - 0
apps/docs/src/zh/guide/advanced/plugin/free-auto-layout-plugin.mdx

@@ -0,0 +1,261 @@
+import { PackageManagerTabs } from '@theme';
+
+# @flowgram.ai/free-auto-layout-plugin
+
+基于 Dagre 算法的自动布局插件,为自由布局画布提供智能的节点排列功能。
+
+## 功能
+
+- 基于 Dagre 有向图布局算法,自动计算节点的最优位置
+- 支持多种布局方向(从左到右、从上到下等)
+- 可配置节点间距、边距等布局参数
+- 支持嵌套容器的递归布局
+- 提供动画效果和视图自适应功能
+- 与历史记录系统集成,支持撤销/重做操作
+
+![Preview](@/public/plugin/auto-layout.gif)
+
+## 快速开始
+
+1. 安装
+
+<PackageManagerTabs command="install @flowgram.ai/free-auto-layout-plugin" />
+
+2. 注册插件
+
+插件的注册方法和 flowgram 的其他插件基本相同,只需要保证不要重复创建以及最终传入到对应的 FreeLayoutEditorProvider 即可
+
+```tsx
+import { createFreeAutoLayoutPlugin } from '@flowgram.ai/free-auto-layout-plugin';
+
+const editorProps = useMemo(() => ({
+  plugins: () => [
+    createFreeAutoLayoutPlugin({
+      layoutConfig: {
+        rankdir: 'LR', // 布局方向:从左到右
+        nodesep: 100,  // 节点间距
+        ranksep: 100,  // 层级间距
+      }
+    })
+  ]
+}), []);
+
+return (
+  <FreeLayoutEditorProvider {...editorProps}>
+    <EditorRenderer />
+  </FreeLayoutEditorProvider>
+)
+```
+
+
+3. 在 React 组件中使用
+
+也可以通过工具类在组件中触发自动布局:
+
+```tsx
+import { WorkflowAutoLayoutTool } from '@flowgram.ai/free-layout-editor';
+
+const AutoLayoutButton = () => {
+  const tools = usePlaygroundTools();
+  const playground = usePlayground();
+
+  const handleAutoLayout = async () => {
+    await tools.autoLayout({
+      enableAnimation: true,      // 启用动画效果
+      animationDuration: 1000,     // 动画持续时间
+      disableFitView: false,      // 布局后自动适应视图
+    });
+  }
+
+  return (
+    <button onClick={handleAutoLayout}>
+      自动布局
+    </button>
+  );
+};
+```
+
+4. 使用自动布局服务
+
+通过依赖注入获取 AutoLayoutService 实例来执行布局:
+
+```tsx
+import { AutoLayoutService } from '@flowgram.ai/free-auto-layout-plugin';
+
+class MyLayoutService {
+  @inject(AutoLayoutService)
+  private autoLayoutService: AutoLayoutService;
+
+  async performAutoLayout() {
+    await this.autoLayoutService.layout({
+      enableAnimation: true,      // 启用动画效果
+      animationDuration: 1000,     // 动画持续时间
+      disableFitView: false,      // 布局后自动适应视图
+    });
+  }
+}
+```
+
+## 配置选项
+
+### LayoutConfig
+
+布局算法的配置参数:
+
+```typescript
+interface LayoutConfig {
+  /** 布局方向 */
+  rankdir?: 'TB' | 'BT' | 'LR' | 'RL';
+  /** 对齐方式 */
+  align?: 'UL' | 'UR' | 'DL' | 'DR';
+  /** 同层节点间距 */
+  nodesep?: number;
+  /** 边的间距 */
+  edgesep?: number;
+  /** 层级间距 */
+  ranksep?: number;
+  /** 水平边距 */
+  marginx?: number;
+  /** 垂直边距 */
+  marginy?: number;
+  /** 环路处理算法 */
+  acyclicer?: string;
+  /** 排序算法 */
+  ranker?: 'network-simplex' | 'tight-tree' | 'longest-path';
+}
+```
+
+### LayoutOptions
+
+布局执行时的选项:
+
+```typescript
+interface LayoutOptions {
+  /** 容器节点,默认为根节点 */
+  containerNode?: WorkflowNodeEntity;
+  /** 获取跟随节点的函数 */
+  getFollowNode?: GetFollowNode;
+  /** 禁用自动适应视图 */
+  disableFitView?: boolean;
+  /** 启用动画效果 */
+  enableAnimation?: boolean;
+  /** 动画持续时间(毫秒) */
+  animationDuration?: number;
+}
+```
+
+## 布局算法
+
+### Dagre 算法
+
+插件基于 Dagre 库实现,这是一个专门用于有向图布局的 JavaScript 库。算法特点:
+
+- **分层布局**:将节点按照依赖关系分为不同层级
+- **最小交叉**:尽量减少连线的交叉
+- **均匀分布**:在满足约束的前提下均匀分布节点
+
+### 布局流程
+
+1. **图构建**:将工作流节点和连线转换为 Dagre 图结构
+2. **层级计算**:根据节点依赖关系计算层级(rank)
+3. **顺序优化**:在每个层级内优化节点顺序以减少交叉
+4. **位置计算**:计算每个节点的最终坐标位置
+5. **动画执行**:如果启用动画,平滑过渡到新位置
+
+## 高级用法
+
+### 自定义跟随节点
+
+可以通过 `getFollowNode` 函数自定义节点的跟随关系:
+
+```typescript
+const layoutOptions: LayoutOptions = {
+  getFollowNode: (node: LayoutNode) => {
+    // 返回应该跟随当前节点的节点ID
+    if (node.flowNodeType.type === 'comment') {
+      return getNearestNode(node);
+    }
+    return undefined;
+  }
+};
+```
+
+### 嵌套容器布局
+
+插件支持嵌套容器的递归布局,会自动处理容器内部的节点排列:
+
+```typescript
+// 对特定容器执行布局
+await autoLayoutService.layout({
+  containerNode: specificContainerNode,
+  enableAnimation: true,
+});
+```
+
+## 常见问题
+
+### Q: 如何在初始化时触发自动布局?
+
+A: 在画布渲染完成后调用 `ctx.tool.autoLayout()` 方法即可触发自动布局。
+```typescript
+const editorProps = useMemo(() => ({
+  onAllLayersRendered: (ctx) => {
+    ctx.tool.autoLayout({
+      enableAnimation: false, // 初始化时自动布局禁用动画,可优化用户体验
+    }
+    );
+  }
+}), []);
+```
+
+### Q: 如何实现自定义布局方向?
+
+A: 方法一:在 EditorProps 中注册 AutoLayout 插件,通过 `rankdir` 参数控制布局方向:
+
+```typescript
+
+import { createFreeAutoLayoutPlugin } from '@flowgram.ai/free-auto-layout-plugin';
+
+const editorProps = useMemo(() => ({
+  plugins: () => [
+    createFreeAutoLayoutPlugin({
+      layoutConfig: {
+        rankdir: 'TB', // 从上到下
+        // rankdir: 'LR', // 从左到右(默认)
+        // rankdir: 'RL', // 从右到左
+        // rankdir: 'BT', // 从下到上
+      }
+    })
+  ]
+}), []);
+```
+
+方法二:在调用 `autoLayout` 方法时,通过 `layoutConfig` 参数传递布局配置:
+
+```typescript
+const tools = usePlaygroundTools();
+const playground = usePlayground();
+
+const handleAutoLayout = async () => {
+  await tools.autoLayout({
+    layoutConfig: {
+      rankdir: 'TB', // 从上到下
+      // rankdir: 'LR', // 从左到右(默认)
+      // rankdir: 'RL', // 从右到左
+      // rankdir: 'BT', // 从下到上
+    }
+  });
+}
+```
+
+### Q: 布局动画卡顿怎么优化?
+
+A: 对于复杂图形,建议禁用动画或减少动画时长:
+
+```typescript
+layoutOptions: {
+  enableAnimation: false, // 禁用动画
+  // 或者
+  animationDuration: 150, // 减少动画时长
+}
+```

+ 1 - 1
apps/docs/src/zh/guide/advanced/plugin/panel-manager-plugin.mdx

@@ -1,6 +1,6 @@
 import { PackageManagerTabs } from '@theme';
 import { PackageManagerTabs } from '@theme';
 
 
-# Panel Manager 插件
+# @flowgram.ai/panel-manager-plugin
 
 
 管理各类面板的插件。
 管理各类面板的插件。