Skip to content

Latest commit

Β 

History

History
387 lines (333 loc) Β· 13.7 KB

File metadata and controls

387 lines (333 loc) Β· 13.7 KB

FlowGram.AI - Demo Free Layout

Best-practice demo for free layout

Installation

npx @flowgram.ai/create-app@latest free-layout

Project Overview

Core Tech Stack

  • 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

Core Dependencies

  • @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

Code Guide

Directory Structure

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

Key Directory Functions

1. /components - Component Library

  • 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

2. /nodes - Node System

Each node type has its own directory, including:

  • Node registration (index.ts)
  • Form metadata (form-meta.ts)
  • Node-specific components and logic

3. /plugins - Plugin System

  • runtime-plugin: Supports both browser and server modes
  • context-menu-plugin: Right-click context menu
  • variable-panel-plugin: Variable management panel

4. /shortcuts - Shortcuts System

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

Application Architecture

Core Design Patterns

1. Plugin Architecture

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({})
]

2. Node Registry Pattern

Manage different workflow node types via a registry:

export const nodeRegistries: FlowNodeRegistry[] = [
  ConditionNodeRegistry,    // Condition node
  StartNodeRegistry,        // Start node
  EndNodeRegistry,          // End node
  LLMNodeRegistry,          // LLM node
  LoopNodeRegistry,         // Loop node
  CommentNodeRegistry,      // Comment node
  HTTPNodeRegistry,         // HTTP node
  CodeNodeRegistry,         // Code node
  // ... more node types
];

3. Dependency Injection

Use Inversify for service DI:

onBind: ({ bind }) => {
  bind(CustomService).toSelf().inSingletonScope();
}

Core Features

1. Editor Configuration System

useEditorProps is the configuration center of the editor:

export function useEditorProps(
  initialData: FlowDocumentJSON,
  nodeRegistries: FlowNodeRegistry[]
): FreeLayoutProps {
  return useMemo<FreeLayoutProps>(() => ({
    background: true,                    // Background grid
    readonly: false,                     // Readonly mode
    initialData,                         // Initial data
    nodeRegistries,                      // Node registries

    // Core feature configs
    playground: { preventGlobalGesture: true /* Prevent Mac browser swipe gestures */ },
    nodeEngine: { enable: true },
    variableEngine: { enable: true },
    history: { enable: true, enableChangeNode: true },

    // Business rules
    canAddLine: (ctx, fromPort, toPort) => { /* Connection rules */ },
    canDeleteLine: (ctx, line) => { /* Line deletion rules */ },
    canDeleteNode: (ctx, node) => { /* Node deletion rules */ },
    canDropToNode: (ctx, params) => { /* Drag-and-drop rules */ },

    // Plugins
    plugins: () => [/* Plugin list */],

    // Events
    onContentChange: debounce((ctx, event) => { /* Auto save */ }, 1000),
    onInit: (ctx) => { /* Initialization */ },
    onAllLayersRendered: (ctx) => { /* After render */ }
  }), []);
}

2. Node Type System

The app supports multiple workflow node types:

export enum WorkflowNodeType {
  Start = 'start',           // Start node
  End = 'end',               // End node
  LLM = 'llm',               // Large language model node
  HTTP = 'http',             // HTTP request node
  Code = 'code',             // Code execution node
  Variable = 'variable',     // Variable node
  Condition = 'condition',   // Conditional node
  Loop = 'loop',             // Loop node
  BlockStart = 'block-start', // Sub-canvas start node
  BlockEnd = 'block-end',    // Sub-canvas end node
  Comment = 'comment',       // Comment node
  Continue = 'continue',     // Continue node
  Break = 'break',           // Break node
}

Each node follows a unified registration pattern:

export const StartNodeRegistry: FlowNodeRegistry = {
  type: WorkflowNodeType.Start,
  meta: {
    isStart: true,
    deleteDisable: true,        // Not deletable
    copyDisable: true,          // Not copyable
    nodePanelVisible: false,    // Hidden in node panel
    defaultPorts: [{ 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 configuration
  canAdd() { return false; }    // Disallow multiple start nodes
};

3. Plugin Architecture

App features are modularized via the plugin system:

Core Plugin List

  1. FreeLinesPlugin - Connection rendering and interaction
  2. MinimapPlugin - Minimap navigation
  3. FreeSnapPlugin - Auto-alignment and guide-lines
  4. FreeNodePanelPlugin - Node add panel
  5. ContainerNodePlugin - Container nodes (e.g., loop nodes)
  6. FreeGroupPlugin - Node grouping
  7. ContextMenuPlugin - Right-click context menu
  8. RuntimePlugin - Workflow runtime
  9. VariablePanelPlugin - Variable management panel

4. Runtime System

Two run modes are supported:

createRuntimePlugin({
  mode: 'browser',              // Browser mode
  // mode: 'server',            // Server mode
  // serverConfig: {
  //   domain: 'localhost',
  //   port: 4000,
  //   protocol: 'http',
  // },
})

Design Philosophy and Advantages

1. Highly Modular

  • 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

2. Type Safety

  • 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

3. User Experience

  • 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

4. Extensibility

  • 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

5. Performance

  • On-demand loading: Components and plugins support lazy loading
  • Debounce: Performance optimizations for high-frequency operations like auto-save

Technical Highlights

1. In-house Editor Framework

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

2. Advanced Build Configuration

Using Rsbuild as the build tool:

export default defineConfig({
  plugins: [pluginReact(), pluginLess()],
  source: {
    entry: { index: './src/app.tsx' },
    decorators: { version: 'legacy' }  // Enable decorators
  },
  tools: {
    rspack: {
      ignoreWarnings: [/Critical dependency/]  // Ignore specific warnings
    }
  }
});

3. Internationalization

Built-in multilingual support:

i18n: {
  locale: navigator.language,
  languages: {
    'zh-CN': {
      'Never Remind': '不再提瀺',
      'Hold {{key}} to drag node out': 'ζŒ‰δ½ {{key}} 可δ»₯ε°†θŠ‚η‚Ήζ‹–ε‡Ί',
    },
    'en-US': {},
  }
}