Best-practice demo for free layout
npx @flowgram.ai/create-app@latest free-layout- Frontend framework: React 18 + TypeScript
- Build tool: Rsbuild (a modern build tool based on Rspack)
- Styling: Less + Styled Components + CSS Variables
- UI library: Semi Design (@douyinfe/semi-ui)
- State management: Flowgram’s in-house editor framework
- Dependency injection: Inversify
- @flowgram.ai/free-layout-editor: Core dependency for the free layout editor
- @flowgram.ai/free-snap-plugin: Auto-alignment and guide-lines plugin
- @flowgram.ai/free-lines-plugin: Connection line rendering plugin
- @flowgram.ai/free-node-panel-plugin: Node add-panel rendering plugin
- @flowgram.ai/minimap-plugin: Minimap plugin
- @flowgram.ai/free-container-plugin: Sub-canvas plugin
- @flowgram.ai/free-group-plugin: Grouping plugin
- @flowgram.ai/form-materials: Form materials
- @flowgram.ai/runtime-interface: Runtime interfaces
- @flowgram.ai/runtime-js: JS runtime module
- @flowgram.ai/panel-manager-plugin: Sidebar panel management
src/
├── app.tsx # Application entry file
├── editor.tsx # Main editor component
├── initial-data.ts # Initial data configuration
├── assets/ # Static assets
├── components/ # Component library
│ ├── index.ts
│ ├── add-node/ # Add-node component
│ ├── base-node/ # Base node components
│ ├── comment/ # Comment components
│ ├── group/ # Group components
│ ├── line-add-button/ # Connection add button
│ ├── node-menu/ # Node menu
│ ├── node-panel/ # Node add panel
│ ├── selector-box-popover/ # Selection box popover
│ ├── sidebar/ # Sidebar
│ ├── testrun/ # Test-run module
│ │ ├── hooks/ # Test-run hooks
│ │ ├── node-status-bar/ # Node status bar
│ │ ├── testrun-button/ # Test-run button
│ │ ├── testrun-form/ # Test-run form
│ │ ├── testrun-json-input/ # JSON input component
│ │ └── testrun-panel/ # Test-run panel
│ └── tools/ # Utility components
├── context/ # React Context
│ ├── node-render-context.ts # Current rendering node context
│ ├── sidebar-context # Sidebar context
├── form-components/ # Form component library
│ ├── form-content/ # Form content
│ ├── form-header/ # Form header
│ ├── form-inputs/ # Form inputs
│ └── form-item/ # Form item
│ └── feedback.tsx # Validation error rendering
├── hooks/
│ ├── index.ts
│ ├── use-editor-props.tsx # Editor props hook
│ ├── use-is-sidebar.ts # Sidebar state hook
│ ├── use-node-render-context.ts # Node render context hook
│ └── use-port-click.ts # Port click hook
├── nodes/ # Node definitions
│ ├── index.ts
│ ├── constants.ts # Node constants
│ ├── default-form-meta.ts # Default form metadata
│ ├── block-end/ # Block end node
│ ├── block-start/ # Block start node
│ ├── break/ # Break node
│ ├── code/ # Code node
│ ├── comment/ # Comment node
│ ├── condition/ # Condition node
│ ├── continue/ # Continue node
│ ├── end/ # End node
│ ├── group/ # Group node
│ ├── http/ # HTTP node
│ ├── llm/ # LLM node
│ ├── loop/ # Loop node
│ ├── start/ # Start node
│ └── variable/ # Variable node
├── plugins/ # Plugin system
│ ├── index.ts
│ ├── context-menu-plugin/ # Right-click context menu plugin
│ ├── runtime-plugin/ # Runtime plugin
│ │ ├── client/ # Client
│ │ │ ├── browser-client/ # Browser client
│ │ │ └── server-client/ # Server client
│ │ └── runtime-service/ # Runtime service
│ └── variable-panel-plugin/ # Variable panel plugin
│ └── components/ # Variable panel components
├── services/ # Service layer
│ ├── index.ts
│ └── custom-service.ts # Custom service
├── shortcuts/ # Shortcuts system
│ ├── index.ts
│ ├── constants.ts # Shortcut constants
│ ├── shortcuts.ts # Shortcut definitions
│ ├── type.ts # Type definitions
│ ├── collapse/ # Collapse shortcut
│ ├── copy/ # Copy shortcut
│ ├── delete/ # Delete shortcut
│ ├── expand/ # Expand shortcut
│ ├── paste/ # Paste shortcut
│ ├── select-all/ # Select-all shortcut
│ ├── zoom-in/ # Zoom-in shortcut
│ └── zoom-out/ # Zoom-out shortcut
├── styles/ # Styles
├── typings/ # Type definitions
│ ├── index.ts
│ ├── json-schema.ts # JSON Schema types
│ └── node.ts # Node type definitions
└── utils/ # Utility functions
├── index.ts
└── on-drag-line-end.ts # Handle end of drag line
- base-node: Base rendering components for all nodes
- testrun: Complete test-run module, including status bar, form, and panel
- sidebar: Sidebar components providing tools and property panels
- node-panel: Node add panel with drag-to-add capability
Each node type has its own directory, including:
- Node registration (
index.ts) - Form metadata (
form-meta.ts) - Node-specific components and logic
- runtime-plugin: Supports both browser and server modes
- context-menu-plugin: Right-click context menu
- variable-panel-plugin: Variable management panel
Complete keyboard shortcut support, including:
- Basic actions: copy, paste, delete, select-all
- View actions: zoom-in, zoom-out, collapse, expand
- Each shortcut has its own implementation module
Highly modular plugin system; each feature is an independent plugin:
plugins: ()=>[createFreeLinesPlugin({renderInsideLine: LineAddButton}),createMinimapPlugin({/* config */}),createFreeSnapPlugin({/* alignment config */}),createFreeNodePanelPlugin({renderer: NodePanel}),createContainerNodePlugin({}),createFreeGroupPlugin({groupNodeRender: GroupNodeRender}),createContextMenuPlugin({}),createRuntimePlugin({mode: 'browser'}),createVariablePanelPlugin({})]Manage different workflow node types via a registry:
exportconstnodeRegistries: FlowNodeRegistry[]=[ConditionNodeRegistry,// Condition nodeStartNodeRegistry,// Start nodeEndNodeRegistry,// End nodeLLMNodeRegistry,// LLM nodeLoopNodeRegistry,// Loop nodeCommentNodeRegistry,// Comment nodeHTTPNodeRegistry,// HTTP nodeCodeNodeRegistry,// Code node// ... more node types];Use Inversify for service DI:
onBind: ({ bind })=>{bind(CustomService).toSelf().inSingletonScope();}useEditorProps is the configuration center of the editor:
exportfunctionuseEditorProps(initialData: FlowDocumentJSON,nodeRegistries: FlowNodeRegistry[]): FreeLayoutProps{returnuseMemo<FreeLayoutProps>(()=>({background: true,// Background gridreadonly: false,// Readonly mode
initialData,// Initial data
nodeRegistries,// Node registries// Core feature configsplayground: {preventGlobalGesture: true/* Prevent Mac browser swipe gestures */},nodeEngine: {enable: true},variableEngine: {enable: true},history: {enable: true,enableChangeNode: true},// Business rulescanAddLine: (ctx,fromPort,toPort)=>{/* Connection rules */},canDeleteLine: (ctx,line)=>{/* Line deletion rules */},canDeleteNode: (ctx,node)=>{/* Node deletion rules */},canDropToNode: (ctx,params)=>{/* Drag-and-drop rules */},// Pluginsplugins: ()=>[/* Plugin list */],// EventsonContentChange: debounce((ctx,event)=>{/* Auto save */},1000),onInit: (ctx)=>{/* Initialization */},onAllLayersRendered: (ctx)=>{/* After render */}}),[]);}The app supports multiple workflow node types:
exportenumWorkflowNodeType{Start='start',// Start nodeEnd='end',// End nodeLLM='llm',// Large language model nodeHTTP='http',// HTTP request nodeCode='code',// Code execution nodeVariable='variable',// Variable nodeCondition='condition',// Conditional nodeLoop='loop',// Loop nodeBlockStart='block-start',// Sub-canvas start nodeBlockEnd='block-end',// Sub-canvas end nodeComment='comment',// Comment nodeContinue='continue',// Continue nodeBreak='break',// Break node}Each node follows a unified registration pattern:
exportconstStartNodeRegistry: FlowNodeRegistry={type: WorkflowNodeType.Start,meta: {isStart: true,deleteDisable: true,// Not deletablecopyDisable: true,// Not copyablenodePanelVisible: false,// Hidden in node paneldefaultPorts: [{type: 'output'}],size: {width: 360,height: 211}},info: {icon: iconStart,description: 'The starting node of the workflow, used to set up information needed to launch the workflow.'},
formMeta,// Form configurationcanAdd(){returnfalse;}// Disallow multiple start nodes};App features are modularized via the plugin system:
- FreeLinesPlugin - Connection rendering and interaction
- MinimapPlugin - Minimap navigation
- FreeSnapPlugin - Auto-alignment and guide-lines
- FreeNodePanelPlugin - Node add panel
- ContainerNodePlugin - Container nodes (e.g., loop nodes)
- FreeGroupPlugin - Node grouping
- ContextMenuPlugin - Right-click context menu
- RuntimePlugin - Workflow runtime
- VariablePanelPlugin - Variable management panel
Two run modes are supported:
createRuntimePlugin({mode: 'browser',// Browser mode// mode: 'server', // Server mode// serverConfig: {// domain: 'localhost',// port: 4000,// protocol: 'http',// },})- Plugin architecture: Each feature is an independent plugin, easy to extend and maintain
- Node registry system: Add new node types without changing core code
- Componentized UI: Highly reusable components with clear responsibilities
- Full TypeScript support: End-to-end type safety from configuration to runtime
- JSON Schema integration: Node data validated by schemas
- Strongly typed plugin interfaces: Clear type constraints for plugin development
- Real-time preview: Run and debug workflows live
- Rich interactions: Dragging, zooming, snapping, shortcuts for a complete editing experience
- Visual feedback: Minimap, status indicators, line animations
- Open plugin system: Third parties can easily develop custom plugins
- Flexible node system: Custom node types and form configurations supported
- Multiple runtimes: Both browser and server modes
- On-demand loading: Components and plugins support lazy loading
- Debounce: Performance optimizations for high-frequency operations like auto-save
Based on @flowgram.ai/free-layout-editor, providing:
- Free-layout canvas system
- Full undo/redo functionality
- Lifecycle management for nodes and connections
- Variable engine and expression system
Using Rsbuild as the build tool:
exportdefaultdefineConfig({plugins: [pluginReact(),pluginLess()],source: {entry: {index: './src/app.tsx'},decorators: {version: 'legacy'}// Enable decorators},tools: {rspack: {ignoreWarnings: [/Criticaldependency/]// Ignore specific warnings}}});Built-in multilingual support:
i18n: {locale: navigator.language,languages: {'zh-CN': {'Never Remind': '不再提示','Hold {{key}} to drag node out': '按住 {{key}} 可以将节点拖出',},'en-US': {},}}