diff --git a/cspell.json b/cspell.json index b0e9a96c..33359260 100644 --- a/cspell.json +++ b/cspell.json @@ -7,11 +7,14 @@ "closebracket", "codesandbox", "douyinfe", + "edgesep", "flowgram", "flowgram.ai", "gedit", "Hoverable", "langchain", + "marginx", + "marginy", "openbracket", "rsbuild", "rspack", diff --git a/packages/plugins/free-auto-layout-plugin/src/create-auto-layout-plugin.tsx b/packages/plugins/free-auto-layout-plugin/src/create-auto-layout-plugin.tsx index c7ce649e..76983df5 100644 --- a/packages/plugins/free-auto-layout-plugin/src/create-auto-layout-plugin.tsx +++ b/packages/plugins/free-auto-layout-plugin/src/create-auto-layout-plugin.tsx @@ -1,9 +1,14 @@ import { definePluginCreator } from '@flowgram.ai/core'; +import { AutoLayoutOptions } from './type'; import { AutoLayoutService } from './services'; -export const createFreeAutoLayoutPlugin = definePluginCreator({ +export const createFreeAutoLayoutPlugin = definePluginCreator({ onBind: ({ bind }) => { bind(AutoLayoutService).toSelf().inSingletonScope(); }, + onInit: (ctx, opts) => { + ctx.get(AutoLayoutService).init(opts); + }, + singleton: true, }); diff --git a/packages/plugins/free-auto-layout-plugin/src/layout/constant.ts b/packages/plugins/free-auto-layout-plugin/src/layout/constant.ts index 40b552e9..d618df49 100644 --- a/packages/plugins/free-auto-layout-plugin/src/layout/constant.ts +++ b/packages/plugins/free-auto-layout-plugin/src/layout/constant.ts @@ -1,6 +1,13 @@ -export const DagreLayoutOptions = { +import { LayoutConfig } from './type'; + +export const DefaultLayoutConfig: LayoutConfig = { rankdir: 'LR', + align: undefined, nodesep: 100, + edgesep: 10, ranksep: 100, + marginx: 0, + marginy: 0, + acyclicer: undefined, ranker: 'network-simplex', }; diff --git a/packages/plugins/free-auto-layout-plugin/src/layout/dagre.ts b/packages/plugins/free-auto-layout-plugin/src/layout/dagre.ts index 8c269118..52f90927 100644 --- a/packages/plugins/free-auto-layout-plugin/src/layout/dagre.ts +++ b/packages/plugins/free-auto-layout-plugin/src/layout/dagre.ts @@ -4,7 +4,6 @@ import { Graph as DagreGraph } from '@dagrejs/graphlib'; import { dagreLib } from '../dagre-lib/index'; import { DagreNode, LayoutNode } from './type'; import { LayoutStore } from './store'; -import { DagreLayoutOptions } from './constant'; export class DagreLayout { private readonly graph: DagreGraph; @@ -59,7 +58,7 @@ export class DagreLayout { private createGraph(): DagreGraph { const graph = new DagreGraph({ multigraph: true }); graph.setDefaultEdgeLabel(() => ({})); - graph.setGraph(DagreLayoutOptions); + graph.setGraph(this.store.config); return graph; } diff --git a/packages/plugins/free-auto-layout-plugin/src/layout/index.ts b/packages/plugins/free-auto-layout-plugin/src/layout/index.ts index 0d4ba705..31b5e40e 100644 --- a/packages/plugins/free-auto-layout-plugin/src/layout/index.ts +++ b/packages/plugins/free-auto-layout-plugin/src/layout/index.ts @@ -1,3 +1,4 @@ export { Layout } from './layout'; export type { LayoutNode, LayoutEdge, GetFollowNode, LayoutOptions } from './type'; export type { LayoutStore } from './store'; +export { DefaultLayoutConfig } from './constant'; diff --git a/packages/plugins/free-auto-layout-plugin/src/layout/layout.ts b/packages/plugins/free-auto-layout-plugin/src/layout/layout.ts index 9bd28821..a5efaca6 100644 --- a/packages/plugins/free-auto-layout-plugin/src/layout/layout.ts +++ b/packages/plugins/free-auto-layout-plugin/src/layout/layout.ts @@ -1,4 +1,4 @@ -import { GetFollowNode, LayoutOptions, LayoutParams } from './type'; +import { GetFollowNode, LayoutConfig, LayoutOptions, LayoutParams } from './type'; import { LayoutStore } from './store'; import { LayoutPosition } from './position'; import { DagreLayout } from './dagre'; @@ -10,8 +10,8 @@ export class Layout { private readonly _position: LayoutPosition; - constructor() { - this._store = new LayoutStore(); + constructor(config: LayoutConfig) { + this._store = new LayoutStore(config); this._layout = new DagreLayout(this._store); this._position = new LayoutPosition(this._store); } diff --git a/packages/plugins/free-auto-layout-plugin/src/layout/store.ts b/packages/plugins/free-auto-layout-plugin/src/layout/store.ts index 015910ae..393cb4d7 100644 --- a/packages/plugins/free-auto-layout-plugin/src/layout/store.ts +++ b/packages/plugins/free-auto-layout-plugin/src/layout/store.ts @@ -5,7 +5,7 @@ import { } from '@flowgram.ai/free-layout-core'; import { FlowNodeBaseType, FlowNodeTransformData } from '@flowgram.ai/document'; -import { LayoutEdge, LayoutNode, LayoutParams } from './type'; +import type { LayoutConfig, LayoutEdge, LayoutNode, LayoutParams } from './type'; interface LayoutStoreData { nodes: Map; @@ -21,6 +21,8 @@ export class LayoutStore { private container: WorkflowNodeEntity; + constructor(public readonly config: LayoutConfig) {} + public get initialized(): boolean { return this.init; } diff --git a/packages/plugins/free-auto-layout-plugin/src/layout/type.ts b/packages/plugins/free-auto-layout-plugin/src/layout/type.ts index 42f830a1..f97bb93b 100644 --- a/packages/plugins/free-auto-layout-plugin/src/layout/type.ts +++ b/packages/plugins/free-auto-layout-plugin/src/layout/type.ts @@ -68,6 +68,27 @@ export interface LayoutOptions { getFollowNode?: GetFollowNode; } +export interface LayoutConfig { + /** Direction for rank nodes. Can be TB, BT, LR, or RL, where T = top, B = bottom, L = left, and R = right. */ + rankdir: 'TB' | 'BT' | 'LR' | 'RL'; + /** Alignment for rank nodes. Can be UL, UR, DL, or DR, where U = up, D = down, L = left, and R = right. */ + align: 'UL' | 'UR' | 'DL' | 'DR' | undefined; + /** Number of pixels that separate nodes horizontally in the layout. */ + nodesep: number; + /** Number of pixels that separate edges horizontally in the layout. */ + edgesep: number; + /** Number of pixels that separate edges horizontally in the layout. */ + ranksep: number; + /** Number of pixels to use as a margin around the left and right of the graph. */ + marginx: number; + /** Number of pixels to use as a margin around the top and bottom of the graph. */ + marginy: number; + /** If set to greedy, uses a greedy heuristic for finding a feedback arc set for a graph. A feedback arc set is a set of edges that can be removed to make a graph acyclic. */ + acyclicer: 'greedy' | undefined; + /** Type of algorithm to assigns a rank to each node in the input graph. Possible values: network-simplex, tight-tree or longest-path */ + ranker: 'network-simplex' | 'tight-tree' | 'longest-path'; +} + export type GetFollowNode = ( node: LayoutNode, context: { diff --git a/packages/plugins/free-auto-layout-plugin/src/services.ts b/packages/plugins/free-auto-layout-plugin/src/services.ts index 049d4267..e3abc29d 100644 --- a/packages/plugins/free-auto-layout-plugin/src/services.ts +++ b/packages/plugins/free-auto-layout-plugin/src/services.ts @@ -6,12 +6,23 @@ import { WorkflowNodeLinesData, } from '@flowgram.ai/free-layout-core'; -import { Layout, type LayoutOptions } from './layout'; +import { AutoLayoutOptions } from './type'; +import { LayoutConfig } from './layout/type'; +import { DefaultLayoutConfig, Layout, type LayoutOptions } from './layout'; @injectable() export class AutoLayoutService { @inject(WorkflowDocument) private readonly document: WorkflowDocument; + private layoutConfig: LayoutConfig = DefaultLayoutConfig; + + public init(options: AutoLayoutOptions) { + this.layoutConfig = { + ...this.layoutConfig, + ...options.layoutConfig, + }; + } + public async layout(options: LayoutOptions = {}): Promise { await this.layoutNode(this.document.root, options); } @@ -29,7 +40,7 @@ export class AutoLayoutService { // 先递归执行子节点 autoLayout await Promise.all(nodes.map(async (child) => this.layoutNode(child, options))); - const layout = new Layout(); + const layout = new Layout(this.layoutConfig); layout.init({ nodes, edges, container: node }, options); layout.layout(); await layout.position(); diff --git a/packages/plugins/free-auto-layout-plugin/src/type.ts b/packages/plugins/free-auto-layout-plugin/src/type.ts new file mode 100644 index 00000000..6ec305d5 --- /dev/null +++ b/packages/plugins/free-auto-layout-plugin/src/type.ts @@ -0,0 +1,5 @@ +import { LayoutConfig } from './layout/type'; + +export interface AutoLayoutOptions { + layoutConfig?: Partial; +}