Browse Source

feat(fixed-layout): add Agent and Slot node and remove reactor-plugin (#514)

* feat(fixed-layout): add slot node and remove reactor-plugin

* feat(fixed-layout): add slot node and remove reactor-plugin

* feat(fixed-layout): add agent node
xiamidaxia 6 months ago
parent
commit
f489ba51b2
51 changed files with 768 additions and 710 deletions
  1. 11 0
      apps/demo-fixed-layout/src/assets/icon-memory.svg
  2. 10 0
      apps/demo-fixed-layout/src/assets/icon-robot.svg
  3. 11 0
      apps/demo-fixed-layout/src/assets/icon-tool.svg
  4. 82 0
      apps/demo-fixed-layout/src/components/agent-label/index.tsx
  5. 1 0
      apps/demo-fixed-layout/src/components/index.ts
  6. 2 1
      apps/demo-fixed-layout/src/hooks/use-editor-props.ts
  7. 105 0
      apps/demo-fixed-layout/src/initial-data.ts
  8. 22 0
      apps/demo-fixed-layout/src/nodes/agent/agent-llm.ts
  9. 21 0
      apps/demo-fixed-layout/src/nodes/agent/agent-memory.ts
  10. 38 0
      apps/demo-fixed-layout/src/nodes/agent/agent-tools.ts
  11. 57 0
      apps/demo-fixed-layout/src/nodes/agent/agent.ts
  12. 20 0
      apps/demo-fixed-layout/src/nodes/agent/index.ts
  13. 35 0
      apps/demo-fixed-layout/src/nodes/agent/memory.ts
  14. 35 0
      apps/demo-fixed-layout/src/nodes/agent/tool.ts
  15. 2 0
      apps/demo-fixed-layout/src/nodes/index.ts
  16. 7 0
      apps/demo-fixed-layout/src/nodes/llm/index.ts
  17. 0 1
      apps/docs/package.json
  18. 6 64
      common/config/rush/pnpm-lock.yaml
  19. 7 0
      packages/canvas-engine/document/src/entities/flow-node-entity.ts
  20. 2 0
      packages/canvas-engine/document/src/typings/flow.ts
  21. 2 0
      packages/canvas-engine/fixed-layout-core/package.json
  22. 1 0
      packages/canvas-engine/fixed-layout-core/src/activities/index.ts
  23. 17 0
      packages/canvas-engine/fixed-layout-core/src/activities/slot/constants.ts
  24. 8 0
      packages/canvas-engine/fixed-layout-core/src/activities/slot/extends/index.ts
  25. 9 9
      packages/canvas-engine/fixed-layout-core/src/activities/slot/extends/slot-icon.ts
  26. 12 16
      packages/canvas-engine/fixed-layout-core/src/activities/slot/extends/slot-inline-blocks.ts
  27. 13 13
      packages/canvas-engine/fixed-layout-core/src/activities/slot/extends/slot-port.ts
  28. 7 0
      packages/canvas-engine/fixed-layout-core/src/activities/slot/index.ts
  29. 16 15
      packages/canvas-engine/fixed-layout-core/src/activities/slot/slot.ts
  30. 13 0
      packages/canvas-engine/fixed-layout-core/src/activities/slot/typings.ts
  31. 24 30
      packages/canvas-engine/fixed-layout-core/src/activities/slot/utils/create.ts
  32. 4 4
      packages/canvas-engine/fixed-layout-core/src/activities/slot/utils/layout.ts
  33. 38 0
      packages/canvas-engine/fixed-layout-core/src/activities/slot/utils/node.ts
  34. 14 14
      packages/canvas-engine/fixed-layout-core/src/activities/slot/utils/transition.ts
  35. 5 1
      packages/canvas-engine/fixed-layout-core/src/flow-registers.ts
  36. 2 0
      packages/canvas-engine/fixed-layout-core/src/index.ts
  37. 3 0
      packages/canvas-engine/renderer/src/flow-renderer-registry.ts
  38. 4 0
      packages/materials/fixed-semi-materials/src/components/index.tsx
  39. 48 0
      packages/materials/fixed-semi-materials/src/components/slot-collapse.tsx
  40. 54 0
      packages/materials/fixed-semi-materials/src/components/slot-label.tsx
  41. 0 11
      packages/plugins/fixed-reactor-plugin/.eslintrc.js
  42. 0 57
      packages/plugins/fixed-reactor-plugin/package.json
  43. 0 15
      packages/plugins/fixed-reactor-plugin/src/constants.ts
  44. 0 120
      packages/plugins/fixed-reactor-plugin/src/create-fixed-reactor-plugin.ts
  45. 0 10
      packages/plugins/fixed-reactor-plugin/src/index.ts
  46. 0 226
      packages/plugins/fixed-reactor-plugin/src/layout/reactor-shrink-layout.ts
  47. 0 11
      packages/plugins/fixed-reactor-plugin/src/typings.ts
  48. 0 45
      packages/plugins/fixed-reactor-plugin/src/utils/node.ts
  49. 0 7
      packages/plugins/fixed-reactor-plugin/tsconfig.json
  50. 0 31
      packages/plugins/fixed-reactor-plugin/vitest.config.ts
  51. 0 9
      rush.json

+ 11 - 0
apps/demo-fixed-layout/src/assets/icon-memory.svg

@@ -0,0 +1,11 @@
+<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
+     width="44" height="44">
+    <defs>
+        <linearGradient id="paint0_linear_memory" x1="1024" y1="1024" x2="8.09686" y2="4.6982" gradientUnits="userSpaceOnUse">
+            <stop stop-color="#3370FF"/>
+            <stop offset="0.997908" stop-color="#33A9FF"/>
+        </linearGradient>
+    </defs>
+    <path d="M960 160v96c0 88.4-200.6 160-448 160S64 344.4 64 256V160C64 71.6 264.6 0 512 0s448 71.6 448 160z m-109.6 269.4c41.6-14.8 79.8-33.8 109.6-57.2V576c0 88.4-200.6 160-448 160S64 664.4 64 576V372.2c29.8 23.6 68 42.4 109.6 57.2C263.4 461.4 383 480 512 480s248.6-18.6 338.4-50.6zM64 692.2c29.8 23.6 68 42.4 109.6 57.2C263.4 781.4 383 800 512 800s248.6-18.6 338.4-50.6c41.6-14.8 79.8-33.8 109.6-57.2V864c0 88.4-200.6 160-448 160S64 952.4 64 864v-171.8z"
+          fill="url(#paint0_linear_memory)"></path>
+</svg>

+ 10 - 0
apps/demo-fixed-layout/src/assets/icon-robot.svg

@@ -0,0 +1,10 @@
+<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="44" height="44">
+    <defs>
+        <linearGradient id="paint0_linear_robot" x1="1024" y1="1024" x2="8.09686" y2="4.6982" gradientUnits="userSpaceOnUse">
+            <stop stop-color="#3370FF"/>
+            <stop offset="0.997908" stop-color="#33A9FF"/>
+        </linearGradient>
+    </defs>
+    <path d="M717.12 274H762c82.842 0 150 67.158 150 150v200c0 82.842-67.158 150-150 150H262c-82.842 0-150-67.158-150-150V424c0-82.842 67.158-150 150-150h44.88l-18.268-109.602c-4.086-24.514 12.476-47.7 36.99-51.786 24.514-4.086 47.7 12.476 51.786 36.99l20 120c0.246 1.472 0.416 2.94 0.516 4.398h228.192c0.1-1.46 0.27-2.926 0.516-4.398l20-120c4.086-24.514 27.272-41.076 51.786-36.99 24.514 4.086 41.076 27.272 36.99 51.786L717.12 274zM308 484v40c0 24.852 20.148 45 45 45S398 548.852 398 524v-40c0-24.852-20.148-45-45-45S308 459.148 308 484z m318 0v40c0 24.852 20.148 45 45 45S716 548.852 716 524v-40c0-24.852-20.148-45-45-45S626 459.148 626 484zM312 912c-24.852 0-45-20.148-45-45S287.148 822 312 822h400c24.852 0 45 20.148 45 45S736.852 912 712 912H312z"
+          fill="url(#paint0_linear_robot)" ></path>
+</svg>

+ 11 - 0
apps/demo-fixed-layout/src/assets/icon-tool.svg

@@ -0,0 +1,11 @@
+<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
+     width="44" height="4">
+    <defs>
+        <linearGradient id="paint0_linear_tool" x1="1024" y1="1024" x2="8.09686" y2="4.6982" gradientUnits="userSpaceOnUse">
+            <stop stop-color="#3370FF"/>
+            <stop offset="0.997908" stop-color="#33A9FF"/>
+        </linearGradient>
+    </defs>
+    <path d="M1024 716.8v204.8a102.4 102.4 0 0 1-102.4 102.4h-204.8v-102.4a102.4 102.4 0 0 0-102.4-102.4 102.4 102.4 0 0 0-102.4 102.4v102.4H307.2a102.4 102.4 0 0 1-102.4-102.4v-204.8H102.4a102.4 102.4 0 0 1-102.4-102.4 102.4 102.4 0 0 1 102.4-102.4h102.4V307.2c0-56.32 46.08-102.4 102.4-102.4h204.8V102.4a102.4 102.4 0 0 1 102.4-102.4 102.4 102.4 0 0 1 102.4 102.4v102.4h204.8a102.4 102.4 0 0 1 102.4 102.4v204.8h-102.4a102.4 102.4 0 0 0-102.4 102.4 102.4 102.4 0 0 0 102.4 102.4h102.4z"
+          fill="url(#paint0_linear_tool)"></path>
+</svg>

+ 82 - 0
apps/demo-fixed-layout/src/components/agent-label/index.tsx

@@ -0,0 +1,82 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import {
+  type FlowNodeEntity,
+  FlowNodeRenderData,
+  FlowDocument,
+  useService,
+  useClientContext,
+} from '@flowgram.ai/fixed-layout-editor';
+import { Button, Typography } from '@douyinfe/semi-ui';
+import { IconPlus } from '@douyinfe/semi-icons';
+
+import { ToolNodeRegistry } from '../../nodes/agent/tool';
+const { Text } = Typography;
+
+interface PropsType {
+  node: FlowNodeEntity;
+}
+
+export function AgentLabel(props: PropsType) {
+  const { node } = props;
+
+  const nodeData = node.firstChild?.getData<FlowNodeRenderData>(FlowNodeRenderData);
+  const document = useService(FlowDocument) as FlowDocument;
+  const ctx = useClientContext();
+
+  async function addPort() {
+    document.addNode({
+      ...ToolNodeRegistry.onAdd!(ctx, node),
+      parent: node,
+    });
+  }
+  let label = <span>{node.flowNodeType}</span>;
+  switch (node.flowNodeType) {
+    case 'agentMemory':
+      label = (
+        <Button style={{ paddingLeft: 6, paddingRight: 6 }} disabled size="small">
+          <Text ellipsis={{ showTooltip: true }} style={{ color: 'inherit', maxWidth: 65 }}>
+            Memory
+          </Text>
+        </Button>
+      );
+      break;
+    case 'agentLLM':
+      label = (
+        <Button style={{ paddingLeft: 6, paddingRight: 6 }} disabled size="small">
+          <Text ellipsis={{ showTooltip: true }} style={{ color: 'inherit', maxWidth: 65 }}>
+            LLM
+          </Text>
+        </Button>
+      );
+      break;
+    case 'agentTools':
+      label = (
+        <Button
+          onClick={() => {
+            addPort();
+          }}
+          size="small"
+          icon={<IconPlus />}
+        >
+          Tool
+        </Button>
+      );
+  }
+
+  return (
+    <div
+      style={{
+        display: 'flex',
+        background: 'var(--semi-color-bg-0)',
+      }}
+      onMouseEnter={() => nodeData?.toggleMouseEnter()}
+      onMouseLeave={() => nodeData?.toggleMouseLeave()}
+    >
+      {label}
+    </div>
+  );
+}

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

@@ -5,3 +5,4 @@
 
 export { DemoTools } from './tools';
 export { DragNode } from './drag-node';
+export { AgentLabel } from './agent-label';

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

@@ -27,7 +27,7 @@ import { SelectorBoxPopover } from '../components/selector-box-popover';
 import NodeAdder from '../components/node-adder';
 import BranchAdder from '../components/branch-adder';
 import { BaseNode } from '../components/base-node';
-import { DragNode } from '../components';
+import { DragNode, AgentLabel } from '../components';
 
 export function useEditorProps(
   initialData: FlowDocumentJSON,
@@ -174,6 +174,7 @@ export function useEditorProps(
           [FlowRendererKey.ADDER]: NodeAdder, // Node Add Button
           [FlowRendererKey.BRANCH_ADDER]: BranchAdder, // Branch Add Button
           [FlowRendererKey.DRAG_NODE]: DragNode, // Component in node dragging
+          [FlowRendererKey.SLOT_LABEL_RENDER]: AgentLabel, // Agent Label
         },
         renderDefaultNode: BaseNode, // node render
         renderTexts: {

+ 105 - 0
apps/demo-fixed-layout/src/initial-data.ts

@@ -42,6 +42,111 @@ export const initialData: FlowDocumentJSON = {
         },
       },
     },
+    {
+      id: 'agent_0',
+      type: 'agent',
+      data: {
+        title: 'Agent',
+      },
+      blocks: [
+        {
+          id: 'agentLLM_0',
+          type: 'agentLLM',
+          blocks: [
+            {
+              id: 'llm_5',
+              type: 'llm',
+              meta: {
+                defaultExpanded: false,
+              },
+              data: {
+                title: 'LLM',
+                inputsValues: {
+                  modelType: {
+                    type: 'constant',
+                    content: 'gpt-3.5-turbo',
+                  },
+                  temperature: {
+                    type: 'constant',
+                    content: 0.5,
+                  },
+                  systemPrompt: {
+                    type: 'constant',
+                    content: '# Role\nYou are an AI assistant.\n',
+                  },
+                  prompt: {
+                    type: 'constant',
+                    content: '',
+                  },
+                },
+                inputs: {
+                  type: 'object',
+                  required: ['modelType', 'temperature', 'prompt'],
+                  properties: {
+                    modelType: {
+                      type: 'string',
+                    },
+                    temperature: {
+                      type: 'number',
+                    },
+                    systemPrompt: {
+                      type: 'string',
+                      extra: { formComponent: 'prompt-editor' },
+                    },
+                    prompt: {
+                      type: 'string',
+                      extra: { formComponent: 'prompt-editor' },
+                    },
+                  },
+                },
+                outputs: {
+                  type: 'object',
+                  properties: {
+                    result: { type: 'string' },
+                  },
+                },
+              },
+            },
+          ],
+        },
+        {
+          id: 'agentMemory_0',
+          type: 'agentMemory',
+          blocks: [
+            {
+              id: 'memory_0',
+              type: 'memory',
+              meta: {
+                defaultExpanded: false,
+              },
+              data: {
+                title: 'Memory',
+              },
+            },
+          ],
+        },
+        {
+          id: 'agentTools_0',
+          type: 'agentTools',
+          blocks: [
+            {
+              id: 'tool_0',
+              type: 'tool',
+              data: {
+                title: 'Tool0',
+              },
+            },
+            {
+              id: 'tool_1',
+              type: 'tool',
+              data: {
+                title: 'Tool1',
+              },
+            },
+          ],
+        },
+      ],
+    },
     {
       id: 'llm_0',
       type: 'llm',

+ 22 - 0
apps/demo-fixed-layout/src/nodes/agent/agent-llm.ts

@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { FlowNodeBaseType } from '@flowgram.ai/fixed-layout-editor';
+
+import { FlowNodeRegistry } from '../../typings';
+
+export const AgentLLMNodeRegistry: FlowNodeRegistry = {
+  type: 'agentLLM',
+  extend: FlowNodeBaseType.SLOT_PORT,
+  meta: {
+    addDisable: true,
+    sidebarDisable: true,
+    draggable: false,
+  },
+  info: {
+    icon: '',
+    description: 'Agent LLM.',
+  },
+};

+ 21 - 0
apps/demo-fixed-layout/src/nodes/agent/agent-memory.ts

@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { FlowNodeBaseType } from '@flowgram.ai/fixed-layout-editor';
+
+import { FlowNodeRegistry } from '../../typings';
+
+export const AgentMemoryNodeRegistry: FlowNodeRegistry = {
+  type: 'agentMemory',
+  extend: FlowNodeBaseType.SLOT_PORT,
+  meta: {
+    addDisable: true,
+    sidebarDisable: true,
+  },
+  info: {
+    icon: '',
+    description: 'Agent Memory.',
+  },
+};

+ 38 - 0
apps/demo-fixed-layout/src/nodes/agent/agent-tools.ts

@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { nanoid } from 'nanoid';
+import { FlowNodeBaseType } from '@flowgram.ai/fixed-layout-editor';
+
+import { FlowNodeRegistry } from '../../typings';
+
+let index = 0;
+export const AgentToolsNodeRegistry: FlowNodeRegistry = {
+  type: 'agentTools',
+  extend: FlowNodeBaseType.SLOT_PORT,
+  info: {
+    icon: '',
+    description: 'Agent Tools.',
+  },
+  meta: {
+    addDisable: true,
+    sidebarDisable: true,
+  },
+  onAdd() {
+    return {
+      id: `tool_${nanoid(5)}`,
+      type: 'agentTool',
+      data: {
+        title: `Tool_${++index}`,
+        outputs: {
+          type: 'object',
+          properties: {
+            result: { type: 'string' },
+          },
+        },
+      },
+    };
+  },
+};

+ 57 - 0
apps/demo-fixed-layout/src/nodes/agent/agent.ts

@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { nanoid } from 'nanoid';
+import { FlowNodeBaseType } from '@flowgram.ai/fixed-layout-editor';
+
+import { LLMNodeRegistry } from '../llm';
+import { defaultFormMeta } from '../default-form-meta';
+import { FlowNodeRegistry } from '../../typings';
+import iconRobot from '../../assets/icon-robot.svg';
+import { ToolNodeRegistry } from './tool';
+import { MemoryNodeRegistry } from './memory';
+
+let index = 0;
+export const AgentNodeRegistry: FlowNodeRegistry = {
+  type: 'agent',
+  extend: FlowNodeBaseType.SLOT,
+  info: {
+    icon: iconRobot,
+    description: 'AI Agent.',
+  },
+  formMeta: defaultFormMeta,
+  onAdd(ctx, from) {
+    return {
+      id: `agent_${nanoid(5)}`,
+      type: 'agent',
+      blocks: [
+        {
+          id: `agentLLM_${nanoid(5)}`,
+          type: 'agentLLM',
+          blocks: [LLMNodeRegistry.onAdd!(ctx, from)],
+        },
+        {
+          id: `agentMemory_${nanoid(5)}`,
+          type: 'agentMemory',
+          blocks: [MemoryNodeRegistry.onAdd!(ctx, from)],
+        },
+        {
+          id: `agentTools_${nanoid(5)}`,
+          type: 'agentTools',
+          blocks: [ToolNodeRegistry.onAdd!(ctx, from)],
+        },
+      ],
+      data: {
+        title: `Agent_${++index}`,
+        outputs: {
+          type: 'object',
+          properties: {
+            result: { type: 'string' },
+          },
+        },
+      },
+    };
+  },
+};

+ 20 - 0
apps/demo-fixed-layout/src/nodes/agent/index.ts

@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { ToolNodeRegistry } from './tool';
+import { MemoryNodeRegistry } from './memory';
+import { AgentToolsNodeRegistry } from './agent-tools';
+import { AgentMemoryNodeRegistry } from './agent-memory';
+import { AgentLLMNodeRegistry } from './agent-llm';
+import { AgentNodeRegistry } from './agent';
+
+export const AgentNodeRegistries = [
+  AgentNodeRegistry,
+  AgentMemoryNodeRegistry,
+  AgentToolsNodeRegistry,
+  AgentLLMNodeRegistry,
+  MemoryNodeRegistry,
+  ToolNodeRegistry,
+];

+ 35 - 0
apps/demo-fixed-layout/src/nodes/agent/memory.ts

@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { nanoid } from 'nanoid';
+
+import { defaultFormMeta } from '../default-form-meta';
+import { FlowNodeRegistry } from '../../typings';
+import iconMemory from '../../assets/icon-memory.svg';
+
+let index = 0;
+export const MemoryNodeRegistry: FlowNodeRegistry = {
+  type: 'memory',
+  info: {
+    icon: iconMemory,
+    description: 'Memory.',
+  },
+  meta: {
+    addDisable: true,
+    deleteDisable: true, // memory 不能单独删除,只能通过 agent
+    copyDisable: true,
+    draggable: false,
+  },
+  formMeta: defaultFormMeta,
+  onAdd() {
+    return {
+      id: `memory_${nanoid(5)}`,
+      type: 'memory',
+      data: {
+        title: `Memory_${++index}`,
+      },
+    };
+  },
+};

+ 35 - 0
apps/demo-fixed-layout/src/nodes/agent/tool.ts

@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { nanoid } from 'nanoid';
+
+import { defaultFormMeta } from '../default-form-meta';
+import { FlowNodeRegistry } from '../../typings';
+import iconTool from '../../assets/icon-tool.svg';
+
+let index = 0;
+export const ToolNodeRegistry: FlowNodeRegistry = {
+  type: 'tool',
+  info: {
+    icon: iconTool,
+    description: 'Tool.',
+  },
+  meta: {
+    addDisable: true,
+    deleteDisable: true, // memory 不能单独删除,只能通过 agent
+    copyDisable: true,
+    draggable: false,
+  },
+  formMeta: defaultFormMeta,
+  onAdd() {
+    return {
+      id: `tool${nanoid(5)}`,
+      type: 'tool',
+      data: {
+        title: `Tool_${++index}`,
+      },
+    };
+  },
+};

+ 2 - 0
apps/demo-fixed-layout/src/nodes/index.ts

@@ -16,6 +16,7 @@ import { CatchBlockNodeRegistry } from './catch-block';
 import { CaseDefaultNodeRegistry } from './case-default';
 import { CaseNodeRegistry } from './case';
 import { BreakLoopNodeRegistry } from './break-loop';
+import { AgentNodeRegistries } from './agent';
 
 export const FlowNodeRegistries: FlowNodeRegistry[] = [
   StartNodeRegistry,
@@ -30,4 +31,5 @@ export const FlowNodeRegistries: FlowNodeRegistry[] = [
   IFBlockNodeRegistry,
   BreakLoopNodeRegistry,
   CaseDefaultNodeRegistry,
+  ...AgentNodeRegistries,
 ];

+ 7 - 0
apps/demo-fixed-layout/src/nodes/llm/index.ts

@@ -6,6 +6,7 @@
 import { nanoid } from 'nanoid';
 
 import { defaultFormMeta } from '../default-form-meta';
+import { AgentLLMNodeRegistry } from '../agent/agent-llm';
 import { FlowNodeRegistry } from '../../typings';
 import iconLLM from '../../assets/icon-llm.jpg';
 
@@ -18,6 +19,12 @@ export const LLMNodeRegistry: FlowNodeRegistry = {
       'Call the large language model and use variables and prompt words to generate responses.',
   },
   formMeta: defaultFormMeta,
+  meta: {
+    draggable: (node) => node.parent?.flowNodeType !== AgentLLMNodeRegistry.type,
+  },
+  canDelete(ctx, node) {
+    return node.parent?.flowNodeType !== AgentLLMNodeRegistry.type;
+  },
   onAdd() {
     return {
       id: `llm_${nanoid(5)}`,

+ 0 - 1
apps/docs/package.json

@@ -27,7 +27,6 @@
     "@flowgram.ai/free-layout-editor": "workspace:*",
     "@flowgram.ai/group-plugin": "workspace:*",
     "@flowgram.ai/form-core": "workspace:*",
-    "@flowgram.ai/fixed-reactor-plugin": "workspace:*",
     "@flowgram.ai/free-auto-layout-plugin": "workspace:*",
     "@flowgram.ai/minimap-plugin": "workspace:*",
     "@flowgram.ai/free-stack-plugin": "workspace:*",

+ 6 - 64
common/config/rush/pnpm-lock.yaml

@@ -783,9 +783,6 @@ importers:
       '@flowgram.ai/fixed-layout-editor':
         specifier: workspace:*
         version: link:../../packages/client/fixed-layout-editor
-      '@flowgram.ai/fixed-reactor-plugin':
-        specifier: workspace:*
-        version: link:../../packages/plugins/fixed-reactor-plugin
       '@flowgram.ai/fixed-semi-materials':
         specifier: workspace:*
         version: link:../../packages/materials/fixed-semi-materials
@@ -1161,6 +1158,9 @@ importers:
       inversify:
         specifier: ^6.0.1
         version: 6.2.0(reflect-metadata@0.2.2)
+      lodash:
+        specifier: ^4.17.21
+        version: 4.17.21
       react:
         specifier: '>=16.8'
         version: 18.3.1
@@ -1177,6 +1177,9 @@ importers:
       '@flowgram.ai/ts-config':
         specifier: workspace:*
         version: link:../../../config/ts-config
+      '@types/lodash':
+        specifier: ^4.14.137
+        version: 4.17.13
       '@types/react':
         specifier: ^18
         version: 18.3.16
@@ -2543,67 +2546,6 @@ importers:
         specifier: ^0.34.6
         version: 0.34.6(jsdom@22.1.0)
 
-  ../../packages/plugins/fixed-reactor-plugin:
-    dependencies:
-      '@flowgram.ai/core':
-        specifier: workspace:*
-        version: link:../../canvas-engine/core
-      '@flowgram.ai/document':
-        specifier: workspace:*
-        version: link:../../canvas-engine/document
-      '@flowgram.ai/renderer':
-        specifier: workspace:*
-        version: link:../../canvas-engine/renderer
-      '@flowgram.ai/utils':
-        specifier: workspace:*
-        version: link:../../common/utils
-      inversify:
-        specifier: ^6.0.1
-        version: 6.2.0(reflect-metadata@0.2.2)
-      lodash:
-        specifier: ^4.17.21
-        version: 4.17.21
-      react:
-        specifier: '>=16.8'
-        version: 18.3.1
-      react-dom:
-        specifier: '>=16.8'
-        version: 18.3.1(react@18.3.1)
-      reflect-metadata:
-        specifier: ~0.2.2
-        version: 0.2.2
-    devDependencies:
-      '@flowgram.ai/eslint-config':
-        specifier: workspace:*
-        version: link:../../../config/eslint-config
-      '@flowgram.ai/ts-config':
-        specifier: workspace:*
-        version: link:../../../config/ts-config
-      '@types/lodash':
-        specifier: ^4.14.137
-        version: 4.17.13
-      '@types/react':
-        specifier: ^18
-        version: 18.3.16
-      '@types/react-dom':
-        specifier: ^18
-        version: 18.3.5(@types/react@18.3.16)
-      '@vitest/coverage-v8':
-        specifier: ^0.32.0
-        version: 0.32.4(vitest@0.34.6)
-      eslint:
-        specifier: ^8.54.0
-        version: 8.57.1
-      tsup:
-        specifier: ^8.0.1
-        version: 8.3.5(typescript@5.0.4)
-      typescript:
-        specifier: ^5.0.4
-        version: 5.0.4
-      vitest:
-        specifier: ^0.34.6
-        version: 0.34.6(jsdom@22.1.0)
-
   ../../packages/plugins/free-auto-layout-plugin:
     dependencies:
       '@dagrejs/graphlib':

+ 7 - 0
packages/canvas-engine/document/src/entities/flow-node-entity.ts

@@ -388,6 +388,13 @@ export class FlowNodeEntity extends Entity<FlowNodeEntityConfig> {
   get bounds(): Rectangle {
     return this.transform.bounds;
   }
+
+  /**
+   * Check node extend
+   */
+  isExtend(parentType: string): boolean {
+    return this.document.isExtend(this.flowNodeType, parentType);
+  }
 }
 
 export namespace FlowNodeEntity {

+ 2 - 0
packages/canvas-engine/document/src/typings/flow.ts

@@ -41,6 +41,8 @@ export enum FlowNodeBaseType {
   MULTI_OUTPUTS = 'multiOutputs', // 多输出
   INPUT = 'input', // 输入节点
   OUTPUT = 'output', // 输出节点
+  SLOT = 'slot', // 插槽节点
+  SLOT_PORT = 'slotPort', // 插槽端口节点
 }
 
 export enum FlowNodeSplitType {

+ 2 - 0
packages/canvas-engine/fixed-layout-core/package.json

@@ -30,12 +30,14 @@
     "@flowgram.ai/document": "workspace:*",
     "@flowgram.ai/renderer": "workspace:*",
     "@flowgram.ai/utils": "workspace:*",
+    "lodash": "^4.17.21",
     "inversify": "^6.0.1",
     "reflect-metadata": "~0.2.2"
   },
   "devDependencies": {
     "@flowgram.ai/eslint-config": "workspace:*",
     "@flowgram.ai/ts-config": "workspace:*",
+    "@types/lodash": "^4.14.137",
     "@types/react": "^18",
     "@types/react-dom": "^18",
     "@vitest/coverage-v8": "^0.32.0",

+ 1 - 0
packages/canvas-engine/fixed-layout-core/src/activities/index.ts

@@ -21,3 +21,4 @@ export * from './input';
 export * from './output';
 export * from './multi-outputs';
 export * from './multi-inputs';
+export * from './slot';

+ 17 - 0
packages/canvas-engine/fixed-layout-core/src/activities/slot/constants.ts

@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { FlowRendererKey } from '@flowgram.ai/renderer';
+
+export const RENDER_SLOT_LABEL_KEY = FlowRendererKey.SLOT_LABEL_RENDER;
+export const RENDER_SLOT_COLLAPSE_KEY = FlowRendererKey.SLOT_COLLPASE_RENDER;
+
+export const SLOT_PORT_DISTANCE = 60;
+export const SLOT_COLLAPSE_MARGIN = 20;
+export const SLOT_SPACING = 32;
+
+export const SLOT_NODE_LAST_SPACING = 10;
+
+export const SLOT_INLINE_BLOCKS_DELTA = SLOT_COLLAPSE_MARGIN + SLOT_PORT_DISTANCE * 2;

+ 8 - 0
packages/canvas-engine/fixed-layout-core/src/activities/slot/extends/index.ts

@@ -0,0 +1,8 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+export { SlotIconRegistry } from './slot-icon';
+export { SlotInlineBlocksRegistry } from './slot-inline-blocks';
+export { SlotPortRegistry } from './slot-port';

+ 9 - 9
packages/plugins/fixed-reactor-plugin/src/extends/reactor-icon.ts → packages/canvas-engine/fixed-layout-core/src/activities/slot/extends/slot-icon.ts

@@ -6,23 +6,23 @@
 import { FlowNodeRegistry, FlowNodeBaseType } from '@flowgram.ai/document';
 
 import { drawCollapseLabel, drawCollapseLine } from '../utils/transition';
-import { canReactorDrilldown, insideReactor } from '../utils/node';
+import { canSlotDrilldown, insideSlot } from '../utils/node';
 
-export const reactorIcon: FlowNodeRegistry = {
+export const SlotIconRegistry: FlowNodeRegistry = {
   type: FlowNodeBaseType.BLOCK_ICON,
   meta: {
     defaultExpanded: false,
     spacing: 0,
   },
-  getLines: transition => [
-    ...(canReactorDrilldown(transition.entity.parent!) ? drawCollapseLine(transition) : []),
+  getLines: (transition) => [
+    ...(canSlotDrilldown(transition.entity.parent!) ? drawCollapseLine(transition) : []),
   ],
-  getLabels: transition => [
-    ...(canReactorDrilldown(transition.entity.parent!) ? drawCollapseLabel(transition) : []),
+  getLabels: (transition) => [
+    ...(canSlotDrilldown(transition.entity.parent!) ? drawCollapseLabel(transition) : []),
   ],
-  getDelta: transform => {
-    // Reactor 节点内部时,重新纠正重心调整产生的偏移
-    if (insideReactor(transform.entity.parent)) {
+  getDelta: (transform) => {
+    // Slot 节点内部时,重新纠正重心调整产生的偏移
+    if (insideSlot(transform.entity.parent)) {
       return transform.entity.isVertical
         ? { x: -transform.originDeltaX, y: 0 }
         : { x: 0, y: -transform.originDeltaY };

+ 12 - 16
packages/plugins/fixed-reactor-plugin/src/extends/reactor-inline-blocks.ts → packages/canvas-engine/fixed-layout-core/src/activities/slot/extends/slot-inline-blocks.ts

@@ -6,21 +6,17 @@
 import { FlowNodeRegistry, FlowNodeBaseType } from '@flowgram.ai/document';
 import { FlowNodeTransformData } from '@flowgram.ai/document';
 
-import { ReactorNodeType } from '../typings';
-import {
-  REACTOR_COLLAPSE_MARGIN,
-  REACTOR_INLINE_BLOCKS_DELTA,
-  REACTOR_PORT_DISTANCE,
-} from '../constants';
+import { SlotNodeType } from '../typings';
+import { SLOT_COLLAPSE_MARGIN, SLOT_INLINE_BLOCKS_DELTA, SLOT_PORT_DISTANCE } from '../constants';
 
-export const reactorInlineBlocks: FlowNodeRegistry = {
-  type: ReactorNodeType.ReactorInlineBlocks,
+export const SlotInlineBlocksRegistry: FlowNodeRegistry = {
+  type: SlotNodeType.SlotInlineBlocks,
   extend: FlowNodeBaseType.BLOCK,
   meta: {
     spacing: 0,
     inlineSpacingPre: 0,
     inlineSpacingAfter: 0,
-    isInlineBlocks: node => !node.isVertical,
+    isInlineBlocks: (node) => !node.isVertical,
   },
   getLines() {
     return [];
@@ -58,24 +54,24 @@ export const reactorInlineBlocks: FlowNodeRegistry = {
     }
 
     if (!transform.entity.isVertical) {
-      const noChildren = transform?.children?.every?.(_port => !_port.children.length);
+      const noChildren = transform?.children?.every?.((_port) => !_port.children.length);
       /**
-       * 如果没有 children 的时候,不需要有右侧的间距,避免水平布局的时候 reactor 右侧空间过大。
+       * 如果没有 children 的时候,不需要有右侧的间距,避免水平布局的时候 Slot 右侧空间过大。
        */
       if (noChildren) {
         return {
-          x: REACTOR_PORT_DISTANCE - icon.localBounds.width / 2,
-          y: icon.localBounds.bottom + REACTOR_COLLAPSE_MARGIN,
+          x: SLOT_PORT_DISTANCE - icon.localBounds.width / 2,
+          y: icon.localBounds.bottom + SLOT_COLLAPSE_MARGIN,
         };
       }
       return {
-        x: 2 * REACTOR_PORT_DISTANCE - icon.localBounds.width / 2,
-        y: icon.localBounds.bottom + REACTOR_COLLAPSE_MARGIN,
+        x: 2 * SLOT_PORT_DISTANCE - icon.localBounds.width / 2,
+        y: icon.localBounds.bottom + SLOT_COLLAPSE_MARGIN,
       };
     }
 
     return {
-      x: icon.localBounds.right + REACTOR_INLINE_BLOCKS_DELTA,
+      x: icon.localBounds.right + SLOT_INLINE_BLOCKS_DELTA,
       y: -icon.localBounds.height,
     };
   },

+ 13 - 13
packages/plugins/fixed-reactor-plugin/src/extends/reactor-port.ts → packages/canvas-engine/fixed-layout-core/src/activities/slot/extends/slot-port.ts

@@ -12,28 +12,28 @@ import {
 } from '@flowgram.ai/document';
 import { FlowNodeTransformData } from '@flowgram.ai/document';
 
-import { getPortChildInput, getReactorChildLineStartPoint } from '../utils/transition';
-import { ReactorNodeType } from '../typings';
-import { REACTOR_PORT_DISTANCE, RENDER_REACTOR_PORT_KEY } from '../constants';
+import { getPortChildInput, getSlotChildLineStartPoint } from '../utils/transition';
+import { SlotNodeType } from '../typings';
+import { SLOT_PORT_DISTANCE, RENDER_SLOT_LABEL_KEY } from '../constants';
 
-export const reactorPort: FlowNodeRegistry = {
-  type: ReactorNodeType.ReactorPort,
+export const SlotPortRegistry: FlowNodeRegistry = {
+  type: SlotNodeType.SlotPort,
   extend: FlowNodeBaseType.BLOCK,
   meta: {
     inlineSpacingAfter: 0,
     inlineSpacingPre: 0,
-    spacing: transform => {
+    spacing: (transform) => {
       // 水平布局没有子节点情况
       if (!transform.entity.isVertical && transform.size.width === 0) {
         return 90;
       }
       return 30;
     },
-    isInlineBlocks: node => !node.isVertical,
+    isInlineBlocks: (node) => !node.isVertical,
   },
   getLines(transition) {
     const icon = transition.transform.parent?.pre;
-    const start = getReactorChildLineStartPoint(icon);
+    const start = getSlotChildLineStartPoint(icon);
     const portPoint = transition.transform.inputPoint;
 
     return [
@@ -47,7 +47,7 @@ export const reactorPort: FlowNodeRegistry = {
         },
         radius: 5,
       },
-      ...transition.transform.children.map(_child => {
+      ...transition.transform.children.map((_child) => {
         const childInput = getPortChildInput(_child);
 
         return {
@@ -69,9 +69,9 @@ export const reactorPort: FlowNodeRegistry = {
     return [
       {
         type: FlowTransitionLabelEnum.CUSTOM_LABEL,
-        renderKey: RENDER_REACTOR_PORT_KEY,
+        renderKey: RENDER_SLOT_LABEL_KEY,
         props: {
-          port: transition.entity,
+          node: transition.entity,
         },
         offset: portPoint,
       },
@@ -79,7 +79,7 @@ export const reactorPort: FlowNodeRegistry = {
   },
   getInputPoint(transform) {
     const icon = transform.parent?.pre;
-    const start = getReactorChildLineStartPoint(icon);
+    const start = getSlotChildLineStartPoint(icon);
 
     let inputY = transform.bounds.center.y;
     if (transform.children.length) {
@@ -90,7 +90,7 @@ export const reactorPort: FlowNodeRegistry = {
     }
 
     return {
-      x: start.x + REACTOR_PORT_DISTANCE,
+      x: start.x + SLOT_PORT_DISTANCE,
       y: inputY,
     };
   },

+ 7 - 0
packages/canvas-engine/fixed-layout-core/src/activities/slot/index.ts

@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+export { SlotRegistry } from './slot';
+export { SlotPortRegistry } from './extends';

+ 16 - 15
packages/plugins/fixed-reactor-plugin/src/reactor-node.ts → packages/canvas-engine/fixed-layout-core/src/activities/slot/slot.ts

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: MIT
  */
 
-import { FlowNodeRegistry } from '@flowgram.ai/document';
+import { FlowNodeRegistry, FlowNodeBaseType } from '@flowgram.ai/document';
 
 import {
   drawStraightAdder,
@@ -11,38 +11,38 @@ import {
   getInputPoint,
   getOutputPoint,
 } from './utils/transition';
-import { insideReactor } from './utils/node';
+import { insideSlot } from './utils/node';
 import { getAllPortsMiddle } from './utils/layout';
-import { createReactorFromJSON } from './utils/create';
-import { REACTOR_COLLAPSE_MARGIN, REACTOR_NODE_LAST_SPACING, REACTOR_SPACING } from './constants';
+import { createSlotFromJSON } from './utils/create';
+import { SlotInlineBlocksRegistry, SlotIconRegistry } from './extends';
+import { SLOT_COLLAPSE_MARGIN, SLOT_NODE_LAST_SPACING, SLOT_SPACING } from './constants';
 
-export const Reactor: FlowNodeRegistry = {
-  type: 'reactor',
+export const SlotRegistry: FlowNodeRegistry = {
+  type: FlowNodeBaseType.SLOT,
   extend: 'block',
   meta: {
-    // Reactor 节点内部暂时不允许拖拽
-    draggable: (node) => !insideReactor(node),
+    // Slot 节点内部暂时不允许拖拽
+    draggable: (node) => !insideSlot(node),
     hidden: true,
-    spacing: REACTOR_SPACING,
+    spacing: SLOT_SPACING,
     padding: (node) => ({
       left: 0,
-      right: node.collapsed ? REACTOR_COLLAPSE_MARGIN : 0,
-      bottom: !insideReactor(node.entity) && node.isLast ? REACTOR_NODE_LAST_SPACING : 0,
+      right: node.collapsed ? SLOT_COLLAPSE_MARGIN : 0,
+      bottom: !insideSlot(node.entity) && node.isLast ? SLOT_NODE_LAST_SPACING : 0,
       top: 0,
     }),
     copyDisable: false,
     defaultExpanded: false,
-    isReactor: true,
   },
   /**
    * 业务通常需要重载方法
    */
-  onCreate: createReactorFromJSON,
+  onCreate: createSlotFromJSON,
   getLines: (transition) => [
-    ...(!insideReactor(transition.entity) ? drawStraightLine(transition) : []),
+    ...(!insideSlot(transition.entity) ? drawStraightLine(transition) : []),
   ],
   getLabels: (transition) => [
-    ...(!insideReactor(transition.entity) ? drawStraightAdder(transition) : []),
+    ...(!insideSlot(transition.entity) ? drawStraightAdder(transition) : []),
   ],
   getInputPoint,
   getOutputPoint,
@@ -97,4 +97,5 @@ export const Reactor: FlowNodeRegistry = {
       },
     });
   },
+  extendChildRegistries: [SlotIconRegistry, SlotInlineBlocksRegistry],
 };

+ 13 - 0
packages/canvas-engine/fixed-layout-core/src/activities/slot/typings.ts

@@ -0,0 +1,13 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { FlowNodeBaseType } from '@flowgram.ai/document';
+
+export enum SlotNodeType {
+  Slot = FlowNodeBaseType.SLOT,
+  SlotPort = FlowNodeBaseType.SLOT_PORT,
+  SlotInlineBlocks = 'slotInlineBlocks',
+  SlotPortInlineBlocks = 'slotPortInlineBlocks',
+}

+ 24 - 30
packages/plugins/fixed-reactor-plugin/src/utils/create.ts → packages/canvas-engine/fixed-layout-core/src/activities/slot/utils/create.ts

@@ -7,16 +7,16 @@ import { FlowNodeBaseType } from '@flowgram.ai/document';
 import { type FlowNodeEntity } from '@flowgram.ai/document';
 import { FlowNodeJSON } from '@flowgram.ai/document';
 
-import { ReactorNodeType } from '../typings';
+import { SlotNodeType } from '../typings';
 
-// Reactor 样例数据
+// Slot 样例数据
 // const mock = {
-//   type: 'reactor',
+//   type: 'slot',
 //   id: 'reactor_parent',
 //   blocks: [
 //     {
 //       id: 'port_LnSdK',
-//       blocks: [{ type: 'reactor', id: 'reactor_child' }],
+//       blocks: [{ type: 'Slot', id: 'reactor_child' }],
 //     },
 //     {
 //       id: 'port_60X7U',
@@ -31,24 +31,24 @@ import { ReactorNodeType } from '../typings';
 // };
 
 /**
- * 创建 Reactor 子节点
- * - Reactor
- *  - ReactorBlockIcon
- *  - ReactorInlineBlocks
- *    - ReactorPort 1
- *        - ReactorPortIcon 1
- *          - ChildReactor 1
- *          - ChildReactor 2
- *    - ReactorPort 2
+ * 创建 Slot 子节点
+ * - Slot
+ *  - SlotBlockIcon
+ *  - SlotInlineBlocks
+ *    - SlotPort 1
+ *        - SlotPortIcon 1
+ *          - ChildSlot 1
+ *          - ChildSlot 2
+ *    - SlotPort 2
  *
  * 范例数据:
  * {
- *  type: 'reactor',
+ *  type: 'Slot',
  *  id: 'reactor_parent',
  *  blocks: [
  *    {
  *      id: 'port_LnSdK',
- *      blocks: [{ type: 'reactor', id: 'reactor_child' }],
+ *      blocks: [{ type: 'Slot', id: 'reactor_child' }],
  *    },
  *    {
  *      id: 'port_60X7U',
@@ -56,28 +56,22 @@ import { ReactorNodeType } from '../typings';
  *  ],
  * }
  *
- * @param node
- * @param json
- * @returns
  */
-export const createReactorFromJSON = (
-  node: FlowNodeEntity,
-  json: FlowNodeJSON,
-): FlowNodeEntity[] => {
+export const createSlotFromJSON = (node: FlowNodeEntity, json: FlowNodeJSON): FlowNodeEntity[] => {
   const { document } = node;
 
   const addedNodes: FlowNodeEntity[] = [];
 
   // 块列表开始节点,用来展示块的按钮
   const blockIconNode = document.addNode({
-    id: `$reactorIcon$${node.id}`,
+    id: `$slotIcon$${node.id}`,
     type: FlowNodeBaseType.BLOCK_ICON,
     originParent: node,
     parent: node,
   });
   const inlineBlocksNode = document.addNode({
-    id: `$reactorInlineBlocks$${node.id}`,
-    type: ReactorNodeType.ReactorInlineBlocks,
+    id: `$slotInlineBlocks$${node.id}`,
+    type: SlotNodeType.SlotInlineBlocks,
     originParent: node,
     parent: node,
   });
@@ -86,23 +80,23 @@ export const createReactorFromJSON = (
 
   const portJSONList = json.blocks || [];
 
-  portJSONList.forEach(_portJSON => {
+  portJSONList.forEach((_portJSON) => {
     const port = document.addNode({
+      type: SlotNodeType.SlotPort,
       ..._portJSON,
-      type: ReactorNodeType.ReactorPort,
       originParent: node,
       parent: inlineBlocksNode,
     });
     addedNodes.push(port);
 
-    (_portJSON.blocks || []).forEach(_portChild => {
+    (_portJSON.blocks || []).forEach((_portChild) => {
       document.addNode(
         {
-          type: ReactorNodeType.Reactor,
+          type: SlotNodeType.Slot,
           ..._portChild,
           parent: port,
         },
-        addedNodes,
+        addedNodes
       );
     });
   });

+ 4 - 4
packages/plugins/fixed-reactor-plugin/src/utils/layout.ts → packages/canvas-engine/fixed-layout-core/src/activities/slot/utils/layout.ts

@@ -24,8 +24,8 @@ export const getPortMiddle = (_port: FlowNodeTransformData) => {
     return _port.localBounds.top;
   }
 
-  const portChildInputs = [_port.firstChild!, _port.lastChild!].map(_portChild =>
-    getDisplayFirstChildTop(_portChild),
+  const portChildInputs = [_port.firstChild!, _port.lastChild!].map((_portChild) =>
+    getDisplayFirstChildTop(_portChild)
   );
 
   return _port.localBounds.top + mean(portChildInputs);
@@ -41,8 +41,8 @@ export const getAllPortsMiddle = (inlineBlocks: FlowNodeTransformData) => {
     return inlineBlocks.localBounds.height / 2;
   }
 
-  const portInputs = [inlineBlocks.firstChild!, inlineBlocks.lastChild!].map(_port =>
-    getPortMiddle(_port),
+  const portInputs = [inlineBlocks.firstChild!, inlineBlocks.lastChild!].map((_port) =>
+    getPortMiddle(_port)
   );
 
   return mean(portInputs);

+ 38 - 0
packages/canvas-engine/fixed-layout-core/src/activities/slot/utils/node.ts

@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { FlowNodeEntity } from '@flowgram.ai/document';
+import { FlowNodeTransformData } from '@flowgram.ai/document';
+
+import { SlotNodeType } from '../typings';
+
+/**
+ * Slot 节点是否可下钻,看 inlineBlocks 是否有子节点
+ * @param Slot Slot 节点
+ */
+export const canSlotDrilldown = (Slot: FlowNodeEntity): boolean =>
+  !!Slot?.lastCollapsedChild?.blocks.length;
+
+/**
+ * 是否是 Slot 内部
+ * @param entity
+ * @returns
+ */
+export const insideSlot = (entity?: FlowNodeEntity): boolean =>
+  !!entity?.parent?.isExtend(SlotNodeType.SlotPort);
+
+/**
+ * 获取在页面上实际渲染的第一个 Child 节点
+ * @param node
+ */
+export const getDisplayFirstChildTransform = (
+  transform: FlowNodeTransformData
+): FlowNodeTransformData => {
+  if (transform.firstChild) {
+    return getDisplayFirstChildTransform(transform.firstChild);
+  }
+
+  return transform;
+};

+ 14 - 14
packages/plugins/fixed-reactor-plugin/src/utils/transition.ts → packages/canvas-engine/fixed-layout-core/src/activities/slot/utils/transition.ts

@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: MIT
  */
 
+import { IPoint, Point } from '@flowgram.ai/utils';
 import {
   type FlowNodeTransitionData,
   FlowTransitionLineEnum,
@@ -11,17 +12,16 @@ import {
   type FlowTransitionLine,
   type FlowTransitionLabel,
 } from '@flowgram.ai/document';
-import { IPoint, Point } from '@flowgram.ai/utils';
 
-import { REACTOR_COLLAPSE_MARGIN, RENDER_REACTOR_COLLAPSE_KEY } from '../constants';
+import { SLOT_COLLAPSE_MARGIN, RENDER_SLOT_COLLAPSE_KEY } from '../constants';
 import { getDisplayFirstChildTransform } from './node';
 
 /**
- * 画 Reactor 节点虚线起点
+ * 画 Slot 节点虚线起点
  * @param iconTransform blockIcon 的 transform
  * @returns
  */
-export const getReactorChildLineStartPoint = (iconTransform?: FlowNodeTransformData): IPoint => {
+export const getSlotChildLineStartPoint = (iconTransform?: FlowNodeTransformData): IPoint => {
   if (!iconTransform) {
     return { x: 0, y: 0 };
   }
@@ -29,11 +29,11 @@ export const getReactorChildLineStartPoint = (iconTransform?: FlowNodeTransformD
   if (!iconTransform.entity.isVertical) {
     return {
       x: iconTransform?.bounds.center.x,
-      y: iconTransform?.bounds.bottom + REACTOR_COLLAPSE_MARGIN,
+      y: iconTransform?.bounds.bottom + SLOT_COLLAPSE_MARGIN,
     };
   }
   return {
-    x: iconTransform?.bounds.right + REACTOR_COLLAPSE_MARGIN,
+    x: iconTransform?.bounds.right + SLOT_COLLAPSE_MARGIN,
     y: iconTransform?.bounds.center.y,
   };
 };
@@ -109,7 +109,7 @@ export const getTransitionToPoint = (transition: FlowNodeTransitionData): IPoint
 
 /**
  * 画实现线
- * @param transition Reactor 节点的 transition
+ * @param transition Slot 节点的 transition
  * @returns
  */
 export const drawStraightLine = (transition: FlowNodeTransitionData): FlowTransitionLine[] => {
@@ -140,10 +140,10 @@ export const drawCollapseLabel = (transition: FlowNodeTransitionData): FlowTrans
   return [
     {
       type: FlowTransitionLabelEnum.CUSTOM_LABEL,
-      renderKey: RENDER_REACTOR_COLLAPSE_KEY,
-      offset: getReactorChildLineStartPoint(icon),
+      renderKey: RENDER_SLOT_COLLAPSE_KEY,
+      offset: getSlotChildLineStartPoint(icon),
       props: {
-        reactor: transition.entity.parent,
+        node: transition.entity.parent,
       },
     },
   ];
@@ -152,12 +152,12 @@ export const drawCollapseLabel = (transition: FlowNodeTransitionData): FlowTrans
 export const drawCollapseLine = (transition: FlowNodeTransitionData): FlowTransitionLine[] => [
   {
     type: FlowTransitionLineEnum.STRAIGHT_LINE,
-    from: getReactorChildLineStartPoint(transition.transform),
+    from: getSlotChildLineStartPoint(transition.transform),
     to: Point.move(
-      getReactorChildLineStartPoint(transition.transform),
+      getSlotChildLineStartPoint(transition.transform),
       transition.entity.isVertical
-        ? { x: -REACTOR_COLLAPSE_MARGIN, y: 0 }
-        : { x: 0, y: -REACTOR_COLLAPSE_MARGIN },
+        ? { x: -SLOT_COLLAPSE_MARGIN, y: 0 }
+        : { x: 0, y: -SLOT_COLLAPSE_MARGIN }
     ),
     style: {
       strokeDasharray: '5 5',

+ 5 - 1
packages/canvas-engine/fixed-layout-core/src/flow-registers.ts

@@ -40,6 +40,8 @@ import {
   MultiInputsRegistry,
   InputRegistry,
   OuputRegistry,
+  SlotRegistry,
+  SlotPortRegistry,
 } from './activities';
 
 @injectable()
@@ -72,7 +74,9 @@ export class FlowRegisters
       MultiOuputsRegistry,
       MultiInputsRegistry,
       InputRegistry,
-      OuputRegistry
+      OuputRegistry,
+      SlotRegistry,
+      SlotPortRegistry
     );
     /**
      * 注册节点数据 (ECS - Component)

+ 2 - 0
packages/canvas-engine/fixed-layout-core/src/index.ts

@@ -17,6 +17,7 @@ import {
   RootRegistry,
   InlineBlocksRegistry,
   EndRegistry,
+  SlotRegistry,
 } from './activities';
 
 export const FixedLayoutRegistries = {
@@ -32,4 +33,5 @@ export const FixedLayoutRegistries = {
   RootRegistry,
   InlineBlocksRegistry,
   EndRegistry,
+  SlotRegistry,
 };

+ 3 - 0
packages/canvas-engine/renderer/src/flow-renderer-registry.ts

@@ -29,6 +29,9 @@ export enum FlowRendererKey {
   CONTEXT_MENU_POPOVER = 'context-menu-popover', // 右键菜单
   SUB_CANVAS = 'sub-canvas', // 子画布渲染
 
+  SLOT_LABEL_RENDER = 'slot-label-render', // 插槽端口渲染
+  SLOT_COLLPASE_RENDER = 'slot-collapse-render', // 插槽收起按钮渲染
+
   // 工作流线条箭头自定义渲染
   ARROW_RENDERER = 'arrow-renderer', // 工作流线条箭头渲染器
 

+ 4 - 0
packages/materials/fixed-semi-materials/src/components/index.tsx

@@ -7,6 +7,8 @@ import { FlowRendererKey } from '@flowgram.ai/fixed-layout-editor';
 
 import { Ellipse } from '../assets';
 import TryCatchCollapse from './try-catch-collapse';
+import { SlotLabel } from './slot-label';
+import { SlotCollapse } from './slot-collapse';
 import DraggingAdder from './dragging-adder';
 import DragNode from './drag-node';
 import DragHighlightAdder from './drag-highlight-adder';
@@ -23,4 +25,6 @@ export const defaultFixedSemiMaterials = {
   [FlowRendererKey.DRAGGABLE_ADDER]: DraggingAdder,
   [FlowRendererKey.DRAG_HIGHLIGHT_ADDER]: DragHighlightAdder,
   [FlowRendererKey.DRAG_BRANCH_HIGHLIGHT_ADDER]: Ellipse,
+  [FlowRendererKey.SLOT_COLLPASE_RENDER]: SlotCollapse,
+  [FlowRendererKey.SLOT_LABEL_RENDER]: SlotLabel,
 };

+ 48 - 0
packages/materials/fixed-semi-materials/src/components/slot-collapse.tsx

@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import React, { useState } from 'react';
+
+import {
+  type FlowNodeEntity,
+  FlowNodeRenderData,
+  FlowNodeTransformData,
+} from '@flowgram.ai/fixed-layout-editor';
+
+import Collapse from './collapse';
+
+export function SlotCollapse({ node }: { node: FlowNodeEntity }) {
+  const [hoverActivated, setHoverActivated] = useState(false);
+
+  const icon = node.firstChild!;
+  const iconActivated = icon.getData(FlowNodeRenderData).activated;
+  const iconHeight = icon.getData(FlowNodeTransformData).size.height;
+
+  const isChildVisible = node.collapsed || hoverActivated || iconActivated;
+
+  return (
+    <div
+      style={{
+        width: 30,
+        height: iconHeight || 100,
+        display: 'flex',
+        alignItems: 'center',
+        justifyContent: 'center',
+      }}
+      onMouseEnter={() => setHoverActivated(true)}
+      onMouseLeave={() => setHoverActivated(false)}
+    >
+      {isChildVisible && (
+        <Collapse
+          node={node}
+          activateNode={icon}
+          collapseNode={node}
+          arrowDirection="left"
+          hoverActivated={hoverActivated}
+        />
+      )}
+    </div>
+  );
+}

+ 54 - 0
packages/materials/fixed-semi-materials/src/components/slot-label.tsx

@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import React from 'react';
+
+import { nanoid } from 'nanoid';
+import {
+  type FlowNodeEntity,
+  FlowNodeRenderData,
+  FlowDocument,
+  useService,
+} from '@flowgram.ai/fixed-layout-editor';
+import { Button } from '@douyinfe/semi-ui';
+import { IconPlus } from '@douyinfe/semi-icons';
+
+interface PropsType {
+  node: FlowNodeEntity;
+}
+
+export function SlotLabel(props: PropsType) {
+  const { node } = props;
+
+  const nodeData = node.firstChild?.getData<FlowNodeRenderData>(FlowNodeRenderData);
+  const document = useService(FlowDocument) as FlowDocument;
+
+  async function addPort() {
+    document.addNode({
+      id: nanoid(5),
+      type: 'custom',
+      parent: node,
+    });
+  }
+
+  return (
+    <div
+      style={{
+        display: 'flex',
+        background: 'var(--semi-color-bg-0)',
+      }}
+      onMouseEnter={() => nodeData?.toggleMouseEnter()}
+      onMouseLeave={() => nodeData?.toggleMouseLeave()}
+    >
+      <Button
+        onClick={() => {
+          addPort();
+        }}
+        size="small"
+        icon={<IconPlus />}
+      />
+    </div>
+  );
+}

+ 0 - 11
packages/plugins/fixed-reactor-plugin/.eslintrc.js

@@ -1,11 +0,0 @@
-/**
- * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
- * SPDX-License-Identifier: MIT
- */
-
-const { defineConfig } = require('@flowgram.ai/eslint-config');
-
-module.exports = defineConfig({
-  preset: 'web',
-  packageRoot: __dirname,
-});

+ 0 - 57
packages/plugins/fixed-reactor-plugin/package.json

@@ -1,57 +0,0 @@
-{
-  "name": "@flowgram.ai/fixed-reactor-plugin",
-  "version": "0.1.8",
-  "homepage": "https://flowgram.ai/",
-  "repository": "https://github.com/bytedance/flowgram.ai",
-  "license": "MIT",
-  "exports": {
-    "types": "./dist/index.d.ts",
-    "import": "./dist/esm/index.js",
-    "require": "./dist/index.js"
-  },
-  "main": "./dist/index.js",
-  "module": "./dist/esm/index.js",
-  "types": "./dist/index.d.ts",
-  "files": [
-    "dist"
-  ],
-  "scripts": {
-    "build": "npm run build:fast -- --dts-resolve",
-    "build:fast": "tsup src/index.ts --format cjs,esm --sourcemap --legacy-output",
-    "build:watch": "npm run build:fast -- --dts-resolve",
-    "clean": "rimraf dist",
-    "test": "exit 0",
-    "test:cov": "exit 0",
-    "ts-check": "tsc --noEmit",
-    "watch": "npm run build:fast -- --dts-resolve --watch --ignore-watch dist"
-  },
-  "dependencies": {
-    "@flowgram.ai/core": "workspace:*",
-    "@flowgram.ai/document": "workspace:*",
-    "@flowgram.ai/renderer": "workspace:*",
-    "@flowgram.ai/utils": "workspace:*",
-    "inversify": "^6.0.1",
-    "reflect-metadata": "~0.2.2",
-    "lodash": "^4.17.21"
-  },
-  "devDependencies": {
-    "@flowgram.ai/eslint-config": "workspace:*",
-    "@flowgram.ai/ts-config": "workspace:*",
-    "@types/lodash": "^4.14.137",
-    "@types/react": "^18",
-    "@types/react-dom": "^18",
-    "@vitest/coverage-v8": "^0.32.0",
-    "eslint": "^8.54.0",
-    "tsup": "^8.0.1",
-    "typescript": "^5.0.4",
-    "vitest": "^0.34.6"
-  },
-  "peerDependencies": {
-    "react": ">=16.8",
-    "react-dom": ">=16.8"
-  },
-  "publishConfig": {
-    "access": "public",
-    "registry": "https://registry.npmjs.org/"
-  }
-}

+ 0 - 15
packages/plugins/fixed-reactor-plugin/src/constants.ts

@@ -1,15 +0,0 @@
-/**
- * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
- * SPDX-License-Identifier: MIT
- */
-
-export const RENDER_REACTOR_PORT_KEY = 'render-reactor-port';
-export const RENDER_REACTOR_COLLAPSE_KEY = 'render-reactor-collapse';
-
-export const REACTOR_PORT_DISTANCE = 60;
-export const REACTOR_COLLAPSE_MARGIN = 20;
-export const REACTOR_SPACING = 32;
-
-export const REACTOR_NODE_LAST_SPACING = 10;
-
-export const REACTOR_INLINE_BLOCKS_DELTA = REACTOR_COLLAPSE_MARGIN + REACTOR_PORT_DISTANCE * 2;

+ 0 - 120
packages/plugins/fixed-reactor-plugin/src/create-fixed-reactor-plugin.ts

@@ -1,120 +0,0 @@
-/**
- * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
- * SPDX-License-Identifier: MIT
- */
-
-import React from 'react';
-
-import { bindContributions } from '@flowgram.ai/utils';
-import { FlowRendererRegistry } from '@flowgram.ai/renderer';
-import {
-  FlowNodeRegistry,
-  FlowDocument,
-  type FlowNodeEntity,
-  FlowLayoutContribution,
-} from '@flowgram.ai/document';
-import { FlowNodeMeta } from '@flowgram.ai/document';
-import { FlowNodeJSON } from '@flowgram.ai/document';
-import { definePluginCreator } from '@flowgram.ai/core';
-
-import { createReactorFromJSON } from './utils/create';
-import { Reactor } from './reactor-node';
-import { ReactorShrinkLayout } from './layout/reactor-shrink-layout';
-import { reactorPort } from './extends/reactor-port';
-import { reactorInlineBlocks } from './extends/reactor-inline-blocks';
-import { reactorIcon } from './extends/reactor-icon';
-import { RENDER_REACTOR_COLLAPSE_KEY, RENDER_REACTOR_PORT_KEY } from './constants';
-
-export type MaterialReactComponent<T = any> = (props: T) => React.ReactNode | null;
-
-export interface FixedReactorPluginOpts {
-  extendReactorRegistry?: Partial<FlowNodeRegistry>;
-  extendReactorIconRegistry?: Partial<FlowNodeMeta>;
-  extendReactorPortRegistry?: Partial<FlowNodeRegistry>;
-  extendReactorMeta?: Partial<FlowNodeMeta>;
-  extendReactorIconMeta?: Partial<FlowNodeMeta>;
-  extendReactorPortMeta?: Partial<FlowNodeMeta>;
-
-  // 是否实现 Reactor 紧凑化布局
-  shrink?: boolean;
-
-  /**
-   * 业务根据范例数据,将业务自定义的 JSON 结构转换成 reactor 模式渲染所依赖的 JSON 结构
-   * 范例数据:
-   * {
-   *  type: 'reactor',
-   *  id: 'reactor_parent',
-   *  blocks: [
-   *    {
-   *      id: 'port_LnSdK',
-   *      blocks: [{ type: 'reactor', id: 'reactor_child' }],
-   *    },
-   *    {
-   *      id: 'port_60X7U',
-   *    }
-   *  ],
-   * }
-   * @param json
-   * @returns
-   */
-  transformFlowNodeJSON?: (json?: FlowNodeJSON) => FlowNodeJSON;
-  /**
-   * 渲染 Reactor 端口
-   */
-  renderReactorPort: MaterialReactComponent<{ port: FlowNodeEntity }>;
-  /**
-   * 渲染 Reactor 展开收起按钮
-   */
-  renderReactorCollapse: MaterialReactComponent<{ reactor: FlowNodeEntity }>;
-}
-
-export const createFixedReactorPlugin = definePluginCreator<FixedReactorPluginOpts>({
-  onBind({ bind }, opts) {
-    // 如果要收缩,则注册收缩布局优化逻辑
-    if (opts?.shrink) {
-      bindContributions(bind, ReactorShrinkLayout, [FlowLayoutContribution]);
-    }
-  },
-  onInit(ctx, opts): void {
-    const { transformFlowNodeJSON } = opts;
-    const document: FlowDocument = ctx.get(FlowDocument);
-
-    const registry: FlowRendererRegistry = ctx.get<FlowRendererRegistry>(FlowRendererRegistry);
-
-    document.registerFlowNodes({
-      ...Reactor,
-      ...(transformFlowNodeJSON
-        ? {
-            onCreate: (node, json) => createReactorFromJSON(node, transformFlowNodeJSON(json)),
-          }
-        : {}),
-      ...(opts.extendReactorRegistry || {}),
-      meta: {
-        ...Reactor.meta,
-        ...(opts.extendReactorMeta || {}),
-      },
-      extendChildRegistries: [
-        {
-          ...reactorIcon,
-          meta: {
-            ...reactorIcon.meta,
-            ...(opts.extendReactorIconMeta || {}),
-          },
-          ...(opts.extendReactorIconRegistry || {}),
-        },
-        reactorInlineBlocks,
-        {
-          ...reactorPort,
-          meta: {
-            ...reactorPort.meta,
-            ...(opts.extendReactorPortMeta || {}),
-          },
-          ...(opts.extendReactorPortRegistry || {}),
-        },
-      ],
-    });
-
-    registry.registerReactComponent(RENDER_REACTOR_PORT_KEY, opts.renderReactorPort);
-    registry.registerReactComponent(RENDER_REACTOR_COLLAPSE_KEY, opts.renderReactorCollapse);
-  },
-});

+ 0 - 10
packages/plugins/fixed-reactor-plugin/src/index.ts

@@ -1,10 +0,0 @@
-/**
- * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
- * SPDX-License-Identifier: MIT
- */
-
-export { RENDER_REACTOR_PORT_KEY, RENDER_REACTOR_COLLAPSE_KEY } from './constants';
-export * from './typings';
-export { createReactorFromJSON } from './utils/create';
-export { isReactor, insideReactor } from './utils/node';
-export { createFixedReactorPlugin, FixedReactorPluginOpts } from './create-fixed-reactor-plugin';

+ 0 - 226
packages/plugins/fixed-reactor-plugin/src/layout/reactor-shrink-layout.ts

@@ -1,226 +0,0 @@
-/**
- * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
- * SPDX-License-Identifier: MIT
- */
-
-import { injectable } from 'inversify';
-import {
-  FlowLayout,
-  FlowLayoutContribution,
-  FlowNodeTransformData,
-} from '@flowgram.ai/document';
-import { FlowLayoutDefault } from '@flowgram.ai/document';
-
-import { insideReactor, isReactor } from '../utils/node';
-import { ReactorNodeType } from '../typings';
-
-interface ShrinkData {
-  // 收缩到哪个 Reactor 节点的底部空间
-  reactor: FlowNodeTransformData;
-  // Reactor 节点 Icon 右侧的坐标
-  iconEdge: number;
-  // Reactor 节点整个 Block 的底部坐标
-  blockEnd: number;
-}
-
-/**
- * Reactor 后续节点紧凑化布局
- */
-@injectable()
-export class ReactorShrinkLayout implements FlowLayoutContribution {
-  // 节点是否被缩进过
-  shrinkDataMap = new WeakMap<FlowNodeTransformData, ShrinkData>();
-
-  onAfterUpdateLocalTransform(transform: FlowNodeTransformData, layout: FlowLayout): void {
-    // 在 Reactor 内部不运行 Shrink 算法
-    if (
-      insideReactor(transform.entity) ||
-      transform.entity.flowNodeType === ReactorNodeType.ReactorPort ||
-      isReactor(transform.parent?.entity)
-    ) {
-      return;
-    }
-
-    if (FlowLayoutDefault.isVertical(layout)) {
-      // 垂直布局实现
-      this.layoutReactorShrinkVertical(transform);
-      return;
-    }
-
-    // 水平布局实现
-    this.layoutReactorShrinkInHorizontal(transform);
-  }
-
-  // 垂直布局实现
-  layoutReactorShrinkVertical(transform: FlowNodeTransformData) {
-    /**
-     * 1. Reactor 节点向上填充空白区域
-     */
-    if (isReactor(transform?.entity)) {
-      const icon = transform.firstChild;
-      const iconRight = icon?.localBounds.right || 0;
-
-      const reactorTop = transform?.localBounds.top || 0;
-      const topEmptySpace = icon?.localBounds.top || 0;
-
-      // Reactor 偏移不会超过该边界
-      let edgeY = reactorTop;
-
-      // 向上一直找到 Block 的右侧大于 icon 的右侧区域
-      let curr = transform.pre;
-      while (curr && curr.localBounds.right <= iconRight) {
-        edgeY = curr.localBounds.top;
-        curr = curr.pre;
-      }
-
-      const reactorDelta = Math.min(reactorTop - edgeY, topEmptySpace);
-
-      transform.transform.update({
-        position: {
-          x: transform.transform.position.x,
-          y: transform.transform.position.y - reactorDelta,
-        },
-      });
-
-      transform.entity.clearMemoLocal();
-      return;
-    }
-
-    const pre = transform.pre;
-
-    if (!pre) {
-      this.shrinkDataMap.delete(transform);
-      return;
-    }
-
-    /**
-     * 2. Reactor 节点后续节点向上填充空白
-     */
-    if (isReactor(pre?.entity)) {
-      const pre = transform.pre;
-      const icon = pre.firstChild;
-      const iconRight = icon?.localBounds.right || 0;
-      const y = (pre.localBounds?.top || 0) + (icon?.localBounds.bottom || 0) + (pre.spacing || 0);
-
-      if (transform.localBounds?.right > iconRight) {
-        this.shrinkDataMap.delete(transform);
-        // 空间不够,不进行收缩操作
-        return;
-      }
-
-      transform.transform.update({
-        position: {
-          x: transform.transform.position.x,
-          y,
-        },
-      });
-
-      this.shrinkDataMap.set(transform, {
-        reactor: pre!,
-        iconEdge: iconRight,
-        blockEnd: pre?.localBounds.bottom! + (pre.spacing || 0),
-      });
-
-      transform.entity.clearMemoLocal();
-
-      return;
-    }
-
-    /**
-     * 3. 前序节点被 shrink 到 reactor 内部时,且当前节点依旧在 Reactor shrink 的范围内
-     */
-    const preShrinkData = this.shrinkDataMap.get(pre);
-    if (preShrinkData && transform.localBounds.top <= preShrinkData.blockEnd) {
-      if (transform.localBounds.right > preShrinkData.iconEdge) {
-        // 空间不够,移到 shrink 范围外
-        transform.transform.update({
-          position: {
-            x: transform.transform.position.x,
-            y: preShrinkData.blockEnd,
-          },
-        });
-        transform.entity.clearMemoLocal();
-        this.shrinkDataMap.delete(transform);
-      } else {
-        // 当前节点依旧在 shrink 范围内
-        this.shrinkDataMap.set(transform, preShrinkData);
-      }
-      return;
-    }
-
-    // 不在 shrink 范围内的节点删除 shrink 数据
-    this.shrinkDataMap.delete(transform);
-
-    return;
-  }
-
-  // 水平布局实现
-  layoutReactorShrinkInHorizontal(transform: FlowNodeTransformData) {
-    const pre = transform.pre;
-
-    if (!pre) {
-      this.shrinkDataMap.delete(transform);
-      return;
-    }
-
-    /**
-     * 1. Reactor 节点后续节点填充空白
-     */
-    if (isReactor(pre?.entity)) {
-      const pre = transform.pre;
-      const icon = pre.firstChild;
-      const iconBottom = icon?.localBounds.bottom || 0;
-      const x = (pre.localBounds?.left || 0) + (icon?.localBounds.right || 0) + (pre.spacing || 0);
-
-      if (transform.localBounds?.bottom > iconBottom) {
-        this.shrinkDataMap.delete(transform);
-        // 空间不够,不进行收缩操作
-        return;
-      }
-
-      transform.transform.update({
-        position: {
-          x: x,
-          y: transform.transform.position.y,
-        },
-      });
-
-      this.shrinkDataMap.set(transform, {
-        reactor: pre!,
-        iconEdge: iconBottom,
-        blockEnd: pre?.localBounds.right! + (pre.spacing || 0),
-      });
-
-      transform.entity.clearMemoLocal();
-
-      return;
-    }
-
-    /**
-     * 2. 前序节点被 shrink 到 reactor 内部时,且当前节点依旧在 Reactor shrink 的范围内
-     */
-    const preShrinkData = this.shrinkDataMap.get(pre);
-    if (preShrinkData && transform.localBounds.left <= preShrinkData.blockEnd) {
-      if (transform.localBounds.bottom > preShrinkData.iconEdge) {
-        // 空间不够,移到 shrink 范围外
-        transform.transform.update({
-          position: {
-            x: preShrinkData.blockEnd,
-            y: transform.transform.position.y,
-          },
-        });
-        transform.entity.clearMemoLocal();
-        this.shrinkDataMap.delete(transform);
-      } else {
-        // 当前节点依旧在 shrink 范围内
-        this.shrinkDataMap.set(transform, preShrinkData);
-      }
-      return;
-    }
-
-    // 不在 shrink 范围内的节点删除 shrink 数据
-    this.shrinkDataMap.delete(transform);
-
-    return;
-  }
-}

+ 0 - 11
packages/plugins/fixed-reactor-plugin/src/typings.ts

@@ -1,11 +0,0 @@
-/**
- * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
- * SPDX-License-Identifier: MIT
- */
-
-export enum ReactorNodeType {
-  Reactor = 'reactor',
-  ReactorInlineBlocks = 'reactorInlineBlocks',
-  ReactorPort = 'reactorPort',
-  ReactorPortInlineBlocks = 'reactorPortInlineBlocks',
-}

+ 0 - 45
packages/plugins/fixed-reactor-plugin/src/utils/node.ts

@@ -1,45 +0,0 @@
-/**
- * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
- * SPDX-License-Identifier: MIT
- */
-
-import { FlowNodeEntity } from '@flowgram.ai/document';
-import { FlowNodeTransformData } from '@flowgram.ai/document';
-
-import { ReactorNodeType } from '../typings';
-
-/**
- * Reactor 节点是否可下钻,看 inlineBlocks 是否有子节点
- * @param reactor Reactor 节点
- */
-export const canReactorDrilldown = (reactor: FlowNodeEntity): boolean =>
-  !!reactor?.lastCollapsedChild?.collapsedChildren.length;
-
-/**
- * 是否是 reactor 内部
- * @param entity
- * @returns
- */
-export const insideReactor = (entity?: FlowNodeEntity): boolean =>
-  entity?.parent?.flowNodeType === ReactorNodeType.ReactorPort;
-
-/**
- * 判断是否是 Reactor 节点
- * @param entity
- * @returns
- */
-export const isReactor = (entity?: FlowNodeEntity): boolean => !!entity?.getNodeMeta().isReactor;
-
-/**
- * 获取在页面上实际渲染的第一个 Child 节点
- * @param node
- */
-export const getDisplayFirstChildTransform = (
-  transform: FlowNodeTransformData,
-): FlowNodeTransformData => {
-  if (transform.firstChild) {
-    return getDisplayFirstChildTransform(transform.firstChild);
-  }
-
-  return transform;
-};

+ 0 - 7
packages/plugins/fixed-reactor-plugin/tsconfig.json

@@ -1,7 +0,0 @@
-{
-  "extends": "@flowgram.ai/ts-config/tsconfig.flow.path.json",
-  "compilerOptions": {
-  },
-  "include": ["./src"],
-  "exclude": ["node_modules"]
-}

+ 0 - 31
packages/plugins/fixed-reactor-plugin/vitest.config.ts

@@ -1,31 +0,0 @@
-/**
- * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
- * SPDX-License-Identifier: MIT
- */
-
-const path = require('path');
-import { defineConfig } from 'vitest/config';
-
-export default defineConfig({
-  build: {
-    commonjsOptions: {
-      transformMixedEsModules: true,
-    },
-  },
-  test: {
-    globals: true,
-    mockReset: false,
-    environment: 'jsdom',
-    setupFiles: [path.resolve(__dirname, './vitest.setup.ts')],
-    include: ['**/?(*.){test,spec}.?(c|m)[jt]s?(x)'],
-    exclude: [
-      '**/__mocks__**',
-      '**/node_modules/**',
-      '**/dist/**',
-      '**/lib/**', // lib 编译结果忽略掉
-      '**/cypress/**',
-      '**/.{idea,git,cache,output,temp}/**',
-      '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*',
-    ],
-  },
-});

+ 0 - 9
rush.json

@@ -572,15 +572,6 @@
                 "team-flow"
             ]
         },
-        {
-            "packageName": "@flowgram.ai/fixed-reactor-plugin",
-            "projectFolder": "packages/plugins/fixed-reactor-plugin",
-            "versionPolicyName": "publishPolicy",
-            "tags": [
-                "level-1",
-                "team-flow"
-            ]
-        },
         {
             "packageName": "@flowgram.ai/form",
             "projectFolder": "packages/node-engine/form",